Title: [224608] trunk/Source/WebKit
Revision
224608
Author
commit-qu...@webkit.org
Date
2017-11-08 16:45:47 -0800 (Wed, 08 Nov 2017)

Log Message

Make WKFullScreenWidnowController more robust against modification by the embedding app.
https://bugs.webkit.org/show_bug.cgi?id=179413
rdar://problem/35408061

Patch by Jeremy Jones <jere...@apple.com> on 2017-11-08
Reviewed by Darin Adler.

Present fullscreen UViewController in a separate UIWindow to prevent interaction with the embedding app's
UIViewController hierarchy.

Immediately tear down the fullscreen interface if the embedding app removes the WKWebView from the fullscreen window.
This prevents the fullscreen interface from getting into an invalid state.

Preserve scrollView.zoomScale because it is not effectively preserved by the more indirect _viewScale.

Use a custom root view controller to allow hiding of the status bar.

Remove the no-longer-necessary dispatch_after calls during enter and exit fullscreen.

* UIProcess/API/Cocoa/WKWebView.mm:
(-[WKWebView removeFromSuperview]):
* UIProcess/ios/WKFullScreenWindowControllerIOS.h:
* UIProcess/ios/WKFullScreenWindowControllerIOS.mm:
(WebKit::WKWebViewState::applyTo):
(WebKit::WKWebViewState::store):
(-[_WKFullScreenViewController loadView]):
(-[_WKFullscreenRootViewController prefersStatusBarHidden]):
(-[WKFullScreenWindowController enterFullScreen]):
(-[WKFullScreenWindowController beganEnterFullScreenWithInitialFrame:finalFrame:]):
(-[WKFullScreenWindowController completedExitFullScreen]):
(-[WKFullScreenWindowController exitFullscreenImmediately]):
(-[WKFullScreenWindowController close]):
(-[WKFullScreenWindowController webViewDidRemoveFromSuperviewWhileInFullscreen]):
(-[_WKFullScreenViewController viewDidDisappear:]): Deleted.

Modified Paths

Diff

Modified: trunk/Source/WebKit/ChangeLog (224607 => 224608)


--- trunk/Source/WebKit/ChangeLog	2017-11-09 00:43:35 UTC (rev 224607)
+++ trunk/Source/WebKit/ChangeLog	2017-11-09 00:45:47 UTC (rev 224608)
@@ -1,5 +1,41 @@
 2017-11-08  Jeremy Jones  <jere...@apple.com>
 
+        Make WKFullScreenWidnowController more robust against modification by the embedding app.
+        https://bugs.webkit.org/show_bug.cgi?id=179413
+        rdar://problem/35408061
+
+        Reviewed by Darin Adler.
+
+        Present fullscreen UViewController in a separate UIWindow to prevent interaction with the embedding app's
+        UIViewController hierarchy.
+
+        Immediately tear down the fullscreen interface if the embedding app removes the WKWebView from the fullscreen window.
+        This prevents the fullscreen interface from getting into an invalid state.
+
+        Preserve scrollView.zoomScale because it is not effectively preserved by the more indirect _viewScale.
+
+        Use a custom root view controller to allow hiding of the status bar.
+
+        Remove the no-longer-necessary dispatch_after calls during enter and exit fullscreen.
+
+        * UIProcess/API/Cocoa/WKWebView.mm:
+        (-[WKWebView removeFromSuperview]):
+        * UIProcess/ios/WKFullScreenWindowControllerIOS.h:
+        * UIProcess/ios/WKFullScreenWindowControllerIOS.mm:
+        (WebKit::WKWebViewState::applyTo):
+        (WebKit::WKWebViewState::store):
+        (-[_WKFullScreenViewController loadView]):
+        (-[_WKFullscreenRootViewController prefersStatusBarHidden]):
+        (-[WKFullScreenWindowController enterFullScreen]):
+        (-[WKFullScreenWindowController beganEnterFullScreenWithInitialFrame:finalFrame:]):
+        (-[WKFullScreenWindowController completedExitFullScreen]):
+        (-[WKFullScreenWindowController exitFullscreenImmediately]):
+        (-[WKFullScreenWindowController close]):
+        (-[WKFullScreenWindowController webViewDidRemoveFromSuperviewWhileInFullscreen]):
+        (-[_WKFullScreenViewController viewDidDisappear:]): Deleted.
+
+2017-11-08  Jeremy Jones  <jere...@apple.com>
+
         HTMLMediaElement should not use element fullscreen on iOS
         https://bugs.webkit.org/show_bug.cgi?id=179418
         rdar://problem/35409277

Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm (224607 => 224608)


