[WebKitLegacy] Media playback pauses on scroll
authorjer.noble@apple.com <jer.noble@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 4 Jan 2019 22:23:15 +0000 (22:23 +0000)
committerjer.noble@apple.com <jer.noble@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 4 Jan 2019 22:23:15 +0000 (22:23 +0000)
https://bugs.webkit.org/show_bug.cgi?id=192829

Reviewed by Eric Carlson.

Source/WebCore:

New API tests:
    WebKitLegacy.ScrollingDoesNotPauseMedia
    WKWebView.StopAllMediaPlayback
    WKWebView.SuspendResumeAllMediaPlayback

Do not use suspendActiveDOMObjects(ReasonForSuspension::PageWillBeSuspended) to pause
video. Roll back the changes to HTMLMediaElement, and introduce a new set of Page calls
suspendAllMediaPlayback() & resumeAllMediaPlayback() which replaces the removed bahavior.

* dom/Document.cpp:
(WebCore::Document::~Document):
(WebCore::Document::stopAllMediaPlayback):
(WebCore::Document::suspendAllMediaPlayback):
(WebCore::Document::resumeAllMediaPlayback):
* dom/Document.h:
* html/HTMLMediaElement.cpp:
(WebCore::HTMLMediaElement::HTMLMediaElement):
(WebCore::HTMLMediaElement::parseAttribute):
(WebCore::HTMLMediaElement::didFinishInsertingNode):
(WebCore::HTMLMediaElement::setSrcObject):
(WebCore::HTMLMediaElement::updateActiveTextTrackCues):
(WebCore::HTMLMediaElement::suspend):
(WebCore::HTMLMediaElement::resume):
(WebCore::HTMLMediaElement::webkitCurrentPlaybackTargetIsWireless const):
* html/HTMLMediaElement.h:
(WebCore::HTMLMediaElement::webkitCurrentPlaybackTargetIsWireless const): Deleted.
* html/MediaElementSession.cpp:
(WebCore::MediaElementSession::playbackPermitted const):
* page/Page.cpp:
(WebCore::Page::stopAllMediaPlayback):
(WebCore::Page::suspendAllMediaPlayback):
(WebCore::Page::resumeAllMediaPlayback):
* page/Page.h:
(WebCore::Page::mediaPlaybackIsSuspended):
* platform/audio/PlatformMediaSession.h:
* platform/audio/PlatformMediaSessionManager.cpp:
(WebCore::PlatformMediaSessionManager::suspendAllMediaPlaybackForDocument):
(WebCore::PlatformMediaSessionManager::resumeAllMediaPlaybackForDocument):
* platform/audio/PlatformMediaSessionManager.h:

Source/WebKit:

Add a new WebPage, WebPageProxy, & WKWebView SPI to stop, suspend, and resume all
existing media playback in the page.

* UIProcess/API/Cocoa/WKWebView.mm:
(-[WKWebView _stopAllMediaPlayback]):
(-[WKWebView _suspendAllMediaPlayback]):
(-[WKWebView _resumeAllMediaPlayback]):
* UIProcess/API/Cocoa/WKWebViewPrivate.h:
* UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::stopAllMediaPlayback):
(WebKit::WebPageProxy::suspendAllMediaPlayback):
(WebKit::WebPageProxy::resumeAllMediaPlayback):
* UIProcess/WebPageProxy.h:
* UIProcess/ios/fullscreen/WKFullScreenViewController.mm:
(-[WKFullScreenViewController _showPhishingAlert]):
* WebProcess/WebPage/WebPage.cpp:
(WebKit::WebPage::stopAllMediaPlayback):
(WebKit::WebPage::suspendAllMediaPlayback):
(WebKit::WebPage::resumeAllMediaPlayback):
* WebProcess/WebPage/WebPage.h:
* WebProcess/WebPage/WebPage.messages.in:

Tools:

* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/WebKitCocoa/StopSuspendResumeAllMedia.mm: Added.
(TestWebKitAPI::TEST):
* TestWebKitAPI/Tests/WebKitLegacy/ios/ScrollingDoesNotPauseMedia.mm: Added.
(-[ScrollingDoesNotPauseMediaDelegate webViewDidFinishLoad:]):
(-[ScrollingDoesNotPauseMediaDelegate uiWebView:didCommitLoadForFrame:]):
(-[ScrollingDoesNotPauseMediaDelegate handleEvent:]):
(TestWebKitAPI::TEST):

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

24 files changed:
Source/WebCore/ChangeLog
Source/WebCore/dom/Document.cpp
Source/WebCore/dom/Document.h
Source/WebCore/html/HTMLMediaElement.cpp
Source/WebCore/html/HTMLMediaElement.h
Source/WebCore/html/MediaElementSession.cpp
Source/WebCore/page/Page.cpp
Source/WebCore/page/Page.h
Source/WebCore/platform/audio/PlatformMediaSession.h
Source/WebCore/platform/audio/PlatformMediaSessionManager.cpp
Source/WebCore/platform/audio/PlatformMediaSessionManager.h
Source/WebKit/ChangeLog
Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm
Source/WebKit/UIProcess/API/Cocoa/WKWebViewPrivate.h
Source/WebKit/UIProcess/WebPageProxy.cpp
Source/WebKit/UIProcess/WebPageProxy.h
Source/WebKit/UIProcess/ios/fullscreen/WKFullScreenViewController.mm
Source/WebKit/WebProcess/WebPage/WebPage.cpp
Source/WebKit/WebProcess/WebPage/WebPage.h
Source/WebKit/WebProcess/WebPage/WebPage.messages.in
Tools/ChangeLog
Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj
Tools/TestWebKitAPI/Tests/WebKitCocoa/StopSuspendResumeAllMedia.mm [new file with mode: 0644]
Tools/TestWebKitAPI/Tests/WebKitLegacy/ios/ScrollingDoesNotPauseMedia.mm [new file with mode: 0644]

