Scrunching a video to PiP can result in broken animation and leave Safari in a bad state
https://bugs.webkit.org/show_bug.cgi?id=213175

Reviewed by Jer Noble.

Source/WebCore:

* html/HTMLMediaElement.cpp:
(WebCore::HTMLMediaElement::setVideoFullscreenStandby):
(WebCore::HTMLMediaElement::setVideoFullscreenStandby):
The "standby" state is relevant to a video element only when its presentation mode is VideoFullscreenModeNone.

* platform/ios/VideoFullscreenInterfaceAVKit.h:
* platform/ios/VideoFullscreenInterfaceAVKit.mm:
(VideoFullscreenInterfaceAVKit::exitFullscreen):
(VideoFullscreenInterfaceAVKit::cleanupFullscreen):
Add a flag m_enteringPictureInPicture. Function exitFullscreen() and cleanupFullscreen() will check
m_enteringPictureInPicture and they will abort the process to exit fullscreen/picture-in-picture if the flag
is true. However, VideoFullscreenManager will retry to exit fullscreen/picture-in-picture after it confirms that
the previous starting picture-in-picture process is completed.

(VideoFullscreenInterfaceAVKit::willStartPictureInPicture):
Set the flag m_enteringPictureInPicture.

(VideoFullscreenInterfaceAVKit::didStartPictureInPicture):
Call m_fullscreenChangeObserver->didEnterFullscreen() if the entering picture-in-picture process is
started by the UI process (e.g., swipe-up gesture).
Clear m_standby and m_enteringPictureInPicture after the video element enters picture-in-picture.

Source/WebKit:

* UIProcess/ios/fullscreen/WKFullScreenWindowControllerIOS.mm:
(-[WKFullScreenWindowController _completedExitFullScreen]):
Update _exitRequested after exiting fullscreen to make sure the following
enter fullscreen request can be processed.
(-[WKFullScreenWindowController _dismissFullscreenViewController]):
Make sure _completedExitFullScreen function will always execute.
(-[WKFullScreenWindowController _interactivePinchDismissChanged:]):
Remove a function call which corrupts the state machine under stress test.


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@263760 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index 3785c61..e59ca1a 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,32 @@
+2020-06-30  Peng Liu  <peng.liu6@apple.com>
+
+        Scrunching a video to PiP can result in broken animation and leave Safari in a bad state
+        https://bugs.webkit.org/show_bug.cgi?id=213175
+
+        Reviewed by Jer Noble.
+
+        * html/HTMLMediaElement.cpp:
+        (WebCore::HTMLMediaElement::setVideoFullscreenStandby):
+        (WebCore::HTMLMediaElement::setVideoFullscreenStandby):
+        The "standby" state is relevant to a video element only when its presentation mode is VideoFullscreenModeNone.
+
+        * platform/ios/VideoFullscreenInterfaceAVKit.h:
+        * platform/ios/VideoFullscreenInterfaceAVKit.mm:
+        (VideoFullscreenInterfaceAVKit::exitFullscreen):
+        (VideoFullscreenInterfaceAVKit::cleanupFullscreen):
+        Add a flag m_enteringPictureInPicture. Function exitFullscreen() and cleanupFullscreen() will check
+        m_enteringPictureInPicture and they will abort the process to exit fullscreen/picture-in-picture if the flag
+        is true. However, VideoFullscreenManager will retry to exit fullscreen/picture-in-picture after it confirms that
+        the previous starting picture-in-picture process is completed.
+
+        (VideoFullscreenInterfaceAVKit::willStartPictureInPicture):
+        Set the flag m_enteringPictureInPicture.
+
+        (VideoFullscreenInterfaceAVKit::didStartPictureInPicture):
+        Call m_fullscreenChangeObserver->didEnterFullscreen() if the entering picture-in-picture process is
+        started by the UI process (e.g., swipe-up gesture).
+        Clear m_standby and m_enteringPictureInPicture after the video element enters picture-in-picture.
+
 2020-06-30  Alex Christensen  <achristensen@webkit.org>
 
         Remove WTF::MD5
diff --git a/Source/WebCore/html/HTMLMediaElement.cpp b/Source/WebCore/html/HTMLMediaElement.cpp
index bbd21b0..e4c44a4 100644
--- a/Source/WebCore/html/HTMLMediaElement.cpp
+++ b/Source/WebCore/html/HTMLMediaElement.cpp
@@ -6049,7 +6049,10 @@
         m_player->videoFullscreenStandbyChanged();
 #endif
 
-    if (m_videoFullscreenStandby || m_videoFullscreenMode != VideoFullscreenModeNone)
+    if (m_videoFullscreenMode != VideoFullscreenModeNone)
+        return;
+
+    if (m_videoFullscreenStandby)
         document().page()->chrome().client().enterVideoFullscreenForVideoElement(downcast<HTMLVideoElement>(*this), m_videoFullscreenMode, m_videoFullscreenStandby);
     else
         document().page()->chrome().client().exitVideoFullscreenForVideoElement(downcast<HTMLVideoElement>(*this));
diff --git a/Source/WebCore/platform/ios/VideoFullscreenInterfaceAVKit.h b/Source/WebCore/platform/ios/VideoFullscreenInterfaceAVKit.h
index 357d9ae..24d1358 100644
--- a/Source/WebCore/platform/ios/VideoFullscreenInterfaceAVKit.h
+++ b/Source/WebCore/platform/ios/VideoFullscreenInterfaceAVKit.h
@@ -219,6 +219,7 @@
 
     bool m_waitingForPreparedToExit { false };
     bool m_shouldIgnoreAVKitCallbackAboutExitFullscreenReason { false };
