├── .gitignore ├── w3c.json ├── LICENSE.md ├── .github └── workflows │ └── build.yml ├── Makefile ├── CONTRIBUTING.md ├── security-privacy-questionnaire.md ├── svg ├── custom-video-player-1.svg ├── custom-video-player-2.svg ├── pomodoro-timers-1.svg ├── video-conferencing-1.svg └── video-conferencing-2.svg ├── README.md └── spec.bs /.gitignore: -------------------------------------------------------------------------------- 1 | /out/ 2 | /spec.html 3 | *.swp 4 | -------------------------------------------------------------------------------- /w3c.json: -------------------------------------------------------------------------------- 1 | { 2 | "group": [80485] 3 | , "contacts": ["cwilso"] 4 | , "repo-type": "cg-report" 5 | } 6 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | All Reports in this Repository are licensed by Contributors 2 | under the 3 | [W3C Software and Document License](http://www.w3.org/Consortium/Legal/2015/copyright-software-and-document). 4 | 5 | Contributions to Specifications are made under the 6 | [W3C CLA](https://www.w3.org/community/about/agreements/cla/). 7 | 8 | Contributions to Test Suites are made under the 9 | [W3C 3-clause BSD License](https://www.w3.org/Consortium/Legal/2008/03-bsd-license.html) 10 | 11 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: 3 | pull_request: 4 | branches: 5 | - main 6 | push: 7 | branches: 8 | - main 9 | jobs: 10 | build: 11 | name: Build 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | - name: Build 16 | run: make ci 17 | - name: Deploy 18 | if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} 19 | uses: peaceiris/actions-gh-pages@v3 20 | with: 21 | github_token: ${{ secrets.GITHUB_TOKEN }} 22 | publish_dir: ./out 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SHELL=/bin/bash 2 | 3 | local: spec.bs 4 | bikeshed --die-on=warning spec spec.bs spec.html 5 | 6 | spec.html: spec.bs 7 | @ (HTTP_STATUS=$$(curl https://api.csswg.org/bikeshed/ \ 8 | --output spec.html \ 9 | --write-out "%{http_code}" \ 10 | --header "Accept: text/plain, text/html" \ 11 | -F die-on=warning \ 12 | -F file=@spec.bs) && \ 13 | [[ "$$HTTP_STATUS" -eq "200" ]]) || ( \ 14 | echo ""; cat spec.html; echo ""; \ 15 | rm -f spec.html; \ 16 | exit 22 \ 17 | ); 18 | 19 | remote: spec.html 20 | 21 | ci: spec.bs 22 | mkdir -p out 23 | make remote 24 | mv spec.html out/index.html 25 | 26 | clean: 27 | rm spec.html 28 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Web Platform Incubator Community Group 2 | 3 | This repository is being used for work in the W3C Web Platform Incubator Community Group, governed by the [W3C Community License 4 | Agreement (CLA)](http://www.w3.org/community/about/agreements/cla/). To make substantive contributions, 5 | you must join the CG. 6 | 7 | If you are not the sole contributor to a contribution (pull request), please identify all 8 | contributors in the pull request comment. 9 | 10 | To add a contributor (other than yourself, that's automatic), mark them one per line as follows: 11 | 12 | ``` 13 | +@github_username 14 | ``` 15 | 16 | If you added a contributor by mistake, you can remove them in a comment with: 17 | 18 | ``` 19 | -@github_username 20 | ``` 21 | 22 | If you are making a pull request on behalf of someone else but you had no part in designing the 23 | feature, you can remove yourself with the above syntax. 24 | -------------------------------------------------------------------------------- /security-privacy-questionnaire.md: -------------------------------------------------------------------------------- 1 | https://www.w3.org/TR/security-privacy-questionnaire/ 2 | 3 | ### 2.1 What information might this feature expose to Web sites or other parties, and for what purposes is that exposure necessary? 4 | 5 | None 6 | 7 | ### 2.2. Do features in your specification expose the minimum amount of information necessary to enable their intended uses? 8 | 9 | Yes 10 | 11 | ### 2.3. How do the features in your specification deal with personal information, personally-identifiable information (PII), or information derived from them? 12 | 13 | This feature does not deal with any PII 14 | 15 | ### 2.4. How do the features in your specification deal with sensitive information? 16 | 17 | N/A 18 | 19 | ### 2.5. Do the features in your specification introduce new state for an origin that persists across browsing sessions? 20 | 21 | No 22 | 23 | ### 2.6. Do the features in your specification expose information about the underlying platform to origins? 24 | 25 | No 26 | 27 | ### 2.7. Does this specification allow an origin to send data to the underlying platform? 28 | 29 | No 30 | 31 | ### 2.8. Do features in this specification enable access to device sensors? 32 | 33 | No 34 | 35 | ### 2.9. Do features in this specification enable new script execution/loading mechanisms? 36 | 37 | No 38 | 39 | ### 2.10. Do features in this specification allow an origin to access other devices? 40 | 41 | No 42 | 43 | ### 2.11. Do features in this specification allow an origin some measure of control over a user agent’s native UI? 44 | 45 | This feature allows the origin to open an always-on-top window. The origin can 46 | specify an initial width/height/aspect ratio of the window but cannot set a 47 | position. 48 | 49 | ### 2.12. What temporary identifiers do the features in this specification create or expose to the web? 50 | 51 | None 52 | 53 | ### 2.13. How does this specification distinguish between behavior in first-party and third-party contexts? 54 | 55 | No difference 56 | 57 | ### 2.14. How do the features in this specification work in the context of a browser’s Private Browsing or Incognito mode? 58 | 59 | No difference 60 | 61 | ### 2.15. Does this specification have both "Security Considerations" and "Privacy Considerations" sections? 62 | 63 | Yes 64 | 65 | ### 2.16. Do features in your specification enable origins to downgrade default security protections? 66 | 67 | No 68 | 69 | ### 2.17. How does your feature handle non-"fully active" documents? 70 | 71 | The Document Picture-in-Picture window closes when navigating away, so the 72 | window won't exist for a non-"fully active" document 73 | 74 | ### 2.18. What should this questionnaire have asked? 75 | 76 | N/A 77 | -------------------------------------------------------------------------------- /svg/custom-video-player-1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /svg/custom-video-player-2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /svg/pomodoro-timers-1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /svg/video-conferencing-1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Document Picture-in-Picture Explained 2 | 3 | 2023-10-26 4 | 5 | Spec: https://wicg.github.io/document-picture-in-picture/ 6 | 7 | ## What's all this then? 8 | 9 | There currently exists a Web API for putting an `HTMLVideoElement` into a 10 | Picture-in-Picture window (`HTMLVideoElement.requestPictureInPicture()`). This 11 | limits a website's ability to provide a custom picture-in-picture experience 12 | (PiP). We want to expand upon that functionality by giving websites the ability 13 | to open a picture-in-picture (i.e., always-on-top) window with a blank document 14 | that can be populated with arbitrary `HTMLElement`s instead of only a single 15 | `HTMLVideoElement`. 16 | 17 | This new window will be much like a blank same-origin window opened via the 18 | existing `window.open()` API, with some differences: 19 | 20 | - The PiP window will float on top of other windows. 21 | - The PiP window will never outlive the opening window. This means any 22 | navigations that change the opener to a new document (even same-origin 23 | navigations) will cause the PiP window to close, similar to the existing 24 | `HTMLVideoElement.requestPictureInPicture()` API. 25 | - The website cannot set the position of the PiP window. 26 | - The PiP window cannot be navigated (any `window.history` or `window.location` 27 | calls that change to a new document will close the PiP window). 28 | - The website can have only one PiP window open at a time, and the user agent 29 | may also restrict how many PiP windows can be open globally, similar to 30 | `HTMLVideoElement.requestPictureInPicture()` API. 31 | 32 | ### Goals 33 | 34 | - Allow a website to display arbitrary `HTMLElements` in an always-on-top 35 | window. 36 | - To be simple for web developers to use and understand. Note that while 37 | allowing websites to call `requestPictureInPicture()` on any element would be 38 | the simplest way, for reasons described below, this isn't feasible. 39 | 40 | ### Non-goals 41 | 42 | - This API is not attempting to handle placeholder content for elements that are 43 | moved out of the page (that is the responsibility of the website to handle). 44 | - Allowing websites to open always-on-top widgets that outlive the webpage (the 45 | PiP window will close when the webpage is closed). 46 | 47 | ### Use cases 48 | 49 | #### Custom video player 50 | 51 | While the existing Picture-in-Picture API for HTMLVideoElement allows a website 52 | to provide a Picture-in-Picture video experience, it is very limited in what 53 | inputs the window can take and the look-and-feel of those inputs. With a full 54 | Document in Picture-in-Picture, the website can provide custom controls and 55 | inputs (e.g. captions, playlists, time scrubber, liking/disliking videos, etc) 56 | to improve the user's PiP video experience. 57 | 58 | ![Custom video player wireframe 1](svg/custom-video-player-1.svg) 59 | ![Custom video player wireframe 2](svg/custom-video-player-2.svg) 60 | 61 | #### Video conferencing 62 | 63 | It is common for users to leave the tab during a video conferencing session for 64 | various reasons (e.g. presenting another tab to the call or multitasking) while 65 | still wishing to see the call, so it's a prime use case for Picture-in-Picture. 66 | As above, the current experience a video conferencing website can provide via 67 | the HTMLVideoElement PiP API is limited in style and input. With a full Document 68 | in Picture-in-Picture, the website can easily combine multiple video streams 69 | into a single PiP window and provide custom controls like sending a message, 70 | muting another user, raising a hand, etc. 71 | 72 | ![Video conferencing wireframe 1](svg/video-conferencing-1.svg) 73 | ![Video conferencing wireframe 2](svg/video-conferencing-2.svg) 74 | 75 | #### Pomodoro timers 76 | 77 | The Pomodoro technique is a time management method that uses a kitchen timer to 78 | break work into intervals, typically 25 minutes in length, separated by short 79 | breaks. Pomodoro timer apps on desktop and mobile can use the PiP feature to 80 | display the current timer permanently on the screen as a floating timer for 81 | timed focus management while sat at a desk or while on the go. 82 | 83 | ![Pomodoro timers wireframe 1](svg/pomodoro-timers-1.svg) 84 | 85 | ## Example code 86 | 87 | ### HTML 88 | 89 | ```html 90 | 91 |
92 |
93 | 94 | 95 |
96 |
97 | 98 | 99 | ``` 100 | 101 | ### JavaScript 102 | 103 | ```js 104 | // Handle to the picture-in-picture window. 105 | let pipWindow = null; 106 | 107 | async function enterPiP() { 108 | const player = document.querySelector("#player"); 109 | 110 | const pipOptions = { 111 | width: player.clientWidth, 112 | height: player.clientHeight, 113 | }; 114 | 115 | pipWindow = await documentPictureInPicture.requestWindow(pipOptions); 116 | 117 | // Style remaining container to imply the player is in PiP. 118 | const playerContainer = document.querySelector("#player-container"); 119 | playerContainer.classList.add("pip-mode"); 120 | 121 | // Add player to the PiP window. 122 | pipWindow.document.body.append(player); 123 | 124 | // Listen for the PiP closing event to put the video back. 125 | pipWindow.addEventListener("pagehide", onLeavePiP.bind(pipWindow), { 126 | once: true, 127 | }); 128 | } 129 | 130 | // Called when the PiP window has closed. 131 | function onLeavePiP() { 132 | if (this !== pipWindow) { 133 | return; 134 | } 135 | 136 | // Remove PiP styling from the container. 137 | const playerContainer = document.querySelector("#player-container"); 138 | playerContainer.classList.remove("pip-mode"); 139 | 140 | // Add the player back to the main window. 141 | const pipPlayer = pipWindow.document.querySelector("#player"); 142 | playerContainer.append(pipPlayer); 143 | 144 | pipWindow = null; 145 | } 146 | ``` 147 | 148 | ## Key scenarios 149 | 150 | ### Accessing elements on the PiP window 151 | 152 | ```js 153 | const pipVideo = pipWindow.document.querySelector("#video"); 154 | pipVideo.loop = true; 155 | ``` 156 | 157 | ### Listening to events on the PiP window 158 | 159 | As part of creating an improved picture-in-picture experience, websites will 160 | often want customize buttons and controls that need to respond to user input 161 | events such as clicks. 162 | 163 | ```js 164 | const pipVideo = pipWindow.document.querySelector("#video"); 165 | const pipMuteButton = pipWindow.document.createElement("button"); 166 | pipMuteButton.textContent = "Toggle mute"; 167 | pipMuteButton.addEventListener("click", () => { 168 | pipVideo.muted = !pipVideo.muted; 169 | }); 170 | pipWindow.document.body.append(pipMuteButton); 171 | ``` 172 | 173 | ### Exiting PiP 174 | 175 | The website may decide to close the `DocumentPictureInPicture` window without 176 | the user explicitly clicking on the window's close button. They can do this by 177 | using the `close()` method on the `Window` object: 178 | 179 | ```js 180 | // This will close the PiP window and trigger our existing onLeavePiP() 181 | // listener. 182 | pipWindow.close(); 183 | ``` 184 | 185 | ### Getting elements out of the PiP window when it closes 186 | 187 | When the PiP window is closed for any reason (either because the website 188 | initiated it or the user closed it), the website will often want to get the 189 | elements back out of the PiP window. The website can perform this in an event 190 | handler for the `pagehide` event on the `Window` object. This is shown in the 191 | `onLeavePiP()` handler in [Example code](#example-code) section above and is 192 | copied below: 193 | 194 | ```js 195 | // Called when the PiP window has closed. 196 | function onLeavePiP() { 197 | if (this !== pipWindow) { 198 | return; 199 | } 200 | 201 | // Remove PiP styling from the container. 202 | const playerContainer = document.querySelector("#player-container"); 203 | playerContainer.classList.remove("pip-mode"); 204 | 205 | // Add the player back to the main window. 206 | const pipPlayer = pipWindow.document.querySelector("#player"); 207 | playerContainer.append(pipPlayer); 208 | 209 | pipWindow = null; 210 | } 211 | ``` 212 | 213 | ### Programatically resize the PiP window 214 | 215 | The document picture-in-picture window supports the resizeTo() and resizeBy() 216 | APIs, but only with a user gesture on the PiP window: 217 | 218 | ```js 219 | const expandButton = pipWindow.document.createElement('button'); 220 | expandButton.textContent = 'Expand PiP Window'; 221 | expandButton.addEventListener('click', () => { 222 | // Expand the PiP window's width by 20px and height by 30px. 223 | pipWindow.resizeBy(20, 30); 224 | }); 225 | pipWindow.document.body.append(expandButton); 226 | ``` 227 | 228 | ## Detailed design discussion 229 | 230 | ### Why not extend the `HTMLVideoElement.requestPictureInPicture()` idea to allow it to be called on any `HTMLElement`? 231 | 232 | Any API where the UA is taking elements out of the page and then reinserting 233 | them ends up with tricky questions on what to show in the current document when 234 | those elements are gone (do elements shift around? Is there a placeholder? What 235 | magic needs to happen when things resize? etc). By leaving it up to websites to 236 | move their own elements, the API contract between the UA and website is much 237 | clearer and simpler to understand. 238 | 239 | ### Since this is pretty close to `window.open()`, why not just add an `alwaysOnTop` flag to `window.open()`? 240 | 241 | The main reason we decided to have a completely separate API is to make it 242 | easier for websites to detect it (since in most cases, falling back to a 243 | standard window would be undesirable and websites would rather use 244 | `HTMLVideoElement` PiP instead). Additionally, it also works differently enough 245 | from `window.open()` (e.g., never outliving the opener) that having it separate 246 | makes sense. 247 | 248 | ### Why not give the website more control over the size/position of the window? 249 | 250 | Giving websites less control over the size/position of the window will help 251 | prevent, e.g., phishing attacks where a website pops a small always-on-top 252 | window over an `input` element to steal your password. 253 | 254 | ## Considered alternatives 255 | 256 | Surface Element was a proposal where the website would wrap PiP-able content in 257 | advance with a new type of iframe-like element that could be pulled out into a 258 | separate window when requested. This had some downsides including always 259 | requiring the overhead of a separate document (even in the most common case of 260 | never entering picture-in-picture). 261 | 262 | We also considered a similar approach to the one in this document, but with no 263 | input allowed in the DOM (only allowlisted controls from a predetermined list in 264 | a similar fashion to the existing `HTMLVideoElement` PiP). One issue with this 265 | approach is that it really didn't help websites do much more than they already 266 | can today, since a website can draw anything in a canvas element and PiP a video 267 | with the canvas as a source. `Having HTMLElements` that can actually be 268 | interacted with is what makes the Document Picture-in-Picture feature worth 269 | implementing. 270 | 271 | ## References and acknowledgements 272 | 273 | Many thanks to Frank Liberato, Mark Foltz, Klaus Weidner, François Beaufort, 274 | Charlie Reis, Joe DeBlasio, Domenic Denicola, and Yiren Wang for their comments 275 | and contributions to this document and to the discussions that have informed it. 276 | Special thanks to Mikaela Watson and Glen Anderson for the initial wireframes. 277 | -------------------------------------------------------------------------------- /svg/video-conferencing-2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /spec.bs: -------------------------------------------------------------------------------- 1 |
  2 | Title: Document Picture-in-Picture Specification
  3 | Repository: WICG/document-picture-in-picture
  4 | Shortname: document-pip-spec
  5 | Level: 1
  6 | Status: CG-DRAFT
  7 | Group: WICG
  8 | URL: https://wicg.github.io/document-picture-in-picture/
  9 | Editor: Tommy Steimel,  Google Inc.,  steimel@google.com
 10 | Abstract: This specification enables web developers to populate an HTMLDocument
 11 | Abstract: in an always-on-top window.
 12 | 
13 | 14 | 21 | 22 | # Introduction # {#intro} 23 | 24 | This section is non-normative. 25 | 26 | There currently exists a Web API for putting an {{HTMLVideoElement}} into a 27 | Picture-in-Picture window (requestPictureInPicture()). This limits 28 | a website's ability to provide a custom picture-in-picture experience (PiP). We 29 | want to expand upon that functionality by providing the website with a full 30 | {{Document}} on an always-on-top window. 31 | 32 | This new window will be much like a blank same-origin window opened via the 33 | existing open() method on {{Window}}, with some minor 34 | differences: 35 | 36 | - The PiP window will float on top of other windows. 37 | - The PiP window will never outlive the opening window. 38 | - The website cannot set the position of the PiP window. 39 | - The PiP window cannot be navigated (any `window.history` or `window.location` 40 | calls that change to a new document will close the PiP window). 41 | 42 | # Dependencies # {#dependencies} 43 | 44 | The IDL fragments in this specification must be interpreted as required for 45 | conforming IDL fragments, as described in the Web IDL specification. [[!WEBIDL]] 46 | 47 | # Security Considerations # {#security-considerations} 48 | 49 | ## Secure Context ## {#secure-context} 50 | 51 | The API is limited to [[SECURE-CONTEXTS]]. 52 | 53 | ## Spoofing ## {#spoofing} 54 | 55 | It is required that the user agent provides enough UI on the 56 | {{DocumentPictureInPicture}} window to prevent malicious websites from abusing 57 | the ability to float on top of other windows to spoof other websites or system 58 | UI. 59 | 60 | ### Positioning ### {#positioning} 61 | 62 | The user agent must prevent the website from setting the position of the window 63 | in order to prevent the website from purposefully positioning the window in a 64 | location that may trick a user into thinking it is part of another page's UI. In 65 | particular, this means the {{Window/moveTo()}} and {{Window/moveBy()}} APIs must 66 | be disabled for document picture-in-picture windows. 67 | 68 | ### Origin Visibility ### {#origin-visibility} 69 | 70 | It is required that the user agent makes it clear to the user which origin is 71 | controlling the {{DocumentPictureInPicture}} window at all times to ensure that 72 | the user is aware of where the content is coming from. For example, the user 73 | agent may display the origin of the website in a titlebar on the window. 74 | 75 | ### Maximum size ### {#maximum-size} 76 | 77 | The user agent should restrict the maximum size of the document 78 | picture-in-picture window to prevent the website from covering the screen with 79 | an always-on-top window and locking the user in the picture-in-picture window. 80 | This also helps prevent spoofing the user's desktop. 81 | 82 | ## IFrames ## {#iframes} 83 | 84 | This API is only available on a top-level traversable. However, the 85 | {{DocumentPictureInPicture}} {{Window}} itself may contain {{HTMLIFrameElement}}s, even 86 | cross-origin 87 | {{HTMLIFrameElement}}s. 88 | 89 | # Privacy Considerations # {#privacy-considerations} 90 | 91 | ## Fingerprinting ## {#fingerprinting} 92 | 93 | When a PiP window is closed and then later re-opened, it can be useful for the 94 | user agent to re-use size and location of the previous PiP window to provide a 95 | smoother user experience. However, it is recommended that the user agent does 96 | not re-use size/location across different origins as this may provide malicious 97 | websites an avenue for fingerprinting a user. 98 | 99 | # API # {#api} 100 | 101 |
102 | [Exposed=Window]
103 | partial interface Window {
104 |   [SameObject, SecureContext] readonly attribute DocumentPictureInPicture
105 |     documentPictureInPicture;
106 | };
107 | 
108 | [Exposed=Window, SecureContext]
109 | interface DocumentPictureInPicture : EventTarget {
110 |   [NewObject] Promise<Window> requestWindow(
111 |     optional DocumentPictureInPictureOptions options = {});
112 |   readonly attribute Window window;
113 |   attribute EventHandler onenter;
114 | };
115 | 
116 | dictionary DocumentPictureInPictureOptions {
117 |   [EnforceRange] unsigned long long width = 0;
118 |   [EnforceRange] unsigned long long height = 0;
119 |   boolean disallowReturnToOpener = false;
120 |   boolean preferInitialWindowPlacement = false;
121 | };
122 | 
123 | [Exposed=Window, SecureContext]
124 | interface DocumentPictureInPictureEvent : Event {
125 |   constructor(DOMString type, DocumentPictureInPictureEventInit eventInitDict);
126 |   [SameObject] readonly attribute Window window;
127 | };
128 | 
129 | dictionary DocumentPictureInPictureEventInit : EventInit {
130 |   required Window window;
131 | };
132 | 
133 | 134 |

135 | A {{DocumentPictureInPicture}} object allows websites to create and open a new 136 | always-on-top {{Window}} as well as listen for events related to opening and 137 | closing that {{Window}}. 138 | 139 | Each {{Window}} object has an associated documentPictureInPicture API, 140 | which is a new {{DocumentPictureInPicture}} instance created alongside the {{Window}}. 141 | 142 |

143 | The documentPictureInPicture getter steps are: 144 | 145 | 1. Return this's documentPictureInPicture API. 146 | 147 |
148 | 149 | Each {{DocumentPictureInPicture}} object has an associated 150 | last-opened window which is a 151 | {{Window}} object that is initially null and is set as part of the 152 | requestWindow() method steps. 153 | 154 |
155 | The window getter steps are: 156 | 157 | 1. Let |win| be this's last-opened window. 158 | 2. If |win| is not null and |win|'s closed 159 | attribute is false, return |win|. 160 | 3. Return null. 161 | 162 |
163 | 164 |
165 | The requestWindow(options) method steps are: 166 | 167 | 1. If Document Picture-in-Picture support is false, throw a 168 | "{{NotSupportedError}}" {{DOMException}}. 169 | 2. If this's relevant global object's navigable is not a 170 | top-level traversable, throw a "{{NotAllowedError}}" 171 | {{DOMException}}. 172 | 3. If this's relevant global object's navigable's 173 | Is Document Picture-in-Picture boolean is true, throw a 174 | "{{NotAllowedError}}" {{DOMException}}. 175 | 4. If this's relevant global object does not have 176 | transient activation, throw a "{{NotAllowedError}}" 177 | {{DOMException}}. 178 | 5. If |options|["{{DocumentPictureInPictureOptions/width}}"] exists and is greater than zero, but 179 | |options|["{{DocumentPictureInPictureOptions/height}}"] does not exist or is zero, throw a 180 | {{RangeError}}. 181 | 6. If |options|["{{DocumentPictureInPictureOptions/height}}"] exists and is greater than zero, but 182 | |options|["{{DocumentPictureInPictureOptions/width}}"] does not exist or is zero, throw a 183 | {{RangeError}}. 184 | 7. Consume user activation given this's relevant global object. 185 | 8. Let |win| be this's last-opened window. If |win| is not 186 | null and |win|'s closed attribute is 187 | false, then 188 | close 189 | |win|'s navigable. 190 | 9. Optionally, the user agent can close any existing picture-in-picture windows. 191 | 10. Set |pip traversable| to be the result of 192 | creating a new top-level traversable 193 | given this's relevant global object's navigable's 194 | active browsing context and "_blank". 195 | 196 |

197 | The resulting {{Document}}'s [=Document/URL=] will be `about:blank`, but its 198 | [=document base URL=] will fall back to be that of the initiator that called 199 | {{DocumentPictureInPicture/requestWindow()}}. Some browsers do not implement 200 | this fallback behavior for normal `about:blank` popups; see 201 | whatwg/html#421 for 202 | discussion. Implementers are advised to make sure this inheritance happens as 203 | specified for document picture-in-picture windows, to avoid further interop 204 | problems. 205 |

206 | 207 | 11. Set |pip traversable|'s active document's mode to 208 | this's relevant global object's associated Document's 209 | mode. 210 | 12. Set |pip traversable|'s Is Document Picture-in-Picture boolean to 211 | true. 212 | 13. If |options|["{{DocumentPictureInPictureOptions/width}}"] exists and is 213 | greater than zero: 214 | 1. Optionally, clamp or ignore |options|["{{DocumentPictureInPictureOptions/width}}"] if it is too large or too 215 | small in order to fit a user-friendly window size. 216 | 2. Optionally, size |pip traversable|'s active browsing context's 217 | window such that the distance between the left and right edges of the 218 | viewport are |options|["{{DocumentPictureInPictureOptions/width}}"] 219 | pixels. 220 | 14. If |options|["{{DocumentPictureInPictureOptions/height}}"] exists and is 221 | greater than zero: 222 | 1. Optionally, clamp or ignore |options|["{{DocumentPictureInPictureOptions/height}}"] if it is too large or too 223 | small in order to fit a user-friendly window size. 224 | 2. Optionally, size |pip traversable|'s active browsing context's 225 | window such that the distance between the top and bottom edges of the 226 | viewport are |options|["{{DocumentPictureInPictureOptions/height}}"] 227 | pixels. 228 | 229 | If |options|["{{DocumentPictureInPictureOptions/preferInitialWindowPlacement}}"] 230 | exists and is true, then the user agent may use this hint to prefer behavior 231 | that is similar that is similar to steps 13 and 14, rather than considering any 232 | previous position or size of any previously closed |pip traversable| window. 233 | 234 | 15. If |options|["{{DocumentPictureInPictureOptions/disallowReturnToOpener}}"] exists 235 | and is true, the user agent should not display UI affordances 236 | on the picture-in-picture window that allow the user to return to the 237 | opener window. 238 | 239 |

240 | For both video and document picture-in-picture, user agents often display a 241 | button for the user to return to the original page and close the 242 | picture-in-picture window. While this action makes sense in most cases 243 | (especially for a video picture-in-picture window that returns the video to the 244 | main document), it does not always make sense for document picture-in-picture 245 | windows. {{DocumentPictureInPictureOptions/disallowReturnToOpener}} is a hint to 246 | the user agent from the website as to whether that action makes sense for their 247 | particular document picture-in-picture experience. 248 |

249 | 250 | 15. Configure |pip traversable|'s active browsing context's window to 251 | float on top of other windows. 252 | 16. Set this's last-opened window to |pip traversable|'s active window. 253 | 17. Queue a global task on the 254 | DOM manipulation task source 255 | given this's relevant global object to fire an event 256 | named {{enter}} using {{DocumentPictureInPictureEvent}} on 257 | this with its {{DocumentPictureInPictureEvent/window}} attribute 258 | initialized to |pip traversable|'s active window. 259 | 18. Return |pip traversable|'s active window. 260 | 261 |
262 | 263 |

264 | While the size of the window can be configured by the website, the initial 265 | position is left to the discretion of the user agent. 266 |

267 | 268 |

269 | 270 | 271 | : enter 272 | :: Fired on {{DocumentPictureInPicture}} when a PiP window is opened. 273 | 274 | # Concepts # {#concepts} 275 | 276 | ## Document Picture-in-Picture Support ## {#pip-support} 277 | 278 | Each user agent has a Document Picture-in-Picture Support boolean, 279 | whose value is implementation-defined (and might vary according to user 280 | preferences). 281 | 282 | ## DocumentPictureInPicture Window ## {#is-document-picture-in-picture-window} 283 | 284 | Each top-level traversable has an Is Document Picture-in-Picture 285 | boolean, whose value defaults to false, but can be set to 286 | true in the requestWindow() method steps. 287 | 288 | ## Closing a Document Picture-in-Picture window ## {#close-document-pip-window} 289 | 290 |

291 | Merge this into 292 | close 293 | once it has enough consensus. 294 |

295 | 296 | Modify step 2 of 297 | close, 298 | "If the result of checking if unloading is user-canceled for toUnload is true, 299 | then return." to be: 300 | 301 | 2. If traversable's Is Document Picture-in-Picture boolean is 302 | true, then skip this step. Otherwise, if the result of 303 | checking if unloading is user-canceled 304 | for toUnload is true, then return. 305 | 306 | ## Close any existing PiP windows ## {#close-existing-pip-windows} 307 | 308 | To close any existing picture-in-picture windows: 309 | 310 | 1. For each |top-level traversable| of the user agent's 311 | top-level traversable set: 312 | 1. If |top-level traversable|'s Is Document Picture-in-Picture 313 | boolean is true, then 314 | close 315 | |top-level traversable|. 316 | 2. If |top-level traversable|'s active document's 317 | pictureInPictureElement 318 | is not null, run the 319 | exit Picture-in-Picture algorithm 320 | with |top-level traversable|'s active document. 321 | 3. For each |navigable| of |top-level traversable|'s active document's 322 | descendant navigables: 323 | 1. If |navigable|'s active document's 324 | pictureInPictureElement 325 | is not null, run the 326 | exit Picture-in-Picture algorithm 327 | with |navigable|'s active document. 328 | 329 | ## One PiP Window ## {#one-pip-window} 330 | 331 | Any top-level traversable must have at most one document 332 | picture-in-picture window open at a time. If a top-level traversable 333 | whose active window's documentPictureInPicture API's 334 | last-opened window is not null tries to open another 335 | document picture-in-picture window, the user agent must close the existing 336 | last-opened window as described in the requestWindow() method 337 | steps. 338 | 339 | However, whether only one window is allowed in Picture-in-Picture mode across 340 | all top-level traversables is left to the implementation and the platform. 341 | As such, what happens when there is a Picture-in-Picture request while there is 342 | a top-level traversable whose Is Document Picture-in-Picture 343 | boolean is true or whose active document's 344 | pictureInPictureElement 345 | is not null will be left as an implementation detail: the user 346 | agent could close any existing picture-in-picture windows or multiple 347 | Picture-in-Picture windows could be created. 348 | 349 | ## Closing the PiP window when either the original or PiP document is destroyed ## {#close-on-destroy} 350 | 351 | To close any associated Document Picture-in-Picture windows given a 352 | {{Document}} |document|: 353 | 354 | 1. Let |navigable| be |document|'s node navigable. 355 | 2. If |navigable| is not a top-level traversable, abort these steps. 356 | 3. If |navigable|'s Is Document Picture-in-Picture boolean is 357 | true, then 358 | close 359 | |navigable| and abort these steps. 360 | 4. Let |win| be |navigable|'s active window's 361 | documentPictureInPicture API's last-opened window. 362 | 5. If |win| is not null and |win|'s closed 363 | attribute is false, then 364 | close 365 | |win|'s navigable. 366 | 367 |

368 | Merge this into 369 | destroy 370 | once it has enough consensus. 371 |

372 | 373 | Add a step 10 to the end of 374 | destroy: 375 | 376 | 5. Close any associated Document Picture-in-Picture windows given |document|. 377 | 378 |

379 | This ensures that when a page with an open Document Picture-in-Picture window is 380 | closed, then its PiP window is closed as well. 381 |

382 | 383 | ## Closing the PiP window when either the original or PiP document is navigated ## {#close-on-navigate} 384 | 385 |

386 | Merge this into 387 | navigate 388 | once it has enough consensus. 389 |

390 | 391 | Modify step 16.3 of 392 | navigate, 393 | "Queue a global task on the navigation and traversal task source given navigable's active window to abort navigable's active document.", 394 | and also insert a step 16.4 immediately after it: 395 | 396 | 3. Queue a global task on the 397 | navigation and traversal task source 398 | given |navigable|'s active window to 399 | abort 400 | |navigable|'s active document and close any associated Document Picture-in-Picture windows 401 | given |navigable|'s active document. 402 | 4. If |navigable| is a top-level traversable whose 403 | Is Document Picture-in-Picture boolean is true, then 404 | abort these steps. 405 | 406 |

407 | This ensures that when a page with an open Document Picture-in-Picture window is 408 | navigated, then its PiP window is closed as well. It also ensures that when the 409 | document in a Document Picture-in-Picture window is navigated, the Document 410 | Picture-in-Picture window is closed. 411 |

412 | 413 | ## Resizing the PiP window ## {#resizing-the-pip-window} 414 | 415 |

416 | While programmatically resizing a document picture-in-picture window can be 417 | useful, the always-on-top nature of the window means an unrestricted ability 418 | to resize the window could be abused in annoying or intrusive way. To mitigate 419 | these concerns without completely preventing the use of window resize APIs, we 420 | will have those APIs consume a user gesture for document picture-in-picture 421 | windows. 422 |

423 | 424 |

425 | Merge this into {{Window/resizeTo()}} once it has enough consensus. 426 |

427 | 428 | Add a new step to {{Window/resizeTo()}} after step 3, "If |target| is not an 429 | [=auxiliary browsing context=] that was created by a script (as opposed to by an 430 | action of the user), then return.": 431 | 432 | 4. If |target|'s top-level traversable's 433 | Is Document Picture-in-Picture boolean is true, then: 434 | 1. If this's relevant global object does not have 435 | transient activation, throw a "{{NotAllowedError}}" 436 | {{DOMException}}. 437 | 2. Consume user activation given this's relevant global object. 438 | 439 |

440 | Merge this into {{Window/resizeBy()}} once it has enough consensus. 441 |

442 | 443 | Add a new step to {{Window/resizeBy()}} after step 3, "If |target| is not an 444 | [=auxiliary browsing context=] that was created by a script (as opposed to by an 445 | action of the user), then return.": 446 | 447 | 4. If |target|'s top-level traversable's 448 | Is Document Picture-in-Picture boolean is true, then: 449 | 1. If this's relevant global object does not have 450 | transient activation, throw a "{{NotAllowedError}}" 451 | {{DOMException}}. 452 | 2. Consume user activation given this's relevant global object. 453 | 454 | ## Focusing the opener window ## {#focusing-the-opener-window} 455 | 456 |

457 | It can often be useful for the picture-in-picture window to be able to re-focus 458 | its opener tab, e.g. when the smaller form-factor of the window doesn't fit the 459 | experience the user needs. We modify the {{Window/focus()}} API to allow it to 460 | take system-level focus when a picture-in-picture window is focusing its 461 | opener. 462 |

463 | 464 |

465 | Merge this into {{Window/focus()}} once it has enough consensus. 466 |

467 | 468 | Add a new step to {{Window/focus()}} after step 3, "Run the focusing steps with |current|.": 469 | 470 | 4. If |current| is a top-level traversable, then: 471 | 1. Let |pipWindow| be |current|'s active window's 472 | documentPictureInPicture API's last-opened window. 473 | 2. If |pipWindow| is not null and |pipWindow|'s relevant global object 474 | has transient activation, then: 475 | 1. Consume user activation given |pipWindow|'s relevant global object. 476 | 2. Give |current| system focus. 477 | 478 |

479 | Giving system focus to the opener does not necessarily need to close the 480 | document picture-in-picture window. If the website wants to close the document 481 | picture-in-picture window after focusing, they can always do so using 482 | {{Window/close()}} on the document picture-in-picture window itself. 483 |

484 | 485 | ## CSS display-mode ## {#css-display-mode} 486 | 487 |

488 | The CSS display mode media feature ''@media/display-mode/picture-in-picture'' lets web developers 489 | write specific CSS rules that are only applied when (part of the) the web app is shown in 490 | picture-in-picture mode. 491 |

492 | 493 | ## User activation propagation ## {#user-activation-propagation} 494 | 495 |

496 | Due to the nature of document picture-in-picture windows, event handlers on 497 | buttons within the window often end up actually running in the opener's context. 498 | This can make it unergonomic for websites to call 499 | activation consuming APIs, 500 | since sometimes the document 501 | picture-in-picture window has transient activation while the opener does 502 | not. 503 | 504 | To make this easier, we will update the 505 | activation notification 506 | steps to also trigger user activation in the opener when triggering user 507 | activation in a document picture-in-picture window. Additionally, when user 508 | activation is triggered in the opener, we will activate same-origin frames 509 | insides the document picture-in-picture window, similar to how same-origin 510 | descendant frames are activated. 511 |

512 | 513 |

514 | Merge this into 515 | activation notification 516 | steps once it has enough consensus. 517 |

518 | 519 | Add three new steps to 520 | activation notification 521 | after step 4, "Extend 522 | |windows| with the active window of each of |document|'s 523 | descendant navigables, filtered to include only those navigables 524 | whose active document's origin is same origin with 525 | |document|'s origin": 526 | 527 | 5. If |document|'s node navigable's top-level traversable's 528 | Is Document Picture-in-Picture boolean is true, then 529 | extend |windows| with |document|'s node navigable's 530 | top-level traversable's active browsing context's 531 | opener browsing context's active window. 532 | 533 | 6. Let |document picture-in-picture window| be |document|'s node navigable's 534 | top-level traversable's active window's 535 | documentPictureInPicture API's last-opened window. 536 | 537 | 7. If |document picture-in-picture window| is not null then 538 | extend |windows| with the active window of each of 539 | |document picture-in-picture window|'s associated document's 540 | descendant navigables, filtered to include only those 541 | navigables whose active document's origin is 542 | same origin with |document picture-in-picture window|'s 543 | associated document's origin. 544 | 545 |

546 | Additionally, we need to make sure that this activation is properly consumed so 547 | it can't be used twice (once in the opener and once in the picture-in-picture 548 | window). We do this by adding steps to consume user activation which 549 | consume user activation from the opener when consuming a picture-in-picture 550 | window's user activation, and consuming an associated picture-in-picture 551 | window's user activation when consuming an opener's user activation. 552 |

553 | 554 |

555 | Merge this into consume user activation steps once it has enough 556 | consensus. 557 |

558 | 559 | Add three new steps to consume user activation after step 3, "Let 560 | |navigables| be the inclusive descendant navigables of |top|'s 561 | active document.": 562 | 563 | 4. If |top|'s Is Document Picture-in-Picture boolean is 564 | true, then extend |navigables| with the 565 | inclusive descendant navigables of |top|'s 566 | active browsing context's opener browsing context's 567 | active document. 568 | 569 | 5. Let |document picture-in-picture window| be |top|'s active window's 570 | documentPictureInPicture API's last-opened window. 571 | 572 | 6. If |document picture-in-picture window| is not null then 573 | extend |navigables| with the inclusive descendant navigables 574 | of |document picture-in-picture window|'s associated document. 575 | 576 | # Examples # {#examples} 577 | 578 | This section is non-normative 579 | 580 | ## Extracting a video player into PiP ## {#example-video-player} 581 | 582 | ### HTML ### {#example-video-player-html} 583 | 584 |
585 | <body>
586 |   <div id="player-container">
587 |     <div id="player">
588 |       <video id="video" src="foo.webm"></video>
589 |       <!-- More player elements here. -->
590 |     </div>
591 |   </div>
592 |   <input type="button" onclick="enterPiP();" value="Enter PiP" />
593 | </body>
594 | 
595 | 596 | ### JavaScript ### {#example-video-player-js} 597 | 598 |
599 | // Handle to the picture-in-picture window.
600 | let pipWindow = null;
601 | 
602 | function enterPiP() {
603 |   const player = document.querySelector('#player');
604 | 
605 |   // Set the width/height so the window is properly sized to the video.
606 |   const pipOptions = {
607 |     width: player.clientWidth,
608 |     height: player.clientHeight,
609 |   };
610 | 
611 |   documentPictureInPicture.requestWindow(pipOptions).then((pipWin) => {
612 |     pipWindow = pipWin;
613 | 
614 |     // Style remaining container to imply the player is in PiP.
615 |     playerContainer.classList.add('pip-mode');
616 | 
617 |     // Add player to the PiP window.
618 |     pipWindow.document.body.append(player);
619 | 
620 |     // Listen for the PiP closing event to put the video back.
621 |     pipWindow.addEventListener('pagehide', onLeavePiP.bind(pipWindow), { once: true });
622 |   });
623 | }
624 | 
625 | // Called when the PiP window has closed.
626 | function onLeavePiP() {
627 |   if (this !== pipWindow) {
628 |     return;
629 |   }
630 | 
631 |   // Remove PiP styling from the container.
632 |   const playerContainer = document.querySelector('#player-container');
633 |   playerContainer.classList.remove('pip-mode');
634 | 
635 |   // Add the player back to the main window.
636 |   const player = pipWindow.document.querySelector('#player');
637 |   playerContainer.append(player);
638 | 
639 |   pipWindow = null;
640 | }
641 | 
642 | 643 | ## Accessing elements on the PiP Window ## {#example-access-elements} 644 | 645 |
646 | const video = pipWindow.document.querySelector('#video');
647 | video.loop = true;
648 | 
649 | 650 | ## Listening to events on the PiP Window ## {#example-listen-events} 651 | 652 | As part of creating an improved picture-in-picture experience, websites will often want 653 | customize buttons and controls that need to respond to user input events such as clicks. 654 | 655 |
656 | const pipDocument = pipWindow.document;
657 | const video = pipDocument.querySelector('#video');
658 | const muteButton = pipDocument.document.createElement('button');
659 | muteButton.textContent = 'Toggle mute';
660 | muteButton.addEventListener('click', () => {
661 |   video.muted = !video.muted;
662 | });
663 | pipDocument.body.append(muteButton);
664 | 
665 | 666 | ## Exiting PiP ## {#example-exiting-pip} 667 | 668 | The website may want to close the {{DocumentPictureInPicture}} {{Window}} 669 | without the user explicitly clicking on the window's close button. They can do 670 | this by using the close() method on the {{Window}} 671 | object: 672 | 673 |
674 | // This will close the PiP window and trigger our existing onLeavePiP()
675 | // listener.
676 | pipWindow.close();
677 | 
678 | 679 | ## Getting elements out of the PiP window when it closes ## {#example-elements-out-on-close} 680 | 681 | When the PiP window is closed for any reason (either because the website 682 | initiated it or the user closed it), the website will often want to get the 683 | elements back out of the PiP window. The website can perform this in an event 684 | handler for the {{Window/pagehide}} event on the 685 | {{Window}} object. This is shown in the 686 | onLeavePiP() handler in 687 | video player example above and is copied 688 | below: 689 | 690 |
691 | // Called when the PiP window has closed.
692 | function onLeavePiP() {
693 |   if (this !== pipWindow) {
694 |     return;
695 |   }
696 | 
697 |   // Remove PiP styling from the container.
698 |   const playerContainer = document.querySelector('#player-container');
699 |   playerContainer.classList.remove('pip-mode');
700 | 
701 |   // Add the player back to the main window.
702 |   const player = pipWindow.document.querySelector('#player');
703 |   playerContainer.append(player);
704 | 
705 |   pipWindow = null;
706 | }
707 | 
708 | 709 | ## Programatically resize the PiP window ## {#example-programmatic-resize} 710 | 711 | The document picture-in-picture window supports the {{Window/resizeTo()}} and 712 | {{Window/resizeBy()}} APIs, but only with a user gesture on the PiP window: 713 | 714 |
715 | const expandButton = pipWindow.document.createElement('button');
716 | expandButton.textContent = 'Expand PiP Window';
717 | expandButton.addEventListener('click', () => {
718 |   // Expand the PiP window's width by 20px and height by 30px.
719 |   pipWindow.resizeBy(20, 30);
720 | });
721 | pipWindow.document.body.append(expandButton);
722 | 
723 | 724 | ## Return to the opener tab ## {#example-return-to-tab} 725 | 726 | The {{Window/focus()}} API can be used to focus the opener tab from a 727 | picture-in-picture window (requiring a user gesture): 728 | 729 |
730 | const returnToTabButton = pipWindow.document.createElement('button');
731 | returnToTabButton.textContent = 'Return to opener tab';
732 | returnToTabButton.addEventListener('click', () => {
733 |   window.focus();
734 | });
735 | pipWindow.document.body.append(returnToTabButton);
736 | 
737 | 738 | ## CSS picture-in-picture display mode usage ## {#example-display-mode} 739 | 740 | The following example shows how to remove margins on the body element 741 | and reduce the font size of titles in PiP window to better fit the 742 | content in question inside the PiP window: 743 | 744 |
745 | @media all and (display-mode: picture-in-picture) {
746 |   body {
747 |     margin: 0;
748 |   }
749 |   h1 {
750 |     font-size: 0.8em;
751 |   }
752 | }
753 | 
754 | 755 | ## Hide return-to-opener button ## {#example-hide-return-to-opener} 756 | 757 | While user agents often display a button on their video and document 758 | picture-in-picture windows to return to the opener and close the window, 759 | this button doesn't always make sense for some websites' document 760 | picture-in-picture experience. Use the 761 | {{DocumentPictureInPictureOptions/disallowReturnToOpener}} option to hide the 762 | button. 763 | 764 |
765 | await documentPictureInPicture.requestWindow({
766 |   disallowReturnToOpener: true
767 | });
768 | 
769 | 770 | ## Prefer initial window placement ## {#example-prefer-initial-window-placement} 771 | 772 | While a document picture-in-picture window is open, the user may manually 773 | resize or reposition it. If the document picture-in-picture window is closed, 774 | then reopened later, the user agent may use the previous position and size as 775 | a hint for where to place the new window rather than opening it in is original, 776 | default position. 777 | 778 | The site can provide a hint to the user agent that reusing the previous 779 | document picture-in-picture window position and size is not desirable 780 | by setting the {{DocumentPictureInPictureOptions/preferInitialWindowPlacement}} 781 | value to true. For example, if the site is requesting the new document 782 | picture-in-picture window for an unrelated activity from the previous one, then 783 | the site might provide this hint to the user agent. In response, the user 784 | agent may choose to use the default position, the default size, or the size 785 | hint provided by the site instead. 786 | 787 |
788 | await documentPictureInPicture.requestWindow({
789 |   preferInitialWindowPlacement: true
790 | });
791 | 
792 | 793 | # Acknowledgments # {#acknowledgments} 794 | 795 | Many thanks to Frank Liberato, Mark Foltz, Klaus Weidner, François Beaufort, 796 | Charlie Reis, Joe DeBlasio, Domenic Denicola, and Yiren Wang for their comments 797 | and contributions to this document and to the discussions that have informed it. 798 | --------------------------------------------------------------------------------