New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Define CaptureController.setFocusBehavior() #240
Changes from all commits
9143a5c
9d3d932
caa0c98
e33458c
ab9a59a
56eb1d2
175ad5e
1771128
e5022db
9498e58
d35b1e8
a25e292
29b6f67
cb90ee1
cba6d19
8eb6140
ae0bb29
c111550
a679e42
4c453dd
30d9b4f
626770b
2f93cb9
76e917c
f8fb1bd
1a9b4d1
b45cc3e
46cccef
872f8a0
ad4ab9f
030f18b
fdab181
4105b52
2acb011
b36a47e
f427948
16ade48
075115d
333b0f7
9b1dfb1
aa8f42a
2d0256b
8259423
33f356e
12d9854
f95635f
43d8346
e5ff0a6
6d185b7
606a8c4
430378b
2aeccb8
f759c31
4ddd9ad
ec84ef6
fd547b0
915875d
9f2eebf
1926f0b
7cb1236
32d1a96
43ad5b7
8d1a8c0
b5a6f8f
ce4a025
242391e
d0d2ce8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -201,16 +201,6 @@ <h2> | |
method is called, the user agent MUST run the following | ||
steps:</p> | ||
<ol> | ||
<li> | ||
<p>If the [=relevant global object=] of [=this=] does not have | ||
[=transient activation=], return a promise <a>rejected</a> | ||
with a {{DOMException}} object whose {{DOMException/name}} | ||
attribute has the value {{InvalidStateError}}.</p> | ||
</li> | ||
<li> | ||
<p>Let <var>options</var> be the method's first | ||
argument.</p> | ||
</li> | ||
<li> | ||
<p> | ||
Let <var>controller</var> be <var>options</var>.<code>controller</code> if present, | ||
|
@@ -219,20 +209,36 @@ <h2> | |
</li> | ||
<li> | ||
<p> | ||
If controller is not <code>null</code>, run the following steps: | ||
If <var>controller</var> is not <code>null</code>, run the following steps: | ||
</p> | ||
<ol> | ||
<li> | ||
If <var>controller</var>.{{CaptureController/[[isBound]]}} | ||
<ol> | ||
<li> | ||
<p> | ||
If <var>controller</var>.{{CaptureController/[[IsBound]]}} | ||
is <code>true</code>, return a promise [=reject|rejected=] with | ||
a {{DOMException}} object whose {{DOMException/name}} attribute has | ||
the value {{InvalidStateError}}. | ||
</li> | ||
<li> | ||
Set <var>controller</var>.{{CaptureController/[[isBound]]}} to <code>true</code>. | ||
</li> | ||
</p> | ||
</li> | ||
<li> | ||
<p> | ||
Set <var>controller</var>.{{CaptureController/[[IsBound]]}} to <code>true</code>. | ||
eladalon1983 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
</p> | ||
</li> | ||
</ol> | ||
</li> | ||
<li> | ||
<p> | ||
If the [=relevant global object=] of [=this=] does not have | ||
[=transient activation=], return a promise <a>rejected</a> | ||
with a {{DOMException}} object whose {{DOMException/name}} | ||
attribute has the value {{InvalidStateError}}. | ||
</p> | ||
</li> | ||
<li> | ||
<p>Let <var>options</var> be the method's first | ||
argument.</p> | ||
</li> | ||
<li> | ||
<p>Let <var>constraints</var> be | ||
<code>[</code><var>options</var>.<code>audio</code>, | ||
|
@@ -293,7 +299,7 @@ <h2> | |
<p>Let <var>p</var> be a new promise.</p> | ||
</li> | ||
<li> | ||
<p>Run the following steps in parallel:</p> | ||
<p>Run the following steps [=in parallel=]:</p> | ||
<ol> | ||
<li> | ||
<p>For each media type <var>T</var> in | ||
|
@@ -383,9 +389,39 @@ <h2> | |
<var>message</var>)</code>.</p> | ||
</li> | ||
<li> | ||
<p><a>Resolve</a> <var>p</var> with <var>stream</var> and | ||
abort these steps. This invocation of {{MediaDevices/getDisplayMedia()}} | ||
is now considered to have produced a new <dfn>capture-session</dfn></p> | ||
<p> | ||
This invocation of {{MediaDevices/getDisplayMedia()}} is now | ||
considered to have produced a new <dfn>capture-session</dfn>. | ||
</p> | ||
</li> | ||
<li> | ||
If <var>controller</var> is not <code>null</code>, run the following steps: | ||
<ol> | ||
<li> | ||
<p> | ||
Set <var>controller</var>.{{CaptureController/[[Source]]}} | ||
to <var>stream</var>'s video track's <a data-cite="GETUSERMEDIA#dfn-source-0">[[\Source]]</a>. | ||
</p> | ||
</li> | ||
<li> | ||
<p> | ||
Set <var>controller</var>.{{CaptureController/[[DisplaySurfaceType]]}} to the | ||
to <var>stream</var>'s video track's {{DisplayCaptureSurfaceType}}. | ||
</p> | ||
</li> | ||
<li> | ||
<p> | ||
Queue a task to run the [=finalize focus decision algorithm=] | ||
on <var>controller</var>. | ||
</p> | ||
</li> | ||
</ol> | ||
</li> | ||
<li> | ||
<p> | ||
<a>Resolve</a> <var>p</var> with <var>stream</var> and | ||
abort these steps. | ||
</p> | ||
</li> | ||
<li> | ||
<p><em>Permission Failure</em>: [=Reject=] | ||
|
@@ -399,6 +435,23 @@ <h2> | |
<p>Return <var>p</var>.</p> | ||
</li> | ||
</ol> | ||
<p> | ||
When the top-level document loses focus, run the following steps on all | ||
{{CaptureController}} objects in that document and in documents of its nested | ||
[=browsing contexts=]: | ||
</p> | ||
<ol> | ||
<li> | ||
<p> | ||
If {{CaptureController/[[Source]]}} is <code>undefined</code>, abort these steps. | ||
</p> | ||
</li> | ||
<li> | ||
<p> | ||
Set {{CaptureController/[[FocusChangeDisabled]]}} to <code>true</code>. | ||
</p> | ||
</li> | ||
</ol> | ||
<p> | ||
The <a>user agent</a> MUST NOT capture content that's behind a | ||
partially transparent captured <a>display surface</a>. | ||
|
@@ -743,6 +796,46 @@ <h2> | |
</p> | ||
</div> | ||
</section> | ||
<section> | ||
<h2><dfn>CaptureStartFocusBehavior</dfn></h2> | ||
<p> | ||
Describes whether an application invoking {{CaptureController/setFocusBehavior()}} | ||
would like the user agent to focus the [=display surface=] associated with | ||
that {{CaptureController}}'s [=capture-session=]. | ||
</p> | ||
<pre class="idl"> | ||
enum CaptureStartFocusBehavior { | ||
"focus-captured-surface", | ||
"no-focus-change" | ||
}; | ||
</pre> | ||
<table data-dfn-for="CaptureStartFocusBehavior" class="simple"> | ||
<tbody> | ||
<tr> | ||
<th colspan="2"> | ||
Enumeration description | ||
</th> | ||
</tr> | ||
<tr> | ||
<td style="white-space:nowrap"> | ||
<dfn id="idl-def-CaptureStartFocusBehavior.focus-captured-surface">focus-captured-surface</dfn> | ||
</td> | ||
<td> | ||
The application prefers that the [=display surface=] associated with | ||
this {{CaptureController}}'s [=capture-session=] be focused. | ||
</td> | ||
</tr> | ||
<tr> | ||
<td> | ||
<dfn id="idl-def-CaptureStartFocusBehavior.no-focus-change">no-focus-change</dfn> | ||
</td> | ||
<td> | ||
The application prefers that the user agent not change focus. | ||
</td> | ||
</tr> | ||
</tbody> | ||
</table> | ||
</section> | ||
<section> | ||
<h2><dfn>CaptureController</dfn></h2> | ||
<p> | ||
|
@@ -761,20 +854,191 @@ <h2><dfn>CaptureController</dfn></h2> | |
[Exposed=Window, SecureContext] | ||
interface CaptureController { | ||
constructor(); | ||
// TODO: Add setFocusBehavior() in a separate PR. | ||
undefined setFocusBehavior(CaptureStartFocusBehavior focusBehavior); | ||
}; | ||
</pre> | ||
<dl data-link-for="CaptureController" data-dfn-for="CaptureController" class="methods"> | ||
<dt> | ||
<dfn>constructor</dfn> | ||
</dt> | ||
<dd> | ||
eladalon1983 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Create a new {{CaptureController}} object with the following internal slots: | ||
<table class="simple"> | ||
<thead> | ||
<tr> | ||
<th>Internal Slot</th> | ||
<th>Initial value</th> | ||
<th>Description (<em>non-normative</em>)</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
<tr> | ||
<td><dfn>[[\IsBound]]</dfn></td> | ||
<td><code>false</code></td> | ||
<td> | ||
Whether an application has attempted to associate [=this=] | ||
with a [=capture-session=]. | ||
</td> | ||
</tr> | ||
<tr> | ||
<td><dfn>[[\Source]]</dfn></td> | ||
<td><code>null</code></td> | ||
<td> | ||
The source of the associated [=capture-session=]. | ||
</td> | ||
</tr> | ||
<tr> | ||
<td><dfn>[[\DisplaySurfaceType]]</dfn></td> | ||
<td><code>null</code></td> | ||
<td> | ||
Once capture starts, this will be set to the type of the captured [=display surface=]. | ||
</td> | ||
</tr> | ||
<tr> | ||
<td><dfn>[[\FocusChangeDisabled]]</dfn></td> | ||
<td><code>false</code></td> | ||
<td> | ||
Whether focus-change has been disabled by an external event | ||
or a user agent consideration. | ||
</td> | ||
</tr> | ||
<tr> | ||
<td><dfn>[[\FocusDecisionFinalized]]</dfn></td> | ||
<td><code>false</code></td> | ||
<td> | ||
Set to true when the focus decision is finalized. | ||
</td> | ||
</tr> | ||
<tr> | ||
<td><dfn>[[\FocusBehavior]]</dfn></td> | ||
<td><code>null</code></td> | ||
<td> | ||
The focus behavior desired by the application. | ||
</td> | ||
</tr> | ||
</tbody> | ||
</table> | ||
<p> | ||
The user agent MAY set {{CaptureController/[[FocusChangeDisabled]]}} | ||
to <code>true</code> at any moment based on its own logic. | ||
</p> | ||
</dd> | ||
<dt> | ||
<dfn>setFocusBehavior</dfn> | ||
</dt> | ||
<dd> | ||
<p> | ||
Create one internal slot: <dfn data-dfn-for="CaptureController">[[\isBound]]</dfn>, | ||
initialized to <code>false</code>. | ||
Run the following steps: | ||
</p> | ||
<ol> | ||
<li> | ||
<p> | ||
Let <var>focusBehavior</var> be the method's first argument. | ||
</p> | ||
</li> | ||
<li> | ||
<p> | ||
If {{CaptureController/[[Source]]}} is <code>null</code>, | ||
set {{CaptureController/[[FocusBehavior]]}} to <var>focusBehavior</var> | ||
and abort these steps. | ||
eladalon1983 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
</p> | ||
</li> | ||
<li> | ||
<p> | ||
If {{CaptureController/[[Source]]}} has been | ||
<a data-cite="GETUSERMEDIA#source-stopped">stopped</a>, | ||
[=exception/throw=] an "{{InvalidStateError}}" {{DOMException}}. | ||
</p> | ||
</li> | ||
<li> | ||
<p> | ||
If {{CaptureController/[[DisplaySurfaceType]]}} is neither | ||
{{DisplayCaptureSurfaceType/"browser"}} nor {{DisplayCaptureSurfaceType/"window"}}, | ||
[=exception/throw=] an "{{InvalidStateError}}" {{DOMException}}. | ||
Comment on lines
+955
to
+957
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. First thought: this seems like an error of type, not state. Maybe Second thought: the "type" varies by what the user chooses in the picker... should we abort silently instead? Seems a footgun otherwise: Developer tests their code without error, but forgot to manually test choosing "Entire desktop" in the prompt which will throw in production. Also, "Entire desktop" is already "in focus' so to speak, so why not call this success instead of failure? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I've commented on that elsewhere. The comment threads are getting out of hand. I propose that we merge this and iterate. If there are substantive issues to resolve first, like methods vs. attribute, let's focus on them and leave uncontroversial spec-language nits for a follow-up. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
We do abort silently if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
That depends on the OS and whether it's the current desktop or not. |
||
</p> | ||
</li> | ||
<li> | ||
<p> | ||
If {{CaptureController/[[FocusDecisionFinalized]]}} is <code>true</code>, | ||
[=exception/throw=] an "{{InvalidStateError}}" {{DOMException}}. | ||
</p> | ||
</li> | ||
<li> | ||
<p> | ||
Set {{CaptureController/[[FocusBehavior]]}} to <var>focusBehavior</var>. | ||
</p> | ||
</li> | ||
<li> | ||
<p> | ||
Run the [=finalize focus decision algorithm=] on [=this=]. | ||
</p> | ||
</li> | ||
</ol> | ||
</dd> | ||
</section> | ||
</dl> | ||
<p> | ||
The <dfn>finalize focus decision algorithm</dfn>, given a | ||
<var>controller</var>, consists of running | ||
the following steps: | ||
</p> | ||
<ol> | ||
<li> | ||
<p> | ||
If <var>controller</var>.{{CaptureController/[[FocusDecisionFinalized]]}} | ||
is <code>true</code>, abort these steps. | ||
</p> | ||
</li> | ||
<li> | ||
<p> | ||
Set <var>controller</var>.{{CaptureController/[[FocusDecisionFinalized]]}} | ||
to <code>true</code>. | ||
</p> | ||
</li> | ||
<li> | ||
<p> | ||
If <var>controller</var>.{{CaptureController/[[FocusChangeDisabled]]}} | ||
is <code>true</code>, abort these steps. | ||
</p> | ||
</li> | ||
<li> | ||
<p> | ||
If too much time has elapsed since the [=capture-session=] started, | ||
the user agent SHOULD set {{CaptureController/[[FocusDecisionFinalized]]}} | ||
to <code>true</code>. The timespan is left up to the user agent, | ||
but it is recommended that a value of one second be used. | ||
</p> | ||
</li> | ||
<li> | ||
<p> | ||
If <var>controller</var>.{{CaptureController/[[DisplaySurfaceType]]}} is neither | ||
{{DisplayCaptureSurfaceType/"browser"}} nor {{DisplayCaptureSurfaceType/"window"}}, | ||
abort these steps. | ||
</p> | ||
</li> | ||
<li> | ||
<p> | ||
Run the following step [=in parallel=]: | ||
</p> | ||
</li> | ||
<ol> | ||
<li> | ||
<p> | ||
If <var>controller</var>.{{CaptureController/[[FocusBehavior]]}} | ||
is {{CaptureStartFocusBehavior/"no-focus-change"}}, | ||
focus the [=display surface=] representing the capturing document. | ||
</p> | ||
</li> | ||
<li> | ||
<p> | ||
If <var>controller</var>.{{CaptureController/[[FocusBehavior]]}} | ||
is {{CaptureStartFocusBehavior/"focus-captured-surface"}}, | ||
focus the [=display surface=] referred to by | ||
<var>controller</var>.{{CaptureController/[[Source]]}}. | ||
</p> | ||
</li> | ||
</ol> | ||
</ol> | ||
</section> | ||
<section> | ||
<h2><dfn>SelfCapturePreferenceEnum</dfn></h2> | ||
<p> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that the diff-tool just thinks this was removed. In effect, I just put something ahead of it and then fixed the indentation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jan-ivar
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, the controller association moved up. However, as I mentioned this still isn't going to guarantee the controller is always associated on failure e.g. if the WebIDL binding code throws TypeErrors on the constructor arguments. Are we OK with that?
Would it be more logical to move it down instead, after input validation, just ahead of the in-parallel steps, since it has side-effects? That way, if the returned promise is pending (as determined with
Promise.race
), the app can know the binding happened or not (something we can WPT test).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the mental model for the developer should be kept as simple as possible.
"I can call setFocusBehavior() multiple times before gDM is called, then exactly one more time, at latest when gDM's promise resolves"
- this is already at the limit. I don't want to add the caveat of"...unless gDM fails, in which case I can call it again."
As a developer, if your gDM call fails, try again from scratch.
It's a shame that failures over WebIDL bindings don't get captured here, but I see it as an edge-case we can avoid worrying about. I don't think programs would typically run into this and then try to resolve such an error on the fly, programmatically. Rather, it will be a bug that someone will manually fix.