Media elements outside fullscreen should not be considered main content.
authorjer.noble@apple.com <jer.noble@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 30 May 2018 18:00:23 +0000 (18:00 +0000)
committerjer.noble@apple.com <jer.noble@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 30 May 2018 18:00:23 +0000 (18:00 +0000)
https://bugs.webkit.org/show_bug.cgi?id=186063
<rdar://problem/40630437>

Reviewed by Eric Carlson.

Source/WebCore:

Test: platform/mac/media/video-best-element-for-playback-controls-purpose.html

Media elements outside the current fullscreen element are not visible, and thus should not be considered
main content.

Drive-by fix: set the m_hasEverNotifiedAboutPlaying before dispatching the 'playing' event, so that
tests can check bestMediaElementForShowingPlaybackControlsManager() in the 'playing' handler.

* html/HTMLMediaElement.cpp:
(WebCore::HTMLMediaElement::notifyAboutPlaying):
* html/HTMLMediaElement.h:
* html/MediaElementSession.cpp:
(WebCore::MediaElementSession::canShowControlsManager const):
* testing/Internals.cpp:
(WebCore::Internals::bestMediaElementForShowingPlaybackControlsManager):
* testing/Internals.h:
* testing/Internals.idl:

LayoutTests:

* platform/mac/media/video-best-element-for-playback-controls-purpose-expected.txt: Added.
* platform/mac/media/video-best-element-for-playback-controls-purpose.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@232300 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/ChangeLog
LayoutTests/platform/mac/media/video-best-element-for-playback-controls-purpose-expected.txt [new file with mode: 0644]
LayoutTests/platform/mac/media/video-best-element-for-playback-controls-purpose.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/html/HTMLMediaElement.cpp
Source/WebCore/html/HTMLMediaElement.h
Source/WebCore/html/MediaElementSession.cpp
Source/WebCore/testing/Internals.cpp
Source/WebCore/testing/Internals.h
Source/WebCore/testing/Internals.idl

index b846617..3aace47 100644 (file)
@@ -1,3 +1,14 @@
+2018-05-30  Jer Noble  <jer.noble@apple.com>
+
+        Media elements outside fullscreen should not be considered main content.
+        https://bugs.webkit.org/show_bug.cgi?id=186063
+        <rdar://problem/40630437>
+
+        Reviewed by Eric Carlson.
+
+        * platform/mac/media/video-best-element-for-playback-controls-purpose-expected.txt: Added.
+        * platform/mac/media/video-best-element-for-playback-controls-purpose.html: Added.
+
 2018-05-30  Ms2ger  <Ms2ger@igalia.com>
 
         [GTK] Unreviewed test gardening
