Cleanup fullscreen logic
https://bugs.webkit.org/show_bug.cgi?id=241811

Patch by Youssef Soliman <youssefdevelops@gmail.com> on 2022-06-21
Reviewed by Eric Carlson.

Cleaned up the logic used when entering fullscreen.

* Source/WebCore/html/HTMLMediaElement.cpp:
(WebCore::HTMLMediaElement::enterFullscreen):
* Source/WebCore/page/ChromeClient.h:
(WebCore::ChromeClient::canEnterVideoFullscreen const):
* Source/WebCore/page/Quirks.cpp:
(WebCore::Quirks::allowLayeredFullscreenVideos const):
* Source/WebCore/page/Quirks.h:
* Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.cpp:
(WebKit::WebChromeClient::canEnterVideoFullscreen const):
* Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.h:
* Source/WebKit/WebProcess/cocoa/VideoFullscreenManager.h:
* Source/WebKit/WebProcess/cocoa/VideoFullscreenManager.mm:
(WebKit::VideoFullscreenManager::canEnterVideoFullscreen const):
(WebKit::VideoFullscreenManager::enterVideoFullscreenForVideoElement):
(WebKit::VideoFullscreenManager::exitVideoFullscreenForVideoElement):
(WebKit::VideoFullscreenManager::exitVideoFullscreenToModeWithoutAnimation):
(WebKit::VideoFullscreenManager::setCurrentlyInFullscreen):
* Source/WebKitLegacy/mac/WebCoreSupport/WebChromeClient.h:
* Source/WebKitLegacy/mac/WebCoreSupport/WebChromeClient.mm:
(WebChromeClient::canEnterVideoFullscreen const):

Canonical link: https://commits.webkit.org/251714@main

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@295709 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/html/HTMLMediaElement.cpp b/Source/WebCore/html/HTMLMediaElement.cpp
index dacf909..fb415f9 100644
--- a/Source/WebCore/html/HTMLMediaElement.cpp
+++ b/Source/WebCore/html/HTMLMediaElement.cpp
@@ -6386,20 +6386,21 @@
     }
 #endif
 