+    bool m_enteringPictureInPicture { false };
 };
 
 }
diff --git a/Source/WebCore/platform/ios/VideoFullscreenInterfaceAVKit.mm b/Source/WebCore/platform/ios/VideoFullscreenInterfaceAVKit.mm
index 4909e94..5402437 100644
--- a/Source/WebCore/platform/ios/VideoFullscreenInterfaceAVKit.mm
+++ b/Source/WebCore/platform/ios/VideoFullscreenInterfaceAVKit.mm
@@ -921,6 +921,9 @@
     if (m_watchdogTimer.isActive())
         m_watchdogTimer.stop();
 
+    if (m_enteringPictureInPicture)
+        return;
+
     m_targetMode = HTMLMediaElementEnums::VideoFullscreenModeNone;
 
     setInlineRect(finalRect, true);
@@ -930,9 +933,11 @@
 
 void VideoFullscreenInterfaceAVKit::cleanupFullscreen()
 {
+    LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::cleanupFullscreen(%p)", this);
     m_shouldIgnoreAVKitCallbackAboutExitFullscreenReason = false;
 
-    LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::cleanupFullscreen(%p)", this);
+    if (m_enteringPictureInPicture)
+        return;
 
     m_cleanupNeedsReturnVideoContentLayer = true;
     if (m_hasVideoContentLayer && m_fullscreenChangeObserver) {
@@ -1055,6 +1060,8 @@
 void VideoFullscreenInterfaceAVKit::willStartPictureInPicture()
 {
     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::willStartPictureInPicture(%p)", this);
+    m_enteringPictureInPicture = true;
+
     if (m_standby && !m_currentMode.hasVideo()) {
         [m_window setHidden:NO];
         [[m_playerViewController view] setHidden:NO];
@@ -1088,6 +1095,13 @@
 
     if (m_enterFullscreenNeedsEnterPictureInPicture)
         doEnterFullscreen();
+    else {
+        if (m_fullscreenChangeObserver)
+            m_fullscreenChangeObserver->didEnterFullscreen();
+    }
+
+    m_enteringPictureInPicture = false;
+    m_standby = false;
 }
 
 void VideoFullscreenInterfaceAVKit::failedToStartPictureInPicture()
diff --git a/Source/WebKit/ChangeLog b/Source/WebKit/ChangeLog
index f32131d..863dc37 100644
--- a/Source/WebKit/ChangeLog
+++ b/Source/WebKit/ChangeLog
@@ -1,3 +1,19 @@
+2020-06-30  Peng Liu  <peng.liu6@apple.com>
+
+        Scrunching a video to PiP can result in broken animation and leave Safari in a bad state
+        https://bugs.webkit.org/show_bug.cgi?id=213175
+
+        Reviewed by Jer Noble.
+
+        * UIProcess/ios/fullscreen/WKFullScreenWindowControllerIOS.mm:
+        (-[WKFullScreenWindowController _completedExitFullScreen]):
+        Update _exitRequested after exiting fullscreen to make sure the following
+        enter fullscreen request can be processed.
+        (-[WKFullScreenWindowController _dismissFullscreenViewController]):
+        Make sure _completedExitFullScreen function will always execute.
+        (-[WKFullScreenWindowController _interactivePinchDismissChanged:]):
+        Remove a function call which corrupts the state machine under stress test.
+
 2020-06-30  Antoine Quint  <graouts@webkit.org>
 
         [iOS] Crash under WebKit::WebPage::getFocusedElementInformation()
diff --git a/Source/WebKit/UIProcess/ios/fullscreen/WKFullScreenWindowControllerIOS.mm b/Source/WebKit/UIProcess/ios/fullscreen/WKFullScreenWindowControllerIOS.mm
index 8357647..46bce6a 100644
--- a/Source/WebKit/UIProcess/ios/fullscreen/WKFullScreenWindowControllerIOS.mm
+++ b/Source/WebKit/UIProcess/ios/fullscreen/WKFullScreenWindowControllerIOS.mm
@@ -779,6 +779,7 @@
 
     [_fullscreenViewController setPrefersStatusBarHidden:YES];
     _fullscreenViewController = nil;
+    _exitRequested = NO;
 }
 
 - (void)close
@@ -993,6 +994,11 @@
 
 - (void)_dismissFullscreenViewController
 {
+    if (!_fullscreenViewController) {
+        [self _completedExitFullScreen];
+        return;
+    }
+
     [_fullscreenViewController setAnimating:YES];
     [_fullscreenViewController dismissViewControllerAnimated:YES completion:^{
         if (![self._webView _page])
@@ -1036,11 +1042,6 @@
 
 - (void)_interactivePinchDismissChanged:(id)sender
 {
-    if (!_inInteractiveDismiss && _interactivePinchDismissGestureRecognizer.get().state == UIGestureRecognizerStateBegan) {
-        [self _startToDismissFullscreenChanged:sender];
-        return;
-    }
-
     CGFloat scale = [_interactivePinchDismissGestureRecognizer scale];
     CGFloat velocity = [_interactivePinchDismissGestureRecognizer velocity];
     CGFloat progress = std::min(1., std::max(0., 1 - scale));