index 56aa4f4..0e0a078 100644 (file)
@@ -1,3 +1,50 @@
+2019-01-04  Jer Noble  <jer.noble@apple.com>
+
+        [WebKitLegacy] Media playback pauses on scroll
+        https://bugs.webkit.org/show_bug.cgi?id=192829
+
+        Reviewed by Eric Carlson.
+
+        New API tests:
+            WebKitLegacy.ScrollingDoesNotPauseMedia
+            WKWebView.StopAllMediaPlayback
+            WKWebView.SuspendResumeAllMediaPlayback
+
+        Do not use suspendActiveDOMObjects(ReasonForSuspension::PageWillBeSuspended) to pause
+        video. Roll back the changes to HTMLMediaElement, and introduce a new set of Page calls
+        suspendAllMediaPlayback() & resumeAllMediaPlayback() which replaces the removed bahavior.
+
+        * dom/Document.cpp:
+        (WebCore::Document::~Document):
+        (WebCore::Document::stopAllMediaPlayback):
+        (WebCore::Document::suspendAllMediaPlayback):
+        (WebCore::Document::resumeAllMediaPlayback):
+        * dom/Document.h:
+        * html/HTMLMediaElement.cpp:
+        (WebCore::HTMLMediaElement::HTMLMediaElement):
+        (WebCore::HTMLMediaElement::parseAttribute):
+        (WebCore::HTMLMediaElement::didFinishInsertingNode):
+        (WebCore::HTMLMediaElement::setSrcObject):
+        (WebCore::HTMLMediaElement::updateActiveTextTrackCues):
+        (WebCore::HTMLMediaElement::suspend):
+        (WebCore::HTMLMediaElement::resume):
+        (WebCore::HTMLMediaElement::webkitCurrentPlaybackTargetIsWireless const):
+        * html/HTMLMediaElement.h:
+        (WebCore::HTMLMediaElement::webkitCurrentPlaybackTargetIsWireless const): Deleted.
+        * html/MediaElementSession.cpp:
+        (WebCore::MediaElementSession::playbackPermitted const):
+        * page/Page.cpp:
+        (WebCore::Page::stopAllMediaPlayback):
+        (WebCore::Page::suspendAllMediaPlayback):
+        (WebCore::Page::resumeAllMediaPlayback):
+        * page/Page.h:
+        (WebCore::Page::mediaPlaybackIsSuspended):
+        * platform/audio/PlatformMediaSession.h:
+        * platform/audio/PlatformMediaSessionManager.cpp:
+        (WebCore::PlatformMediaSessionManager::suspendAllMediaPlaybackForDocument):
+        (WebCore::PlatformMediaSessionManager::resumeAllMediaPlaybackForDocument):
+        * platform/audio/PlatformMediaSessionManager.h:
+
 2019-01-04  Chris Dumez  <cdumez@apple.com>
 
         Add support for toggling device orientation API support per site
index 78d0f44..f752db8 100644 (file)
@@ -652,8 +652,7 @@ Document::~Document()
         m_cachedResourceLoader->setDocument(nullptr);
 
 #if ENABLE(VIDEO)
-    if (auto* platformMediaSessionManager = PlatformMediaSessionManager::sharedManagerIfExists())
-        platformMediaSessionManager->stopAllMediaPlaybackForDocument(this);
+    stopAllMediaPlayback();
 #endif
 
     // We must call clearRareData() here since a Document class inherits TreeScope
@@ -1722,6 +1721,23 @@ void Document::allowsMediaDocumentInlinePlaybackChanged()
         element->allowsMediaDocumentInlinePlaybackChanged();
 }
 
+void Document::stopAllMediaPlayback()
+{
+    if (auto* platformMediaSessionManager = PlatformMediaSessionManager::sharedManagerIfExists())
+        platformMediaSessionManager->stopAllMediaPlaybackForDocument(this);
+}
+
+void Document::suspendAllMediaPlayback()
+{
+    if (auto* platformMediaSessionManager = PlatformMediaSessionManager::sharedManagerIfExists())
+        platformMediaSessionManager->suspendAllMediaPlaybackForDocument(*this);
+}
+
+void Document::resumeAllMediaPlayback()
+{
+    if (auto* platformMediaSessionManager = PlatformMediaSessionManager::sharedManagerIfExists())
+        platformMediaSessionManager->resumeAllMediaPlaybackForDocument(*this);
+}
 #endif
 
 String Document::nodeName() const
index 6bedf75..8025ef2 100644 (file)
@@ -1119,6 +1119,10 @@ public:
     void registerForAllowsMediaDocumentInlinePlaybackChangedCallbacks(HTMLMediaElement&);
     void unregisterForAllowsMediaDocumentInlinePlaybackChangedCallbacks(HTMLMediaElement&);
     void allowsMediaDocumentInlinePlaybackChanged();
+
+    void stopAllMediaPlayback();
+    void suspendAllMediaPlayback();
+    void resumeAllMediaPlayback();
 #endif
 
     WEBCORE_EXPORT void setShouldCreateRenderers(bool);
index 1513e25..c6f2eae 100644 (file)
@@ -467,7 +467,6 @@ HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document& docum
     , m_haveSetUpCaptionContainer(false)
 #endif
     , m_isScrubbingRemotely(false)
-    , m_shouldUnpauseInternalOnResume(false)
 #if ENABLE(VIDEO_TRACK)
     , m_tracksAreReady(true)
     , m_haveVisibleTextTrack(false)
@@ -920,6 +919,8 @@ void HTMLMediaElement::didFinishInsertingNode()
 {
     Ref<HTMLMediaElement> protectedThis(*this); // prepareForLoad may result in a 'beforeload' event, which can make arbitrary DOM mutations.
 
+    INFO_LOG(LOGIDENTIFIER);
+
     if (m_inActiveDocument && m_networkState == NETWORK_EMPTY && !attributeWithoutSynchronization(srcAttr).isEmpty())
         prepareForLoad();
 
@@ -1131,6 +1132,7 @@ void HTMLMediaElement::setSrcObject(MediaProvider&& mediaProvider)
     // 4.7.14.2. Location of the media resource
     // srcObject: On setting, it must set the element’s assigned media provider object to the new
     // value, and then invoke the element’s media element load algorithm.
+    INFO_LOG(LOGIDENTIFIER);
     m_mediaProvider = WTFMove(mediaProvider);
     prepareForLoad();
 }
@@ -1763,10 +1765,16 @@ void HTMLMediaElement::updateActiveTextTrackCues(const MediaTime& movieTime)
     if (nextCue)
         nextInterestingTime = std::min(nextInterestingTime, nextCue->low());
 