--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm	2017-11-09 00:43:35 UTC (rev 224607)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm	2017-11-09 00:45:47 UTC (rev 224608)
@@ -4822,6 +4822,14 @@
 
 #if PLATFORM(IOS)
 
+- (void)removeFromSuperview
+{
+    [super removeFromSuperview];
+
+    if ([_fullScreenWindowController isFullScreen])
+        [_fullScreenWindowController webViewDidRemoveFromSuperviewWhileInFullscreen];
+}
+
 - (CGSize)_minimumLayoutSizeOverride
 {
     ASSERT(_overridesMinimumLayoutSize);

Modified: trunk/Source/WebKit/UIProcess/ios/WKFullScreenWindowControllerIOS.h (224607 => 224608)


--- trunk/Source/WebKit/UIProcess/ios/WKFullScreenWindowControllerIOS.h	2017-11-09 00:43:35 UTC (rev 224607)
+++ trunk/Source/WebKit/UIProcess/ios/WKFullScreenWindowControllerIOS.h	2017-11-09 00:45:47 UTC (rev 224608)
@@ -52,6 +52,7 @@
 - (void)close;
 - (void)beganEnterFullScreenWithInitialFrame:(CGRect)initialFrame finalFrame:(CGRect)finalFrame;
 - (void)beganExitFullScreenWithInitialFrame:(CGRect)initialFrame finalFrame:(CGRect)finalFrame;
+- (void)webViewDidRemoveFromSuperviewWhileInFullscreen;
 
 @end
 

Modified: trunk/Source/WebKit/UIProcess/ios/WKFullScreenWindowControllerIOS.mm (224607 => 224608)


--- trunk/Source/WebKit/UIProcess/ios/WKFullScreenWindowControllerIOS.mm	2017-11-09 00:43:35 UTC (rev 224607)
+++ trunk/Source/WebKit/UIProcess/ios/WKFullScreenWindowControllerIOS.mm	2017-11-09 00:45:47 UTC (rev 224608)
@@ -70,11 +70,12 @@
     float _savedTopContentInset = 0.0;
     CGFloat _savedPageScale = 1;
     CGFloat _savedViewScale = 1.0;
+    CGFloat _savedZoomScale = 1;
     UIEdgeInsets _savedEdgeInset = UIEdgeInsetsZero;
     UIEdgeInsets _savedObscuredInsets = UIEdgeInsetsZero;
     UIEdgeInsets _savedScrollIndicatorInsets = UIEdgeInsetsZero;
     CGPoint _savedContentOffset = CGPointZero;
-    
+
     void applyTo(WKWebView* webView)
     {
         [webView _setPageScale:_savedPageScale withOrigin:CGPointMake(0, 0)];
@@ -84,6 +85,7 @@
         [[webView scrollView] setScrollIndicatorInsets:_savedScrollIndicatorInsets];
         [webView _page]->setTopContentInset(_savedTopContentInset);
         [webView _setViewScale:_savedViewScale];
+        [[webView scrollView] setZoomScale:_savedZoomScale];
     }
     
     void store(WKWebView* webView)
@@ -95,6 +97,7 @@
         _savedScrollIndicatorInsets = [[webView scrollView] scrollIndicatorInsets];
         _savedTopContentInset = [webView _page]->topContentInset();
         _savedViewScale = [webView _viewScale];
+        _savedZoomScale = [[webView scrollView] zoomScale];
     }
 };
     
@@ -180,7 +183,7 @@
 
 - (void)loadView
 {
-    [self setView:adoptNS([[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]]).get()];
+    [self setView:adoptNS([[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]).get()];
     [[self view] setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)];
 
     CGRect doneButtonRect = CGRectMake(10, 20, 60, 47);
@@ -220,10 +223,6 @@
     [[self view] insertSubview:[self contentView] atIndex:0];
 }
 
-- (void)viewDidDisappear:(BOOL)animated
-{
-}
-
 - (void)cancelAction:(id)sender
 {
     [[self target] performSelector:[self action]];
@@ -275,7 +274,18 @@
 
 @end
 
+@interface _WKFullscreenRootViewController : UIViewController
+@end
 
+@implementation _WKFullscreenRootViewController : UIViewController
+
+- (BOOL)prefersStatusBarHidden
+{
+    return YES;
+}
+
+@end
+
 @interface WKFullscreenAnimationController : NSObject <UIViewControllerAnimatedTransitioning>
 @property (retain, nonatomic) UIViewController* viewController;
 @property (nonatomic) CGRect initialFrame;
@@ -347,6 +357,8 @@
     FullScreenState _fullScreenState;
     WKWebViewState _viewState;
 
+    RetainPtr<UIWindow> _window;
+
     RefPtr<WebKit::VoidCallback> _repaintCallback;
     RetainPtr<UIViewController> _viewControllerForPresentation;
     RetainPtr<_WKFullScreenViewController> _fullscreenViewController;
@@ -398,13 +410,23 @@
         return;
 
     _fullScreenState = WaitingToEnterFullScreen;
-    
-    _viewControllerForPresentation = [UIViewController _viewControllerForFullScreenPresentationFromView:_webView];
+
+    _window = adoptNS([[UIWindow alloc] init]);
+    [_window setBackgroundColor:[UIColor clearColor]];
+    [_window setRootViewController:adoptNS([[_WKFullscreenRootViewController alloc] init]).get()];
+    [[_window rootViewController] setView:adoptNS([[UIView alloc] initWithFrame:[_window bounds]]).get()];
+    [[[_window rootViewController] view] setBackgroundColor:[UIColor clearColor]];
+    [[[_window rootViewController] view] setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)];
+    [_window setWindowLevel:UIWindowLevelNormal - 1];
+    [_window setHidden:NO];
+    _viewControllerForPresentation = [_window rootViewController];
+
     _fullscreenViewController = adoptNS([[_WKFullScreenViewController alloc] init]);
     [_fullscreenViewController setTransitioningDelegate:self];
     [_fullscreenViewController setModalPresentationStyle:UIModalPresentationCustom];
     [_fullscreenViewController setTarget:self action:@selector(requestExitFullScreen)];
-    
+    [[_fullscreenViewController view] setFrame:[[_viewControllerForPresentation view] bounds]];
+
     [self _manager]->saveScrollPosition();
 
     [_webView _page]->setSuppressVisibilityUpdates(true);
@@ -416,37 +438,35 @@
 
     WKSnapshotConfiguration* config = nil;
     [_webView takeSnapshotWithConfiguration:config completionHandler:^(UIImage * snapshotImage, NSError * error){
-        UIScreen* screen = [UIScreen mainScreen];
-        RetainPtr<UIWindow> webWindow = [_webView window];
-        
+        if (![_webView _page])
+            return;
+
         [CATransaction begin];
         [CATransaction setDisableActions:YES];
         
         [[_webViewPlaceholder layer] setContents:(id)[snapshotImage CGImage]];
         replaceViewWithView(_webView, _webViewPlaceholder.get());
-        
+
         WKWebViewState().applyTo(_webView);
         
         [_webView setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)];
-        [_webView setFrame:[screen bounds]];
-        [webWindow insertSubview:_webView atIndex:0];
-        [_webView _overrideLayoutParametersWithMinimumLayoutSize:[screen bounds].size maximumUnobscuredSizeOverride:[screen bounds].size];
-        
+        [_webView setFrame:[_window bounds]];
+        [_window insertSubview:_webView atIndex:0];
+        [_webView _overrideLayoutParametersWithMinimumLayoutSize:[_window bounds].size maximumUnobscuredSizeOverride:[_window bounds].size];
+
         [_webView setNeedsLayout];
         [_webView layoutIfNeeded];
         
         [self _manager]->setAnimatingFullScreen(true);
-        
-        // FIXME: <http://webkit.org/b/178923> Find a better way to do this.
-        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0), dispatch_get_main_queue(), ^{
-            _repaintCallback = VoidCallback::create([protectedSelf = RetainPtr<WKFullScreenWindowController>(self)](WebKit::CallbackBase::Error) {
-                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0), dispatch_get_main_queue(), ^{
-                    [protectedSelf _manager]->willEnterFullScreen();
-                });
-            });
-            [_webView _page]->forceRepaint(_repaintCallback.copyRef());
+
+        _repaintCallback = VoidCallback::create([protectedSelf = retainPtr(self), self](WebKit::CallbackBase::Error) {
+            if (![_webView _page])
+                return;
+
+            [protectedSelf _manager]->willEnterFullScreen();
         });
-        
+        [_webView _page]->forceRepaint(_repaintCallback.copyRef());
+
         [CATransaction commit];
     }];
 }
@@ -459,9 +479,18 @@
 
     _initialFrame = initialFrame;
     _finalFrame = finalFrame;
