Copyright © 2024 the Contributors to the Captured Surface Control Specification, published by the Screen Capture Community Group under the W3C Community Contributor License Agreement (CLA). A human-readable summary is available.
Consider a Web application capturer which has used getDisplayMedia() to
start capturing another display surface, capturee. This specification introduces a set
of APIs that allow capturer the following new capabilities:
Initially, these are only specified for captured display surfaces of type browser, but an extension to window is considered.
This specification was published by the Screen Capture Community Group. It is not a W3C Standard nor is it on the W3C Standards Track. Please note that under the W3C Community Contributor License Agreement (CLA) there is a limited opt-out and other conditions apply. Learn more about W3C Community and Business Groups.
GitHub Issues are preferred for discussion of this specification.
Nearly all video-conferencing Web applications offer their users the ability to share display surfaces - typically a browser tab (browser), a native app's window (window), or an entire screen (monitor).
Many of these applications also show the local user a "preview tile" with a video of the captured display surface.
All these applications suffer from one key drawback - if the user wishes to interact with a captured display surface, the user must first switch to that surface, taking them away from the video-conferencing application. This presents a few issues:
It bears mentioning that Document Picture-in-Picture goes a long way towards addressing some of these issues. However, it not always a suitable solution, as not all use cases are adequately addressed by a floating window which will often be small, which obscures arbitrary other content on the screen, and whose size and positioning must be manually controlled by the user.
This specification defines a policy-controlled feature identified by the string
"captured-surface-control". Its default allowlist is "self".
The API surfaces introduced by this specification can be categorized as either read-access
or write-access. Note that only the write-access APIs (setZoomLevel
and forwardGestures) are gated by the
"captured-surface-control"
permissions policy.
We define a concept of an integer "zoom level" that can be applied to display surfaces of any type, and which is independent of the user agent and the platform. It is expected that in the case of browser display surfaces, this concept will match the concept of zoom level that user agents typically exposed to the user.
For a given display surface of type surfaceType, we define the user agent's set of supported zoom levels for surfaceType as a non-empty set of integers including at least the default zoom level (100), and not including any integers lesser than 1.
We define the permitted event types for setZoomLevel as a set composed of the following event types:
WebIDLpartial interface CaptureController {
sequence<long> getSupportedZoomLevels();
long getZoomLevel();
Promise<undefined> setZoomLevel(long zoomLevel);
attribute EventHandler oncapturedzoomlevelchange;
};
getSupportedZoomLevels()This method allows applications to discover the set of zoom levels supported by the user agent.
When invoked, the user agent MUST run the following steps:
InvalidStateError" DOMException.
NotSupportedError" DOMException.
getZoomLevel()This method allows applications to discover the captured display surface's zoom level.
When invoked, the user agent MUST run the following steps:
InvalidStateError" DOMException.
NotSupportedError" DOMException.
setZoomLevel()This method allows applications to set the captured display surface's zoom level.
When invoked, the user agent MUST run the following steps:
DOMException object whose name attribute has the value
InvalidStateError.
DOMException object whose name
attribute has the value NotSupportedError.
Ensure that the code is running from within the context of an event handler which was triggered by the browser agent firing a trusted event, triggered by the user interacting with the user agent. To do so, run the following steps:
Window.event.undefined, return a promise rejected with a
DOMException object whose name attribute has the value
InvalidStateError.
isTrusted is false, return a promise
rejected with a DOMException object whose name
attribute has the value InvalidStateError.
type is not in
permitted event types for setZoomLevel, return a promise
rejected with a DOMException object whose name
attribute has the value InvalidStateError.
It follows from these steps that setZoomLevel() is
only callable with transient activation, because
permitted event types for setZoomLevel
only contains event types that confer
this activation.
In fact, our API shape implies a stronger guarantee - whereas transient activation persists for several seconds after the user action, the API
shape here limits setZoomLevel() to being called
immediately following the user's action.
DOMException object whose name attribute has the value
InvalidStateError.
Promise.Run the following steps in parallel:
PermissionDescriptor with its
name member set to
"captured-surface-control". If the result of the request is
"denied", reject P with a new DOMException object
whose name is NotAllowedError and abort these steps.
oncapturedzoomlevelchange
The user agent MUST fire a blank event on this EventHandler whenever this.[[Source]]'s zoom level changes.
Examples of causes include:
setZoomLevel().
We introuce APIs to allow forwarding of specific gestures from the capturing application to
the captured application. The set of gestures comprises those gestures that are considered
safe to forward, subject to the "captured-surface-control" PermissionsPolicy.
Future support for such gestures as "click" is NOT envisioned.
WebIDLdictionary ForwardedGestures {
boolean wheel = false;
};
wheel
Whether the application calling forwardGestures() wants
wheel events to be
forwarded from the indicated element to the captured surface.
WebIDLpartial interface CaptureController {
constructor();
Promise<undefined> forwardGestures(
HTMLElement element,
optional ForwardedGestures gestures = {});
};
constructor
CaptureController's
constructor is
extended to also define and initialize the following internal slots:
| Internal Slot | Initial value |
|---|---|
| [[GestureForwardingElementsMap]] | Empty map |
forwardGestures()
This method allows applications to automatically forward gesture events (e.g.
wheel events) from an
HTMLElement to the viewport of a captured display surface.
When invoked, the user agent MUST run the following steps:
DOMException object whose name attribute has the value
InvalidStateError.
DOMException object whose name
attribute has the value NotSupportedError.
Promise."captured-surface-control". If the
result is NOT "granted", and the relevant global object
does NOT have transient activation, return a promise rejected with
a DOMException object whose name attribute has the value
InvalidStateError.
This step ensures that on the one hand, permission prompts are not be shown
without transient activation, while on the one hand, if the permission is
already "granted", forwardGestures()
may be called immediately after getDisplayMedia() resolves,
even if the transient activation that permitted the call to
forwardGestures() has since expired.
PermissionDescriptor with its
name member set to
"captured-surface-control". If the result of the request is
"denied", reject P with a new DOMException object
whose name is NotAllowedError and abort these steps.
[[GestureForwardingElementsMap]].
[[GestureForwardingElement]] to element.
[[GestureForwardingEventListener]] to
null.
[[GestureForwardingElement]] is
null, resolve P and abort these steps.
[[GestureForwardingEventListener]] to an
event listener defined as follows:
wheelEventListener instance representing a
reference to a function of one argument of type Event event. This
function executes the forward wheel event algorithm given this and
event.
Add an event listener with:
[[GestureForwardingElement]] as eventTarget.
[[GestureForwardingEventListener]] as
listener.
To determine if a CaptureController controller is
actively capturing, run the following steps:
null, return false.false.
true.To determine if a display surface surfaceType is supported display surface type, run the following steps:
true.false.Whether window should be supported is under discussion.
The forward wheel event algorithm takes a CaptureController controller
and a WheelEvent event, and runs the following steps:
"captured-surface-control". If the
result is NOT "granted", abort these steps.
isTrusted is false, abort these steps.offsetX, event.offsetY] and
this.[[GestureForwardingElement]].
"wheel" using WheelEvent with the x
attribute initialized to scaledX, the y attribute initialized to
scaledY, the deltaX attribute initialized to
event.deltaX and the deltaY attribute initialized to
event.deltaY, at controller.[[Source]]'s viewport.
The scale element coordinates algorithm takes double coordinates [x, y]
and a CaptureController controller, and run the following steps:
(x /
controller.[[GestureForwardingElement]].getBoundingClientRect().width).
(x /
controller.[[GestureForwardingElement]].getBoundingClientRect().height).
(scaleFactorX * surfaceWidth).
(scaleFactorY * surfaceHeight).
This subroutine assumes that controller is actively capturing.
The API surfaces introduced in this specification allow a capturing application limited control over a captured application. These APIs allow the capturing application to gain access to additional pixels in the captured application. This specification employs multiple means to ensure that new capabilities are used in accordance with the user's intentions. Among these means:
PermissionsPolicy called "captured-surface-control" is used.forwardGestures() is designed such that only the user's scrolling
over an Element can trigger scrolling in the captured application. This API shape
ensures that the capturing application can only forward wheel events to the captured application at the time when the user agent dispatches the
trusted wheel event on the capturing application itself.
setZoomLevel() is only callable from event handlers of specific
event types - the
permitted event types for setZoomLevel. These are events dispatched directly by the
user agent, triggered by user interaction. This specification intentionally excludes from
this set such events as "mousemove", which users are
liable to trigger inadvertently.
The shape of forwardGestures() is intentionally chosen to limit the
capturing application's control. The application designates a specific element which, when
the user scrolls over it, the corresponding wheel events are forwarded to the captured
application. This is in intentional contrast to a prior API shape,
sendWheel(), whereby the capturing application could instigate scrolling of
the captured application at any time.
This specification does not limit the type of Element for which either
setZoomLevel() or forwardGestures() work. Such
a limitation would accomplish nothing, because malicious applications could always overlay
transparent permitted Element types on top of visible non-permitted Elements,
thereby bypassing this restriction.
The limitation of interaction types is sufficient. This is accomplished by
forwardGestures() through its shape, and by
setZoomLevel() through its gating on
event types.
As well as sections marked as non-normative, all authoring guidelines, diagrams, examples, and notes in this specification are non-normative. Everything else in this specification is normative.
The key word MUST in this document is to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in: