Skip to content
Merged
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
4cda122
Fix 244: Address requestPictureInPicture() algorithm
theIDinside Feb 6, 2026
2a2f492
Fix misspellings
theIDinside Feb 9, 2026
2b93a32
Update index.bs
beaufortfrancois Feb 16, 2026
de1ac4e
Use the right task sources
theIDinside Feb 20, 2026
40e81f7
Handle PiP Window instance errors.
theIDinside Feb 20, 2026
ba17715
Update index.bs
theIDinside Feb 26, 2026
74a1e9b
Update index.bs
theIDinside Feb 26, 2026
341d185
Use |promise| and |in parallel|
theIDinside Feb 26, 2026
0c8a405
Use [=list/Append=] syntax
theIDinside Feb 27, 2026
4d8c49f
Folded in #246
theIDinside Mar 3, 2026
de16c35
Add the internal slot for picture in picture element
theIDinside Mar 11, 2026
1364b42
Update index.bs
theIDinside Mar 12, 2026
5e39d26
Use consistent [=foo=] syntax
theIDinside Mar 12, 2026
c154cb6
Update index.bs
theIDinside Mar 12, 2026
77f7fd1
Syntax & Error step fix
theIDinside Mar 13, 2026
a6fec8e
Update reference to requestPictureInPicture
theIDinside Mar 13, 2026
6217225
Grammar fix
theIDinside Mar 13, 2026
5da530d
Add user-agent picture-in-picture parallel-queue
theIDinside Mar 13, 2026
d0df54c
Added synchronous resolve optimization
theIDinside Mar 13, 2026
420a9ba
Change |video| to [=this=]
theIDinside Mar 13, 2026
5b24e17
Make parallel queue per-document instead
theIDinside Mar 15, 2026
5fb327a
Address many things
theIDinside Mar 17, 2026
9ddd4eb
Minor changes
theIDinside Mar 18, 2026
029bd5e
Remove redudant definitions
theIDinside Apr 2, 2026
3a4d6bc
Fix wording
theIDinside Apr 2, 2026
d697ffc
Fix wording
theIDinside Apr 2, 2026
5bcd667
Update index.bs
theIDinside Apr 2, 2026
5d487ce
Use [==] syntax
theIDinside Apr 2, 2026
c1d7547
Change to <a></a>
theIDinside Apr 2, 2026
5caa604
Nits & Fixes
theIDinside Apr 2, 2026
dada3c2
Not allowed should use NotAllowedError exceptions
theIDinside Apr 14, 2026
71bc92d
Remove export
theIDinside Apr 14, 2026
fb4344d
Make queue per-traversable-navigable
theIDinside Apr 14, 2026
95faf4a
Remove reference to API algorithm from concepts
theIDinside Apr 14, 2026
a2df0fd
Fix link to DocumentOrShadowRoot
theIDinside Apr 14, 2026
34dccc1
Fix algorithm to use internal state, not IDL getter
marcoscaceres Apr 30, 2026
943e7b9
Apply suggestion from @marcoscaceres
marcoscaceres Apr 30, 2026
04eb78a
Exit PIP if pipElement != null in requestPictureInPicture completion
theIDinside May 7, 2026
91c2e2a
Add optionality to disablePictureInPicture
theIDinside May 7, 2026
e456881
Don't use all-caps
theIDinside May 10, 2026
e4a9306
Make permission policy be a statement of fact
theIDinside May 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 60 additions & 50 deletions index.bs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ spec:dom; type:dfn; text:origin
spec:dom; type:interface; text:Document
spec:html; type:attribute; for:HTMLMediaElement; text:readyState
spec:html; type:dfn; for:/; text:browsing context
spec:html; type:dfn; text:allowed to use
spec:infra; type:dfn; text:user agent
spec:infra; type:dfn; for:list; text:item
</pre>
Expand Down Expand Up @@ -167,53 +168,31 @@ to initiate and control this behavior by exposing the following sets of properti

# Concepts # {#concepts}

## Internal Slot Definitions ## {#defines}
## Definitions ## {#defines}

A <dfn>Picture-in-Picture window</dfn> is a window displaying the <{video}> element.

A {{DocumentOrShadowRoot}} has:

1. A <dfn>Picture-in-Picture element</dfn>, which is an {{Element}} or `null`, initially `null`.

A <a for="/">traversable navigable</a> has:
1. A <dfn>picture-in-picture parallel queue</dfn>, which is a [=parallel queue=] created by
[=starting a new parallel queue=].


A <a>user agent</a> has:

1. An <dfn>initiators of active Picture-in-Picture sessions</dfn>
list of zero or more <a>origins</a>, which is initially empty.

Note: In case a <a>user agent</a> supports multiple Picture-in-Picture
windows, the list allows duplicates.
Note: In case a <a>user agent</a> supports multiple Picture-in-Picture windows, the list allows duplicates.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think we should move away from this

Suggested change
Note: In case a <a>user agent</a> supports multiple Picture-in-Picture windows, the list allows duplicates.

@theIDinside theIDinside Apr 2, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should follow up with that. I think we should remove the whole entire "originators list". It's hard to understand it's use case, if we already have the document's pictureInPictureElement to query.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kenchris Please speak up if you disagree otherwise we'll be removing the PiP originators list.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems fine, we only support one pip anyways


An origin is said to have an active Picture-in-Picture session if any
of the origins in <a>initiators of active Picture-in-Picture sessions</a>
are <a>same origin-domain</a> with origin.

## Request Picture-in-Picture ## {#request-pip}

When the <dfn>request Picture-in-Picture algorithm</dfn> with |video| is invoked,
the user agent MUST run the following steps:

1. If <a>Picture-in-Picture support</a> is `false`, throw a
{{NotSupportedError}} and abort these steps.
2. If the document is not <a>allowed to use</a> the <a>policy-controlled feature</a>
named `"picture-in-picture"`, throw a {{SecurityError}} and abort these
steps.
3. If |video|'s {{readyState}} attribute is {{HAVE_NOTHING}}, throw a
{{InvalidStateError}} and abort these steps.
4. If |video| has no video track, throw a {{InvalidStateError}} and abort
these steps.
5. If |video|'s {{HTMLVideoElement/disablePictureInPicture}} is true,
the user agent MAY throw an {{InvalidStateError}} and abort these steps.
6. If {{pictureInPictureElement}} is `null`:
1. If <a>this</a>'s <a>relevant global object</a> does not have
<a>transient activation</a>, throw a {{NotAllowedError}} and abort these steps.
2. [=Consume user activation=] given <a>this</a>'s <a>relevant global object</a>.
7. If |video| is {{pictureInPictureElement}}, abort these steps.
8. Set {{pictureInPictureElement}} to |video|.
9. Let <dfn>Picture-in-Picture window</dfn> be a new instance of
{{PictureInPictureWindow}} associated with {{pictureInPictureElement}}.
10. Append <a>relevant settings object</a>'s <a>origin</a> to
<a>initiators of active Picture-in-Picture sessions</a>.
11. <a>Queue a task</a> to <a>fire an event</a> named
{{enterpictureinpicture}} using {{PictureInPictureEvent}} at the
|video| with its {{bubbles}} attribute initialized to `true` and its
{{PictureInPictureEvent/pictureInPictureWindow}} attribute initialized to
<a>Picture-in-Picture window</a>.
12. If {{pictureInPictureElement}} is <a>fullscreenElement</a>, it is
RECOMMENDED to <a>exit fullscreen</a>.
## Picture-in-Picture ## {#pip}

It is RECOMMENDED that video frames are not rendered in the page and in the
Picture-in-Picture window at the same time but if they are, they MUST be kept
Expand All @@ -233,7 +212,7 @@ It is also RECOMMENDED that the Picture-in-Picture window has a maximum and
minimum size. For example, it could be restricted to be between a quarter and
a half of one dimension of the screen.

When a {{DocumentOrShadowRoot}}'s {{pictureInPictureElement}} attribute is set,
When a {{DocumentOrShadowRoot}}'s <a>Picture-in-Picture element</a> is set,
the [=Picture-in-Picture window=] MUST be visible, even when the
{{DocumentOrShadowRoot}}'s [=relevant global object=]'s [=associated Document=]'s
Comment thread
theIDinside marked this conversation as resolved.
[=Document/visibility state=] is "hidden".
Expand Down Expand Up @@ -332,16 +311,48 @@ partial interface HTMLVideoElement {
};
</pre>

The {{requestPictureInPicture()}} method, when invoked, MUST
return <a>a new promise</a> |promise| and run the following steps <a>in
parallel</a>:
The {{requestPictureInPicture()}} method steps <dfn export>request Picture-in-Picture</dfn>:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's open a separate issue for this and do this in a follow-up PR.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, let's do a separate PR for this.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree. That's fine.


1. If [=Picture-in-Picture support=] is `false`, return [=a promise rejected with=] {{NotSupportedError}} {{DOMException}}.
2. Let |doc| be [=this=]'s [=node document=].
3. If |doc| is not [=allowed to use=] the [=policy-controlled feature=]
named `"picture-in-picture"`, return [=a promise rejected with=] {{NotAllowedError}} {{DOMException}}.
4. If [=this=]'s {{readyState}} attribute is {{HAVE_NOTHING}}, return [=a promise rejected with=] {{InvalidStateError}} {{DOMException}}.
5. If [=this=] has no video track, return [=a promise rejected with=] {{InvalidStateError}} {{DOMException}}.
6. If [=this=]'s {{HTMLVideoElement/disablePictureInPicture}} is true, user agent may return [=a promise rejected with=] {{InvalidStateError}} {{DOMException}}.
7. If |doc|'s [=Picture-in-Picture element=] is `null`:

@theIDinside theIDinside May 10, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@marcoscaceres || @beaufortfrancois || @chrisn || @zcorpan

Should we do this change.

Suggested change
7. If |doc|'s [=Picture-in-Picture element=] is `null`:
7. If |doc|'s [=Picture-in-Picture element=] is not [=this=]:

If not, then we would allow subsequent PIP requests to go through without user activation (though for the same document only, so I don't think there's any cross origin shenanigans that can happen here. Well, there could be, but it would not be the fault of the spec, but the implementation in that case).

I think we've discussed this before at some point.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1. If [=this=]'s [=relevant global object=] does not have a [=transient activation=], then return [=a
promise rejected with=] {{NotAllowedError}} {{DOMException}}.
2. [=Consume user activation=] given [=this=]'s [=relevant global object=].
8. If [=this=] is |doc|'s [=Picture-in-Picture element=]:
1. Return [=a promise resolved with=] the [=Picture-in-Picture window=] associated with |doc|'s [=Picture-in-Picture element=].
9. Let |global| be [=this=]'s [=relevant global object=].
10. Let |p| be [=a new promise=] created in [=this=]'s [=relevant realm=].
11. Return |p|, and [=enqueue the following steps=] to |doc|'s [=picture-in-picture parallel queue=]:
1. If [=this=] is |doc|'s [=Picture-in-Picture element=]:
1. [=Queue a global task=] on the [=media element event task source=] given |global| to
[=/resolve=] |p| with the [=Picture-in-Picture window=] associated with
[=this=].
2. Abort these steps.
2. Attempt to associate a [=Picture-in-Picture window=] with [=this=].
3. If the previous step failed:
1. [=Queue a global task=] on the [=media element event task source=] given |global| to
[=/reject=] |p| with {{InvalidStateError}} {{DOMException}}.
2. Abort these steps.
4. Let |pipWindow| be a new instance of {{PictureInPictureWindow}} that represents [=this=]'s associated [=Picture-in-Picture window=].
5. [=Queue a global task=] on the [=media element event task source=] given |global|, to perform
the following steps:
1. If {{pictureInPictureElement}} is not `null`, run the [=exit Picture-in-Picture algorithm=].
2. Set |doc|'s [=Picture-in-Picture element=] to [=this=].
3. [=list/Append=] [=relevant settings object=]'s [=origin=] to [=initiators of active
Picture-in-Picture sessions=].
4. If [=this=] is [=fullscreenElement=], [=exit fullscreen=].
5. [=Fire an event=] named {{enterpictureinpicture}} using {{PictureInPictureEvent}} at
[=this=] with its {{bubbles}} attribute initialized to `true` and its
{{PictureInPictureEvent/pictureInPictureWindow}} attribute initialized to
[=Picture-in-Picture window=].
6. [=/Resolve=] |p| with |pipWindow|.

1. Let |video| be the video element on which the method was invoked.
2. Run the <a>request Picture-in-Picture algorithm</a> with |video|.
3. If the previous step threw an exception, reject |promise| with that
exception and abort these steps.
4. [=/Resolve=] |promise| with the <a>Picture-in-Picture window</a> associated with
{{pictureInPictureElement}}.

## Extensions to <code>Document</code> ## {#document-extensions}

Expand Down Expand Up @@ -382,8 +393,7 @@ The {{pictureInPictureElement}} attribute's getter must run these steps:

1. If <a>this</a> is a <a for=/>shadow root</a> and its <a for=DocumentFragment>host</a>
is not <a>connected</a>, return `null` and abort these steps.
2. Let |candidate| be the result of <a>retargeting</a> Picture-in-Picture
element against <a>this</a>.
2. Let |candidate| be the result of [=retargeting=] [=Picture-in-Picture element=] against [=this=].
3. If |candidate| and <a>this</a> are in the same <a>tree</a>,
return |candidate| and abort these steps.
4. Return `null`.
Expand Down Expand Up @@ -468,8 +478,8 @@ regardless of the <a>browsing context</a>.
## Permissions Policy ## {#permissions-policy}

This specification defines a <a>policy-controlled feature</a> named
`"picture-in-picture"` that controls whether the <a>request Picture-in-Picture
algorithm</a> may return a {{SecurityError}} and whether
`"picture-in-picture"` that controls whether [=request Picture-in-Picture=]
returns a {{SecurityError}} and whether
{{pictureInPictureEnabled}} is `true` or `false`.

The <a>default allowlist</a> for this feature is `*`.
Expand Down
Loading