-    
-    [[_fullscreenViewController view] setFrame:[[UIScreen mainScreen] bounds]];
+
+    [CATransaction begin];
+    [CATransaction setDisableActions:YES];
+
+    [_webView removeFromSuperview];
     [_fullscreenViewController setContentView:_webView];
+
+    [_window setWindowLevel:UIWindowLevelNormal];
+    [_window makeKeyAndVisible];
+
+    [CATransaction commit];
+
     [_viewControllerForPresentation presentViewController:_fullscreenViewController.get() animated:YES completion:^{
         [self completedEnterFullScreen];
     }];
@@ -469,7 +498,11 @@
 
 - (void)completedEnterFullScreen
 {
+    if (![_webView _page])
+        return;
+
     _fullScreenState = InFullScreen;
+
     [self _manager]->didEnterFullScreen();
     [self _manager]->setAnimatingFullScreen(false);
 
@@ -503,6 +536,9 @@
     [_webView _page]->setSuppressVisibilityUpdates(true);
 
     [_fullscreenViewController dismissViewControllerAnimated:YES completion:^{
+        if (![_webView _page])
+            return;
+
         [self completedExitFullScreen];
     }];
 }
@@ -509,41 +545,81 @@
 
 - (void)completedExitFullScreen
 {
+    if (_fullScreenState != ExitingFullScreen)
+        return;
     _fullScreenState = NotInFullScreen;
 
-    [_webView setFrame:[_webViewPlaceholder bounds]];
-    [[_webViewPlaceholder window] insertSubview:_webView atIndex:0];
+    [CATransaction begin];
+    [CATransaction setDisableActions:YES];
 
+    [[_webViewPlaceholder superview] insertSubview:_webView belowSubview:_webViewPlaceholder.get()];
+    [_webView setFrame:[_webViewPlaceholder frame]];
+    [_webView setAutoresizingMask:[_webViewPlaceholder autoresizingMask]];
+
     [[_webView window] makeKeyAndVisible];
 
-    [self _manager]->didExitFullScreen();
-    [self _manager]->setAnimatingFullScreen(false);
-
     _viewState.applyTo(_webView);
 
     [_webView setNeedsLayout];
     [_webView layoutIfNeeded];
 
+    [CATransaction commit];
+
+    [_window setHidden:YES];
+    _window = nil;
+
+    [self _manager]->setAnimatingFullScreen(false);
+    [self _manager]->didExitFullScreen();
+
     if (_repaintCallback) {
         _repaintCallback->invalidate(WebKit::CallbackBase::Error::OwnerWasInvalidated);
         ASSERT(!_repaintCallback);
     }
-    _repaintCallback = VoidCallback::create([protectedSelf = RetainPtr<WKFullScreenWindowController>(self), self](WebKit::CallbackBase::Error) {
-        replaceViewWithView(_webViewPlaceholder.get(), _webView);
+
+    _repaintCallback = VoidCallback::create([protectedSelf = retainPtr(self), self](WebKit::CallbackBase::Error) {
         _repaintCallback = nullptr;
+        [_webViewPlaceholder removeFromSuperview];
+
+        if (![_webView _page])
+            return;
+
         [_webView _page]->setSuppressVisibilityUpdates(false);
     });
+
     [_webView _page]->forceRepaint(_repaintCallback.copyRef());
 }
 
+- (void)exitFullscreenImmediately
+{
+    if (![self isFullScreen])
+        return;
+
+    if (![_webView _page])
+        return;
+
+    [self _manager]->requestExitFullScreen();
+    [self exitFullScreen];
+    _fullScreenState = ExitingFullScreen;
+    [self completedExitFullScreen];
+    replaceViewWithView(_webViewPlaceholder.get(), _webView);
+    [_webView _page]->setSuppressVisibilityUpdates(false);
+    [self _manager]->didExitFullScreen();
+    [self _manager]->setAnimatingFullScreen(false);
+    _webViewPlaceholder = nil;
+}
+
 - (void)close
 {
-    if ([self isFullScreen])
-        [self exitFullScreen];
-    
+    [self exitFullscreenImmediately];
     _webView = nil;
 }
 
+- (void)webViewDidRemoveFromSuperviewWhileInFullscreen
+{
+    if (_fullScreenState == InFullScreen && _webView.window != _window.get())
+        [self exitFullscreenImmediately];
+}
+
 #pragma mark -
 #pragma mark Internal Interface
 
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to