+    INFO_LOG(LOGIDENTIFIER, "nextInterestingTime:", nextInterestingTime);
+
     if (nextInterestingTime.isValid() && m_player) {
-        m_player->performTaskAtMediaTime([weakThis = makeWeakPtr(this), nextInterestingTime] {
-            if (weakThis)
-                weakThis->updateActiveTextTrackCues(weakThis->currentMediaTime());
+        m_player->performTaskAtMediaTime([this, weakThis = makeWeakPtr(this), nextInterestingTime] {
+            if (!weakThis)
+                return;
+
+            auto currentMediaTime = weakThis->currentMediaTime();
+            INFO_LOG(LOGIDENTIFIER, " - lambda, currentMediaTime:", currentMediaTime);
+            weakThis->updateActiveTextTrackCues(currentMediaTime);
         }, nextInterestingTime);
     }
 
@@ -5731,11 +5739,6 @@ void HTMLMediaElement::suspend(ReasonForSuspension reason)
         m_mediaSession->addBehaviorRestriction(MediaElementSession::RequirePageConsentToResumeMedia);
         break;
     case ReasonForSuspension::PageWillBeSuspended:
-        if (!m_pausedInternal) {
-            m_shouldUnpauseInternalOnResume = true;
-            setPausedInternal(true);
-        }
-        break;
     case ReasonForSuspension::JavaScriptDebuggerPaused:
     case ReasonForSuspension::WillDeferLoading:
         // Do nothing, we don't pause media playback in these cases.
@@ -5751,11 +5754,6 @@ void HTMLMediaElement::resume()
 
     m_asyncEventQueue.resume();
 
-    if (m_shouldUnpauseInternalOnResume) {
-        m_shouldUnpauseInternalOnResume = false;
-        setPausedInternal(false);
-    }
-
     setShouldBufferData(true);
 
     if (!m_mediaSession->pageAllowsPlaybackAfterResuming())
@@ -5939,6 +5937,12 @@ void HTMLMediaElement::setShouldPlayToPlaybackTarget(bool shouldPlay)
 
 #endif // ENABLE(WIRELESS_PLAYBACK_TARGET)
 
+bool HTMLMediaElement::webkitCurrentPlaybackTargetIsWireless() const
+{
+    INFO_LOG(LOGIDENTIFIER, m_isPlayingToWirelessTarget);
+    return m_isPlayingToWirelessTarget;
+}
+
 void HTMLMediaElement::setPlayingOnSecondScreen(bool value)
 {
     if (value == m_playingOnSecondScreen)
index 6d08748..23f2293 100644 (file)
@@ -412,7 +412,7 @@ public:
     void setShouldPlayToPlaybackTarget(bool) override;
 #endif
     bool isPlayingToWirelessPlaybackTarget() const override { return m_isPlayingToWirelessTarget; };
-    bool webkitCurrentPlaybackTargetIsWireless() const { return m_isPlayingToWirelessTarget; }
+    bool webkitCurrentPlaybackTargetIsWireless() const;
 
     void setPlayingOnSecondScreen(bool value);
     bool isPlayingOnSecondScreen() const override { return m_playingOnSecondScreen; }
@@ -1110,7 +1110,6 @@ private:
 
     bool m_isScrubbingRemotely : 1;
     bool m_waitingToEnterFullscreen : 1;
-    bool m_shouldUnpauseInternalOnResume : 1;
 
 #if ENABLE(VIDEO_TRACK)
     bool m_tracksAreReady : 1;
index 71b66c0..eae608e 100644 (file)
@@ -263,6 +263,10 @@ SuccessOr<MediaPlaybackDenialReason> MediaElementSession::playbackPermitted() co
     }
 
     auto& document = m_element.document();
+    auto* page = document.page();
+    if (!page || page->mediaPlaybackIsSuspended())
+        return MediaPlaybackDenialReason::PageConsentRequired;
+
     if (document.isMediaDocument() && !document.ownerElement())
         return { };
 
index 296beff..ddf3912 100644 (file)
@@ -1716,6 +1716,41 @@ void Page::stopMediaCapture()
 #endif
 }
 
+void Page::stopAllMediaPlayback()
+{
+    for (Frame* frame = &mainFrame(); frame; frame = frame->tree().traverseNext()) {
+        if (auto* document = frame->document())
+            document->stopAllMediaPlayback();
+    }
+}
+
+void Page::suspendAllMediaPlayback()
+{
+    ASSERT(!m_mediaPlaybackIsSuspended);
+    if (m_mediaPlaybackIsSuspended)
+        return;
+
+    for (Frame* frame = &mainFrame(); frame; frame = frame->tree().traverseNext()) {
+        if (auto* document = frame->document())
+            document->suspendAllMediaPlayback();
+    }
+
+    m_mediaPlaybackIsSuspended = true;
+}
+
+void Page::resumeAllMediaPlayback()
+{
+    ASSERT(m_mediaPlaybackIsSuspended);
+    if (!m_mediaPlaybackIsSuspended)
+        return;
+    m_mediaPlaybackIsSuspended = false;
+
+    for (Frame* frame = &mainFrame(); frame; frame = frame->tree().traverseNext()) {
+        if (auto* document = frame->document())
+            document->resumeAllMediaPlayback();
+    }
+}
+
 #if ENABLE(MEDIA_SESSION)
 void Page::handleMediaEvent(MediaEventType eventType)
 {
index b5b7f59..cf92dec 100644 (file)
@@ -600,6 +600,11 @@ public:
     WEBCORE_EXPORT void setMuted(MediaProducer::MutedStateFlags);
     WEBCORE_EXPORT void stopMediaCapture();
 
+    WEBCORE_EXPORT void stopAllMediaPlayback();
+    WEBCORE_EXPORT void suspendAllMediaPlayback();
+    WEBCORE_EXPORT void resumeAllMediaPlayback();
+    bool mediaPlaybackIsSuspended() { return m_mediaPlaybackIsSuspended; }
+
 #if ENABLE(MEDIA_SESSION)
     WEBCORE_EXPORT void handleMediaEvent(MediaEventType);
     WEBCORE_EXPORT void setVolumeOfMediaElement(double, uint64_t);
@@ -945,6 +950,7 @@ private:
 #endif
 
     bool m_shouldEnableICECandidateFilteringByDefault { true };
+    bool m_mediaPlaybackIsSuspended { false };
 };
 
 inline PageGroup& Page::group()
index 17a30d5..25c1779 100644 (file)
@@ -85,6 +85,7 @@ public:
         SuspendedUnderLock,
         InvisibleAutoplay,
         ProcessInactive,
+        PlaybackSuspended,
     };
     InterruptionType interruptionType() const { return m_interruptionType; }
 
index 89fe550..f68f866 100644 (file)
@@ -413,6 +413,22 @@ void PlatformMediaSessionManager::stopAllMediaPlaybackForProcess()
     });
 }
 
