Bug 201024

Summary: [Picture-in-Picture Web API] Implement HTMLVideoElement.requestPictureInPicture() / Document.exitPictureInPicture()
Product: WebKit Reporter: Carlos Bentzen <cadubentzen>
Component: MediaAssignee: Peng Liu <peng.liu6>
Status: RESOLVED FIXED    
Severity: Normal CC: annulen, benjamin, cadubentzen, calvaris, cdumez, commit-queue, darin, dbates, eric.carlson, esprehn+autocc, ews-watchlist, glenn, gyuyoung.kim, jbedard, jer.noble, jonlee, kangil.han, keith_miller, kondapallykalyan, mark.lam, msaboff, peng.liu6, philipj, rakuco, ryuan.choi, saam, sergio, tzagallo, webkit-bug-importer, youennf
Priority: P2 Keywords: InRadar
Version: WebKit Nightly Build   
Hardware: Unspecified   
OS: Unspecified   
Bug Depends on:    
Bug Blocks: 182688, 202774    
Attachments:
Description Flags
WIP
none
WIP
none
WIP
none
Patch
none
Update the patch to fix several layout test case failures
none
An updated patch to fix iOS build
none
An updated patch to fix gtk build failure
none
Patch
none
Patch
none
Patch
none
Patch
none
Patch
eric.carlson: review+
Patch for landing none

