2 * Copyright (C) 2014 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
27 #import "WKWebViewInternal.h"
31 #import "APIFormClient.h"
32 #import "FindClient.h"
33 #import "NavigationState.h"
34 #import "RemoteLayerTreeTransaction.h"
35 #import "RemoteObjectRegistry.h"
36 #import "RemoteObjectRegistryMessages.h"
37 #import "UIDelegate.h"
38 #import "ViewGestureController.h"
39 #import "ViewSnapshotStore.h"
40 #import "WKBackForwardListInternal.h"
41 #import "WKBackForwardListItemInternal.h"
42 #import "WKBrowsingContextHandleInternal.h"
43 #import "WKErrorInternal.h"
44 #import "WKHistoryDelegatePrivate.h"
46 #import "WKNSURLExtras.h"
47 #import "WKNavigationDelegate.h"
48 #import "WKNavigationInternal.h"
49 #import "WKPreferencesInternal.h"
50 #import "WKProcessPoolInternal.h"
51 #import "WKUIDelegate.h"
52 #import "WKUIDelegatePrivate.h"
53 #import "WKUserContentControllerInternal.h"
54 #import "WKWebViewConfigurationInternal.h"
55 #import "WKWebViewContentProvider.h"
56 #import "WebBackForwardList.h"
57 #import "WebCertificateInfo.h"
58 #import "WebContext.h"
59 #import "WebFormSubmissionListenerProxy.h"
60 #import "WebKitSystemInterface.h"
61 #import "WebPageGroup.h"
62 #import "WebPageProxy.h"
63 #import "WebPreferencesKeys.h"
64 #import "WebProcessProxy.h"
65 #import "WebSerializedScriptValue.h"
66 #import "_WKFindDelegate.h"
67 #import "_WKFormDelegate.h"
68 #import "_WKRemoteObjectRegistryInternal.h"
69 #import "_WKVisitedLinkProviderInternal.h"
70 #import "_WKWebsiteDataStoreInternal.h"
71 #import <JavaScriptCore/JSContext.h>
72 #import <JavaScriptCore/JSValue.h>
73 #import <wtf/HashMap.h>
74 #import <wtf/NeverDestroyed.h>
75 #import <wtf/RetainPtr.h>
78 #import "_WKFrameHandleInternal.h"
79 #import "_WKWebViewPrintFormatter.h"
81 #import "ProcessThrottler.h"
83 #import "WKScrollView.h"
84 #import "WKWebViewContentProviderRegistry.h"
85 #import "WebPageMessages.h"
86 #import <CoreGraphics/CGFloat.h>
87 #import <CoreGraphics/CGPDFDocumentPrivate.h>
88 #import <UIKit/UIApplication.h>
89 #import <UIKit/UIPeripheralHost_Private.h>
90 #import <UIKit/UIWindow_Private.h>
91 #import <QuartzCore/CARenderServer.h>
92 #import <QuartzCore/QuartzCorePrivate.h>
94 @interface UIScrollView (UIScrollViewInternal)
95 - (void)_adjustForAutomaticKeyboardInfo:(NSDictionary*)info animated:(BOOL)animated lastAdjustment:(CGFloat*)lastAdjustment;
96 - (BOOL)_isScrollingToTop;
99 @interface UIPeripheralHost(UIKitInternal)
100 - (CGFloat)getVerticalOverlapForView:(UIView *)view usingKeyboardInfo:(NSDictionary *)info;
103 @interface UIView (UIViewInternal)
104 - (UIViewController *)_viewControllerForAncestor;
107 @interface UIWindow (UIWindowInternal)
108 - (BOOL)_isHostedInAnotherProcess;
111 @interface UIViewController (UIViewControllerInternal)
112 - (UIViewController *)_rootAncestorViewController;
113 - (UIViewController *)_viewControllerForSupportedInterfaceOrientations;
118 #import "WKViewInternal.h"
119 #import <WebCore/ColorMac.h>
123 static HashMap<WebKit::WebPageProxy*, WKWebView *>& pageToViewMap()
125 static NeverDestroyed<HashMap<WebKit::WebPageProxy*, WKWebView *>> map;
129 WKWebView* fromWebPageProxy(WebKit::WebPageProxy& page)
131 return pageToViewMap().get(&page);
134 @implementation WKWebView {
135 std::unique_ptr<WebKit::NavigationState> _navigationState;
136 std::unique_ptr<WebKit::UIDelegate> _uiDelegate;
138 RetainPtr<_WKRemoteObjectRegistry> _remoteObjectRegistry;
139 _WKRenderingProgressEvents _observedRenderingProgressEvents;
141 WebKit::WeakObjCPtr<id <_WKFormDelegate>> _formDelegate;
143 RetainPtr<WKScrollView> _scrollView;
144 RetainPtr<WKContentView> _contentView;
146 BOOL _overridesMinimumLayoutSize;
147 CGSize _minimumLayoutSizeOverride;
148 BOOL _overridesMinimumLayoutSizeForMinimalUI;
149 CGSize _minimumLayoutSizeOverrideForMinimalUI;
150 BOOL _overridesMaximumUnobscuredSize;
151 CGSize _maximumUnobscuredSizeOverride;
153 BOOL _needsToNotifyDelegateAboutMinimalUI;
154 CGRect _inputViewBounds;
155 CGFloat _viewportMetaTagWidth;
157 UIEdgeInsets _obscuredInsets;
158 bool _isChangingObscuredInsetsInteractively;
160 UIInterfaceOrientation _interfaceOrientationOverride;
161 BOOL _overridesInterfaceOrientation;
163 BOOL _needsResetViewStateAfterCommitLoadForMainFrame;
164 BOOL _isAnimatingResize;
165 CATransform3D _resizeAnimationTransformAdjustments;
166 RetainPtr<UIView> _resizeAnimationView;
167 CGFloat _lastAdjustmentForScroller;
169 std::unique_ptr<WebKit::ViewGestureController> _gestureController;
170 BOOL _allowsBackForwardNavigationGestures;
172 RetainPtr<UIView <WKWebViewContentProvider>> _customContentView;
173 RetainPtr<UIView> _customContentFixedOverlayView;
175 WebCore::Color _scrollViewBackgroundColor;
177 BOOL _delayUpdateVisibleContentRects;
178 BOOL _hadDelayedUpdateVisibleContentRects;
180 BOOL _pageIsPrintingToPDF;
181 RetainPtr<CGPDFDocumentRef> _printedDocument;
184 RetainPtr<WKView> _wkView;
188 - (instancetype)initWithFrame:(CGRect)frame
190 return [self initWithFrame:frame configuration:adoptNS([[WKWebViewConfiguration alloc] init]).get()];
194 static int32_t deviceOrientationForUIInterfaceOrientation(UIInterfaceOrientation orientation)
196 switch (orientation) {
197 case UIInterfaceOrientationUnknown:
198 case UIInterfaceOrientationPortrait:
200 case UIInterfaceOrientationPortraitUpsideDown:
202 case UIInterfaceOrientationLandscapeLeft:
204 case UIInterfaceOrientationLandscapeRight:
209 static int32_t deviceOrientation()
211 return deviceOrientationForUIInterfaceOrientation([[UIApplication sharedApplication] statusBarOrientation]);
215 - (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration
217 if (!(self = [super initWithFrame:frame]))
220 _configuration = adoptNS([configuration copy]);
222 if (WKWebView *relatedWebView = [_configuration _relatedWebView]) {
223 WKProcessPool *processPool = [_configuration processPool];
224 WKProcessPool *relatedWebViewProcessPool = [relatedWebView->_configuration processPool];
225 if (processPool && processPool != relatedWebViewProcessPool)
226 [NSException raise:NSInvalidArgumentException format:@"Related web view %@ has process pool %@ but configuration specifies a different process pool %@", relatedWebView, relatedWebViewProcessPool, configuration.processPool];
228 [_configuration setProcessPool:relatedWebViewProcessPool];
231 [_configuration _validate];
233 CGRect bounds = self.bounds;
235 WebKit::WebContext& context = *[_configuration processPool]->_context;
237 WebKit::WebPageConfiguration webPageConfiguration;
238 webPageConfiguration.preferences = [_configuration preferences]->_preferences.get();
239 if (WKWebView *relatedWebView = [_configuration _relatedWebView])
240 webPageConfiguration.relatedPage = relatedWebView->_page.get();
242 webPageConfiguration.userContentController = [_configuration userContentController]->_userContentControllerProxy.get();
243 webPageConfiguration.visitedLinkProvider = [_configuration _visitedLinkProvider]->_visitedLinkProvider.get();
244 webPageConfiguration.session = [_configuration _websiteDataStore]->_session.get();
246 RefPtr<WebKit::WebPageGroup> pageGroup;
247 NSString *groupIdentifier = configuration._groupIdentifier;
248 if (groupIdentifier.length) {
249 pageGroup = WebKit::WebPageGroup::create(configuration._groupIdentifier);
250 webPageConfiguration.pageGroup = pageGroup.get();
253 webPageConfiguration.preferenceValues.set(WebKit::WebPreferencesKey::suppressesIncrementalRenderingKey(), WebKit::WebPreferencesStore::Value(!![_configuration suppressesIncrementalRendering]));
256 webPageConfiguration.preferenceValues.set(WebKit::WebPreferencesKey::mediaPlaybackAllowsAirPlayKey(), WebKit::WebPreferencesStore::Value(!![_configuration allowsInlineMediaPlayback]));
257 webPageConfiguration.preferenceValues.set(WebKit::WebPreferencesKey::mediaPlaybackRequiresUserGestureKey(), WebKit::WebPreferencesStore::Value(!![_configuration mediaPlaybackRequiresUserAction]));
258 webPageConfiguration.preferenceValues.set(WebKit::WebPreferencesKey::mediaPlaybackAllowsAirPlayKey(), WebKit::WebPreferencesStore::Value(!![_configuration mediaPlaybackAllowsAirPlay]));
262 _scrollView = adoptNS([[WKScrollView alloc] initWithFrame:bounds]);
263 [_scrollView setInternalDelegate:self];
264 [_scrollView setBouncesZoom:YES];
266 [self addSubview:_scrollView.get()];
267 [_scrollView setBackgroundColor:[UIColor whiteColor]];
269 _contentView = adoptNS([[WKContentView alloc] initWithFrame:bounds context:context configuration:std::move(webPageConfiguration) webView:self]);
270 _page = [_contentView page];
272 _page->setDeviceOrientation(deviceOrientation());
274 [_contentView layer].anchorPoint = CGPointZero;
275 [_contentView setFrame:bounds];
276 [_scrollView addSubview:_contentView.get()];
277 _viewportMetaTagWidth = -1;
279 [self _frameOrBoundsChanged];
281 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
282 [center addObserver:self selector:@selector(_keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];
283 [center addObserver:self selector:@selector(_keyboardDidChangeFrame:) name:UIKeyboardDidChangeFrameNotification object:nil];
284 [center addObserver:self selector:@selector(_keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
285 [center addObserver:self selector:@selector(_keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
286 [center addObserver:self selector:@selector(_windowDidRotate:) name:UIWindowDidRotateNotification object:nil];
287 [center addObserver:self selector:@selector(_contentSizeCategoryDidChange:) name:UIContentSizeCategoryDidChangeNotification object:nil];
288 _page->contentSizeCategoryDidChange([self _contentSizeCategory]);
290 [[_configuration _contentProviderRegistry] addPage:*_page];
294 _wkView = [[WKView alloc] initWithFrame:bounds context:context configuration:std::move(webPageConfiguration) webView:self];
295 [self addSubview:_wkView.get()];
296 _page = WebKit::toImpl([_wkView pageRef]);
299 _page->setBackgroundExtendsBeyondPage(true);
301 _navigationState = std::make_unique<WebKit::NavigationState>(self);
302 _page->setPolicyClient(_navigationState->createPolicyClient());
303 _page->setLoaderClient(_navigationState->createLoaderClient());
305 _uiDelegate = std::make_unique<WebKit::UIDelegate>(self);
306 _page->setUIClient(_uiDelegate->createUIClient());
308 _page->setFindClient(std::make_unique<WebKit::FindClient>(self));
310 pageToViewMap().add(_page.get(), self);
315 - (instancetype)initWithCoder:(NSCoder *)coder
325 [_remoteObjectRegistry _invalidate];
327 [[_configuration _contentProviderRegistry] removePage:*_page];
328 [[NSNotificationCenter defaultCenter] removeObserver:self];
331 pageToViewMap().remove(_page.get());
336 - (WKWebViewConfiguration *)configuration
338 return [[_configuration copy] autorelease];
341 - (WKBackForwardList *)backForwardList
343 return wrapper(_page->backForwardList());
346 - (id <WKNavigationDelegate>)navigationDelegate
348 return [_navigationState->navigationDelegate().leakRef() autorelease];
351 - (void)setNavigationDelegate:(id <WKNavigationDelegate>)navigationDelegate
353 _navigationState->setNavigationDelegate(navigationDelegate);
356 - (id <WKUIDelegate>)UIDelegate
358 return [_uiDelegate->delegate().leakRef() autorelease];
361 - (void)setUIDelegate:(id<WKUIDelegate>)UIDelegate
363 _uiDelegate->setDelegate(UIDelegate);
366 - (WKNavigation *)loadRequest:(NSURLRequest *)request
368 uint64_t navigationID = _page->loadRequest(request);
369 auto navigation = _navigationState->createLoadRequestNavigation(navigationID, request);
371 return [navigation.leakRef() autorelease];
374 - (WKNavigation *)loadHTMLString:(NSString *)string baseURL:(NSURL *)baseURL
376 uint64_t navigationID = _page->loadHTMLString(string, baseURL.absoluteString);
377 auto navigation = _navigationState->createLoadDataNavigation(navigationID);
379 return [navigation.leakRef() autorelease];
382 - (WKNavigation *)goToBackForwardListItem:(WKBackForwardListItem *)item
384 uint64_t navigationID = _page->goToBackForwardItem(&item._item);
386 auto navigation = _navigationState->createBackForwardNavigation(navigationID, item._item);
388 return [navigation.leakRef() autorelease];
393 return _page->pageLoadState().title();
398 return [NSURL _web_URLWithWTFString:_page->pageLoadState().activeURL()];
403 return _page->pageLoadState().isLoading();
406 - (double)estimatedProgress
408 return _page->pageLoadState().estimatedProgress();
411 - (BOOL)hasOnlySecureContent
413 return _page->pageLoadState().hasOnlySecureContent();
418 return _page->pageLoadState().canGoBack();
423 return _page->pageLoadState().canGoForward();
426 - (WKNavigation *)goBack
428 uint64_t navigationID = _page->goBack();
432 ASSERT(_page->backForwardList().currentItem());
433 auto navigation = _navigationState->createBackForwardNavigation(navigationID, *_page->backForwardList().currentItem());
435 return [navigation.leakRef() autorelease];
438 - (WKNavigation *)goForward
440 uint64_t navigationID = _page->goForward();
444 ASSERT(_page->backForwardList().currentItem());
445 auto navigation = _navigationState->createBackForwardNavigation(navigationID, *_page->backForwardList().currentItem());
447 return [navigation.leakRef() autorelease];
450 - (WKNavigation *)reload
452 uint64_t navigationID = _page->reload(false);
453 ASSERT(navigationID);
455 auto navigation = _navigationState->createReloadNavigation(navigationID);
456 return [navigation.leakRef() autorelease];
459 - (WKNavigation *)reloadFromOrigin
461 uint64_t navigationID = _page->reload(true);
462 ASSERT(navigationID);
464 auto navigation = _navigationState->createReloadNavigation(navigationID);
465 return [navigation.leakRef() autorelease];
470 _page->stopLoading();
473 static WKErrorCode callbackErrorCode(WebKit::CallbackBase::Error error)
476 case WebKit::CallbackBase::Error::None:
477 ASSERT_NOT_REACHED();
478 return WKErrorUnknown;
480 case WebKit::CallbackBase::Error::Unknown:
481 return WKErrorUnknown;
483 case WebKit::CallbackBase::Error::ProcessExited:
484 return WKErrorWebContentProcessTerminated;
486 case WebKit::CallbackBase::Error::OwnerWasInvalidated:
487 return WKErrorWebViewInvalidated;
491 - (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^)(id, NSError *))completionHandler
493 auto handler = adoptNS([completionHandler copy]);
495 _page->runJavaScriptInMainFrame(javaScriptString, WebKit::ScriptValueCallback::create([handler](WebKit::WebSerializedScriptValue* serializedScriptValue, WebKit::ScriptValueCallback::Error errorCode) {
499 auto completionHandler = (void (^)(id, NSError *))handler.get();
501 if (errorCode != WebKit::ScriptValueCallback::Error::None) {
502 auto error = createNSError(callbackErrorCode(errorCode));
503 if (errorCode == WebKit::ScriptValueCallback::Error::OwnerWasInvalidated) {
504 // The OwnerWasInvalidated callback is synchronous. We don't want to call the block from within it
505 // because that can trigger re-entrancy bugs in WebKit.
506 // FIXME: It would be even better if GenericCallback did this for us.
507 dispatch_async(dispatch_get_main_queue(), [completionHandler, error] {
508 completionHandler(nil, error.get());
513 completionHandler(nil, error.get());
517 if (!serializedScriptValue) {
518 completionHandler(nil, createNSError(WKErrorJavaScriptExceptionOccurred).get());
522 auto context = adoptNS([[JSContext alloc] init]);
523 JSValueRef valueRef = serializedScriptValue->deserialize([context JSGlobalContextRef], 0);
524 JSValue *value = [JSValue valueWithJSValueRef:valueRef inContext:context.get()];
526 completionHandler([value toObject], nil);
530 #pragma mark iOS-specific methods
533 - (void)setFrame:(CGRect)frame
535 CGRect oldFrame = self.frame;
536 [super setFrame:frame];
538 if (!CGSizeEqualToSize(oldFrame.size, frame.size))
539 [self _frameOrBoundsChanged];
542 - (void)setBounds:(CGRect)bounds
544 CGRect oldBounds = self.bounds;
545 [super setBounds:bounds];
546 [_customContentFixedOverlayView setFrame:self.bounds];
548 if (!CGSizeEqualToSize(oldBounds.size, bounds.size))
549 [self _frameOrBoundsChanged];
552 - (UIScrollView *)scrollView
554 return _scrollView.get();
557 - (WKBrowsingContextController *)browsingContextController
559 return [_contentView browsingContextController];
562 static inline CGFloat floorToDevicePixel(CGFloat input, float deviceScaleFactor)
564 return CGFloor(input * deviceScaleFactor) / deviceScaleFactor;
567 static CGSize roundScrollViewContentSize(const WebKit::WebPageProxy& page, CGSize contentSize)
569 float deviceScaleFactor = page.deviceScaleFactor();
570 return CGSizeMake(floorToDevicePixel(contentSize.width, deviceScaleFactor), floorToDevicePixel(contentSize.height, deviceScaleFactor));
573 - (void)_setHasCustomContentView:(BOOL)pageHasCustomContentView loadedMIMEType:(const WTF::String&)mimeType
575 if (pageHasCustomContentView) {
576 [_customContentView removeFromSuperview];
577 [_customContentFixedOverlayView removeFromSuperview];
579 Class representationClass = [[_configuration _contentProviderRegistry] providerForMIMEType:mimeType];
580 ASSERT(representationClass);
581 _customContentView = adoptNS([[representationClass alloc] init]);
582 _customContentFixedOverlayView = adoptNS([[UIView alloc] initWithFrame:self.bounds]);
583 [_customContentFixedOverlayView setUserInteractionEnabled:NO];
585 [_contentView removeFromSuperview];
586 [_scrollView addSubview:_customContentView.get()];
587 [self addSubview:_customContentFixedOverlayView.get()];
589 [_customContentView web_setMinimumSize:self.bounds.size];
590 [_customContentView web_setScrollView:_scrollView.get()];
591 [_customContentView web_setObscuredInsets:_obscuredInsets];
592 [_customContentView web_setFixedOverlayView:_customContentFixedOverlayView.get()];
593 } else if (_customContentView) {
594 [_customContentView removeFromSuperview];
595 _customContentView = nullptr;
597 [_customContentFixedOverlayView removeFromSuperview];
598 _customContentFixedOverlayView = nullptr;
600 [_scrollView addSubview:_contentView.get()];
601 [_scrollView setContentSize:roundScrollViewContentSize(*_page, [_contentView frame].size)];
603 [_customContentFixedOverlayView setFrame:self.bounds];
604 [self addSubview:_customContentFixedOverlayView.get()];
608 - (void)_didFinishLoadingDataForCustomContentProviderWithSuggestedFilename:(const String&)suggestedFilename data:(NSData *)data
610 ASSERT(_customContentView);
611 [_customContentView web_setContentProviderData:data suggestedFilename:suggestedFilename];
614 - (void)_setViewportMetaTagWidth:(float)newWidth
616 _viewportMetaTagWidth = newWidth;
619 - (void)_willInvokeUIScrollViewDelegateCallback
621 _delayUpdateVisibleContentRects = YES;
624 - (void)_didInvokeUIScrollViewDelegateCallback
626 _delayUpdateVisibleContentRects = NO;
627 if (_hadDelayedUpdateVisibleContentRects) {
628 _hadDelayedUpdateVisibleContentRects = NO;
629 [self _updateVisibleContentRects];
633 static CGFloat contentZoomScale(WKWebView* webView)
636 if (webView->_customContentView)
637 zoomView = webView->_customContentView.get();
639 zoomView = webView->_contentView.get();
641 CGFloat scale = [[zoomView layer] affineTransform].a;
642 ASSERT(scale == [webView->_scrollView zoomScale]);
646 - (void)_updateScrollViewBackground
648 WebCore::Color color;
649 if (_customContentView)
650 color = [_customContentView backgroundColor].CGColor;
652 color = _page->pageExtendedBackgroundColor();
653 CGFloat zoomScale = contentZoomScale(self);
654 CGFloat minimumZoomScale = [_scrollView minimumZoomScale];
655 if (zoomScale < minimumZoomScale) {
657 CGFloat opacity = std::max<CGFloat>(1 - slope * (minimumZoomScale - zoomScale), 0);
658 color = WebCore::colorWithOverrideAlpha(color.rgb(), opacity);
661 if (_scrollViewBackgroundColor == color)
664 _scrollViewBackgroundColor = color;
666 RetainPtr<UIColor*> uiBackgroundColor = adoptNS([[UIColor alloc] initWithCGColor:cachedCGColor(color, WebCore::ColorSpaceDeviceRGB)]);
667 [_scrollView setBackgroundColor:uiBackgroundColor.get()];
670 - (void)_setUsesMinimalUI:(BOOL)usesMinimalUI
672 _usesMinimalUI = usesMinimalUI;
673 _needsToNotifyDelegateAboutMinimalUI = YES;
676 - (BOOL)_usesMinimalUI
678 return _usesMinimalUI;
681 - (void)_processDidExit
683 if (!_customContentView && _isAnimatingResize) {
684 NSUInteger indexOfResizeAnimationView = [[_scrollView subviews] indexOfObject:_resizeAnimationView.get()];
685 [_scrollView insertSubview:_contentView.get() atIndex:indexOfResizeAnimationView];
686 [_resizeAnimationView removeFromSuperview];
687 _resizeAnimationView = nil;
689 _isAnimatingResize = NO;
690 _resizeAnimationTransformAdjustments = CATransform3DIdentity;
692 [_contentView setFrame:self.bounds];
693 [_scrollView setBackgroundColor:[UIColor whiteColor]];
694 [_scrollView setContentOffset:CGPointMake(-_obscuredInsets.left, -_obscuredInsets.top)];
695 [_scrollView setZoomScale:1];
697 _viewportMetaTagWidth = -1;
698 _needsResetViewStateAfterCommitLoadForMainFrame = NO;
699 _scrollViewBackgroundColor = WebCore::Color();
700 _delayUpdateVisibleContentRects = NO;
701 _hadDelayedUpdateVisibleContentRects = NO;
704 - (void)_didCommitLoadForMainFrame
706 _needsResetViewStateAfterCommitLoadForMainFrame = YES;
710 - (void)_didCommitLayerTree:(const WebKit::RemoteLayerTreeTransaction&)layerTreeTransaction
712 if (_customContentView)
715 if (_isAnimatingResize) {
716 [_resizeAnimationView layer].sublayerTransform = _resizeAnimationTransformAdjustments;
720 [_scrollView setContentSize:roundScrollViewContentSize(*_page, [_contentView frame].size)];
721 [_scrollView setMinimumZoomScale:layerTreeTransaction.minimumScaleFactor()];
722 [_scrollView setMaximumZoomScale:layerTreeTransaction.maximumScaleFactor()];
723 [_scrollView setZoomEnabled:layerTreeTransaction.allowsUserScaling()];
724 if (!layerTreeTransaction.scaleWasSetByUIProcess() && ![_scrollView isZooming] && ![_scrollView isZoomBouncing] && ![_scrollView _isAnimatingZoom])
725 [_scrollView setZoomScale:layerTreeTransaction.pageScaleFactor()];
727 [self _updateScrollViewBackground];
729 if (_gestureController)
730 _gestureController->setRenderTreeSize(layerTreeTransaction.renderTreeSize());
732 if (_needsToNotifyDelegateAboutMinimalUI || _needsResetViewStateAfterCommitLoadForMainFrame) {
733 _needsToNotifyDelegateAboutMinimalUI = NO;
735 auto delegate = _uiDelegate->delegate();
736 if ([delegate respondsToSelector:@selector(_webView:usesMinimalUI:)])
737 [static_cast<id <WKUIDelegatePrivate>>(delegate.get()) _webView:self usesMinimalUI:_usesMinimalUI];
740 if (_needsResetViewStateAfterCommitLoadForMainFrame) {
741 _needsResetViewStateAfterCommitLoadForMainFrame = NO;
742 [_scrollView setContentOffset:CGPointMake(-_obscuredInsets.left, -_obscuredInsets.top)];
746 - (void)_dynamicViewportUpdateChangedTargetToScale:(double)newScale position:(CGPoint)newScrollPosition
748 if (_isAnimatingResize) {
749 CGFloat animatingScaleTarget = [[_resizeAnimationView layer] transform].m11;
750 double currentTargetScale = animatingScaleTarget * [[_contentView layer] transform].m11;
751 double scale = newScale / currentTargetScale;
752 _resizeAnimationTransformAdjustments = CATransform3DMakeScale(scale, scale, 0);
754 CGPoint newContentOffset = CGPointMake(newScrollPosition.x * newScale, newScrollPosition.y * newScale);
755 newContentOffset.x -= _obscuredInsets.left;
756 newContentOffset.y -= _obscuredInsets.top;
757 CGPoint currentContentOffset = [_scrollView contentOffset];
759 _resizeAnimationTransformAdjustments.m41 = (currentContentOffset.x - newContentOffset.x) / animatingScaleTarget;
760 _resizeAnimationTransformAdjustments.m42 = (currentContentOffset.y - newContentOffset.y) / animatingScaleTarget;
764 - (WebKit::ViewSnapshot)_takeViewSnapshot
766 float deviceScale = WKGetScreenScaleFactor();
767 CGSize snapshotSize = self.bounds.size;
768 snapshotSize.width *= deviceScale;
769 snapshotSize.height *= deviceScale;
771 WebKit::ViewSnapshot snapshot;
772 snapshot.slotID = [WebKit::ViewSnapshotStore::snapshottingContext() createImageSlot:snapshotSize hasAlpha:YES];
774 CATransform3D transform = CATransform3DMakeScale(deviceScale, deviceScale, 1);
775 CARenderServerCaptureLayerWithTransform(MACH_PORT_NULL, self.layer.context.contextId, (uint64_t)self.layer, snapshot.slotID, 0, 0, &transform);
777 snapshot.size = WebCore::expandedIntSize(WebCore::FloatSize(snapshotSize));
778 snapshot.imageSizeInBytes = snapshotSize.width * snapshotSize.height * 4;
783 - (void)_zoomToPoint:(WebCore::FloatPoint)point atScale:(double)scale
785 double maximumZoomDuration = 0.4;
786 double minimumZoomDuration = 0.1;
787 double zoomDurationFactor = 0.3;
789 CGFloat zoomScale = contentZoomScale(self);
790 CFTimeInterval duration = std::min(fabs(log(zoomScale) - log(scale)) * zoomDurationFactor + minimumZoomDuration, maximumZoomDuration);
792 if (scale != zoomScale)
793 _page->willStartUserTriggeredZooming();
795 [_scrollView _zoomToCenter:point scale:scale duration:duration];
798 - (void)_zoomToRect:(WebCore::FloatRect)targetRect atScale:(double)scale origin:(WebCore::FloatPoint)origin
800 // FIMXE: Some of this could be shared with _scrollToRect.
801 const double visibleRectScaleChange = contentZoomScale(self) / scale;
802 const WebCore::FloatRect visibleRect([self convertRect:self.bounds toView:_contentView.get()]);
803 const WebCore::FloatRect unobscuredRect([self _contentRectForUserInteraction]);
805 const WebCore::FloatSize topLeftObscuredInsetAfterZoom((unobscuredRect.minXMinYCorner() - visibleRect.minXMinYCorner()) * visibleRectScaleChange);
806 const WebCore::FloatSize bottomRightObscuredInsetAfterZoom((visibleRect.maxXMaxYCorner() - unobscuredRect.maxXMaxYCorner()) * visibleRectScaleChange);
808 const WebCore::FloatSize unobscuredRectSizeAfterZoom(unobscuredRect.size() * visibleRectScaleChange);
810 // Center to the target rect.
811 WebCore::FloatPoint unobscuredRectLocationAfterZoom = targetRect.location() - (unobscuredRectSizeAfterZoom - targetRect.size()) * 0.5;
813 // Center to the tap point instead in case the target rect won't fit in a direction.
814 if (targetRect.width() > unobscuredRectSizeAfterZoom.width())
815 unobscuredRectLocationAfterZoom.setX(origin.x() - unobscuredRectSizeAfterZoom.width() / 2);
816 if (targetRect.height() > unobscuredRectSizeAfterZoom.height())
817 unobscuredRectLocationAfterZoom.setY(origin.y() - unobscuredRectSizeAfterZoom.height() / 2);
819 // We have computed where we want the unobscured rect to be. Now adjust for the obscuring insets.
820 WebCore::FloatRect visibleRectAfterZoom(unobscuredRectLocationAfterZoom, unobscuredRectSizeAfterZoom);
821 visibleRectAfterZoom.move(-topLeftObscuredInsetAfterZoom);
822 visibleRectAfterZoom.expand(topLeftObscuredInsetAfterZoom + bottomRightObscuredInsetAfterZoom);
824 [self _zoomToPoint:visibleRectAfterZoom.center() atScale:scale];
827 static WebCore::FloatPoint constrainContentOffset(WebCore::FloatPoint contentOffset, WebCore::FloatSize contentSize, WebCore::FloatSize unobscuredContentSize)
829 WebCore::FloatSize maximumContentOffset = contentSize - unobscuredContentSize;
830 contentOffset = contentOffset.shrunkTo(WebCore::FloatPoint(maximumContentOffset.width(), maximumContentOffset.height()));
831 contentOffset = contentOffset.expandedTo(WebCore::FloatPoint());
832 return contentOffset;
835 - (void)_scrollToContentOffset:(WebCore::FloatPoint)contentOffset
837 if (_isAnimatingResize)
840 [_scrollView _stopScrollingAndZoomingAnimations];
842 WebCore::FloatPoint scaledOffset = contentOffset;
843 CGFloat zoomScale = contentZoomScale(self);
844 scaledOffset.scale(zoomScale, zoomScale);
846 scaledOffset -= WebCore::FloatSize(_obscuredInsets.left, _obscuredInsets.top);
847 [_scrollView setContentOffset:scaledOffset];
850 - (BOOL)_scrollToRect:(WebCore::FloatRect)targetRect origin:(WebCore::FloatPoint)origin minimumScrollDistance:(float)minimumScrollDistance
852 WebCore::FloatRect unobscuredContentRect([self _contentRectForUserInteraction]);
853 WebCore::FloatPoint unobscuredContentOffset = unobscuredContentRect.location();
854 WebCore::FloatSize contentSize([_contentView bounds].size);
856 // Center the target rect in the scroll view.
857 // If the target doesn't fit in the scroll view, center on the gesture location instead.
858 WebCore::FloatPoint newUnobscuredContentOffset;
859 if (targetRect.width() <= unobscuredContentRect.width())
860 newUnobscuredContentOffset.setX(targetRect.x() - (unobscuredContentRect.width() - targetRect.width()) / 2);
862 newUnobscuredContentOffset.setX(origin.x() - unobscuredContentRect.width() / 2);
863 if (targetRect.height() <= unobscuredContentRect.height())
864 newUnobscuredContentOffset.setY(targetRect.y() - (unobscuredContentRect.height() - targetRect.height()) / 2);
866 newUnobscuredContentOffset.setY(origin.y() - unobscuredContentRect.height() / 2);
867 newUnobscuredContentOffset = constrainContentOffset(newUnobscuredContentOffset, contentSize, unobscuredContentRect.size());
869 if (unobscuredContentOffset == newUnobscuredContentOffset) {
870 if (targetRect.width() > unobscuredContentRect.width())
871 newUnobscuredContentOffset.setX(origin.x() - unobscuredContentRect.width() / 2);
872 if (targetRect.height() > unobscuredContentRect.height())
873 newUnobscuredContentOffset.setY(origin.y() - unobscuredContentRect.height() / 2);
874 newUnobscuredContentOffset = constrainContentOffset(newUnobscuredContentOffset, contentSize, unobscuredContentRect.size());
877 WebCore::FloatSize scrollViewOffsetDelta = newUnobscuredContentOffset - unobscuredContentOffset;
878 scrollViewOffsetDelta.scale(contentZoomScale(self));
880 float scrollDistance = scrollViewOffsetDelta.diagonalLength();
881 if (scrollDistance < minimumScrollDistance)
884 [_scrollView setContentOffset:([_scrollView contentOffset] + scrollViewOffsetDelta) animated:YES];
888 - (void)_zoomOutWithOrigin:(WebCore::FloatPoint)origin
890 [self _zoomToPoint:origin atScale:[_scrollView minimumZoomScale]];
893 // focusedElementRect and selectionRect are both in document coordinates.
894 - (void)_zoomToFocusRect:(WebCore::FloatRect)focusedElementRectInDocumentCoordinates selectionRect:(WebCore::FloatRect)selectionRectInDocumentCoordinates fontSize:(float)fontSize minimumScale:(double)minimumScale maximumScale:(double)maximumScale allowScaling:(BOOL)allowScaling forceScroll:(BOOL)forceScroll
896 const double WKWebViewStandardFontSize = 16;
897 const double kMinimumHeightToShowContentAboveKeyboard = 106;
898 const CFTimeInterval UIWebFormAnimationDuration = 0.25;
899 const double CaretOffsetFromWindowEdge = 20;
901 // Zoom around the element's bounding frame. We use a "standard" size to determine the proper frame.
902 double scale = allowScaling ? std::min(std::max(WKWebViewStandardFontSize / fontSize, minimumScale), maximumScale) : contentZoomScale(self);
903 CGFloat documentWidth = [_contentView bounds].size.width;
904 scale = CGRound(documentWidth * scale) / documentWidth;
906 UIWindow *window = [_scrollView window];
908 WebCore::FloatRect focusedElementRectInNewScale = focusedElementRectInDocumentCoordinates;
909 focusedElementRectInNewScale.scale(scale);
910 focusedElementRectInNewScale.moveBy([_contentView frame].origin);
912 // Find the portion of the view that is visible on the screen.
913 UIViewController *topViewController = [[[_scrollView _viewControllerForAncestor] _rootAncestorViewController] _viewControllerForSupportedInterfaceOrientations];
914 UIView *fullScreenView = topViewController.view;
916 fullScreenView = window;
918 CGRect unobscuredScrollViewRectInWebViewCoordinates = UIEdgeInsetsInsetRect([self bounds], _obscuredInsets);
919 CGRect visibleScrollViewBoundsInWebViewCoordinates = CGRectIntersection(unobscuredScrollViewRectInWebViewCoordinates, [fullScreenView convertRect:[fullScreenView bounds] toView:self]);
920 CGRect formAssistantFrameInWebViewCoordinates = [window convertRect:_inputViewBounds toView:self];
921 CGRect intersectionBetweenScrollViewAndFormAssistant = CGRectIntersection(visibleScrollViewBoundsInWebViewCoordinates, formAssistantFrameInWebViewCoordinates);
922 CGSize visibleSize = visibleScrollViewBoundsInWebViewCoordinates.size;
924 CGFloat visibleOffsetFromTop = 0;
925 if (!CGRectIsEmpty(intersectionBetweenScrollViewAndFormAssistant)) {
926 CGFloat heightVisibleAboveFormAssistant = CGRectGetMinY(intersectionBetweenScrollViewAndFormAssistant) - CGRectGetMinY(visibleScrollViewBoundsInWebViewCoordinates);
927 CGFloat heightVisibleBelowFormAssistant = CGRectGetMaxY(visibleScrollViewBoundsInWebViewCoordinates) - CGRectGetMaxY(intersectionBetweenScrollViewAndFormAssistant);
929 if (heightVisibleAboveFormAssistant >= kMinimumHeightToShowContentAboveKeyboard || heightVisibleBelowFormAssistant < heightVisibleAboveFormAssistant)
930 visibleSize.height = heightVisibleAboveFormAssistant;
932 visibleSize.height = heightVisibleBelowFormAssistant;
933 visibleOffsetFromTop = CGRectGetMaxY(intersectionBetweenScrollViewAndFormAssistant) - CGRectGetMinY(visibleScrollViewBoundsInWebViewCoordinates);
937 BOOL selectionRectIsNotNull = !selectionRectInDocumentCoordinates.isZero();
939 CGRect currentlyVisibleRegionInWebViewCoordinates;
940 currentlyVisibleRegionInWebViewCoordinates.origin = unobscuredScrollViewRectInWebViewCoordinates.origin;
941 currentlyVisibleRegionInWebViewCoordinates.origin.y += visibleOffsetFromTop;
942 currentlyVisibleRegionInWebViewCoordinates.size = visibleSize;
944 // Don't bother scrolling if the entire node is already visible, whether or not we got a selectionRect.
945 if (CGRectContainsRect(currentlyVisibleRegionInWebViewCoordinates, [self convertRect:focusedElementRectInDocumentCoordinates fromView:_contentView.get()]))
948 // Don't bother scrolling if we have a valid selectionRect and it is already visible.
949 if (selectionRectIsNotNull && CGRectContainsRect(currentlyVisibleRegionInWebViewCoordinates, [self convertRect:selectionRectInDocumentCoordinates fromView:_contentView.get()]))
953 // We want to zoom to the left/top corner of the DOM node, with as much spacing on all sides as we
954 // can get based on the visible area after zooming (workingFrame). The spacing in either dimension is half the
955 // difference between the size of the DOM node and the size of the visible frame.
956 CGFloat horizontalSpaceInWebViewCoordinates = std::max((visibleSize.width - focusedElementRectInNewScale.width()) / 2.0, 0.0);
957 CGFloat verticalSpaceInWebViewCoordinates = std::max((visibleSize.height - focusedElementRectInNewScale.height()) / 2.0, 0.0);
960 topLeft.x = focusedElementRectInNewScale.x() - horizontalSpaceInWebViewCoordinates;
961 topLeft.y = focusedElementRectInNewScale.y() - verticalSpaceInWebViewCoordinates - visibleOffsetFromTop;
963 CGFloat minimumAllowableHorizontalOffsetInWebViewCoordinates = -INFINITY;
964 CGFloat minimumAllowableVerticalOffsetInWebViewCoordinates = -INFINITY;
965 if (selectionRectIsNotNull) {
966 WebCore::FloatRect selectionRectInNewScale = selectionRectInDocumentCoordinates;
967 selectionRectInNewScale.scale(scale);
968 selectionRectInNewScale.moveBy([_contentView frame].origin);
969 minimumAllowableHorizontalOffsetInWebViewCoordinates = CGRectGetMaxX(selectionRectInNewScale) + CaretOffsetFromWindowEdge - visibleSize.width;
970 minimumAllowableVerticalOffsetInWebViewCoordinates = CGRectGetMaxY(selectionRectInNewScale) + CaretOffsetFromWindowEdge - visibleSize.height - visibleOffsetFromTop;
973 WebCore::FloatRect documentBoundsInNewScale = [_contentView bounds];
974 documentBoundsInNewScale.scale(scale);
975 documentBoundsInNewScale.moveBy([_contentView frame].origin);
977 // Constrain the left edge in document coordinates so that:
978 // - it isn't so small that the scrollVisibleRect isn't visible on the screen
979 // - it isn't so great that the document's right edge is less than the right edge of the screen
980 if (selectionRectIsNotNull && topLeft.x < minimumAllowableHorizontalOffsetInWebViewCoordinates)
981 topLeft.x = minimumAllowableHorizontalOffsetInWebViewCoordinates;
983 CGFloat maximumAllowableHorizontalOffset = CGRectGetMaxX(documentBoundsInNewScale) - visibleSize.width;
984 if (topLeft.x > maximumAllowableHorizontalOffset)
985 topLeft.x = maximumAllowableHorizontalOffset;
988 // Constrain the top edge in document coordinates so that:
989 // - it isn't so small that the scrollVisibleRect isn't visible on the screen
990 // - it isn't so great that the document's bottom edge is higher than the top of the form assistant
991 if (selectionRectIsNotNull && topLeft.y < minimumAllowableVerticalOffsetInWebViewCoordinates)
992 topLeft.y = minimumAllowableVerticalOffsetInWebViewCoordinates;
994 CGFloat maximumAllowableVerticalOffset = CGRectGetMaxY(documentBoundsInNewScale) - visibleSize.height;
995 if (topLeft.y > maximumAllowableVerticalOffset)
996 topLeft.y = maximumAllowableVerticalOffset;
999 WebCore::FloatPoint newCenter = CGPointMake(topLeft.x + unobscuredScrollViewRectInWebViewCoordinates.size.width / 2.0, topLeft.y + unobscuredScrollViewRectInWebViewCoordinates.size.height / 2.0);
1001 if (scale != contentZoomScale(self))
1002 _page->willStartUserTriggeredZooming();
1004 // The newCenter has been computed in the new scale, but _zoomToCenter expected the center to be in the original scale.
1005 newCenter.scale(1 / scale, 1 / scale);
1006 [_scrollView _zoomToCenter:newCenter
1008 duration:UIWebFormAnimationDuration
1012 - (BOOL)_zoomToRect:(WebCore::FloatRect)targetRect withOrigin:(WebCore::FloatPoint)origin fitEntireRect:(BOOL)fitEntireRect minimumScale:(double)minimumScale maximumScale:(double)maximumScale minimumScrollDistance:(float)minimumScrollDistance
1014 const float maximumScaleFactorDeltaForPanScroll = 0.02;
1016 double currentScale = contentZoomScale(self);
1018 WebCore::FloatSize unobscuredContentSize([self _contentRectForUserInteraction].size);
1019 double horizontalScale = unobscuredContentSize.width() * currentScale / targetRect.width();
1020 double verticalScale = unobscuredContentSize.height() * currentScale / targetRect.height();
1022 horizontalScale = std::min(std::max(horizontalScale, minimumScale), maximumScale);
1023 verticalScale = std::min(std::max(verticalScale, minimumScale), maximumScale);
1025 double targetScale = fitEntireRect ? std::min(horizontalScale, verticalScale) : horizontalScale;
1026 if (fabs(targetScale - currentScale) < maximumScaleFactorDeltaForPanScroll) {
1027 if ([self _scrollToRect:targetRect origin:origin minimumScrollDistance:minimumScrollDistance])
1029 } else if (targetScale != currentScale) {
1030 [self _zoomToRect:targetRect atScale:targetScale origin:origin];
1037 - (void)didMoveToWindow
1039 _page->viewStateDidChange(WebCore::ViewState::IsInWindow);
1042 #pragma mark - UIScrollViewDelegate
1044 - (BOOL)usesStandardContentView
1046 return !_customContentView;
1049 - (CGSize)scrollView:(UIScrollView*)scrollView contentSizeForZoomScale:(CGFloat)scale withProposedSize:(CGSize)proposedSize
1051 return roundScrollViewContentSize(*_page, proposedSize);
1054 - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
1056 ASSERT(_scrollView == scrollView);
1058 if (_customContentView)
1059 return _customContentView.get();
1061 return _contentView.get();
1064 - (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view
1066 if (![self usesStandardContentView])
1069 if (scrollView.pinchGestureRecognizer.state == UIGestureRecognizerStateBegan) {
1070 _page->willStartUserTriggeredZooming();
1071 [_contentView scrollViewWillStartPanOrPinchGesture];
1073 [_contentView willStartZoomOrScroll];
1076 - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
1078 if (![self usesStandardContentView])
1081 if (scrollView.panGestureRecognizer.state == UIGestureRecognizerStateBegan)
1082 [_contentView scrollViewWillStartPanOrPinchGesture];
1083 [_contentView willStartZoomOrScroll];
1086 - (void)_didFinishScrolling
1088 if (![self usesStandardContentView])
1091 [self _updateVisibleContentRects];
1092 [_contentView didFinishScrolling];
1095 - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
1097 // Work around <rdar://problem/16374753> by avoiding deceleration while
1098 // zooming. We'll animate to the right place once the zoom finishes.
1099 if ([scrollView isZooming])
1100 *targetContentOffset = [scrollView contentOffset];
1103 - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
1105 // If we're decelerating, scroll offset will be updated when scrollViewDidFinishDecelerating: is called.
1107 [self _didFinishScrolling];
1110 - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
1112 [self _didFinishScrolling];
1115 - (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView
1117 [self _didFinishScrolling];
1120 - (void)scrollViewDidScroll:(UIScrollView *)scrollView
1122 if (![self usesStandardContentView])
1123 [_customContentView scrollViewDidScroll:(UIScrollView *)scrollView];
1125 [self _updateVisibleContentRects];
1128 - (void)scrollViewDidZoom:(UIScrollView *)scrollView
1130 [self _updateScrollViewBackground];
1131 [self _updateVisibleContentRects];
1134 - (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale
1136 ASSERT(scrollView == _scrollView);
1137 [self _updateVisibleContentRects];
1138 [_contentView didZoomToScale:scale];
1141 - (void)_frameOrBoundsChanged
1143 CGRect bounds = self.bounds;
1144 if (!_isAnimatingResize) {
1145 if (!_overridesMinimumLayoutSize)
1146 _page->setViewportConfigurationMinimumLayoutSize(WebCore::FloatSize(bounds.size));
1147 if (!_overridesMinimumLayoutSizeForMinimalUI)
1148 _page->setViewportConfigurationMinimumLayoutSizeForMinimalUI(WebCore::FloatSize(bounds.size));
1149 if (!_overridesMaximumUnobscuredSize)
1150 _page->setMaximumUnobscuredSize(WebCore::FloatSize(bounds.size));
1153 [_scrollView setFrame:bounds];
1154 [_contentView setMinimumSize:bounds.size];
1155 [_customContentView web_setMinimumSize:bounds.size];
1156 [self _updateVisibleContentRects];
1159 // Unobscured content rect where the user can interact. When the keyboard is up, this should be the area above or bellow the keyboard, wherever there is enough space.
1160 - (CGRect)_contentRectForUserInteraction
1162 // FIXME: handle split keyboard.
1163 UIEdgeInsets obscuredInsets = _obscuredInsets;
1164 obscuredInsets.bottom = std::max(_obscuredInsets.bottom, _inputViewBounds.size.height);
1165 CGRect unobscuredRect = UIEdgeInsetsInsetRect(self.bounds, obscuredInsets);
1166 return [self convertRect:unobscuredRect toView:_contentView.get()];
1169 - (void)_updateVisibleContentRects
1171 if (![self usesStandardContentView])
1174 if (_delayUpdateVisibleContentRects) {
1175 _hadDelayedUpdateVisibleContentRects = YES;
1179 if (_isAnimatingResize)
1182 CGRect fullViewRect = self.bounds;
1183 CGRect visibleRectInContentCoordinates = [self convertRect:fullViewRect toView:_contentView.get()];
1185 CGRect unobscuredRect = UIEdgeInsetsInsetRect(fullViewRect, _obscuredInsets);
1186 CGRect unobscuredRectInContentCoordinates = [self convertRect:unobscuredRect toView:_contentView.get()];
1188 CGFloat scaleFactor = contentZoomScale(self);
1190 BOOL isStableState = !(_isChangingObscuredInsetsInteractively || [_scrollView isDragging] || [_scrollView isDecelerating] || [_scrollView isZooming] || [_scrollView isZoomBouncing] || [_scrollView _isAnimatingZoom] || [_scrollView _isScrollingToTop]);
1191 [_contentView didUpdateVisibleRect:visibleRectInContentCoordinates
1192 unobscuredRect:unobscuredRectInContentCoordinates
1193 unobscuredRectInScrollViewCoordinates:unobscuredRect
1194 scale:scaleFactor minimumScale:[_scrollView minimumZoomScale]
1195 inStableState:isStableState isChangingObscuredInsetsInteractively:_isChangingObscuredInsetsInteractively];
1198 - (void)_keyboardChangedWithInfo:(NSDictionary *)keyboardInfo adjustScrollView:(BOOL)adjustScrollView
1200 NSValue *endFrameValue = [keyboardInfo objectForKey:UIKeyboardFrameEndUserInfoKey];
1204 // The keyboard rect is always in screen coordinates. In the view services case the window does not
1205 // have the interface orientation rotation transformation; its host does. So, it makes no sense to
1206 // clip the keyboard rect against its screen.
1207 if ([[self window] _isHostedInAnotherProcess])
1208 _inputViewBounds = [self.window convertRect:[endFrameValue CGRectValue] fromWindow:nil];
1210 _inputViewBounds = [self.window convertRect:CGRectIntersection([endFrameValue CGRectValue], self.window.screen.bounds) fromWindow:nil];
1212 [self _updateVisibleContentRects];
1214 if (adjustScrollView)
1215 [_scrollView _adjustForAutomaticKeyboardInfo:keyboardInfo animated:YES lastAdjustment:&_lastAdjustmentForScroller];
1218 - (void)_keyboardWillChangeFrame:(NSNotification *)notification
1220 if ([_contentView isAssistingNode])
1221 [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES];
1224 - (void)_keyboardDidChangeFrame:(NSNotification *)notification
1226 [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:NO];
1229 - (void)_keyboardWillShow:(NSNotification *)notification
1231 if ([_contentView isAssistingNode])
1232 [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES];
1235 - (void)_keyboardWillHide:(NSNotification *)notification
1237 // Ignore keyboard will hide notifications sent during rotation. They're just there for
1238 // backwards compatibility reasons and processing the will hide notification would
1239 // temporarily screw up the the unobscured view area.
1240 if ([[UIPeripheralHost sharedInstance] rotationState])
1243 [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES];
1246 - (void)_windowDidRotate:(NSNotification *)notification
1248 if (!_overridesInterfaceOrientation)
1249 _page->setDeviceOrientation(deviceOrientation());
1252 - (void)_contentSizeCategoryDidChange:(NSNotification *)notification
1254 _page->contentSizeCategoryDidChange([self _contentSizeCategory]);
1257 - (NSString *)_contentSizeCategory
1259 return [[UIApplication sharedApplication] preferredContentSizeCategory];
1262 - (void)setAllowsBackForwardNavigationGestures:(BOOL)allowsBackForwardNavigationGestures
1264 if (_allowsBackForwardNavigationGestures == allowsBackForwardNavigationGestures)
1267 _allowsBackForwardNavigationGestures = allowsBackForwardNavigationGestures;
1269 if (allowsBackForwardNavigationGestures) {
1270 if (!_gestureController) {
1271 _gestureController = std::make_unique<WebKit::ViewGestureController>(*_page);
1272 _gestureController->installSwipeHandler(self, [self scrollView]);
1275 _gestureController = nullptr;
1277 _page->setShouldRecordNavigationSnapshots(allowsBackForwardNavigationGestures);
1280 - (BOOL)allowsBackForwardNavigationGestures
1282 return _allowsBackForwardNavigationGestures;
1287 #pragma mark OS X-specific methods
1291 - (void)resizeSubviewsWithOldSize:(NSSize)oldSize
1293 [_wkView setFrame:self.bounds];
1296 - (void)setAllowsBackForwardNavigationGestures:(BOOL)allowsBackForwardNavigationGestures
1298 [_wkView setAllowsBackForwardNavigationGestures:allowsBackForwardNavigationGestures];
1301 - (BOOL)allowsBackForwardNavigationGestures
1303 return [_wkView allowsBackForwardNavigationGestures];
1306 - (void)setAllowsMagnification:(BOOL)allowsMagnification
1308 [_wkView setAllowsMagnification:allowsMagnification];
1311 - (BOOL)allowsMagnification
1313 return [_wkView allowsMagnification];
1316 - (void)setMagnification:(CGFloat)magnification
1318 [_wkView setMagnification:magnification];
1321 - (CGFloat)magnification
1323 return [_wkView magnification];
1326 - (void)setMagnification:(CGFloat)magnification centeredAtPoint:(CGPoint)point
1328 [_wkView setMagnification:magnification centeredAtPoint:NSPointFromCGPoint(point)];
1335 @implementation WKWebView (WKPrivate)
1337 - (_WKRemoteObjectRegistry *)_remoteObjectRegistry
1339 if (!_remoteObjectRegistry) {
1340 _remoteObjectRegistry = adoptNS([[_WKRemoteObjectRegistry alloc] _initWithMessageSender:*_page]);
1341 _page->process().context().addMessageReceiver(Messages::RemoteObjectRegistry::messageReceiverName(), _page->pageID(), [_remoteObjectRegistry remoteObjectRegistry]);
1344 return _remoteObjectRegistry.get();
1347 - (WKBrowsingContextHandle *)_handle
1349 return [[[WKBrowsingContextHandle alloc] _initWithPageID:_page->pageID()] autorelease];
1352 - (_WKRenderingProgressEvents)_observedRenderingProgressEvents
1354 return _observedRenderingProgressEvents;
1357 - (id <WKHistoryDelegatePrivate>)_historyDelegate
1359 return [_navigationState->historyDelegate().leakRef() autorelease];
1362 - (void)_setHistoryDelegate:(id <WKHistoryDelegatePrivate>)historyDelegate
1364 _navigationState->setHistoryDelegate(historyDelegate);
1367 - (NSURL *)_unreachableURL
1369 return [NSURL _web_URLWithWTFString:_page->pageLoadState().unreachableURL()];
1372 - (void)_loadAlternateHTMLString:(NSString *)string baseURL:(NSURL *)baseURL forUnreachableURL:(NSURL *)unreachableURL
1374 _page->loadAlternateHTMLString(string, [baseURL _web_originalDataAsWTFString], [unreachableURL _web_originalDataAsWTFString]);
1377 - (WKNavigation *)_reload
1379 return [self reload];
1382 - (NSArray *)_certificateChain
1384 if (WebKit::WebFrameProxy* mainFrame = _page->mainFrame())
1385 return mainFrame->certificateInfo() ? (NSArray *)mainFrame->certificateInfo()->certificateInfo().certificateChain() : nil;
1390 - (NSURL *)_committedURL
1392 return [NSURL _web_URLWithWTFString:_page->pageLoadState().url()];
1395 - (NSString *)_applicationNameForUserAgent
1397 return _page->applicationNameForUserAgent();
1400 - (void)_setApplicationNameForUserAgent:(NSString *)applicationNameForUserAgent
1402 _page->setApplicationNameForUserAgent(applicationNameForUserAgent);
1405 - (NSString *)_customUserAgent
1407 return _page->customUserAgent();
1410 - (void)_setCustomUserAgent:(NSString *)_customUserAgent
1412 _page->setCustomUserAgent(_customUserAgent);
1415 - (pid_t)_webProcessIdentifier
1417 return _page->isValid() ? _page->processIdentifier() : 0;
1420 - (void)_killWebContentProcess
1422 if (!_page->isValid())
1425 _page->process().terminate();
1428 - (NSData *)_sessionState
1430 return [wrapper(*_page->sessionStateData(nullptr, nullptr).leakRef()) autorelease];
1433 static void releaseNSData(unsigned char*, const void* data)
1435 [(NSData *)data release];
1438 - (void)_restoreFromSessionState:(NSData *)sessionState
1440 [sessionState retain];
1441 _page->restoreFromSessionStateData(API::Data::createWithoutCopying((const unsigned char*)sessionState.bytes, sessionState.length, releaseNSData, sessionState).get());
1449 - (BOOL)_privateBrowsingEnabled
1451 return [_configuration preferences]->_preferences->privateBrowsingEnabled();
1454 - (void)_setPrivateBrowsingEnabled:(BOOL)privateBrowsingEnabled
1456 [_configuration preferences]->_preferences->setPrivateBrowsingEnabled(privateBrowsingEnabled);
1459 - (BOOL)_allowsRemoteInspection
1461 #if ENABLE(REMOTE_INSPECTOR)
1462 return _page->allowsRemoteInspection();
1468 - (void)_setAllowsRemoteInspection:(BOOL)allow
1470 #if ENABLE(REMOTE_INSPECTOR)
1471 _page->setAllowsRemoteInspection(allow);
1475 - (BOOL)_addsVisitedLinks
1477 return _page->addsVisitedLinks();
1480 - (void)_setAddsVisitedLinks:(BOOL)addsVisitedLinks
1482 _page->setAddsVisitedLinks(addsVisitedLinks);
1485 static inline WebCore::LayoutMilestones layoutMilestones(_WKRenderingProgressEvents events)
1487 WebCore::LayoutMilestones milestones = 0;
1489 if (events & _WKRenderingProgressEventFirstLayout)
1490 milestones |= WebCore::DidFirstLayout;
1492 if (events & _WKRenderingProgressEventFirstPaintWithSignificantArea)
1493 milestones |= WebCore::DidHitRelevantRepaintedObjectsAreaThreshold;
1498 - (void)_setObservedRenderingProgressEvents:(_WKRenderingProgressEvents)observedRenderingProgressEvents
1500 _observedRenderingProgressEvents = observedRenderingProgressEvents;
1501 _page->listenForLayoutMilestones(layoutMilestones(observedRenderingProgressEvents));
1504 - (void)_runJavaScriptInMainFrame:(NSString *)scriptString
1506 [self evaluateJavaScript:scriptString completionHandler:^(id, NSError *) { }];
1509 - (void)_getWebArchiveDataWithCompletionHandler:(void (^)(NSData *, NSError *))completionHandler
1511 auto handler = adoptNS([completionHandler copy]);
1513 _page->getWebArchiveOfFrame(_page->mainFrame(), WebKit::DataCallback::create([handler](bool isError, API::Data* data) {
1514 void (^completionHandlerBlock)(NSData *, NSError *) = (void (^)(NSData *, NSError *))handler.get();
1516 // FIXME: Pipe a proper error in from the WebPageProxy.
1517 RetainPtr<NSError> error = adoptNS([[NSError alloc] init]);
1518 completionHandlerBlock(nil, error.get());
1520 completionHandlerBlock(wrapper(*data), nil);
1524 - (_WKPaginationMode)_paginationMode
1526 switch (_page->paginationMode()) {
1527 case WebCore::Pagination::Unpaginated:
1528 return _WKPaginationModeUnpaginated;
1529 case WebCore::Pagination::LeftToRightPaginated:
1530 return _WKPaginationModeLeftToRight;
1531 case WebCore::Pagination::RightToLeftPaginated:
1532 return _WKPaginationModeRightToLeft;
1533 case WebCore::Pagination::TopToBottomPaginated:
1534 return _WKPaginationModeTopToBottom;
1535 case WebCore::Pagination::BottomToTopPaginated:
1536 return _WKPaginationModeBottomToTop;
1539 ASSERT_NOT_REACHED();
1540 return _WKPaginationModeUnpaginated;
1543 - (void)_setPaginationMode:(_WKPaginationMode)paginationMode
1545 WebCore::Pagination::Mode mode;
1546 switch (paginationMode) {
1547 case _WKPaginationModeUnpaginated:
1548 mode = WebCore::Pagination::Unpaginated;
1550 case _WKPaginationModeLeftToRight:
1551 mode = WebCore::Pagination::LeftToRightPaginated;
1553 case _WKPaginationModeRightToLeft:
1554 mode = WebCore::Pagination::RightToLeftPaginated;
1556 case _WKPaginationModeTopToBottom:
1557 mode = WebCore::Pagination::TopToBottomPaginated;
1559 case _WKPaginationModeBottomToTop:
1560 mode = WebCore::Pagination::BottomToTopPaginated;
1566 _page->setPaginationMode(mode);
1569 - (BOOL)_paginationBehavesLikeColumns
1571 return _page->paginationBehavesLikeColumns();
1574 - (void)_setPaginationBehavesLikeColumns:(BOOL)behavesLikeColumns
1576 _page->setPaginationBehavesLikeColumns(behavesLikeColumns);
1579 - (CGFloat)_pageLength
1581 return _page->pageLength();
1584 - (void)_setPageLength:(CGFloat)pageLength
1586 _page->setPageLength(pageLength);
1589 - (CGFloat)_gapBetweenPages
1591 return _page->gapBetweenPages();
1594 - (void)_setGapBetweenPages:(CGFloat)gapBetweenPages
1596 _page->setGapBetweenPages(gapBetweenPages);
1599 - (NSUInteger)_pageCount
1601 return _page->pageCount();
1604 - (BOOL)_supportsTextZoom
1606 return _page->supportsTextZoom();
1609 - (double)_textZoomFactor
1611 return _page->textZoomFactor();
1614 - (void)_setTextZoomFactor:(double)zoomFactor
1616 _page->setTextZoomFactor(zoomFactor);
1619 - (double)_pageZoomFactor
1621 return _page->pageZoomFactor();
1624 - (void)_setPageZoomFactor:(double)zoomFactor
1626 _page->setPageZoomFactor(zoomFactor);
1629 - (id <_WKFindDelegate>)_findDelegate
1631 return [static_cast<WebKit::FindClient&>(_page->findClient()).delegate().leakRef() autorelease];
1634 - (void)_setFindDelegate:(id<_WKFindDelegate>)findDelegate
1636 static_cast<WebKit::FindClient&>(_page->findClient()).setDelegate(findDelegate);
1639 static inline WebKit::FindOptions toFindOptions(_WKFindOptions wkFindOptions)
1641 unsigned findOptions = 0;
1643 if (wkFindOptions & _WKFindOptionsCaseInsensitive)
1644 findOptions |= WebKit::FindOptionsCaseInsensitive;
1645 if (wkFindOptions & _WKFindOptionsAtWordStarts)
1646 findOptions |= WebKit::FindOptionsAtWordStarts;
1647 if (wkFindOptions & _WKFindOptionsTreatMedialCapitalAsWordStart)
1648 findOptions |= WebKit::FindOptionsTreatMedialCapitalAsWordStart;
1649 if (wkFindOptions & _WKFindOptionsBackwards)
1650 findOptions |= WebKit::FindOptionsBackwards;
1651 if (wkFindOptions & _WKFindOptionsWrapAround)
1652 findOptions |= WebKit::FindOptionsWrapAround;
1653 if (wkFindOptions & _WKFindOptionsShowOverlay)
1654 findOptions |= WebKit::FindOptionsShowOverlay;
1655 if (wkFindOptions & _WKFindOptionsShowFindIndicator)
1656 findOptions |= WebKit::FindOptionsShowFindIndicator;
1657 if (wkFindOptions & _WKFindOptionsShowHighlight)
1658 findOptions |= WebKit::FindOptionsShowHighlight;
1659 if (wkFindOptions & _WKFindOptionsDetermineMatchIndex)
1660 findOptions |= WebKit::FindOptionsDetermineMatchIndex;
1662 return static_cast<WebKit::FindOptions>(findOptions);
1665 - (void)_countStringMatches:(NSString *)string options:(_WKFindOptions)options maxCount:(NSUInteger)maxCount
1667 _page->countStringMatches(string, toFindOptions(options), maxCount);
1670 - (void)_findString:(NSString *)string options:(_WKFindOptions)options maxCount:(NSUInteger)maxCount
1672 _page->findString(string, toFindOptions(options), maxCount);
1677 _page->hideFindUI();
1680 - (id <_WKFormDelegate>)_formDelegate
1682 return _formDelegate.getAutoreleased();
1685 - (void)_setFormDelegate:(id <_WKFormDelegate>)formDelegate
1687 _formDelegate = formDelegate;
1689 class FormClient : public API::FormClient {
1691 explicit FormClient(WKWebView *webView)
1692 : m_webView(webView)
1696 virtual ~FormClient() { }
1698 virtual bool willSubmitForm(WebKit::WebPageProxy*, WebKit::WebFrameProxy*, WebKit::WebFrameProxy* sourceFrame, const Vector<std::pair<WTF::String, WTF::String>>& textFieldValues, API::Object* userData, WebKit::WebFormSubmissionListenerProxy* listener) override
1700 if (userData && userData->type() != API::Object::Type::Data) {
1701 ASSERT(!userData || userData->type() == API::Object::Type::Data);
1702 m_webView->_page->process().connection()->markCurrentlyDispatchedMessageAsInvalid();
1706 auto formDelegate = m_webView->_formDelegate.get();
1708 if (![formDelegate respondsToSelector:@selector(_webView:willSubmitFormValues:userObject:submissionHandler:)])
1711 auto valueMap = adoptNS([[NSMutableDictionary alloc] initWithCapacity:textFieldValues.size()]);
1712 for (const auto& pair : textFieldValues)
1713 [valueMap setObject:pair.second forKey:pair.first];
1715 NSObject <NSSecureCoding> *userObject = nil;
1716 if (API::Data* data = static_cast<API::Data*>(userData)) {
1717 auto nsData = adoptNS([[NSData alloc] initWithBytesNoCopy:const_cast<void*>(static_cast<const void*>(data->bytes())) length:data->size() freeWhenDone:NO]);
1718 auto unarchiver = adoptNS([[NSKeyedUnarchiver alloc] initForReadingWithData:nsData.get()]);
1719 [unarchiver setRequiresSecureCoding:YES];
1721 userObject = [unarchiver decodeObjectOfClass:[NSObject class] forKey:@"userObject"];
1722 } @catch (NSException *exception) {
1723 LOG_ERROR("Failed to decode user data: %@", exception);
1727 [formDelegate _webView:m_webView willSubmitFormValues:valueMap.get() userObject:userObject submissionHandler:^{
1728 listener->continueSubmission();
1734 WKWebView *m_webView;
1738 _page->setFormClient(std::make_unique<FormClient>(self));
1740 _page->setFormClient(nullptr);
1743 #pragma mark iOS-specific methods
1747 - (CGSize)_minimumLayoutSizeOverride
1749 ASSERT(_overridesMinimumLayoutSize);
1750 return _minimumLayoutSizeOverride;
1753 - (void)_setMinimumLayoutSizeOverride:(CGSize)minimumLayoutSizeOverride
1755 _overridesMinimumLayoutSize = YES;
1756 if (CGSizeEqualToSize(_minimumLayoutSizeOverride, minimumLayoutSizeOverride))
1759 _minimumLayoutSizeOverride = minimumLayoutSizeOverride;
1760 if (!_isAnimatingResize)
1761 _page->setViewportConfigurationMinimumLayoutSize(WebCore::FloatSize(minimumLayoutSizeOverride));
1764 - (CGSize)_minimumLayoutSizeOverrideForMinimalUI
1766 ASSERT(_overridesMinimumLayoutSizeForMinimalUI);
1767 return _minimumLayoutSizeOverrideForMinimalUI;
1770 - (void)_setMinimumLayoutSizeOverrideForMinimalUI:(CGSize)size
1772 _overridesMinimumLayoutSizeForMinimalUI = YES;
1773 if (CGSizeEqualToSize(_minimumLayoutSizeOverrideForMinimalUI, size))
1776 _minimumLayoutSizeOverrideForMinimalUI = size;
1777 if (!_isAnimatingResize)
1778 _page->setViewportConfigurationMinimumLayoutSizeForMinimalUI(WebCore::FloatSize(size));
1781 - (UIEdgeInsets)_obscuredInsets
1783 return _obscuredInsets;
1786 - (void)_setObscuredInsets:(UIEdgeInsets)obscuredInsets
1788 ASSERT(obscuredInsets.top >= 0);
1789 ASSERT(obscuredInsets.left >= 0);
1790 ASSERT(obscuredInsets.bottom >= 0);
1791 ASSERT(obscuredInsets.right >= 0);
1793 if (UIEdgeInsetsEqualToEdgeInsets(_obscuredInsets, obscuredInsets))
1796 _obscuredInsets = obscuredInsets;
1798 [self _updateVisibleContentRects];
1799 [_customContentView web_setObscuredInsets:obscuredInsets];
1802 - (void)_setInterfaceOrientationOverride:(UIInterfaceOrientation)interfaceOrientation
1804 if (!_overridesInterfaceOrientation)
1805 [[NSNotificationCenter defaultCenter] removeObserver:self name:UIWindowDidRotateNotification object:nil];
1807 _overridesInterfaceOrientation = YES;
1809 if (interfaceOrientation == _interfaceOrientationOverride)
1812 _interfaceOrientationOverride = interfaceOrientation;
1814 if (!_isAnimatingResize)
1815 _page->setDeviceOrientation(deviceOrientationForUIInterfaceOrientation(_interfaceOrientationOverride));
1818 - (UIInterfaceOrientation)_interfaceOrientationOverride
1820 ASSERT(_overridesInterfaceOrientation);
1821 return _interfaceOrientationOverride;
1824 - (CGSize)_maximumUnobscuredSizeOverride
1826 ASSERT(_overridesMaximumUnobscuredSize);
1827 return _maximumUnobscuredSizeOverride;
1830 - (void)_setMaximumUnobscuredSizeOverride:(CGSize)size
1832 ASSERT(size.width <= self.bounds.size.width && size.height <= self.bounds.size.height);
1833 _overridesMaximumUnobscuredSize = YES;
1834 if (CGSizeEqualToSize(_maximumUnobscuredSizeOverride, size))
1837 _maximumUnobscuredSizeOverride = size;
1838 if (!_isAnimatingResize)
1839 _page->setMaximumUnobscuredSize(WebCore::FloatSize(size));
1842 - (void)_setBackgroundExtendsBeyondPage:(BOOL)backgroundExtends
1844 _page->setBackgroundExtendsBeyondPage(backgroundExtends);
1847 - (BOOL)_backgroundExtendsBeyondPage
1849 return _page->backgroundExtendsBeyondPage();
1852 - (void)_beginInteractiveObscuredInsetsChange
1854 ASSERT(!_isChangingObscuredInsetsInteractively);
1855 _isChangingObscuredInsetsInteractively = YES;
1858 - (void)_endInteractiveObscuredInsetsChange
1860 ASSERT(_isChangingObscuredInsetsInteractively);
1861 _isChangingObscuredInsetsInteractively = NO;
1862 [self _updateVisibleContentRects];
1865 - (void)_beginAnimatedResizeWithUpdates:(void (^)(void))updateBlock
1867 _isAnimatingResize = YES;
1869 if (_customContentView) {
1874 _resizeAnimationTransformAdjustments = CATransform3DIdentity;
1876 NSUInteger indexOfContentView = [[_scrollView subviews] indexOfObject:_contentView.get()];
1877 _resizeAnimationView = adoptNS([[UIView alloc] init]);
1878 [_scrollView insertSubview:_resizeAnimationView.get() atIndex:indexOfContentView];
1879 [_resizeAnimationView addSubview:_contentView.get()];
1880 WebCore::FloatRect oldUnobscuredContentRect = _page->unobscuredContentRect();
1884 CGRect newBounds = self.bounds;
1885 CGSize newMinimumLayoutSize = newBounds.size;
1887 CGSize contentSizeInContentViewCoordinates = [_contentView bounds].size;
1888 [_scrollView setMinimumZoomScale:std::min(newMinimumLayoutSize.width / contentSizeInContentViewCoordinates.width, [_scrollView minimumZoomScale])];
1889 [_scrollView setMaximumZoomScale:std::max(newMinimumLayoutSize.width / contentSizeInContentViewCoordinates.width, [_scrollView maximumZoomScale])];
1891 // Compute the new scale to keep the current content width in the scrollview.
1892 CGFloat oldWebViewWidthInContentViewCoordinates = oldUnobscuredContentRect.width();
1893 CGFloat visibleContentViewWidthInContentCoordinates = std::min(contentSizeInContentViewCoordinates.width, oldWebViewWidthInContentViewCoordinates);
1894 CGFloat targetScale = newMinimumLayoutSize.width / visibleContentViewWidthInContentCoordinates;
1895 CGFloat resizeAnimationViewAnimationScale = targetScale / contentZoomScale(self);
1896 [_resizeAnimationView setTransform:CGAffineTransformMakeScale(resizeAnimationViewAnimationScale, resizeAnimationViewAnimationScale)];
1898 // Compute a new position to keep the content centered.
1899 CGPoint originalContentCenter = oldUnobscuredContentRect.center();
1900 CGPoint originalContentCenterInSelfCoordinates = [self convertPoint:originalContentCenter fromView:_contentView.get()];
1901 CGRect futureUnobscuredRectInSelfCoordinates = UIEdgeInsetsInsetRect(newBounds, _obscuredInsets);
1902 CGPoint futureUnobscuredRectCenterInSelfCoordinates = CGPointMake(futureUnobscuredRectInSelfCoordinates.origin.x + futureUnobscuredRectInSelfCoordinates.size.width / 2, futureUnobscuredRectInSelfCoordinates.origin.y + futureUnobscuredRectInSelfCoordinates.size.height / 2);
1904 CGPoint originalContentOffset = [_scrollView contentOffset];
1905 CGPoint contentOffset = originalContentOffset;
1906 contentOffset.x += (originalContentCenterInSelfCoordinates.x - futureUnobscuredRectCenterInSelfCoordinates.x);
1907 contentOffset.y += (originalContentCenterInSelfCoordinates.y - futureUnobscuredRectCenterInSelfCoordinates.y);
1909 // Limit the new offset within the scrollview, we do not want to rubber band programmatically.
1910 CGSize futureContentSizeInSelfCoordinates = CGSizeMake(contentSizeInContentViewCoordinates.width * targetScale, contentSizeInContentViewCoordinates.height * targetScale);
1911 CGFloat maxHorizontalOffset = futureContentSizeInSelfCoordinates.width - newBounds.size.width + _obscuredInsets.right;
1912 contentOffset.x = std::min(contentOffset.x, maxHorizontalOffset);
1913 CGFloat maxVerticalOffset = futureContentSizeInSelfCoordinates.height - newBounds.size.height + _obscuredInsets.bottom;
1914 contentOffset.y = std::min(contentOffset.y, maxVerticalOffset);
1916 contentOffset.x = std::max(contentOffset.x, -_obscuredInsets.left);
1917 contentOffset.y = std::max(contentOffset.y, -_obscuredInsets.top);
1919 // Make the top/bottom edges "sticky" within 1 pixel.
1920 if (oldUnobscuredContentRect.maxY() > contentSizeInContentViewCoordinates.height - 1)
1921 contentOffset.y = maxVerticalOffset;
1922 if (oldUnobscuredContentRect.y() < 1)
1923 contentOffset.y = -_obscuredInsets.top;
1925 // FIXME: if we have content centered after double tap to zoom, we should also try to keep that rect in view.
1926 [_scrollView setContentSize:roundScrollViewContentSize(*_page, futureContentSizeInSelfCoordinates)];
1927 [_scrollView setContentOffset:contentOffset];
1929 CGRect visibleRectInContentCoordinates = [self convertRect:newBounds toView:_contentView.get()];
1930 CGRect unobscuredRectInContentCoordinates = [self convertRect:futureUnobscuredRectInSelfCoordinates toView:_contentView.get()];
1932 CGSize minimumLayoutSize = newBounds.size;
1933 if (_overridesMinimumLayoutSize)
1934 minimumLayoutSize = _minimumLayoutSizeOverride;
1936 CGSize minimumLayoutSizeForMinimalUI = minimumLayoutSize;
1937 if (_overridesMinimumLayoutSizeForMinimalUI)
1938 minimumLayoutSizeForMinimalUI = _minimumLayoutSizeOverrideForMinimalUI;
1940 CGSize maximumUnobscuredSize = newBounds.size;
1941 if (_overridesMaximumUnobscuredSize)
1942 maximumUnobscuredSize = _maximumUnobscuredSizeOverride;
1944 int32_t orientation;
1945 if (_overridesInterfaceOrientation)
1946 orientation = deviceOrientationForUIInterfaceOrientation(_interfaceOrientationOverride);
1948 orientation = _page->deviceOrientation();
1950 _page->dynamicViewportSizeUpdate(WebCore::FloatSize(minimumLayoutSize), WebCore::FloatSize(minimumLayoutSizeForMinimalUI), WebCore::FloatSize(maximumUnobscuredSize), visibleRectInContentCoordinates, unobscuredRectInContentCoordinates, futureUnobscuredRectInSelfCoordinates, targetScale, orientation);
1953 - (void)_endAnimatedResize
1955 _page->synchronizeDynamicViewportUpdate();
1957 if (!_customContentView && _isAnimatingResize) {
1958 NSUInteger indexOfResizeAnimationView = [[_scrollView subviews] indexOfObject:_resizeAnimationView.get()];
1959 [_scrollView insertSubview:_contentView.get() atIndex:indexOfResizeAnimationView];
1961 CALayer *contentViewLayer = [_contentView layer];
1962 CATransform3D resizeAnimationTransformAdjustements = _resizeAnimationTransformAdjustments;
1963 CGFloat adjustmentScale = resizeAnimationTransformAdjustements.m11;
1964 contentViewLayer.sublayerTransform = CATransform3DIdentity;
1966 CGFloat animatingScaleTarget = [[_resizeAnimationView layer] transform].m11;
1967 CALayer *contentLayer = [_contentView layer];
1968 CATransform3D contentLayerTransform = contentLayer.transform;
1969 CGFloat currentScale = [[_resizeAnimationView layer] transform].m11 * contentLayerTransform.m11;
1971 // We cannot use [UIScrollView setZoomScale:] directly because the UIScrollView delegate would get a callback with
1972 // an invalid contentOffset. The real content offset is only set below.
1973 // Since there is no public API for setting both the zoomScale and the contentOffset, we set the zoomScale manually
1974 // on the zoom layer and then only change the contentOffset.
1975 CGFloat adjustedScale = adjustmentScale * currentScale;
1976 contentLayerTransform.m11 = adjustedScale;
1977 contentLayerTransform.m22 = adjustedScale;
1978 contentLayer.transform = contentLayerTransform;
1980 CGPoint currentScrollOffset = [_scrollView contentOffset];
1981 double horizontalScrollAdjustement = _resizeAnimationTransformAdjustments.m41 * animatingScaleTarget;
1982 double verticalScrollAdjustment = _resizeAnimationTransformAdjustments.m42 * animatingScaleTarget;
1984 [_scrollView setContentSize:roundScrollViewContentSize(*_page, [_contentView frame].size)];
1985 [_scrollView setContentOffset:CGPointMake(currentScrollOffset.x - horizontalScrollAdjustement, currentScrollOffset.y - verticalScrollAdjustment)];
1987 [_resizeAnimationView removeFromSuperview];
1988 _resizeAnimationView = nil;
1991 _isAnimatingResize = NO;
1992 _resizeAnimationTransformAdjustments = CATransform3DIdentity;
1994 [self _updateVisibleContentRects];
1997 - (void)_showInspectorIndication
1999 [_contentView setShowingInspectorIndication:YES];
2002 - (void)_hideInspectorIndication
2004 [_contentView setShowingInspectorIndication:NO];
2007 - (void)_setOverlaidAccessoryViewsInset:(CGSize)inset
2009 [_customContentView web_setOverlaidAccessoryViewsInset:inset];
2012 - (void)_snapshotRect:(CGRect)rectInViewCoordinates intoImageOfWidth:(CGFloat)imageWidth completionHandler:(void(^)(CGImageRef))completionHandler
2014 CGRect snapshotRectInContentCoordinates = [self convertRect:rectInViewCoordinates toView:_contentView.get()];
2015 CGFloat imageHeight = imageWidth / snapshotRectInContentCoordinates.size.width * snapshotRectInContentCoordinates.size.height;
2016 CGSize imageSize = CGSizeMake(imageWidth, imageHeight);
2019 WebKit::ProcessThrottler::BackgroundActivityToken* activityToken = new WebKit::ProcessThrottler::BackgroundActivityToken(_page->process().throttler());
2022 void(^copiedCompletionHandler)(CGImageRef) = [completionHandler copy];
2023 _page->takeSnapshot(WebCore::enclosingIntRect(snapshotRectInContentCoordinates), WebCore::expandedIntSize(WebCore::FloatSize(imageSize)), WebKit::SnapshotOptionsExcludeDeviceScaleFactor, [=](bool, const WebKit::ShareableBitmap::Handle& imageHandle) {
2025 // Automatically delete when this goes out of scope.
2026 auto uniqueActivityToken = std::unique_ptr<WebKit::ProcessThrottler::BackgroundActivityToken>(activityToken);
2029 if (imageHandle.isNull()) {
2030 copiedCompletionHandler(nullptr);
2031 [copiedCompletionHandler release];
2035 RefPtr<WebKit::ShareableBitmap> bitmap = WebKit::ShareableBitmap::create(imageHandle, WebKit::SharedMemory::ReadOnly);
2038 copiedCompletionHandler(nullptr);
2039 [copiedCompletionHandler release];
2043 RetainPtr<CGImageRef> cgImage;
2044 cgImage = bitmap->makeCGImage();
2045 copiedCompletionHandler(cgImage.get());
2046 [copiedCompletionHandler release];
2050 - (void)_overrideLayoutParametersWithMinimumLayoutSize:(CGSize)minimumLayoutSize minimumLayoutSizeForMinimalUI:(CGSize)minimumLayoutSizeForMinimalUI maximumUnobscuredSizeOverride:(CGSize)maximumUnobscuredSizeOverride
2052 // FIXME: After Safari is updated to use this function instead of setting the parameters separately, we should remove
2053 // the individual setters and send a single message to send everything at once to the WebProcess.
2054 self._minimumLayoutSizeOverride = minimumLayoutSize;
2055 self._minimumLayoutSizeOverrideForMinimalUI = minimumLayoutSizeForMinimalUI;
2056 self._maximumUnobscuredSizeOverride = maximumUnobscuredSizeOverride;
2059 - (UIView *)_viewForFindUI
2061 return [self viewForZoomingInScrollView:[self scrollView]];
2064 - (BOOL)_isDisplayingPDF
2066 return [_customContentView isKindOfClass:[WKPDFView class]];
2069 - (NSData *)_dataForDisplayedPDF
2071 if (![self _isDisplayingPDF])
2073 CGPDFDocumentRef pdfDocument = [(WKPDFView *)_customContentView pdfDocument];
2074 return [(NSData *)CGDataProviderCopyData(CGPDFDocumentGetDataProvider(pdfDocument)) autorelease];
2077 - (NSString *)_suggestedFilenameForDisplayedPDF
2079 if (![self _isDisplayingPDF])
2081 return [(WKPDFView *)_customContentView.get() suggestedFilename];
2084 - (CGFloat)_viewportMetaTagWidth
2086 return _viewportMetaTagWidth;
2089 - (_WKWebViewPrintFormatter *)_webViewPrintFormatter
2091 UIViewPrintFormatter *viewPrintFormatter = self.viewPrintFormatter;
2092 ASSERT([viewPrintFormatter isKindOfClass:[_WKWebViewPrintFormatter class]]);
2093 return (_WKWebViewPrintFormatter *)viewPrintFormatter;
2098 #pragma mark - OS X-specific methods
2100 - (NSColor *)_pageExtendedBackgroundColor
2102 WebCore::Color color = _page->pageExtendedBackgroundColor();
2103 if (!color.isValid())
2106 return nsColor(color);
2109 - (BOOL)_drawsTransparentBackground
2111 return _page->drawsTransparentBackground();
2114 - (void)_setDrawsTransparentBackground:(BOOL)drawsTransparentBackground
2116 _page->setDrawsTransparentBackground(drawsTransparentBackground);
2119 - (void)_setTopContentInset:(CGFloat)contentInset
2121 _page->setTopContentInset(contentInset);
2124 - (CGFloat)_topContentInset
2126 return _page->topContentInset();
2133 #if !TARGET_OS_IPHONE
2135 @implementation WKWebView (WKIBActions)
2137 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
2139 SEL action = item.action;
2141 if (action == @selector(goBack:))
2142 return !!_page->backForwardList().backItem();
2144 if (action == @selector(goForward:))
2145 return !!_page->backForwardList().forwardItem();
2147 if (action == @selector(stopLoading:)) {
2148 // FIXME: Return no if we're stopped.
2152 if (action == @selector(reload:) || action == @selector(reloadFromOrigin:)) {
2153 // FIXME: Return no if we're loading.
2160 - (IBAction)goBack:(id)sender
2165 - (IBAction)goForward:(id)sender
2170 - (IBAction)reload:(id)sender
2175 - (IBAction)reloadFromOrigin:(id)sender
2177 [self reloadFromOrigin];
2180 - (IBAction)stopLoading:(id)sender
2182 _page->stopLoading();
2190 @implementation WKWebView (_WKWebViewPrintFormatter)
2192 - (Class)_printFormatterClass
2194 return [_WKWebViewPrintFormatter class];
2197 - (NSInteger)_computePageCountAndStartDrawingToPDFForFrame:(_WKFrameHandle *)frame printInfo:(const WebKit::PrintInfo&)printInfo firstPage:(uint32_t)firstPage computedTotalScaleFactor:(double&)totalScaleFactor
2199 if ([self _isDisplayingPDF])
2200 return CGPDFDocumentGetNumberOfPages([(WKPDFView *)_customContentView pdfDocument]);
2202 _pageIsPrintingToPDF = YES;
2203 Vector<WebCore::IntRect> pageRects;
2204 uint64_t frameID = frame ? frame._frameID : _page->mainFrame()->frameID();
2205 if (!_page->sendSync(Messages::WebPage::ComputePagesForPrintingAndStartDrawingToPDF(frameID, printInfo, firstPage), Messages::WebPage::ComputePagesForPrintingAndStartDrawingToPDF::Reply(pageRects, totalScaleFactor)))
2207 return pageRects.size();
2210 - (void)_endPrinting
2212 _pageIsPrintingToPDF = NO;
2213 _printedDocument = nullptr;
2214 _page->send(Messages::WebPage::EndPrinting());
2217 // FIXME: milliseconds::max() overflows when converted to nanoseconds, causing condition_variable::wait_for() to believe
2218 // a timeout occurred on any spurious wakeup. Use nanoseconds::max() (converted to ms) to avoid this. We should perhaps
2219 // change waitForAndDispatchImmediately() to take nanoseconds to avoid this issue.
2220 static constexpr std::chrono::milliseconds didFinishLoadingTimeout = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::nanoseconds::max());
2222 - (CGPDFDocumentRef)_printedDocument
2224 if ([self _isDisplayingPDF]) {
2225 ASSERT(!_pageIsPrintingToPDF);
2226 return [(WKPDFView *)_customContentView pdfDocument];
2229 if (_pageIsPrintingToPDF) {
2230 if (!_page->process().connection()->waitForAndDispatchImmediately<Messages::WebPageProxy::DidFinishDrawingPagesToPDF>(_page->pageID(), didFinishLoadingTimeout)) {
2231 ASSERT_NOT_REACHED();
2234 ASSERT(!_pageIsPrintingToPDF);
2236 return _printedDocument.get();
2239 - (void)_setPrintedDocument:(CGPDFDocumentRef)printedDocument
2241 if (!_pageIsPrintingToPDF)
2243 ASSERT(![self _isDisplayingPDF]);
2244 _printedDocument = printedDocument;
2245 _pageIsPrintingToPDF = NO;
2251 #endif // WK_API_ENABLED