+void PlatformMediaSessionManager::suspendAllMediaPlaybackForDocument(const Document& document)
+{
+    forEachSession([&] (PlatformMediaSession& session, size_t) {
+        if (session.client().hostingDocument() == &document)
+            session.beginInterruption(PlatformMediaSession::PlaybackSuspended);
+    });
+}
+
+void PlatformMediaSessionManager::resumeAllMediaPlaybackForDocument(const Document& document)
+{
+    forEachSession([&] (PlatformMediaSession& session, size_t) {
+        if (session.client().hostingDocument() == &document)
+            session.endInterruption(PlatformMediaSession::MayResumePlaying);
+    });
+}
+
 void PlatformMediaSessionManager::forEachSession(const Function<void(PlatformMediaSession&, size_t)>& predicate) const
 {
     ++m_iteratingOverSessions;
index 4d87fd8..e670daf 100644 (file)
@@ -77,6 +77,9 @@ public:
     void stopAllMediaPlaybackForDocument(const Document*);
     WEBCORE_EXPORT void stopAllMediaPlaybackForProcess();
 
+    void suspendAllMediaPlaybackForDocument(const Document&);
+    void resumeAllMediaPlaybackForDocument(const Document&);
+
     enum SessionRestrictionFlags {
         NoRestrictions = 0,
         ConcurrentPlaybackNotPermitted = 1 << 0,
index ee26420..5206e42 100644 (file)
@@ -1,3 +1,32 @@
+2019-01-04  Jer Noble  <jer.noble@apple.com>
+
+        [WebKitLegacy] Media playback pauses on scroll
+        https://bugs.webkit.org/show_bug.cgi?id=192829
+
+        Reviewed by Eric Carlson.
+
+        Add a new WebPage, WebPageProxy, & WKWebView SPI to stop, suspend, and resume all
+        existing media playback in the page.
+
+        * UIProcess/API/Cocoa/WKWebView.mm:
+        (-[WKWebView _stopAllMediaPlayback]):
+        (-[WKWebView _suspendAllMediaPlayback]):
+        (-[WKWebView _resumeAllMediaPlayback]):
+        * UIProcess/API/Cocoa/WKWebViewPrivate.h:
+        * UIProcess/WebPageProxy.cpp:
+        (WebKit::WebPageProxy::stopAllMediaPlayback):
+        (WebKit::WebPageProxy::suspendAllMediaPlayback):
+        (WebKit::WebPageProxy::resumeAllMediaPlayback):
+        * UIProcess/WebPageProxy.h:
+        * UIProcess/ios/fullscreen/WKFullScreenViewController.mm:
+        (-[WKFullScreenViewController _showPhishingAlert]):
+        * WebProcess/WebPage/WebPage.cpp:
+        (WebKit::WebPage::stopAllMediaPlayback):
+        (WebKit::WebPage::suspendAllMediaPlayback):
+        (WebKit::WebPage::resumeAllMediaPlayback):
+        * WebProcess/WebPage/WebPage.h:
+        * WebProcess/WebPage/WebPage.messages.in:
+
 2019-01-04  Chris Dumez  <cdumez@apple.com>
 
         Add support for toggling device orientation API support per site
index 8443643..8e1d53d 100644 (file)
@@ -4529,6 +4529,21 @@ FOR_EACH_PRIVATE_WKCONTENTVIEW_ACTION(FORWARD_ACTION_TO_WKCONTENTVIEW)
 #endif
 }
 
+- (void)_stopAllMediaPlayback
+{
+    _page->stopAllMediaPlayback();
+}
+
+- (void)_suspendAllMediaPlayback
+{
+    _page->suspendAllMediaPlayback();
+}
+
+- (void)_resumeAllMediaPlayback
+{
+    _page->resumeAllMediaPlayback();
+}
+
 - (NSURL *)_unreachableURL
 {
     return [NSURL _web_URLWithWTFString:_page->pageLoadState().unreachableURL()];
index 079a812..85822cd 100644 (file)
@@ -405,7 +405,9 @@ typedef NS_OPTIONS(NSUInteger, _WKRectEdge) {
 @property (nonatomic, readonly) BOOL _isPictureInPictureActive;
 - (void)_updateMediaPlaybackControlsManager;
 - (void)_togglePictureInPicture;
-
+- (void)_stopAllMediaPlayback;
+- (void)_suspendAllMediaPlayback;
+- (void)_resumeAllMediaPlayback;
 @end
 
 #if TARGET_OS_IPHONE
index da78ab3..55e3a82 100644 (file)
@@ -5099,6 +5099,30 @@ void WebPageProxy::stopMediaCapture()
 #endif
 }
 
+void WebPageProxy::stopAllMediaPlayback()
+{
+    if (!isValid())
+        return;
+
+    m_process->send(Messages::WebPage::StopAllMediaPlayback(), m_pageID);
+}
+
+void WebPageProxy::suspendAllMediaPlayback()
+{
+    if (!isValid())
+        return;
+
+    m_process->send(Messages::WebPage::SuspendAllMediaPlayback(), m_pageID);
+}
+
+void WebPageProxy::resumeAllMediaPlayback()
+{
+    if (!isValid())
+        return;
+
+    m_process->send(Messages::WebPage::ResumeAllMediaPlayback(), m_pageID);
+}
+
 #if ENABLE(MEDIA_SESSION)
 void WebPageProxy::handleMediaEvent(MediaEventType eventType)
 {
index c18095b..78ea950 100644 (file)
@@ -1125,6 +1125,10 @@ public:
     bool mediaCaptureEnabled() const { return m_mediaCaptureEnabled; }
     void stopMediaCapture();
 
+    void stopAllMediaPlayback();
+    void suspendAllMediaPlayback();
+    void resumeAllMediaPlayback();
+
 #if ENABLE(MEDIA_SESSION)
     bool hasMediaSessionWithActiveMediaElements() const { return m_hasMediaSessionWithActiveMediaElements; }
     void handleMediaEvent(WebCore::MediaEventType);
index c505b26..351b61f 100644 (file)
@@ -556,13 +556,17 @@ private:
     NSString *alertMessage = [NSString stringWithFormat:WEB_UI_STRING("Typing is not allowed in full screen websites. “%@” may be showing a fake keyboard to trick you into disclosing personal or financial information.", "Full Screen Deceptive Website Warning Sheet Content Text"), (NSString *)self.location];
     UIAlertController* alert = [UIAlertController alertControllerWithTitle:alertTitle message:alertMessage preferredStyle:UIAlertControllerStyleAlert];
 
-    if (auto* page = [self._webView _page])
+    if (auto* page = [self._webView _page]) {
+        page->suspendAllMediaPlayback();
         page->suspendActiveDOMObjectsAndAnimations();
+    }
 
     UIAlertAction* exitAction = [UIAlertAction actionWithTitle:WEB_UI_STRING_KEY("Exit Full Screen", "Exit Full Screen (Element Full Screen)", "Full Screen Deceptive Website Exit Action") style:UIAlertActionStyleCancel handler:^(UIAlertAction * action) {
         [self _cancelAction:action];
-        if (auto* page = [self._webView _page])
+        if (auto* page = [self._webView _page]) {
             page->resumeActiveDOMObjectsAndAnimations();
+            page->resumeAllMediaPlayback();
+        }
     }];
 
     UIAlertAction* stayAction = [UIAlertAction actionWithTitle:WEB_UI_STRING_KEY("Stay in Full Screen", "Stay in Full Screen (Element Full Screen)", "Full Screen Deceptive Website Stay Action") style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
index dba4447..f6d4bea 100644 (file)
@@ -656,6 +656,21 @@ void WebPage::enableEnumeratingAllNetworkInterfaces()
 #endif
 #endif
 
+void WebPage::stopAllMediaPlayback()
+{
+    m_page->stopAllMediaPlayback();
+}
+
+void WebPage::suspendAllMediaPlayback()
+{
+    m_page->suspendAllMediaPlayback();
+}
+
+void WebPage::resumeAllMediaPlayback()
+{
+    m_page->resumeAllMediaPlayback();
+}
+
 void WebPage::reinitializeWebPage(WebPageCreationParameters&& parameters)
 {
     ASSERT(m_drawingArea);
index e3d15c3..6e5c92e 100644 (file)
@@ -1385,6 +1385,10 @@ private:
     void enableEnumeratingAllNetworkInterfaces();
 #endif
 
+    void stopAllMediaPlayback();
+    void suspendAllMediaPlayback();
+    void resumeAllMediaPlayback();
+
     void advanceToNextMisspelling(bool startBeforeSelection);
     void changeSpellingToWord(const String& word);
 
index 34be71c..54abbaf 100644 (file)
@@ -356,6 +356,10 @@ messages -> WebPage LegacyReceiver {
     DisableEnumeratingAllNetworkInterfaces()
 #endif
 
+    StopAllMediaPlayback();
+    SuspendAllMediaPlayback();
+    ResumeAllMediaPlayback();
+
     # Notification
     DidReceiveNotificationPermissionDecision(uint64_t notificationID, bool allowed)
 
index b498214..fa3ca73 100644 (file)
@@ -1,3 +1,19 @@
+2019-01-04  Jer Noble  <jer.noble@apple.com>
+
+        [WebKitLegacy] Media playback pauses on scroll
+        https://bugs.webkit.org/show_bug.cgi?id=192829
+
+        Reviewed by Eric Carlson.
+
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        * TestWebKitAPI/Tests/WebKitCocoa/StopSuspendResumeAllMedia.mm: Added.
+        (TestWebKitAPI::TEST):
+        * TestWebKitAPI/Tests/WebKitLegacy/ios/ScrollingDoesNotPauseMedia.mm: Added.
+        (-[ScrollingDoesNotPauseMediaDelegate webViewDidFinishLoad:]):
+        (-[ScrollingDoesNotPauseMediaDelegate uiWebView:didCommitLoadForFrame:]):
+        (-[ScrollingDoesNotPauseMediaDelegate handleEvent:]):
+        (TestWebKitAPI::TEST):
+
 2019-01-04  Chris Dumez  <cdumez@apple.com>
 
         Add support for toggling device orientation API support per site
index 2272d8b..65349c5 100644 (file)
                CDB5DFFF213610FA00D3E189 /* now-playing.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = CDB5DFFE21360ED800D3E189 /* now-playing.html */; };
                CDBFCC451A9FF45300A7B691 /* FullscreenZoomInitialFrame.mm in Sources */ = {isa = PBXBuildFile; fileRef = CDBFCC431A9FF44800A7B691 /* FullscreenZoomInitialFrame.mm */; };
                CDBFCC461A9FF49E00A7B691 /* FullscreenZoomInitialFrame.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = CDBFCC421A9FF44800A7B691 /* FullscreenZoomInitialFrame.html */; };
+               CDC0932B21C872C10030C4B0 /* ScrollingDoesNotPauseMedia.mm in Sources */ = {isa = PBXBuildFile; fileRef = CDC0932A21C872C10030C4B0 /* ScrollingDoesNotPauseMedia.mm */; };
+               CDC0932E21C993440030C4B0 /* StopSuspendResumeAllMedia.mm in Sources */ = {isa = PBXBuildFile; fileRef = CDC0932D21C993440030C4B0 /* StopSuspendResumeAllMedia.mm */; };
                CDC8E48D1BC5CB4500594FEC /* AudioSessionCategoryIOS.mm in Sources */ = {isa = PBXBuildFile; fileRef = CDC8E4851BC5B19400594FEC /* AudioSessionCategoryIOS.mm */; };
                CDC8E4941BC6F10800594FEC /* video-with-audio.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = CDC8E4891BC5C96200594FEC /* video-with-audio.html */; };
                CDC8E4951BC6F10800594FEC /* video-with-audio.mp4 in Copy Resources */ = {isa = PBXBuildFile; fileRef = CDC8E48A1BC5C96200594FEC /* video-with-audio.mp4 */; };
                CDB5DFFE21360ED800D3E189 /* now-playing.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "now-playing.html"; sourceTree = "<group>"; };
                CDBFCC421A9FF44800A7B691 /* FullscreenZoomInitialFrame.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = FullscreenZoomInitialFrame.html; sourceTree = "<group>"; };
                CDBFCC431A9FF44800A7B691 /* FullscreenZoomInitialFrame.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FullscreenZoomInitialFrame.mm; sourceTree = "<group>"; };
+               CDC0932A21C872C10030C4B0 /* ScrollingDoesNotPauseMedia.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ScrollingDoesNotPauseMedia.mm; sourceTree = "<group>"; };
+               CDC0932D21C993440030C4B0 /* StopSuspendResumeAllMedia.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = StopSuspendResumeAllMedia.mm; sourceTree = "<group>"; };
                CDC2C7141797089D00E627FB /* TimeRanges.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = TimeRanges.cpp; sourceTree = "<group>"; };
                CDC8E4851BC5B19400594FEC /* AudioSessionCategoryIOS.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AudioSessionCategoryIOS.mm; sourceTree = "<group>"; };
                CDC8E4891BC5C96200594FEC /* video-with-audio.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "video-with-audio.html"; sourceTree = "<group>"; };
                                37BCA61B1B596BA9002012CA /* ShouldOpenExternalURLsInNewWindowActions.mm */,
                                2D9A53AE1B31FA8D0074D5AA /* ShrinkToFit.mm */,
                                2DFF7B6C1DA487AF00814614 /* SnapshotStore.mm */,
+                               CDC0932D21C993440030C4B0 /* StopSuspendResumeAllMedia.mm */,
                                515BE1701D428BD100DD7C68 /* StoreBlobThenDelete.mm */,
                                1C734B5220788C4800F430EA /* SystemColors.mm */,
                                F4CD74C720FDB49600DE3794 /* TestURLSchemeHandler.h */,
                        children = (
                                CDC8E49A1BC728FE00594FEC /* Resources */,
                                CDC8E4851BC5B19400594FEC /* AudioSessionCategoryIOS.mm */,
+                               CDC0932A21C872C10030C4B0 /* ScrollingDoesNotPauseMedia.mm */,
                                0F4FFA9D1ED3AA8500F7111F /* SnapshotViaRenderInContext.mm */,
                        );
                        path = ios;
                                5CA985532113CF780057EB6B /* SafeBrowsing.mm in Sources */,
                                CDCFA7AA1E45183200C2433D /* SampleMap.cpp in Sources */,
                                CE0947372063223B003C9BA0 /* SchemeRegistry.mm in Sources */,
+                               CDC0932B21C872C10030C4B0 /* ScrollingDoesNotPauseMedia.mm in Sources */,
                                7CCE7F121A411AE600447C4C /* ScrollPinningBehaviors.cpp in Sources */,
                                F4C8797F2059D8D3009CD00B /* ScrollViewInsetTests.mm in Sources */,
                                CE06DF9B1E1851F200E570C9 /* SecurityOrigin.cpp in Sources */,
                                7CCE7EF21A411AE600447C4C /* StopLoadingDuringDidFailProvisionalLoad.cpp in Sources */,
                                7CCE7ECE1A411A7E00447C4C /* StopLoadingFromDidFinishLoading.mm in Sources */,
                                7CCE7ECF1A411A7E00447C4C /* StopLoadingFromDidReceiveResponse.mm in Sources */,
+                               CDC0932E21C993440030C4B0 /* StopSuspendResumeAllMedia.mm in Sources */,
                                515BE1711D428E4B00DD7C68 /* StoreBlobThenDelete.mm in Sources */,
                                7CCE7ED01A411A7E00447C4C /* StringByEvaluatingJavaScriptFromString.mm in Sources */,
                                7CCE7ED11A411A7E00447C4C /* StringTruncator.mm in Sources */,
diff --git a/Tools/TestWebKitAPI/Tests/WebKitCocoa/StopSuspendResumeAllMedia.mm b/Tools/TestWebKitAPI/Tests/WebKitCocoa/StopSuspendResumeAllMedia.mm
new file mode 100644 (file)
index 0000000..0d99ab6
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_API_ENABLED && PLATFORM(MAC)
+
+#import "PlatformUtilities.h"
+#import "TestWKWebView.h"
+#import <WebKit/WKWebViewPrivate.h>
+#import <wtf/RetainPtr.h>
+
+namespace TestWebKitAPI {
+
+TEST(WKWebView, StopAllMediaPlayback)
+{
+    auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 100, 100) configuration:configuration.get() addToWindow:YES]);
+
+    [webView synchronouslyLoadHTMLString:@"<video src=\"video-with-audio.mp4\" webkit-playsinline></video>"];
+
+    [webView objectByEvaluatingJavaScript:@"function eventToMessage(event){window.webkit.messageHandlers.testHandler.postMessage(event.type);} var video = document.querySelector('video'); video.addEventListener('playing', eventToMessage); video.addEventListener('pause', eventToMessage);"];
+
+    __block bool didBeginPlaying = false;
+    [webView performAfterReceivingMessage:@"playing" action:^{ didBeginPlaying = true; }];
+    [webView evaluateJavaScript:@"document.querySelector('video').play()" completionHandler:nil];
+    TestWebKitAPI::Util::run(&didBeginPlaying);
+
+    __block bool didPause = false;
+    [webView performAfterReceivingMessage:@"pause" action:^{ didPause = true; }];
+    [webView _stopAllMediaPlayback];
+    TestWebKitAPI::Util::run(&didPause);
+}
+
+TEST(WKWebView, SuspendResumeAllMediaPlayback)
+{
+    auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 100, 100) configuration:configuration.get() addToWindow:YES]);
+
+    [webView synchronouslyLoadHTMLString:@"<video src=\"video-with-audio.mp4\" webkit-playsinline></video>"];
+
+    [webView objectByEvaluatingJavaScript:@"function eventToMessage(event){window.webkit.messageHandlers.testHandler.postMessage(event.type);} var video = document.querySelector('video'); video.addEventListener('playing', eventToMessage); video.addEventListener('pause', eventToMessage);"];
+
+    __block bool didBeginPlaying = false;
+    [webView performAfterReceivingMessage:@"playing" action:^{ didBeginPlaying = true; }];
+    [webView evaluateJavaScript:@"document.querySelector('video').play()" completionHandler:nil];
+    TestWebKitAPI::Util::run(&didBeginPlaying);
+
+    __block bool didPause = false;
+    [webView performAfterReceivingMessage:@"pause" action:^{ didPause = true; }];
+    [webView _suspendAllMediaPlayback];
+    TestWebKitAPI::Util::run(&didPause);
+
+    __block bool didReject = false;
+    [webView performAfterReceivingMessage:@"rejected" action:^{ didReject = true; }];
+    [webView evaluateJavaScript:@"document.querySelector('video').play().catch(() = { window.webkit.messageHandlers.testHandler.postMessage('rejected'); })" completionHandler:nil];
+
+    didBeginPlaying = false;
+    [webView performAfterReceivingMessage:@"playing" action:^{ didBeginPlaying = true; }];
+    [webView _resumeAllMediaPlayback];
+    TestWebKitAPI::Util::run(&didBeginPlaying);
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKitLegacy/ios/ScrollingDoesNotPauseMedia.mm b/Tools/TestWebKitAPI/Tests/WebKitLegacy/ios/ScrollingDoesNotPauseMedia.mm
new file mode 100644 (file)
index 0000000..dc0e090
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if PLATFORM(IOS_FAMILY)
+
+#import "PlatformUtilities.h"
+#import <WebKit/DOMHTMLMediaElement.h>
+#import <WebKit/WebFramePrivate.h>
+#import <wtf/RetainPtr.h>
+
+@interface ScrollingDoesNotPauseMediaDelegate : NSObject <UIWebViewDelegate, DOMEventListener> {
+}
+@end
+
+static bool didFinishLoad;
+static bool gotMainFrame;
+static WebFrame* mainFrame;
+
+@implementation ScrollingDoesNotPauseMediaDelegate
+
+IGNORE_WARNINGS_BEGIN("deprecated-implementations")
+- (void)webViewDidFinishLoad:(UIWebView *)webView
+IGNORE_WARNINGS_END
+{
+    didFinishLoad = true;
+}
+
+- (void)uiWebView:(UIWebView *)sender didCommitLoadForFrame:(WebFrame *)frame
+{
+    mainFrame = frame;
+    gotMainFrame = true;
+}
+
+static bool didReceivePause;
+static bool didReceivePlaying;
+
+- (void)handleEvent:(DOMEvent *)event
+{
+    if ([event.type isEqualToString:@"pause"])
+        didReceivePause = true;
+    else if ([event.type isEqualToString:@"playing"])
+        didReceivePlaying = true;
+}
+@end
+
+namespace TestWebKitAPI {
+
+TEST(WebKitLegacy, ScrollingDoesNotPauseMedia)
+{
+    RetainPtr<UIWindow> uiWindow = adoptNS([[UIWindow alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]);
+    RetainPtr<UIWebView> uiWebView = adoptNS([[UIWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]);
+    [uiWindow addSubview:uiWebView.get()];
+
+    uiWebView.get().mediaPlaybackRequiresUserAction = NO;
+    uiWebView.get().allowsInlineMediaPlayback = YES;
+
+    RetainPtr<ScrollingDoesNotPauseMediaDelegate> testController = adoptNS([ScrollingDoesNotPauseMediaDelegate new]);
+    uiWebView.get().delegate = testController.get();
+
+    didFinishLoad = false;
+    gotMainFrame = false;
+    mainFrame = nil;
+
+    [uiWebView loadRequest:[NSURLRequest requestWithURL:[NSBundle.mainBundle URLForResource:@"video-with-audio" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]]];
+
+    Util::run(&didFinishLoad);
+    Util::run(&gotMainFrame);
+
+    DOMHTMLMediaElement* video = (DOMHTMLMediaElement*)[mainFrame.DOMDocument querySelector:@"video"];
+    ASSERT_TRUE([video isKindOfClass:[DOMHTMLMediaElement class]]);
+
+    [video addEventListener:@"playing" listener:testController.get() useCapture:NO];
+    [video addEventListener:@"pause" listener:testController.get() useCapture:NO];
+
+    [mainFrame setTimeoutsPaused:YES];
+
+    didReceivePlaying = false;
+    [video play];
+    Util::run(&didReceivePlaying);
+
+    [mainFrame setTimeoutsPaused:NO];
+
+    didReceivePause = false;
+    [video play];
+    Util::run(&didReceivePause);
+}
+
+} // namespace TestWebKitAPI
+
+#endif