-    queueTaskKeepingObjectAlive(*this, TaskSource::MediaElement, [this, mode] {
+    queueTaskKeepingObjectAlive(*this, TaskSource::MediaElement, [this, mode, logIdentifier = LOGIDENTIFIER] {
         if (isContextStopped())
             return;
 
         if (document().hidden()) {
-            ALWAYS_LOG(LOGIDENTIFIER, " returning because document is hidden");
+            ALWAYS_LOG(logIdentifier, " returning because document is hidden");
             m_changingVideoFullscreenMode = false;
             return;
         }
 
         if (is<HTMLVideoElement>(*this)) {
             HTMLVideoElement& asVideo = downcast<HTMLVideoElement>(*this);
-            if (document().page()->chrome().client().supportsVideoFullscreen(mode)) {
-                ALWAYS_LOG(LOGIDENTIFIER, "Entering fullscreen mode ", mode, ", m_videoFullscreenStandby = ", m_videoFullscreenStandby);
+            auto& client = document().page()->chrome().client();
+            if (client.supportsVideoFullscreen(mode) && client.canEnterVideoFullscreen()) {
+                ALWAYS_LOG(logIdentifier, "Entering fullscreen mode ", mode, ", m_videoFullscreenStandby = ", m_videoFullscreenStandby);
 
                 m_temporarilyAllowingInlinePlaybackAfterFullscreen = false;
                 if (mode == VideoFullscreenModeStandard)
@@ -6409,7 +6410,7 @@
                 setFullscreenMode(mode);
                 configureMediaControls();
 
-                document().page()->chrome().client().enterVideoFullscreenForVideoElement(asVideo, m_videoFullscreenMode, m_videoFullscreenStandby);
+                client.enterVideoFullscreenForVideoElement(asVideo, m_videoFullscreenMode, m_videoFullscreenStandby);
                 if (m_videoFullscreenStandby)
                     return;
 
diff --git a/Source/WebCore/page/ChromeClient.h b/Source/WebCore/page/ChromeClient.h
index 4a65cd8..81a36fc 100644
--- a/Source/WebCore/page/ChromeClient.h
+++ b/Source/WebCore/page/ChromeClient.h
@@ -415,6 +415,7 @@
     virtual GraphicsDeviceAdapter* graphicsDeviceAdapter() const { return nullptr; }
 #endif
 
+    virtual bool canEnterVideoFullscreen() const { return false; }
     virtual bool supportsVideoFullscreen(HTMLMediaElementEnums::VideoFullscreenMode) { return false; }
     virtual bool supportsVideoFullscreenStandby() { return false; }
 
diff --git a/Source/WebCore/page/Quirks.cpp b/Source/WebCore/page/Quirks.cpp
index 3c8c4e1..b6e035b 100644
--- a/Source/WebCore/page/Quirks.cpp
+++ b/Source/WebCore/page/Quirks.cpp
@@ -1481,4 +1481,20 @@
     return *m_shouldDisableWebSharePolicy;
 }
 
+#if PLATFORM(IOS)
+bool Quirks::allowLayeredFullscreenVideos() const
+{
+    if (!needsQuirks())
+        return false;
+    
+    if (!m_allowLayeredFullscreenVideos) {
+        auto domain = RegistrableDomain(m_document->topDocument().url());
+        
+        m_allowLayeredFullscreenVideos = domain == "espn.com"_s;
+    }
+    
+    return *m_allowLayeredFullscreenVideos;
+}
+#endif
+
 }
diff --git a/Source/WebCore/page/Quirks.h b/Source/WebCore/page/Quirks.h
index 2f57d64..1def418 100644
--- a/Source/WebCore/page/Quirks.h
+++ b/Source/WebCore/page/Quirks.h
@@ -158,7 +158,11 @@
 #endif
 
     bool shouldDisableWebSharePolicy() const;
-
+    
+#if PLATFORM(IOS)
+    WEBCORE_EXPORT bool allowLayeredFullscreenVideos() const;
+#endif
+    
 private:
     bool needsQuirks() const;
 
@@ -205,6 +209,9 @@
     mutable std::optional<bool> m_blocksReturnToFullscreenFromPictureInPictureQuirk;
     mutable std::optional<bool> m_shouldDisableEndFullscreenEventWhenEnteringPictureInPictureFromFullscreenQuirk;
     mutable std::optional<bool> m_shouldDisableWebSharePolicy;
+#if PLATFORM(IOS)
+    mutable std::optional<bool> m_allowLayeredFullscreenVideos;
+#endif
 };
 
 } // namespace WebCore
diff --git a/Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.cpp b/Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.cpp
index 62fb577..ceb0dee 100644
--- a/Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.cpp
+++ b/Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.cpp
@@ -1030,6 +1030,11 @@
     m_page.videoFullscreenManager();
 }
 
+bool WebChromeClient::canEnterVideoFullscreen() const
+{
+    return m_page.videoFullscreenManager().canEnterVideoFullscreen();
+}
+
 bool WebChromeClient::supportsVideoFullscreen(HTMLMediaElementEnums::VideoFullscreenMode mode)
 {
     return m_page.videoFullscreenManager().supportsVideoFullscreen(mode);
diff --git a/Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.h b/Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.h
index c7a4325..8f02de3 100644
--- a/Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.h
+++ b/Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.h
@@ -281,6 +281,7 @@
 
 #if ENABLE(VIDEO_PRESENTATION_MODE)
     void prepareForVideoFullscreen() final;
+    bool canEnterVideoFullscreen() const final;
     bool supportsVideoFullscreen(WebCore::HTMLMediaElementEnums::VideoFullscreenMode) final;
     bool supportsVideoFullscreenStandby() final;
     void setMockVideoPresentationModeEnabled(bool) final;
diff --git a/Source/WebKit/WebProcess/cocoa/VideoFullscreenManager.h b/Source/WebKit/WebProcess/cocoa/VideoFullscreenManager.h
index e3502ba..b27a80f 100644
--- a/Source/WebKit/WebProcess/cocoa/VideoFullscreenManager.h
+++ b/Source/WebKit/WebProcess/cocoa/VideoFullscreenManager.h
@@ -124,6 +124,7 @@
     void didReceiveMessage(IPC::Connection&, IPC::Decoder&) override;
 
     // Interface to WebChromeClient
+    bool canEnterVideoFullscreen() const;
     bool supportsVideoFullscreen(WebCore::HTMLMediaElementEnums::VideoFullscreenMode) const;
     bool supportsVideoFullscreenStandby() const;
     void enterVideoFullscreenForVideoElement(WebCore::HTMLVideoElement&, WebCore::HTMLMediaElementEnums::VideoFullscreenMode, bool standby);
@@ -166,6 +167,8 @@
     void fullscreenModeChanged(PlaybackSessionContextIdentifier, WebCore::HTMLMediaElementEnums::VideoFullscreenMode);
     void fullscreenMayReturnToInline(PlaybackSessionContextIdentifier, bool isPageVisible);
     void requestRouteSharingPolicyAndContextUID(PlaybackSessionContextIdentifier, Messages::VideoFullscreenManager::RequestRouteSharingPolicyAndContextUIDAsyncReply&&);
+    
+    void setCurrentlyInFullscreen(VideoFullscreenInterfaceContext&, bool);
 
     WebPage* m_page;
     Ref<PlaybackSessionManager> m_playbackSessionManager;
@@ -174,6 +177,7 @@
     PlaybackSessionContextIdentifier m_controlsManagerContextId;
     HashMap<PlaybackSessionContextIdentifier, int> m_clientCounts;
     WeakPtr<WebCore::HTMLVideoElement> m_videoElementInPictureInPicture;
+    bool m_currentlyInFullscreen { false };
 };
 
 } // namespace WebKit
diff --git a/Source/WebKit/WebProcess/cocoa/VideoFullscreenManager.mm b/Source/WebKit/WebProcess/cocoa/VideoFullscreenManager.mm
index 0aa05b0..0878095 100644
--- a/Source/WebKit/WebProcess/cocoa/VideoFullscreenManager.mm
+++ b/Source/WebKit/WebProcess/cocoa/VideoFullscreenManager.mm
@@ -229,6 +229,15 @@
 
 #pragma mark Interface to ChromeClient:
 
+bool VideoFullscreenManager::canEnterVideoFullscreen() const
+{
+#if PLATFORM(IOS)
+    if (m_currentlyInFullscreen)
+        return false;
+#endif
+    return true;
+}
+
 bool VideoFullscreenManager::supportsVideoFullscreen(WebCore::HTMLMediaElementEnums::VideoFullscreenMode mode) const
 {
 #if PLATFORM(IOS_FAMILY)
@@ -260,6 +269,18 @@
 {
     ASSERT(m_page);
     ASSERT(standby || mode != HTMLMediaElementEnums::VideoFullscreenModeNone);
+    
+#if PLATFORM(IOS)
+    auto allowLayeredFullscreenVideos = videoElement.document().quirks().allowLayeredFullscreenVideos();
+    if (m_currentlyInFullscreen
+        && mode == HTMLMediaElementEnums::VideoFullscreenModeStandard
+        && !allowLayeredFullscreenVideos) {
+        LOG(Fullscreen, "VideoFullscreenManager::enterVideoFullscreenForVideoElement(%p) already in fullscreen, aborting", this);
+        ASSERT_NOT_REACHED();
+        return;
+    }
+#endif
+    
     LOG(Fullscreen, "VideoFullscreenManager::enterVideoFullscreenForVideoElement(%p)", this);
 
     auto contextId = m_playbackSessionManager->contextIdForMediaElement(videoElement);
@@ -279,10 +300,10 @@
     auto videoRect = inlineVideoFrame(videoElement);
     FloatRect videoLayerFrame = FloatRect(0, 0, videoRect.width(), videoRect.height());
 
-    if (mode != HTMLMediaElementEnums::VideoFullscreenModeNone)
-        interface->setTargetIsFullscreen(true);
-    else
-        interface->setTargetIsFullscreen(false);
+#if PLATFORM(IOS)
+    if (!allowLayeredFullscreenVideos)
+#endif
+        setCurrentlyInFullscreen(*interface, mode != HTMLMediaElementEnums::VideoFullscreenModeNone);
 
     if (mode == HTMLMediaElementEnums::VideoFullscreenModePictureInPicture)
         m_videoElementInPictureInPicture = videoElement;
@@ -345,7 +366,7 @@
             m_videoElementInPictureInPicture = nullptr;
 
         auto& interface = ensureInterface(contextId);
-        interface.setTargetIsFullscreen(false);
+        protectedThis->setCurrentlyInFullscreen(interface, false);
         interface.setAnimationState(VideoFullscreenInterfaceContext::AnimationType::FromFullscreen);
         completionHandler(true);
     });
@@ -364,7 +385,7 @@
     auto contextId = m_videoElements.get(&videoElement);
     auto& interface = ensureInterface(contextId);
 
-    interface.setTargetIsFullscreen(false);
+    setCurrentlyInFullscreen(interface, false);
 
     // didCleanupFullscreen() will call removeClientForContext() on Mac
 #if PLATFORM(IOS_FAMILY)
@@ -597,6 +618,12 @@
 {
     ensureModel(contextId).requestRouteSharingPolicyAndContextUID(WTFMove(reply));
 }
+
+void VideoFullscreenManager::setCurrentlyInFullscreen(VideoFullscreenInterfaceContext& interface, bool currentlyInFullscreen)
+{
+    interface.setTargetIsFullscreen(currentlyInFullscreen);
+    m_currentlyInFullscreen = currentlyInFullscreen;
+}
     
 void VideoFullscreenManager::setVideoLayerFrameFenced(PlaybackSessionContextIdentifier contextId, WebCore::FloatRect bounds, const WTF::MachSendRight& machSendRight)
 {
diff --git a/Source/WebKitLegacy/mac/WebCoreSupport/WebChromeClient.h b/Source/WebKitLegacy/mac/WebCoreSupport/WebChromeClient.h
index cf3cef5..4b4f156 100644
--- a/Source/WebKitLegacy/mac/WebCoreSupport/WebChromeClient.h
+++ b/Source/WebKitLegacy/mac/WebCoreSupport/WebChromeClient.h
@@ -207,6 +207,7 @@
 #endif
 
 #if ENABLE(VIDEO)
+    bool canEnterVideoFullscreen() const final;
     bool supportsVideoFullscreen(WebCore::HTMLMediaElementEnums::VideoFullscreenMode) final;
 #if ENABLE(VIDEO_PRESENTATION_MODE)
     void setMockVideoPresentationModeEnabled(bool) final;
diff --git a/Source/WebKitLegacy/mac/WebCoreSupport/WebChromeClient.mm b/Source/WebKitLegacy/mac/WebCoreSupport/WebChromeClient.mm
index b4fc500..936a2fc 100644
--- a/Source/WebKitLegacy/mac/WebCoreSupport/WebChromeClient.mm
+++ b/Source/WebKitLegacy/mac/WebCoreSupport/WebChromeClient.mm
@@ -944,6 +944,15 @@
 
 #if ENABLE(VIDEO)
 
+bool WebChromeClient::canEnterVideoFullscreen() const
+{
+#if !PLATFORM(IOS_FAMILY) || HAVE(AVKIT)
+    return true;
+#else
+    return false;
+#endif
+}
+
 bool WebChromeClient::supportsVideoFullscreen(HTMLMediaElementEnums::VideoFullscreenMode)
 {
 #if !PLATFORM(IOS_FAMILY) || HAVE(AVKIT)