/* * Copyright (C) 2017 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. */ #import "config.h" #if PLATFORM(IOS) && ENABLE(FULLSCREEN_API) #import "WKFullScreenWindowControllerIOS.h" #import "UIKitSPI.h" #import "WKWebView.h" #import "WKWebViewInternal.h" #import "WKWebViewPrivate.h" #import "WebFullScreenManagerProxy.h" #import "WebPageProxy.h" #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import using namespace WebKit; using namespace WebCore; SOFT_LINK_PRIVATE_FRAMEWORK_OPTIONAL(LinkPresentation) namespace WebKit { static const NSTimeInterval showHideAnimationDuration = 0.1; static const NSTimeInterval autoHideDelay = 4.0; static void replaceViewWithView(UIView *view, UIView *otherView) { [CATransaction begin]; [CATransaction setDisableActions:YES]; [otherView setFrame:[view frame]]; [otherView setAutoresizingMask:[view autoresizingMask]]; [[view superview] insertSubview:otherView aboveSubview:view]; [view removeFromSuperview]; [CATransaction commit]; } enum FullScreenState : NSInteger { NotInFullScreen, WaitingToEnterFullScreen, EnteringFullScreen, InFullScreen, WaitingToExitFullScreen, ExitingFullScreen, }; struct WKWebViewState { 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)]; [webView _setObscuredInsets:_savedObscuredInsets]; [[webView scrollView] setContentInset:_savedEdgeInset]; [[webView scrollView] setContentOffset:_savedContentOffset]; [[webView scrollView] setScrollIndicatorInsets:_savedScrollIndicatorInsets]; [webView _page]->setTopContentInset(_savedTopContentInset); [webView _setViewScale:_savedViewScale]; [[webView scrollView] setZoomScale:_savedZoomScale]; } void store(WKWebView* webView) { _savedPageScale = [webView _pageScale]; _savedObscuredInsets = [webView _obscuredInsets]; _savedEdgeInset = [[webView scrollView] contentInset]; _savedContentOffset = [[webView scrollView] contentOffset]; _savedScrollIndicatorInsets = [[webView scrollView] scrollIndicatorInsets]; _savedTopContentInset = [webView _page]->topContentInset(); _savedViewScale = [webView _viewScale]; _savedZoomScale = [[webView scrollView] zoomScale]; } }; } // namespace WebKit @interface _WKFullScreenViewController : UIViewController @property (retain, nonatomic) NSArray *savedConstraints; @property (retain, nonatomic) UIView *contentView; @property (retain, nonatomic) id target; @property (assign, nonatomic) SEL action; @end @implementation _WKFullScreenViewController { RetainPtr _backgroundView; RetainPtr _touchGestureRecognizer; RetainPtr _cancelButton; RetainPtr _locationButton; RetainPtr _visualEffectView; } - (void)dealloc { [NSObject cancelPreviousPerformRequestsWithTarget:self]; [[NSNotificationCenter defaultCenter] removeObserver:self]; [super dealloc]; } - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator { [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; [coordinator animateAlongsideTransition: ^(id context) { void (^webViewUpdateBlock)() = ^{ [(WKWebView *)[self contentView] _overrideLayoutParametersWithMinimumLayoutSize:size maximumUnobscuredSizeOverride:size]; }; [(WKWebView *)[self contentView] _beginAnimatedResizeWithUpdates:webViewUpdateBlock]; [(WKWebView *)[self contentView] _setInterfaceOrientationOverride:[UIApp statusBarOrientation]]; } completion:^(id context) { [(WKWebView *)[self contentView] _endAnimatedResize]; }]; } + (void)configureView:(UIView *)view withBackgroundFillOfColor:(UIColor *)fillColor opacity:(CGFloat)opacity filter:(NSString *)filter { _UIVisualEffectLayerConfig *baseLayerConfig = [_UIVisualEffectLayerConfig layerWithFillColor:fillColor opacity:opacity filterType:filter]; [[[_UIVisualEffectConfig configWithContentConfig:baseLayerConfig] contentConfig] configureLayerView:view]; } - (void)_updateTransparencyOfVisualEffectView:(UIVisualEffectView *)visualEffectView { RetainPtr visualEffect; if (UIAccessibilityIsReduceTransparencyEnabled()) { visualEffect = [UIVisualEffect emptyEffect]; [[visualEffectView contentView] setBackgroundColor:[UIColor colorWithRed:(43.0 / 255.0) green:(46.0 / 255.0) blue:(48.0 / 255.0) alpha:1.0]]; } else { RetainPtr saturationEffect = [UIColorEffect colorEffectSaturate:1.8]; RetainPtr blurEffect = [UIBlurEffect effectWithBlurRadius:UIRoundToScreenScale(17.5, [UIScreen mainScreen])]; RetainPtr combinedEffects = [UIVisualEffect effectCombiningEffects:@[blurEffect.get(), saturationEffect.get()]]; visualEffect = combinedEffects; [[visualEffectView contentView] setBackgroundColor:nil]; } [visualEffectView setEffect:visualEffect.get()]; } - (UIVisualEffectView *)visualEffectViewWithFrame:(CGRect)frame { RetainPtr visualEffectView = adoptNS([[UIVisualEffectView alloc] initWithEffect:[UIVisualEffect emptyEffect]]); [self _updateTransparencyOfVisualEffectView:visualEffectView.get()]; RetainPtr backLayerTintView = adoptNS([[UIView alloc] initWithFrame:[visualEffectView bounds]]); [backLayerTintView setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)]; [backLayerTintView setUserInteractionEnabled:NO]; [[self class] configureView:backLayerTintView.get() withBackgroundFillOfColor:[UIColor colorWithWhite:0.0 alpha:0.55] opacity:1.0 filter:kCAFilterNormalBlendMode]; [[visualEffectView contentView] addSubview:backLayerTintView.get()]; RetainPtr topLayerTintView = adoptNS([[UIView alloc] initWithFrame:[visualEffectView bounds]]); [topLayerTintView setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)]; [topLayerTintView setUserInteractionEnabled:NO]; [[self class] configureView:topLayerTintView.get() withBackgroundFillOfColor:[UIColor colorWithWhite:1.0 alpha:0.14] opacity:1.0 filter:kCAFilterNormalBlendMode]; [[visualEffectView contentView] addSubview:topLayerTintView.get()]; [self _updateTransparencyOfVisualEffectView:visualEffectView.get()]; return visualEffectView.autorelease(); } static UIEdgeInsets mirrorEdgeInsets(UIEdgeInsets insets) { return UIEdgeInsetsMake(insets.top, insets.right, insets.bottom, insets.left); } - (void)_updateLayoutMargins { UIView *view = [self view]; [view setPreservesSuperviewLayoutMargins:NO]; UIEdgeInsets targetInsets = [view safeAreaInsets]; targetInsets.top = std::max(targetInsets.top, 20.0); [view setLayoutMargins:targetInsets]; } - (void)viewDidLayoutSubviews { [self _updateLayoutMargins]; } - (void)setLocation:(NSString *)locationName secure:(BOOL)secure trustedName:(BOOL)trustedName trustedSite:(BOOL)trustedSite { UIColor *greenTint = [UIColor colorWithRed:100 / 255.0 green:175 / 255.0 blue:99 / 255.0 alpha:1.0]; UIColor *whiteTint = [UIColor whiteColor]; float hPadding = 14; NSString *lockImageName = @"LockMini"; float lockSpacing = secure ? 10 : 0; UIEdgeInsets locationContentEdgeInsets = UIEdgeInsetsMake(0, hPadding+lockSpacing, 0, hPadding); UIEdgeInsets locationImageEdgeInsets = UIEdgeInsetsMake(0, -lockSpacing, 0, 0); if ([UIView userInterfaceLayoutDirectionForSemanticContentAttribute:[[self view] semanticContentAttribute]] == UIUserInterfaceLayoutDirectionRightToLeft) { locationContentEdgeInsets = mirrorEdgeInsets(locationContentEdgeInsets); locationImageEdgeInsets = mirrorEdgeInsets(locationImageEdgeInsets); [_locationButton setContentHorizontalAlignment:UIControlContentHorizontalAlignmentRight]; } [_locationButton setTitleColor:(trustedName ? greenTint : whiteTint) forState:UIControlStateNormal]; if (secure) { NSBundle *bundle = [NSBundle bundleForClass:[WKFullScreenWindowController class]]; UIImage *lockImage = [UIImage imageNamed:lockImageName inBundle:bundle compatibleWithTraitCollection:nil]; [_locationButton setImage:[lockImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] forState:UIControlStateNormal]; [[_locationButton imageView] setTintColor:trustedSite ? greenTint : whiteTint]; [_locationButton setTintColor:trustedSite ? greenTint : whiteTint]; } else [_locationButton setImage:nil forState:UIControlStateNormal]; [_locationButton setContentEdgeInsets:locationContentEdgeInsets]; [_locationButton setImageEdgeInsets:locationImageEdgeInsets]; [_locationButton setTitle:locationName forState:UIControlStateNormal]; [[_locationButton titleLabel] setLineBreakMode:NSLineBreakByTruncatingTail]; [[_locationButton titleLabel] setAdjustsFontSizeToFitWidth:NO]; } - (void)createSubviews { _visualEffectView = [self visualEffectViewWithFrame:CGRectMake(0, 0, 20, 20)]; [_visualEffectView setTranslatesAutoresizingMaskIntoConstraints:NO]; [[self view] addSubview:_visualEffectView.get()]; _cancelButton = [UIButton buttonWithType:UIButtonTypeSystem]; [_cancelButton setTranslatesAutoresizingMaskIntoConstraints:NO]; [_cancelButton setAdjustsImageWhenHighlighted:NO]; [_cancelButton setBackgroundColor: [UIColor blackColor]]; [[_cancelButton layer] setCompositingFilter:[CAFilter filterWithType:kCAFilterPlusL]]; [_cancelButton setTitle:WEB_UI_STRING("Done", "Text of button that exits element fullscreen.") forState:UIControlStateNormal]; [_cancelButton setTintColor:[UIColor whiteColor]]; [_cancelButton addTarget:self action:@selector(cancelAction:) forControlEvents:UIControlEventTouchUpInside]; [[self view] addSubview:_cancelButton.get()]; _locationButton = [UIButton buttonWithType:UIButtonTypeSystem]; [_locationButton setTranslatesAutoresizingMaskIntoConstraints:NO]; [_locationButton setAdjustsImageWhenHighlighted:NO]; [_locationButton setBackgroundColor:[UIColor blackColor]]; [[_locationButton layer] setCompositingFilter:[CAFilter filterWithType:kCAFilterPlusL]]; [[self view] addSubview:_locationButton.get()]; UILayoutGuide* containerGuide = [[self view] layoutMarginsGuide]; [[[_visualEffectView leftAnchor] constraintEqualToAnchor:[[self view] leftAnchor]] setActive:YES]; [[[_visualEffectView rightAnchor] constraintEqualToAnchor:[[self view] rightAnchor]] setActive:YES]; [[[_visualEffectView topAnchor] constraintEqualToAnchor:[[self view] topAnchor]] setActive:YES]; [[[_visualEffectView heightAnchor] constraintGreaterThanOrEqualToConstant:20] setActive:YES]; NSLayoutConstraint *bottom = [[_visualEffectView bottomAnchor] constraintEqualToAnchor:[containerGuide topAnchor]]; [bottom setPriority:UILayoutPriorityRequired - 1]; [bottom setActive:YES]; [[[_cancelButton leadingAnchor] constraintEqualToAnchor:[containerGuide leadingAnchor]] setActive:YES]; [[[_cancelButton topAnchor] constraintEqualToAnchor:[[self view] topAnchor]] setActive:YES]; [[[_cancelButton bottomAnchor] constraintEqualToAnchor:[_visualEffectView bottomAnchor]] setActive:YES]; [[[_locationButton heightAnchor] constraintEqualToConstant:20] setActive:YES]; [[[_locationButton bottomAnchor] constraintEqualToAnchor:[_visualEffectView bottomAnchor]] setActive:YES]; [[[_locationButton leadingAnchor] constraintGreaterThanOrEqualToAnchor:[_cancelButton trailingAnchor]] setActive:YES]; [[[_locationButton trailingAnchor] constraintLessThanOrEqualToAnchor:[[self view] trailingAnchor]] setActive:YES]; NSLayoutConstraint *centeringConstraint = [[_locationButton centerXAnchor] constraintEqualToAnchor:[[self view] centerXAnchor]]; [centeringConstraint setPriority:UILayoutPriorityDefaultLow]; [centeringConstraint setActive:YES]; [_visualEffectView setAlpha:0]; [_cancelButton setAlpha:0]; [_locationButton setAlpha:0]; [_visualEffectView setHidden:YES]; [_cancelButton setHidden:YES]; [_locationButton setHidden:YES]; } - (void)loadView { [self setView:adoptNS([[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]).get()]; [[self view] setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)]; _touchGestureRecognizer = adoptNS([[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(showCancelButton:)]); [_touchGestureRecognizer setDelegate:self]; [_touchGestureRecognizer setCancelsTouchesInView:NO]; [_touchGestureRecognizer setMinimumPressDuration:0]; [[self view] addGestureRecognizer:_touchGestureRecognizer.get()]; [self createSubviews]; } - (void)viewWillAppear:(BOOL)animated { [[self contentView] setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)]; [[self contentView] setFrame:[[self view] bounds]]; [[self view] insertSubview:[self contentView] atIndex:0]; } - (void)viewDidAppear:(BOOL)animated { [self _updateLayoutMargins]; } - (void)cancelAction:(id)sender { [[self target] performSelector:[self action]]; } - (void)hideCancelButton { [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(hideCancelButton) object:nil]; [UIView animateWithDuration:showHideAnimationDuration animations:^{ [_visualEffectView setAlpha:0]; [_cancelButton setAlpha:0]; [_locationButton setAlpha:0]; } completion:^(BOOL finished){ if (finished) { [_cancelButton setHidden:YES]; [_locationButton setHidden:YES]; [_visualEffectView setHidden:YES]; } }]; } - (void)showCancelButton:(id)sender { [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(hideCancelButton) object:nil]; [self performSelector:@selector(hideCancelButton) withObject:nil afterDelay:autoHideDelay]; [UIView animateWithDuration:showHideAnimationDuration animations: ^{ [_visualEffectView setHidden:NO]; [_cancelButton setHidden:NO]; [_locationButton setHidden:NO]; [_visualEffectView setAlpha:1]; [_cancelButton setAlpha:1]; [_locationButton setAlpha:1]; }]; } - (void)setTarget:(id)target action:(SEL)action { [self setTarget:target]; [self setAction:action]; } // MARK - UIGestureRecognizerDelegate - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { return YES; } @end @interface _WKFullscreenRootViewController : UIViewController @end @implementation _WKFullscreenRootViewController { BOOL _showsStatusBar; } - (void)setShowsStatusBar:(BOOL)value { _showsStatusBar = value; [self setNeedsStatusBarAppearanceUpdate]; } - (BOOL)prefersStatusBarHidden { return !_showsStatusBar; } @end @interface WKFullscreenAnimationController : NSObject @property (retain, nonatomic) UIViewController* viewController; @property (nonatomic) CGRect initialFrame; @property (nonatomic) CGRect finalFrame; @property (nonatomic, getter=isAnimatingIn) BOOL animatingIn; @end @implementation WKFullscreenAnimationController - (NSTimeInterval)transitionDuration:(id)transitionContext { const NSTimeInterval animationDuration = 0.2; return animationDuration; } - (void)animateTransition:(id)transitionContext { UIView *containerView = [transitionContext containerView]; UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey]; UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey]; CGRect inlineFrame = _animatingIn ? _initialFrame : _finalFrame; CGRect fullscreenFrame = _animatingIn ? _finalFrame : _initialFrame; UIView *animatingView = _animatingIn ? toView : fromView; CGRect boundsRect = largestRectWithAspectRatioInsideRect(FloatRect(inlineFrame).size().aspectRatio(), fullscreenFrame); boundsRect.origin = CGPointZero; RetainPtr maskView = adoptNS([[UIView alloc] init]); [maskView setBackgroundColor:[UIColor blackColor]]; [maskView setBounds:_animatingIn ? boundsRect : [animatingView bounds]]; [maskView setCenter:CGPointMake(CGRectGetMidX([animatingView bounds]), CGRectGetMidY([animatingView bounds]))]; [animatingView setMaskView:maskView.get()]; FloatRect scaleRect = smallestRectWithAspectRatioAroundRect(FloatRect(fullscreenFrame).size().aspectRatio(), inlineFrame); CGAffineTransform scaleTransform = CGAffineTransformMakeScale(scaleRect.width() / fullscreenFrame.size.width, scaleRect.height() / fullscreenFrame.size.height); CGAffineTransform translateTransform = CGAffineTransformMakeTranslation(CGRectGetMidX(inlineFrame) - CGRectGetMidX(fullscreenFrame), CGRectGetMidY(inlineFrame) - CGRectGetMidY(fullscreenFrame)); CGAffineTransform finalTransform = CGAffineTransformConcat(scaleTransform, translateTransform); [animatingView setTransform:_animatingIn ? finalTransform : CGAffineTransformIdentity]; [containerView addSubview:animatingView]; [UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{ [animatingView setTransform:_animatingIn ? CGAffineTransformIdentity : finalTransform]; [maskView setBounds:_animatingIn ? animatingView.bounds : boundsRect]; [maskView setCenter:CGPointMake(CGRectGetMidX([animatingView bounds]), CGRectGetMidY([animatingView bounds]))]; } completion:^(BOOL finished){ BOOL success = ![transitionContext transitionWasCancelled]; if (([self isAnimatingIn] && !success) || (![self isAnimatingIn] && success)) [animatingView removeFromSuperview]; [transitionContext completeTransition:success]; [animatingView setMaskView:nil]; }]; } - (void)animationEnded:(BOOL)transitionCompleted { } @end @implementation WKFullScreenWindowController { WKWebView *_webView; // Cannot be retained, see . RetainPtr _webViewPlaceholder; FullScreenState _fullScreenState; WKWebViewState _viewState; RetainPtr _window; RetainPtr<_WKFullscreenRootViewController> _rootViewController; RefPtr _repaintCallback; RetainPtr _viewControllerForPresentation; RetainPtr<_WKFullScreenViewController> _fullscreenViewController; CGRect _initialFrame; CGRect _finalFrame; RetainPtr _EVOrganizationName; BOOL _EVOrganizationNameIsValid; } #pragma mark - #pragma mark Initialization - (id)initWithWebView:(WKWebView *)webView { if (![super init]) return nil; _webView = webView; return self; } - (void)dealloc { [NSObject cancelPreviousPerformRequestsWithTarget:self]; [[NSNotificationCenter defaultCenter] removeObserver:self]; [super dealloc]; } #pragma mark - #pragma mark Accessors - (BOOL)isFullScreen { return _fullScreenState == WaitingToEnterFullScreen || _fullScreenState == EnteringFullScreen || _fullScreenState == InFullScreen; } - (WebCoreFullScreenPlaceholderView *)webViewPlaceholder { return nil; } #pragma mark - #pragma mark Exposed Interface - (void)_invalidateEVOrganizationName { _EVOrganizationName = nil; _EVOrganizationNameIsValid = NO; } - (BOOL)isSecure { return _webView.hasOnlySecureContent; } - (SecTrustRef)_serverTrust { return _webView.serverTrust; } - (NSString *)_EVOrganizationName { if (!self.isSecure) return nil; if (_EVOrganizationNameIsValid) return _EVOrganizationName.get(); ASSERT(!_EVOrganizationName.get()); _EVOrganizationNameIsValid = YES; SecTrustRef trust = [self _serverTrust]; if (!trust) return nil; NSDictionary *infoDictionary = [(__bridge NSDictionary *)SecTrustCopyInfo(trust) autorelease]; // If SecTrustCopyInfo returned NULL then it's likely that the SecTrustRef has not been evaluated // and the only way to get the information we need is to call SecTrustEvaluate ourselves. if (!infoDictionary) { OSStatus err = SecTrustEvaluate(trust, NULL); if (err == noErr) infoDictionary = [(__bridge NSDictionary *)SecTrustCopyInfo(trust) autorelease]; if (!infoDictionary) return nil; } // Make sure that the EV certificate is valid against our certificate chain. id hasEV = [infoDictionary objectForKey:(__bridge NSString *)kSecTrustInfoExtendedValidationKey]; if (![hasEV isKindOfClass:[NSValue class]] || ![hasEV boolValue]) return nil; // Make sure that we could contact revocation server and it is still valid. id isNotRevoked = [infoDictionary objectForKey:(__bridge NSString *)kSecTrustInfoRevocationKey]; if (![isNotRevoked isKindOfClass:[NSValue class]] || ![isNotRevoked boolValue]) return nil; _EVOrganizationName = [infoDictionary objectForKey:(__bridge NSString *)kSecTrustInfoCompanyNameKey]; return _EVOrganizationName.get(); } - (void)updateLocationInfo { NSURL* url = _webView._committedURL; NSString *EVOrganizationName = [self _EVOrganizationName]; BOOL showsEVOrganizationName = [EVOrganizationName length] > 0; NSString *domain = nil; if (LinkPresentationLibrary()) domain = [url _lp_simplifiedDisplayString]; else domain = userVisibleString(url); NSString *text = nil; if ([[url scheme] caseInsensitiveCompare:@"data"] == NSOrderedSame) text = @"data:"; else if (showsEVOrganizationName) text = EVOrganizationName; else text = domain; [_fullscreenViewController setLocation:text secure:self.isSecure trustedName:showsEVOrganizationName trustedSite:!!EVOrganizationName]; } - (void)enterFullScreen { if ([self isFullScreen]) return; [self _invalidateEVOrganizationName]; _fullScreenState = WaitingToEnterFullScreen; _window = adoptNS([[UIWindow alloc] init]); [_window setBackgroundColor:[UIColor clearColor]]; _rootViewController = adoptNS([[_WKFullscreenRootViewController alloc] init]); [_window setRootViewController:_rootViewController.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 updateLocationInfo]; [self _manager]->saveScrollPosition(); [_webView _page]->setSuppressVisibilityUpdates(true); _viewState.store(_webView); _webViewPlaceholder = adoptNS([[UIView alloc] init]); [[_webViewPlaceholder layer] setName:@"Fullscreen Placeholder Vfiew"]; WKSnapshotConfiguration* config = nil; [_webView takeSnapshotWithConfiguration:config completionHandler:^(UIImage * snapshotImage, NSError * error){ 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:[_window bounds]]; [_window insertSubview:_webView atIndex:0]; [_webView _overrideLayoutParametersWithMinimumLayoutSize:[_window bounds].size maximumUnobscuredSizeOverride:[_window bounds].size]; [_webView setNeedsLayout]; [_webView layoutIfNeeded]; [self _manager]->setAnimatingFullScreen(true); _repaintCallback = VoidCallback::create([protectedSelf = retainPtr(self), self](WebKit::CallbackBase::Error) { _repaintCallback = nullptr; if (![_webView _page]) return; [protectedSelf _manager]->willEnterFullScreen(); }); [_webView _page]->forceRepaint(_repaintCallback.copyRef()); [CATransaction commit]; }]; } - (void)beganEnterFullScreenWithInitialFrame:(CGRect)initialFrame finalFrame:(CGRect)finalFrame { if (_fullScreenState != WaitingToEnterFullScreen) return; _fullScreenState = EnteringFullScreen; _initialFrame = initialFrame; _finalFrame = finalFrame; [CATransaction begin]; [CATransaction setDisableActions:YES]; [_webView removeFromSuperview]; [_fullscreenViewController setContentView:_webView]; [_window setWindowLevel:UIWindowLevelNormal]; [_window makeKeyAndVisible]; [_rootViewController setShowsStatusBar:YES]; [CATransaction commit]; [_viewControllerForPresentation presentViewController:_fullscreenViewController.get() animated:YES completion:^{ [self completedEnterFullScreen]; [_rootViewController setShowsStatusBar:NO]; }]; } - (void)completedEnterFullScreen { if (![_webView _page]) return; _fullScreenState = InFullScreen; [self _manager]->didEnterFullScreen(); [self _manager]->setAnimatingFullScreen(false); [_webView _page]->setSuppressVisibilityUpdates(false); } - (void)exitFullScreen { if (![self isFullScreen]) return; _fullScreenState = WaitingToExitFullScreen; [self _manager]->setAnimatingFullScreen(true); [self _manager]->willExitFullScreen(); } - (void)requestExitFullScreen { [self _manager]->requestExitFullScreen(); } - (void)beganExitFullScreenWithInitialFrame:(CGRect)initialFrame finalFrame:(CGRect)finalFrame { if (_fullScreenState != WaitingToExitFullScreen) return; _fullScreenState = ExitingFullScreen; _initialFrame = initialFrame; _finalFrame = finalFrame; [_webView _page]->setSuppressVisibilityUpdates(true); [_rootViewController setShowsStatusBar:YES]; [_fullscreenViewController dismissViewControllerAnimated:YES completion:^{ if (![_webView _page]) return; [self completedExitFullScreen]; [_rootViewController setShowsStatusBar:NO]; }]; } - (void)completedExitFullScreen { if (_fullScreenState != ExitingFullScreen) return; _fullScreenState = NotInFullScreen; [CATransaction begin]; [CATransaction setDisableActions:YES]; [[_webViewPlaceholder superview] insertSubview:_webView belowSubview:_webViewPlaceholder.get()]; [_webView setFrame:[_webViewPlaceholder frame]]; [_webView setAutoresizingMask:[_webViewPlaceholder autoresizingMask]]; [[_webView window] makeKeyAndVisible]; _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(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 { [self exitFullscreenImmediately]; _webView = nil; } - (void)webViewDidRemoveFromSuperviewWhileInFullscreen { if (_fullScreenState == InFullScreen && _webView.window != _window.get()) [self exitFullscreenImmediately]; } #pragma mark - #pragma mark Internal Interface - (WebFullScreenManagerProxy*)_manager { if (![_webView _page]) return nullptr; return [_webView _page]->fullScreenManager(); } #pragma mark - #pragma mark UIViewControllerTransitioningDelegate - (id)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source { RetainPtr animationController = adoptNS([[WKFullscreenAnimationController alloc] init]); [animationController setViewController:presented]; [animationController setInitialFrame:_initialFrame]; [animationController setFinalFrame:_finalFrame]; [animationController setAnimatingIn:YES]; return animationController.autorelease(); } - (id)animationControllerForDismissedController:(UIViewController *)dismissed { RetainPtr animationController = adoptNS([[WKFullscreenAnimationController alloc] init]); [animationController setViewController:dismissed]; [animationController setInitialFrame:_initialFrame]; [animationController setFinalFrame:_finalFrame]; [animationController setAnimatingIn:NO]; return animationController.autorelease(); } @end #endif // PLATFORM(IOS) && ENABLE(FULLSCREEN_API)