diff --git a/LayoutTests/platform/mac/media/video-best-element-for-playback-controls-purpose-expected.txt b/LayoutTests/platform/mac/media/video-best-element-for-playback-controls-purpose-expected.txt
new file mode 100644 (file)
index 0000000..9ac0126
--- /dev/null
@@ -0,0 +1,49 @@
+
+Unloaded video elements should not be considered main content.
+EXPECTED (internals.bestMediaElementForShowingPlaybackControlsManager("ControlsManager") == 'null') OK
+
+Large, autoplay videos with video and audio should be considered main content.
+RUN(video = createVideo({autoplay: true, type: "audio+video", size: "large"}))
+EVENT(playing)
+EXPECTED (internals.bestMediaElementForShowingPlaybackControlsManager("ControlsManager") == '[object HTMLVideoElement]') OK
+
+Small, autoplay videos with video and audio should be considered main content.
+RUN(video = createVideo({autoplay: true, type: "audio+video", size: "small"}))
+EVENT(playing)
+EXPECTED (internals.bestMediaElementForShowingPlaybackControlsManager("ControlsManager") == 'null') OK
+
+Muted autoplay videos should not be considered main content.
+RUN(video = createVideo({autoplay: true, muted: true, type: "audio+video", size: "large"}))
+EVENT(playing)
+EXPECTED (internals.bestMediaElementForShowingPlaybackControlsManager("ControlsManager") == 'null') OK
+
+Video-only autoplay videos should not be considered main content.
+RUN(video = createVideo({autoplay: true, type: "video", size: "large"}))
+EVENT(playing)
+EXPECTED (internals.bestMediaElementForShowingPlaybackControlsManager("ControlsManager") == 'null') OK
+
+Non-playing videos should not be considered main content.
+RUN(video = createVideo({type: "audio+video", size: "large"}))
+EVENT(canplaythrough)
+EXPECTED (internals.bestMediaElementForShowingPlaybackControlsManager("ControlsManager") == 'null') OK
+
+Large, autoplay videos outside fullscreen element should not be considered main content
+RUN(video = createVideo({autoplay: true, muted: true, type: "audio+video", size: "large"}))
+EVENT(playing)
+RUN(document.querySelector("#fullscreen").webkitRequestFullscreen())
+EVENT(webkitfullscreenchange)
+EXPECTED (internals.bestMediaElementForShowingPlaybackControlsManager("ControlsManager") == 'null') OK
+RUN(document.webkitExitFullscreen())
+EVENT(webkitfullscreenchange)
+
+Large, autoplay videos inside fullscreen element should be considered main content
+RUN(video = createVideo({autoplay: true, type: "audio+video", size: "large"}))
+EVENT(playing)
+RUN(document.querySelector("#target").webkitRequestFullscreen())
+EVENT(webkitfullscreenchange)
+EXPECTED (internals.bestMediaElementForShowingPlaybackControlsManager("ControlsManager") == '[object HTMLVideoElement]') OK
+RUN(document.webkitExitFullscreen())
+EVENT(webkitfullscreenchange)
+
+END OF TEST
+
diff --git a/LayoutTests/platform/mac/media/video-best-element-for-playback-controls-purpose.html b/LayoutTests/platform/mac/media/video-best-element-for-playback-controls-purpose.html
new file mode 100644 (file)
index 0000000..2f67b12
--- /dev/null
@@ -0,0 +1,129 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>audio-session-category-track-change</title>
+    <script src="../../../media/video-test.js"></script>
+    <script src="../../../media/media-file.js"></script>
+    <script>
+    async function go() {
+        for (test of tests) {
+            await test();
+            consoleWrite('');
+        }
+        endTest();
+    }
+
+    function createVideo(parameters) {
+        let target = document.querySelector('#target');
+        target.innerHTML = '';
+
+        let video = document.createElement('video');
+        target.appendChild(video);
+
+        if (!parameters)
+            return video;
+
+        if (parameters.autoplay)
+            video.autoplay = parameters.autoplay;
+
+        if (parameters.muted)
+            video.muted = parameters.muted;
+
+        if (parameters.size) {
+            if (parameters.size === 'large') {
+                video.width = 640;
+                video.height = 480;
+            } else if (parameters.size === 'small') {
+                video.width = 320;
+                video.height = 240;
+            }
+        }
+
+        if (parameters.type) {
+            if (parameters.type === 'audio+video')
+                video.src = findMediaFile("video", "../../../media/content/test");
+            else if (parameters.type === 'video')
+                video.src = findMediaFile("video", "../../../media/content/long-test");
+        }
+
+        return video;
+    }
+
+    function enterFullscreen(target) {
+        runWithKeyDown(`document.querySelector("${target}").webkitRequestFullscreen()`);
+        return waitFor(document, 'webkitfullscreenchange');
+    }
+
+    function exitFullscreen() {
+        runWithKeyDown('document.webkitExitFullscreen()');
+        return waitFor(document, 'webkitfullscreenchange');
+    }
+
+    let tests = [
+        function() {
+            consoleWrite('Unloaded video elements should not be considered main content.');
+            video = createVideo();
+            testExpected('internals.bestMediaElementForShowingPlaybackControlsManager("ControlsManager")', null)
+        },
+
+        async function() {
+            consoleWrite('Large, autoplay videos with video and audio should be considered main content.')
+            run('video = createVideo({autoplay: true, type: "audio+video", size: "large"})');
+            await waitFor(video, 'playing');
+            testExpected('internals.bestMediaElementForShowingPlaybackControlsManager("ControlsManager")', video)
+        },
+
+        async function() {
+            consoleWrite('Small, autoplay videos with video and audio should be considered main content.')
+            run('video = createVideo({autoplay: true, type: "audio+video", size: "small"})');
+            await waitFor(video, 'playing');
+            testExpected('internals.bestMediaElementForShowingPlaybackControlsManager("ControlsManager")', null)
+        },
+
+        async function() {
+            consoleWrite('Muted autoplay videos should not be considered main content.')
+            run('video = createVideo({autoplay: true, muted: true, type: "audio+video", size: "large"})');
+            await waitFor(video, 'playing');
+            testExpected('internals.bestMediaElementForShowingPlaybackControlsManager("ControlsManager")', null)
+        },
+
+        async function() {
+            consoleWrite('Video-only autoplay videos should not be considered main content.')
+            run('video = createVideo({autoplay: true, type: "video", size: "large"})');
+            await waitFor(video, 'playing');
+            testExpected('internals.bestMediaElementForShowingPlaybackControlsManager("ControlsManager")', null)
+        },
+
+        async function() {
+            consoleWrite('Non-playing videos should not be considered main content.')
+            run('video = createVideo({type: "audio+video", size: "large"})');
+            await waitFor(video, 'canplaythrough');
+            testExpected('internals.bestMediaElementForShowingPlaybackControlsManager("ControlsManager")', null)
+        },
+
+        async function() {
+            consoleWrite('Large, autoplay videos outside fullscreen element should not be considered main content');
+            run('video = createVideo({autoplay: true, muted: true, type: "audio+video", size: "large"})');
+            await waitFor(video, 'playing');
+            await enterFullscreen('#fullscreen');
+            testExpected('internals.bestMediaElementForShowingPlaybackControlsManager("ControlsManager")', null);
+            await exitFullscreen();
+        },
+
+        async function() {
+            consoleWrite('Large, autoplay videos inside fullscreen element should be considered main content');
+            run('video = createVideo({autoplay: true, type: "audio+video", size: "large"})');
+            await waitFor(video, 'playing');
+            await enterFullscreen('#target');
+            testExpected('internals.bestMediaElementForShowingPlaybackControlsManager("ControlsManager")', video);
+            await exitFullscreen();
+        },
+    ];
+
+    </script>
+</head>
+<body onload="go()">
+    <div id="target"></div>
+    <div id="fullscreen"></div>
+</body>
+</html>
index 760d774..23e2308 100644 (file)
@@ -1,3 +1,29 @@
+2018-05-30  Jer Noble  <jer.noble@apple.com>
+
+        Media elements outside fullscreen should not be considered main content.
+        https://bugs.webkit.org/show_bug.cgi?id=186063
+        <rdar://problem/40630437>
+
+        Reviewed by Eric Carlson.
+
+        Test: platform/mac/media/video-best-element-for-playback-controls-purpose.html
+
+        Media elements outside the current fullscreen element are not visible, and thus should not be considered
+        main content.
+
+        Drive-by fix: set the m_hasEverNotifiedAboutPlaying before dispatching the 'playing' event, so that
+        tests can check bestMediaElementForShowingPlaybackControlsManager() in the 'playing' handler.
+
+        * html/HTMLMediaElement.cpp:
+        (WebCore::HTMLMediaElement::notifyAboutPlaying):
+        * html/HTMLMediaElement.h:
+        * html/MediaElementSession.cpp:
+        (WebCore::MediaElementSession::canShowControlsManager const):
+        * testing/Internals.cpp:
+        (WebCore::Internals::bestMediaElementForShowingPlaybackControlsManager):
+        * testing/Internals.h:
+        * testing/Internals.idl:
+
 2018-05-30  Michael Catanzaro  <mcatanzaro@igalia.com>
 
         Unreviewed, silence a -Wreturn-type warning
