Captured Surface Control

Draft Community Group Report

Latest published version:
https://www.w3.org/captured-surface-control/
Latest editor's draft:
https://screen-share.github.io/captured-surface-control/
Editor:
Elad Alon ( Google )
Feedback:
GitHub screen-share/captured-surface-control ( pull requests , new issue , open issues )

Abstract

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.

Status of This Document

This is a preview

Do not attempt to implement this version of the specification. Do not reference this version as authoritative in any way. Instead, see https://screen-share.github.io/captured-surface-control/ for the Editor's draft.

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.

1. Background

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:

  1. Users can't simultaneously interact with the captured application and see the videos of remote users.
  2. Users are burdened by the need to repeatedly switch between the video-conferencing application and the captured surface.
  3. Users are limited in their ability to see and interact with controls exposed by the video-conferencing application while they are interacting with the captured surface. A non-comprehensive list of examples of such controls includes - embedded chat applications, emoji reactions, "knock-ins" by users asking to join the call, and multimedia controls.

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.

2. Permissions Policy Integration

This specification defines a policy-controlled feature identified by the string "captured-surface-control" . Its default allowlist is "self" .

Note

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 captureWheel forwardGestures ) are gated by the "captured-surface-control" permissions policy.

3. Zoom

3.1 Definition of Zoom

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.

3.2 Permitted Event Types for setZoomLevel()

We define the permitted event types for setZoomLevel as a set composed of the following event types :

3.3 Zoom-control APIs

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:

  1. If this is not actively capturing , throw an " InvalidStateError " DOMException .
  2. Let surfaceType be this . [[DisplaySurfaceType]] .
  3. If surfaceType is not a supported display surface type , throw a " NotSupportedError " DOMException .
  4. Return a monotonically increasing sequence containing all of the values in the supported zoom levels for surfaceType .
getZoomLevel()

This method allows applications to discover the captured display surface 's zoom level .

When invoked, the user agent MUST run the following steps:

  1. If this is not actively capturing , throw an " InvalidStateError " DOMException .
  2. If this . [[DisplaySurfaceType]] is not a supported display surface type , throw a " NotSupportedError " DOMException .
  3. Return this . [[Source]] 's zoom level .
setZoomLevel()

This method allows applications to set the captured display surface 's zoom level .

When invoked, the user agent MUST run the following steps:

  1. If this is not actively capturing , return a promise rejected with a DOMException object whose name attribute has the value InvalidStateError .
  2. Let surfaceType be this . [[DisplaySurfaceType]] .
  3. If surfaceType is not a supported display surface type , return a promise rejected with a DOMException object whose name attribute has the value NotSupportedError .
  4. 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:

    1. Let currentEvent be Window . event .
    2. If currentEvent is undefined , return a promise rejected with a DOMException object whose name attribute has the value InvalidStateError .
    3. If currentEvent . isTrusted is false , return a promise rejected with a DOMException object whose name attribute has the value InvalidStateError .
    4. If currentEvent . type is not in permitted event types for setZoomLevel , return a promise rejected with a DOMException object whose name attribute has the value InvalidStateError .
      Note

      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.

  5. Let targetZoomLevel be the method's first argument.
  6. If targetZoomLevel is not included in the user agent's set of supported zoom levels for surfaceType , return a promise rejected with a DOMException object whose name attribute has the value InvalidStateError .
  7. Let P be a new Promise .
  8. Run the following steps in parallel :

    1. Request permission to use a 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.
    2. Set this . [[Source]] 's zoom level to targetZoomLevel .
    3. Resolve P .
  9. Return P .
oncapturedzoomlevelchange

The user agent MUST fire a blank event on this EventHandler whenever this . [[Source]] 's zoom level changes.

Note

Examples of causes include:

  • The user interacted with the user agent to change the zoom level of a captured tab.
  • The capturing application called setZoomLevel () .
  • The user changed the shared display surface , choosing one which has a different zoom level .

4. Scroll Gesture Forwarding

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 .

Note

Future support for such gestures as "click" is NOT envisioned.

4.1 Scrolling ForwardedGestures

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.

4.2 Gesture-forwarding APIs

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
[[CaptureWheelElement]] null [[CaptureWheelEventListener]] [[GestureForwardingElementsMap]] null Empty map
captureWheel() 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:

  1. If this is not actively capturing , return a promise rejected with a DOMException object whose name attribute has the value InvalidStateError .
  2. Let surfaceType be this . [[DisplaySurfaceType]] .
  3. If surfaceType is not a supported display surface type , return a promise rejected with a DOMException object whose name attribute has the value NotSupportedError .
  4. Let element be the method's first argument.
  5. Let gestures be the method's second argument.
  6. Let P be a new Promise .
  7. Run the following steps in parallel :
    1. Get the current permission state of "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 .
      Note

      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 ", captureWheel forwardGestures () may be called immediately after getDisplayMedia () resolves, even if the transient activation that permitted the call to captureWheel forwardGestures () has since expired.

    2. Request permission to use a 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.
    3. If Let map be this . [[CaptureWheelElement]] [[GestureForwardingElementsMap]] is not null , .
    4. If map [ element ] exists, remove an event listener with: this . [[CaptureWheelElement]] with element as eventTarget . this . [[CaptureWheelEventListener]] and map [ element ] as listener .
    5. Set this . [[CaptureWheelElement]] [[GestureForwardingElement]] to element .
    6. Set this . [[CaptureWheelEventListener]] [[GestureForwardingEventListener]] to null .
    7. If this . [[CaptureWheelElement]] [[GestureForwardingElement]] is null , resolve P and abort these steps.
    8. Set this . [[CaptureWheelEventListener]] [[GestureForwardingEventListener]] to an event listener defined as follows:
      type
      wheel
      callback
      The result of creating a new Web IDL EventListener 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 .
    9. Add an event listener with:

    10. Resolve P .
  8. Return P .