Carlos Bentzen
Reported 2019-08-21 20:28:58 PDT
This should be the first part of the API: the ability to enter and exit Picture-in-Picture. Implementation can reuse code of webkitSetPresentationMode().
Attachments
WIP (180.98 KB, patch)
2019-08-21 20:35 PDT, Carlos Bentzen
no flags
WIP (139.00 KB, patch)
2019-08-21 20:53 PDT, Carlos Bentzen
no flags
WIP (143.11 KB, patch)
2019-08-22 14:41 PDT, Carlos Bentzen
no flags
Patch (162.95 KB, patch)
2019-10-05 22:04 PDT, Peng Liu
no flags
Update the patch to fix several layout test case failures (163.02 KB, patch)
2019-10-06 11:51 PDT, Peng Liu
no flags
An updated patch to fix iOS build (159.22 KB, patch)
2019-10-06 13:57 PDT, Peng Liu
no flags
An updated patch to fix gtk build failure (159.15 KB, patch)
2019-10-06 15:31 PDT, Peng Liu
no flags
Patch (171.21 KB, patch)
2019-10-08 14:23 PDT, Peng Liu
no flags
Patch (169.76 KB, patch)
2019-10-09 16:30 PDT, Peng Liu
no flags
Patch (167.39 KB, patch)
2019-10-11 15:20 PDT, Peng Liu
no flags
Patch (168.42 KB, patch)
2019-10-14 11:31 PDT, Peng Liu
no flags
Patch (168.93 KB, patch)
2019-10-14 18:54 PDT, Peng Liu
eric.carlson: review+
Patch for landing (169.34 KB, patch)
2019-10-15 13:54 PDT, Peng Liu
no flags
Carlos Bentzen
Comment 1 2019-08-21 20:35:39 PDT
Created attachment 376973 [details] WIP WIP patch: - Implements HTMLVideoElement.requestPictureInPicture() - Imports WPT tests - Adds compile flag to XCode (TODO CMake) and runtime flag Need to implement document.exitPictureInPicture() too for the patch to be self-contained, I think. Should be the minimal for the demo at https://googlechrome.github.io/samples/picture-in-picture/ to work minimally. Will split this patch into smaller ones for the flags first and need to skip PIP tests too. However, I accept comments on the requestPictureInPicture() bits. - Is it OK to use Supplement/Supplementable or it preferable add to HTMLVideoElement directly?
Carlos Bentzen
Comment 2 2019-08-21 20:44:32 PDT
(In reply to Carlos Eduardo Ramalho from comment #1) > Created attachment 376973 [details] > WIP Looks like it needs some rebasing. Will do.
Carlos Bentzen
Comment 3 2019-08-21 20:53:58 PDT
Created attachment 376975 [details] WIP Rebased WIP patch.
Carlos Bentzen
Comment 4 2019-08-22 09:24:43 PDT
The UserGestureIndicator::processingUserGesture() check returns false if the call was done via the Web Inspector. Is there any way to enable accepting calls to requestPictureInPicture() in the web inspector too, other than just removing this check? I was thinking of not enabling web-sites to call this by themselves without user activation, but let developers call it on the web inspector. I do it all the time when I can't reach the context menu for Picture-in-Picture easily.
youenn fablet
Comment 5 2019-08-22 09:45:30 PDT
(In reply to Carlos Eduardo Ramalho from comment #4) > The UserGestureIndicator::processingUserGesture() check returns false if the > call was done via the Web Inspector. Is there any way to enable accepting > calls to requestPictureInPicture() in the web inspector too, other than just > removing this check? In Web Inspector console tab, you can select "Emulate User Gesture" for that purpose.
Carlos Bentzen
Comment 6 2019-08-22 09:52:53 PDT
(In reply to youenn fablet from comment #5) > (In reply to Carlos Eduardo Ramalho from comment #4) > > The UserGestureIndicator::processingUserGesture() check returns false if the > > call was done via the Web Inspector. Is there any way to enable accepting > > calls to requestPictureInPicture() in the web inspector too, other than just > > removing this check? > > In Web Inspector console tab, you can select "Emulate User Gesture" for that > purpose. Great! Hadn't noticed that. Thanks :)
Carlos Bentzen
Comment 7 2019-08-22 14:41:20 PDT
Created attachment 377047 [details] WIP - Implements Document.exitPictureInPicture() - Adds Logging utilities As a next step I should probably refactor this messy code into a central "controller" or "manager" class, owned by Document, which gathers the spec algorithms. The current code is breaking inheritance and has no clearly defined lifetimes. Nevertheless, it should be able to enter/exit PIP using the API now. Running the WPT tests with https://w3c-test.org/tools/runner/index.html gives: Passed: 8 Failed: 7 Timeouts: 8 Errors: 0 Not Run: 2 So, some progress.
Carlos Bentzen
Comment 8 2019-08-22 14:42:45 PDT
(In reply to Carlos Eduardo Ramalho from comment #7) > Running the WPT tests with https://w3c-test.org/tools/runner/index.html > gives: The query: /picture-in-picture
Peng Liu
Comment 9 2019-09-16 15:48:19 PDT
Hi Carlos, any update on the progress? Thanks!
Carlos Bentzen
Comment 10 2019-09-17 07:15:11 PDT
(In reply to Peng Liu from comment #9) > Hi Carlos, any update on the progress? Thanks! Hi Peng Liu! Progress was halted last weeks due to other obligations. I shall get back to it this week and send a reviewable patch.
Peng Liu
Comment 11 2019-10-05 22:04:10 PDT
Peng Liu
Comment 12 2019-10-05 22:14:03 PDT
Comment on attachment 380292 [details] Patch Majority work of this patch was done by Carlos Eduardo Ramalho <cadubentzen@gmail.com>. Currently, enter-picture-in-picture.html and exit-picture-in-picture.html pass if we open them in the browser or with https://w3c-test.org/tools/runner/index.html.
Peng Liu
Comment 13 2019-10-06 11:51:08 PDT
Created attachment 380301 [details] Update the patch to fix several layout test case failures
Peng Liu
Comment 14 2019-10-06 13:57:57 PDT
Created attachment 380303 [details] An updated patch to fix iOS build
Peng Liu
Comment 15 2019-10-06 15:31:47 PDT
Created attachment 380304 [details] An updated patch to fix gtk build failure
Peng Liu
Comment 16 2019-10-08 14:23:14 PDT
Eric Carlson
Comment 17 2019-10-09 09:42:33 PDT
Comment on attachment 380464 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=380464&action=review r=me with the suggested fixes. I think this new API *needs* extensive runtime logging. I'm OK with adding that in a followup patch, but file a bug and add it quickly. > Source/WebCore/Modules/pictureinpicture/DocumentPictureInPicture.cpp:47 > + auto element = document.pictureInPictureElement(); > + if (!element) { You should also ensure that document.pictureInPictureElement() returns a video element: if (!element || !is<HTMLVideoElement>(*element) ) { ASSERT(is<HTMLVideoElement>(element)) ... > Source/WebCore/Modules/pictureinpicture/DocumentPictureInPicture.h:44 > + static bool pictureInPictureEnabled(Document&) { return true; } Ports definitely need a way to disable PiP at runtime. Why not check the PictureInPictureAPI runtime flag here instead of always returning true? > Source/WebCore/Modules/pictureinpicture/HTMLVideoElementPictureInPicture.cpp:68 > + if (!videoElement.player() || !videoElement.player()->supportsPictureInPicture()) { > + promise->reject(NotSupportedError); > + return; Nit: all of these rejections should log so it is possible to figure exactly what caused the failure. > Source/WebCore/html/HTMLVideoElement.cpp:495 > + if (toPresentationMode(mode) == HTMLVideoElement::VideoPresentationMode::PictureInPicture && toPresentationMode(fullscreenMode()) != HTMLVideoElement::VideoPresentationMode::PictureInPicture) > + m_pictureInPictureObserver->didEnterPictureInPicture(); > + else if (toPresentationMode(mode) != HTMLVideoElement::VideoPresentationMode::PictureInPicture && toPresentationMode(fullscreenMode()) == HTMLVideoElement::VideoPresentationMode::PictureInPicture) > + m_pictureInPictureObserver->didExitPictureInPicture(); Nit: you might as well cache toPresentationMode() in local variables instead of calling it multiple times. > Source/WebCore/platform/Logging.h:85 > + M(PictureInPictureAPI) \ This is unused, and I think it would be better to use the existing Media logging channel in any case. > Source/WebCore/platform/PictureInPictureObserver.h:28 > +#if PLATFORM(IOS_FAMILY) || (PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE)) Is this the right set of build flags - don't we want "#if ENABLE(PICTURE_IN_PICTURE_API)"
Eric Carlson
Comment 18 2019-10-09 09:51:51 PDT
Comment on attachment 380464 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=380464&action=review >> Source/WebCore/Modules/pictureinpicture/DocumentPictureInPicture.h:44 >> + static bool pictureInPictureEnabled(Document&) { return true; } > > Ports definitely need a way to disable PiP at runtime. Why not check the PictureInPictureAPI runtime flag here instead of always returning true? The runtime flag clearly won't work because it disables the feature completely, but I think do. need a way to have the API available but disabled (which I assume is the purpose of this attribute).
Peng Liu
Comment 19 2019-10-09 16:30:30 PDT
Peng Liu
Comment 20 2019-10-09 16:57:39 PDT
Comment on attachment 380464 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=380464&action=review >> Source/WebCore/Modules/pictureinpicture/DocumentPictureInPicture.cpp:47 >> + if (!element) { > > You should also ensure that document.pictureInPictureElement() returns a video element: > > if (!element || !is<HTMLVideoElement>(*element) ) { > ASSERT(is<HTMLVideoElement>(element)) > ... Right. I will fix it. >>> Source/WebCore/Modules/pictureinpicture/DocumentPictureInPicture.h:44 >>> + static bool pictureInPictureEnabled(Document&) { return true; } >> >> Ports definitely need a way to disable PiP at runtime. Why not check the PictureInPictureAPI runtime flag here instead of always returning true? > > The runtime flag clearly won't work because it disables the feature completely, but I think do. need a way to have the API available but disabled (which I assume is the purpose of this attribute). The API that will be available but disabled is disablePictureInPicture in the Extensions to HTMLVideoElement. In the new patch, EnabledBySetting is used instead of the EnabledAtRuntime in the IDL, then we can always return true in this function. >> Source/WebCore/Modules/pictureinpicture/HTMLVideoElementPictureInPicture.cpp:68 >> + return; > > Nit: all of these rejections should log so it is possible to figure exactly what caused the failure. I will file a bug to add log messages. > Source/WebCore/Modules/pictureinpicture/HTMLVideoElementPictureInPicture.cpp:83 > + if (HTMLVideoElementPictureInPicture::disablePictureInPicture(videoElement)) { This block needs to be removed. >> Source/WebCore/html/HTMLVideoElement.cpp:495 >> + m_pictureInPictureObserver->didExitPictureInPicture(); > > Nit: you might as well cache toPresentationMode() in local variables instead of calling it multiple times. Good idea. >> Source/WebCore/platform/Logging.h:85 >> + M(PictureInPictureAPI) \ > > This is unused, and I think it would be better to use the existing Media logging channel in any case. Right. >> Source/WebCore/platform/PictureInPictureObserver.h:28 >> +#if PLATFORM(IOS_FAMILY) || (PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE)) > > Is this the right set of build flags - don't we want "#if ENABLE(PICTURE_IN_PICTURE_API)" I chose the current flags because the class can be used in other scenarios even though it was added to support Picture-in-Picture API. I will change the flags to "#if ENABLE(PICTURE_IN_PICTURE_API)" anyway.
EWS Watchlist
Comment 21 2019-10-10 21:24:39 PDT
Comment on attachment 380584 [details] Patch Attachment 380584 [details] did not pass jsc-ews (mac): Output: https://webkit-queues.webkit.org/results/13117759 New failing tests: mozilla-tests.yaml/js1_5/Array/regress-101964.js.mozilla-dfg-eager-no-cjit-validate-phases mozilla-tests.yaml/js1_5/Array/regress-101964.js.mozilla-ftl-eager-no-cjit-validate-phases
Eric Carlson
Comment 22 2019-10-11 08:17:22 PDT
Comment on attachment 380584 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=380584&action=review > Source/WebCore/Modules/pictureinpicture/DocumentPictureInPicture.cpp:50 > + auto element = document.pictureInPictureElement(); > + if (!element || !is<HTMLVideoElement>(*element)) { > + promise->reject(InvalidStateError); > + return; > + } The PiP element should *always* be an HTMLVideoElement, so this ASSERT so we can catch it in debug builds. > Source/WebCore/Modules/pictureinpicture/DocumentPictureInPicture.cpp:53 > + HTMLVideoElement& videoElement = downcast<HTMLVideoElement>(*element); > + HTMLVideoElementPictureInPicture::from(videoElement)->exitPictureInPicture(WTFMove(promise)); Nit: this can be done in one line. > Source/WebCore/Modules/pictureinpicture/HTMLVideoElementPictureInPicture.cpp:102 > + videoElementPictureInPicture->m_enteringPictureInPicture = true; > + videoElementPictureInPicture->m_enterPictureInPicturePromise = WTFMove(promise); Do you need a both a bool and a Promise? Couldn't you use just the promises to signal that enter or exit is in progress? > Source/WebKit/UIProcess/API/C/WKPreferencesRef.h:191 > +// Defaults to false > +WK_EXPORT void WKPreferencesSetPictureInPictureAPIEnabled(WKPreferencesRef preferencesRef, bool enabled); > +WK_EXPORT bool WKPreferencesGetPictureInPictureAPIEnabled(WKPreferencesRef preferencesRef); > + Do we need to add new C API?
Peng Liu
Comment 23 2019-10-11 15:12:02 PDT
Comment on attachment 380584 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=380584&action=review >> Source/WebCore/Modules/pictureinpicture/DocumentPictureInPicture.cpp:50 >> + } > > The PiP element should *always* be an HTMLVideoElement, so this ASSERT so we can catch it in debug builds. Right. I think the code can be changed to: if (!element) { promise->reject(InvalidStateError); return; } ASSERT(is<HTMLVideoElement>(element)); >> Source/WebCore/Modules/pictureinpicture/DocumentPictureInPicture.cpp:53 >> + HTMLVideoElementPictureInPicture::from(videoElement)->exitPictureInPicture(WTFMove(promise)); > > Nit: this can be done in one line. Correct. >> Source/WebCore/Modules/pictureinpicture/HTMLVideoElementPictureInPicture.cpp:102 >> + videoElementPictureInPicture->m_enterPictureInPicturePromise = WTFMove(promise); > > Do you need a both a bool and a Promise? Couldn't you use just the promises to signal that enter or exit is in progress? Yes, we can use the promises for that purpose. Will update the patch. >> Source/WebKit/UIProcess/API/C/WKPreferencesRef.h:191 >> + > > Do we need to add new C API? No. I will remove related changes.
Peng Liu
Comment 24 2019-10-11 15:20:48 PDT
Darin Adler
Comment 25 2019-10-11 15:30:41 PDT
Comment on attachment 380792 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=380792&action=review > Source/WebCore/Modules/pictureinpicture/DocumentPictureInPicture.cpp:53 > + ASSERT(is<HTMLVideoElement>(element)); Three comments: 1) The whole point of downcast<> is that it contains this assertion built in. So we don’t need this. 2) Maybe we do want a RELEASE_ASSERT here. Some day maybe we’ll change downcast to include a RELEASE_ASSERT instead of an ASSERT, but until then there may be security hardening benefit in doing this type check. 3) If pictureInPictureElement is guaranteed to be HTMLVideoElement, then why isn’t it defined that way? Or maybe it’s only guaranteed to be that in some contexts?
Peng Liu
Comment 26 2019-10-11 16:59:20 PDT
Comment on attachment 380792 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=380792&action=review >> Source/WebCore/Modules/pictureinpicture/DocumentPictureInPicture.cpp:53 >> + ASSERT(is<HTMLVideoElement>(element)); > > Three comments: > > 1) The whole point of downcast<> is that it contains this assertion built in. So we don’t need this. > > 2) Maybe we do want a RELEASE_ASSERT here. Some day maybe we’ll change downcast to include a RELEASE_ASSERT instead of an ASSERT, but until then there may be security hardening benefit in doing this type check. > > 3) If pictureInPictureElement is guaranteed to be HTMLVideoElement, then why isn’t it defined that way? Or maybe it’s only guaranteed to be that in some contexts? The pictureInPictureElement is guaranteed to be HTMLVideoElement in current implementation. We chose to use Element as the type to follow the picture-in-picture API spec and we may need to support picture-in-picture for other elements in the future. But for now, we can define it as HTMLVideoElement and I will update the patch accordingly. Since we will use HTMLVideoElement as the type, the downcast and assert will not be necessary.
Peng Liu
Comment 27 2019-10-14 11:31:50 PDT
Radar WebKit Bug Importer
Comment 28 2019-10-14 16:03:50 PDT
Peng Liu
Comment 29 2019-10-14 18:54:04 PDT
Created attachment 380945 [details] Patch Rebased
Eric Carlson
Comment 30 2019-10-15 06:25:56 PDT
Comment on attachment 380945 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=380945&action=review > Source/WebCore/Modules/pictureinpicture/PictureInPictureWindow.cpp:59 > + return false; This will prevent any page with a PictureInPictureWindow from entering the PageCache. Can we do better? > Source/WebCore/Modules/pictureinpicture/PictureInPictureWindow.h:47 > + int width() const { return m_width; } > + int height() const { return m_height; } You aren't maintaining PiP window 'state', which is important here as spec says: The [width/height] attribute MUST return the [width/height] in CSS pixels of the Picture-in-Picture window associated with pictureInPictureElement if the state is opened. Otherwise, it MUST return 0. > Source/WebCore/dom/Document.cpp:109 > +#if ENABLE(PICTURE_IN_PICTURE_API) > +#include "HTMLVideoElement.h" > +#endif Nit: conditionally included headers are typically listed after the unconditional ones.
Chris Dumez
Comment 31 2019-10-15 10:26:16 PDT
Comment on attachment 380945 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=380945&action=review >> Source/WebCore/Modules/pictureinpicture/PictureInPictureWindow.cpp:59 >> + return false; > > This will prevent any page with a PictureInPictureWindow from entering the PageCache. Can we do better? This should return true. Please do not add new ActiveDOMObjects that prevent page caching. This definitely does not need to prevent page cache right now. Later on, when you actually do fire the resize event, I suggest you enqueue it to the DocumentEventQueue instead of dispatching it yourself synchronously. The DocumentEventQueue is page-cache aware. > Source/WebCore/Modules/pictureinpicture/PictureInPictureWindow.cpp:62 > +void PictureInPictureWindow::stop() Probably not needed.
Peng Liu
Comment 32 2019-10-15 10:39:56 PDT
Comment on attachment 380945 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=380945&action=review >>> Source/WebCore/Modules/pictureinpicture/PictureInPictureWindow.cpp:59 >>> + return false; >> >> This will prevent any page with a PictureInPictureWindow from entering the PageCache. Can we do better? > > This should return true. Please do not add new ActiveDOMObjects that prevent page caching. > > This definitely does not need to prevent page cache right now. Later on, when you actually do fire the resize event, I suggest you enqueue it to the DocumentEventQueue instead of dispatching it yourself synchronously. The DocumentEventQueue is page-cache aware. Got it, thanks! >> Source/WebCore/Modules/pictureinpicture/PictureInPictureWindow.cpp:62 >> +void PictureInPictureWindow::stop() > > Probably not needed. OK, will remove it. >> Source/WebCore/Modules/pictureinpicture/PictureInPictureWindow.h:47 >> + int height() const { return m_height; } > > You aren't maintaining PiP window 'state', which is important here as spec says: > > The [width/height] attribute MUST return the [width/height] in CSS pixels of > the Picture-in-Picture window associated with pictureInPictureElement if > the state is opened. Otherwise, it MUST return 0. This is planned to be done for https://bugs.webkit.org/show_bug.cgi?id=202615. >> Source/WebCore/dom/Document.cpp:109 >> +#endif > > Nit: conditionally included headers are typically listed after the unconditional ones. I see. Will fix it.
Peng Liu
Comment 33 2019-10-15 13:54:56 PDT
Created attachment 381018 [details] Patch for landing
WebKit Commit Bot
Comment 34 2019-10-15 15:06:31 PDT
Comment on attachment 381018 [details] Patch for landing Clearing flags on attachment: 381018 Committed r251160: <https://trac.webkit.org/changeset/251160>
Note You need to log in before you can comment on or make changes to this bug.