index a848f95..e09f5ae 100644 (file)
@@ -1120,10 +1120,10 @@ void HTMLMediaElement::notifyAboutPlaying(PlayPromiseVector&& pendingPlayPromise
 {
     Ref<HTMLMediaElement> protectedThis(*this); // The 'playing' event can make arbitrary DOM mutations.
     m_playbackStartedTime = currentMediaTime().toDouble();
+    m_hasEverNotifiedAboutPlaying = true;
     dispatchEvent(Event::create(eventNames().playingEvent, false, true));
     resolvePendingPlayPromises(WTFMove(pendingPlayPromises));
 
-    m_hasEverNotifiedAboutPlaying = true;
     scheduleUpdatePlaybackControlsManager();
 }
 
index 42109a3..e2f8ef4 100644 (file)
@@ -155,7 +155,7 @@ public:
 
     static HashSet<HTMLMediaElement*>& allMediaElements();
 
-    static HTMLMediaElement* bestMediaElementForShowingPlaybackControlsManager(MediaElementSession::PlaybackControlsPurpose);
+    WEBCORE_EXPORT static HTMLMediaElement* bestMediaElementForShowingPlaybackControlsManager(MediaElementSession::PlaybackControlsPurpose);
 
     static bool isRunningDestructor();
 
index 6f75202..8314e1e 100644 (file)
@@ -473,6 +473,15 @@ bool MediaElementSession::canShowControlsManager(PlaybackControlsPurpose purpose
         return false;
     }
 
+#if ENABLE(FULLSCREEN_API)
+    // Elements which are not descendents of the current fullscreen element cannot be main content.
+    auto* fullscreenElement = m_element.document().webkitCurrentFullScreenElement();
+    if (fullscreenElement && !m_element.isDescendantOf(*fullscreenElement)) {
+        INFO_LOG(LOGIDENTIFIER, "returning FALSE: outside of full screen");
+        return false;
+    }
+#endif
+
     // Only allow the main content heuristic to forbid videos from showing up if our purpose is the controls manager.
     if (purpose == PlaybackControlsPurpose::ControlsManager && m_element.isVideo()) {
         if (!m_element.renderer()) {
index f0b8c55..18da49e 100644 (file)
@@ -3707,6 +3707,13 @@ ExceptionOr<Internals::NowPlayingState> Internals::nowPlayingState() const
 #endif
 }
 
+#if ENABLE(VIDEO)
+HTMLMediaElement* Internals::bestMediaElementForShowingPlaybackControlsManager(Internals::PlaybackControlsPurpose purpose)
+{
+    return HTMLMediaElement::bestMediaElementForShowingPlaybackControlsManager(purpose);
+}
+#endif
+
 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
 
 void Internals::setMockMediaPlaybackTargetPickerEnabled(bool enabled)
index 8ac4dd7..3880420 100644 (file)
 #include "MediaSessionInterruptionProvider.h"
 #endif
 
+#if ENABLE(VIDEO)
+#include "MediaElementSession.h"
+#endif
+
 namespace WebCore {
 
 class AnimationTimeline;
@@ -680,6 +684,9 @@ public:
     };
     ExceptionOr<NowPlayingState> nowPlayingState() const;
 
+    using PlaybackControlsPurpose = MediaElementSession::PlaybackControlsPurpose;
+    HTMLMediaElement* bestMediaElementForShowingPlaybackControlsManager(PlaybackControlsPurpose);
+
 private:
     explicit Internals(Document&);
     Document* contextDocument() const;
index 35b2f28..e908f74 100644 (file)
@@ -82,6 +82,11 @@ enum EventThrottlingBehavior {
     "unresponsive"
 };
 
+[Conditional=VIDEO] enum PlaybackControlsPurpose {
+    "ControlsManager",
+    "NowPlaying"
+};
+
 [
     ExportMacro=WEBCORE_TESTSUPPORT_EXPORT,
     Conditional=VIDEO,
@@ -616,4 +621,6 @@ enum EventThrottlingBehavior {
     boolean usingAppleInternalSDK();
 
     [Conditional=VIDEO, MayThrowException] readonly attribute NowPlayingState nowPlayingState;
+
+    [Conditional=VIDEO] HTMLMediaElement bestMediaElementForShowingPlaybackControlsManager(PlaybackControlsPurpose purpose);
 };