5. Subroutines

5.1 Subroutine: Actively capturing

To determine if a CaptureController controller is actively capturing , run the following steps:

  1. Let source be controller . [[Source]] .
  2. If source is null , return false .
  3. If source has been stopped , return false .
  4. Return true .

5.2 Subroutine: Supported display surface type

To determine if a display surface surfaceType is supported display surface type , run the following steps:

  1. If surfaceType is browser , return true .
  2. Return false .
Note

Whether window should be supported is under discussion.

5.3 Subroutine: Forward wheel event

The forward wheel event algorithm takes a CaptureController controller and a WheelEvent event , and runs the following steps:

  1. If controller is not actively capturing , abort these steps.
  2. Let surfaceType be controller . [[DisplaySurfaceType]] .
  3. If surfaceType is not a supported display surface type , abort these steps.
  4. Run the following steps in parallel :
    1. Get the current permission state of "captured-surface-control" . If the result is NOT " granted ", abort these steps.
    2. If event . isTrusted is false , abort these steps.
    3. Let [ scaledX , scaledY ] be the result of the scale element coordinates algorithm on [ event . offsetX , event . offsetY ] and this . [[CaptureWheelElement]] [[GestureForwardingElement]] .
    4. Fire an event named "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.

5.4 Subroutine: Scale element coordinates

The scale element coordinates algorithm takes double coordinates [ x , y ] and a CaptureController controller , and run the following steps:

  1. Let scaleFactorX be ( x / controller . [[CaptureWheelElement]] [[GestureForwardingElement]] . getBoundingClientRect () . width ) .
  2. Let scaleFactorX be ( x / controller . [[CaptureWheelElement]] [[GestureForwardingElement]] . getBoundingClientRect () . height ) .
  3. Let surfaceWidth be controller . [[Source]] 's viewport's width.
  4. Let surfaceHeight be controller . [[Source]] 's viewport's height.
  5. Let scaledX be ( scaleFactorX * surfaceWidth ) .
  6. Let scaledY be ( scaleFactorY * surfaceHeight ) .
  7. Return [ scaledX , scaledY ].
Note

This subroutine assumes that controller is actively capturing .

6. Privacy and Security Considerations

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:

6.1 Zoom-setting: Limitation to specific interactions

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.

6.2 Scrolling: Limitation to specific interactions

The shape of captureWheel 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.

6.3 Limiting element types

This specification does not limit the type of Element for which either setZoomLevel () or captureWheel forwardGestures () work. Such a limitation would accomplish nothing, because malicious applications could always overlay transparent permitted Element types on top of visible non-permitted Element s, thereby bypassing this restriction.

The limitation of interaction types is sufficient. This is accomplished by captureWheel forwardGestures () through its shape, and by setZoomLevel () through its gating on event types .

7. Conformance

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.

A. References

A.1 Normative references

[cssom-view]
CSSOM View Module . Simon Pieters. W3C. 17 March 2016. W3C Working Draft. URL: https://www.w3.org/TR/cssom-view-1/
[DOM]
DOM Standard . Anne van Kesteren. WHATWG. Living Standard. URL: https://dom.spec.whatwg.org/
[geometry-1]
Geometry Interfaces Module Level 1 . Simon Pieters; Chris Harrelson. W3C. 4 December 2018. W3C Candidate Recommendation. URL: https://www.w3.org/TR/geometry-1/
[GETUSERMEDIA]
Media Capture and Streams . Cullen Jennings; Bernard Aboba; Jan-Ivar Bruaroey; Henrik Boström; youenn fablet. W3C. 3 October 2024. W3C Candidate Recommendation. URL: https://www.w3.org/TR/mediacapture-streams/
[html]
HTML Standard . Anne van Kesteren; Domenic Denicola; Dominic Farolino; Ian Hickson; Philip Jägenstedt; Simon Pieters. WHATWG. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[infra]
Infra Standard . Anne van Kesteren; Domenic Denicola. WHATWG. Living Standard. URL: https://infra.spec.whatwg.org/
[permissions]
Permissions . Marcos Caceres; Mike Taylor. W3C. 19 March 2024. W3C Working Draft. URL: https://www.w3.org/TR/permissions/
[permissions-policy]
Permissions Policy . Ian Clelland. W3C. 25 September 2024. W3C Working Draft. URL: https://www.w3.org/TR/permissions-policy-1/
[RFC2119]
Key words for use in RFCs to Indicate Requirement Levels . S. Bradner. IETF. March 1997. Best Current Practice. URL: https://www.rfc-editor.org/rfc/rfc2119
[RFC8174]
Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words . B. Leiba. IETF. May 2017. Best Current Practice. URL: https://www.rfc-editor.org/rfc/rfc8174
[screen-capture]
Screen Capture . Jan-Ivar Bruaroey; Elad Alon. W3C. 27 June 2024. W3C Working Draft. URL: https://www.w3.org/TR/screen-capture/
[uievents]
UI Events . Gary Kacmarcik; Travis Leithead. W3C. 7 September 2024. W3C Working Draft. URL: https://www.w3.org/TR/uievents/
[WEBIDL]
Web IDL Standard . Edgar Chen; Timothy Gu. WHATWG. Living Standard. URL: https://webidl.spec.whatwg.org/