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 "CompletionHandlerCallChecker.h"
33 #import "FindClient.h"
34 #import "LegacySessionStateCoding.h"
35 #import "NavigationState.h"
36 #import "RemoteLayerTreeTransaction.h"
37 #import "RemoteObjectRegistry.h"
38 #import "RemoteObjectRegistryMessages.h"
39 #import "UIDelegate.h"
40 #import "ViewGestureController.h"
41 #import "ViewSnapshotStore.h"
42 #import "WKBackForwardListInternal.h"
43 #import "WKBackForwardListItemInternal.h"
44 #import "WKBrowsingContextHandleInternal.h"
45 #import "WKErrorInternal.h"
46 #import "WKHistoryDelegatePrivate.h"
48 #import "WKNSURLExtras.h"
49 #import "WKNavigationDelegate.h"
50 #import "WKNavigationInternal.h"
51 #import "WKPreferencesInternal.h"
52 #import "WKProcessPoolInternal.h"
53 #import "WKUIDelegate.h"
54 #import "WKUIDelegatePrivate.h"
55 #import "WKUserContentControllerInternal.h"
56 #import "WKWebViewConfigurationInternal.h"
57 #import "WKWebViewContentProvider.h"
58 #import "WebBackForwardList.h"
59 #import "WebCertificateInfo.h"
60 #import "WebContext.h"
61 #import "WebFormSubmissionListenerProxy.h"
62 #import "WebKitSystemInterface.h"
63 #import "WebPageGroup.h"
64 #import "WebPageProxy.h"
65 #import "WebPreferencesKeys.h"
66 #import "WebProcessProxy.h"
67 #import "WebSerializedScriptValue.h"
68 #import "_WKFindDelegate.h"
69 #import "_WKFormDelegate.h"
70 #import "_WKRemoteObjectRegistryInternal.h"
71 #import "_WKSessionStateInternal.h"
72 #import "_WKVisitedLinkProviderInternal.h"
73 #import "_WKWebsiteDataStoreInternal.h"
74 #import <JavaScriptCore/JSContext.h>
75 #import <JavaScriptCore/JSValue.h>
76 #import <wtf/HashMap.h>
77 #import <wtf/NeverDestroyed.h>
78 #import <wtf/RetainPtr.h>
81 #import "_WKFrameHandleInternal.h"
82 #import "_WKWebViewPrintFormatter.h"
84 #import "ProcessThrottler.h"
85 #import "RemoteLayerTreeDrawingAreaProxy.h"
86 #import "RemoteScrollingCoordinatorProxy.h"
88 #import "WKScrollView.h"
89 #import "WKWebViewContentProviderRegistry.h"
90 #import "WebPageMessages.h"
91 #import <CoreGraphics/CGFloat.h>
92 #import <CoreGraphics/CGPDFDocumentPrivate.h>
93 #import <UIKit/UIApplication.h>
94 #import <UIKit/UIDevice_Private.h>
95 #import <UIKit/UIPeripheralHost_Private.h>
96 #import <UIKit/UIWindow_Private.h>
97 #import <QuartzCore/CARenderServer.h>
98 #import <QuartzCore/QuartzCorePrivate.h>
99 #import <WebCore/InspectorOverlay.h>
101 @interface UIScrollView (UIScrollViewInternal)
102 - (void)_adjustForAutomaticKeyboardInfo:(NSDictionary*)info animated:(BOOL)animated lastAdjustment:(CGFloat*)lastAdjustment;
103 - (BOOL)_isScrollingToTop;
106 @interface UIPeripheralHost(UIKitInternal)
107 - (CGFloat)getVerticalOverlapForView:(UIView *)view usingKeyboardInfo:(NSDictionary *)info;
110 @interface UIView (UIViewInternal)
111 - (UIViewController *)_viewControllerForAncestor;
114 @interface UIWindow (UIWindowInternal)
115 - (BOOL)_isHostedInAnotherProcess;
118 @interface UIViewController (UIViewControllerInternal)
119 - (UIViewController *)_rootAncestorViewController;
120 - (UIViewController *)_viewControllerForSupportedInterfaceOrientations;
123 enum class DynamicViewportUpdateMode {
125 ResizingWithAnimation,
126 ResizingWithDocumentHidden,
132 #import "WKViewInternal.h"
133 #import <WebCore/ColorMac.h>
137 static HashMap<WebKit::WebPageProxy*, WKWebView *>& pageToViewMap()
139 static NeverDestroyed<HashMap<WebKit::WebPageProxy*, WKWebView *>> map;
143 WKWebView* fromWebPageProxy(WebKit::WebPageProxy& page)
145 return pageToViewMap().get(&page);
148 @implementation WKWebView {
149 std::unique_ptr<WebKit::NavigationState> _navigationState;
150 std::unique_ptr<WebKit::UIDelegate> _uiDelegate;
152 RetainPtr<_WKRemoteObjectRegistry> _remoteObjectRegistry;
153 _WKRenderingProgressEvents _observedRenderingProgressEvents;
155 WebKit::WeakObjCPtr<id <_WKFormDelegate>> _formDelegate;
157 RetainPtr<WKScrollView> _scrollView;
158 RetainPtr<WKContentView> _contentView;
160 BOOL _overridesMinimumLayoutSize;
161 CGSize _minimumLayoutSizeOverride;
162 BOOL _overridesMinimumLayoutSizeForMinimalUI;
163 CGSize _minimumLayoutSizeOverrideForMinimalUI;
164 BOOL _overridesMaximumUnobscuredSize;
165 CGSize _maximumUnobscuredSizeOverride;
167 BOOL _needsToNotifyDelegateAboutMinimalUI;
168 CGRect _inputViewBounds;
169 CGFloat _viewportMetaTagWidth;
171 UIEdgeInsets _obscuredInsets;
172 BOOL _haveSetObscuredInsets;
173 BOOL _isChangingObscuredInsetsInteractively;
175 UIInterfaceOrientation _interfaceOrientationOverride;
176 BOOL _overridesInterfaceOrientation;
178 BOOL _hasCommittedLoadForMainFrame;
179 BOOL _needsResetViewStateAfterCommitLoadForMainFrame;
180 uint64_t _firstPaintAfterCommitLoadTransactionID;
181 DynamicViewportUpdateMode _dynamicViewportUpdateMode;
182 CATransform3D _resizeAnimationTransformAdjustments;
183 uint64_t _resizeAnimationTransformTransactionID;
184 RetainPtr<UIView> _resizeAnimationView;
185 CGFloat _lastAdjustmentForScroller;
187 BOOL _needsToRestoreExposedRect;
188 WebCore::FloatRect _exposedRectToRestore;
189 BOOL _needsToRestoreUnobscuredCenter;
190 WebCore::FloatPoint _unobscuredCenterToRestore;
191 uint64_t _firstTransactionIDAfterPageRestore;
192 double _scaleToRestore;
194 std::unique_ptr<WebKit::ViewGestureController> _gestureController;
195 BOOL _allowsBackForwardNavigationGestures;
197 RetainPtr<UIView <WKWebViewContentProvider>> _customContentView;
198 RetainPtr<UIView> _customContentFixedOverlayView;
200 WebCore::Color _scrollViewBackgroundColor;
202 BOOL _delayUpdateVisibleContentRects;
203 BOOL _hadDelayedUpdateVisibleContentRects;
205 BOOL _pageIsPrintingToPDF;
206 RetainPtr<CGPDFDocumentRef> _printedDocument;
209 RetainPtr<WKView> _wkView;
213 - (instancetype)initWithFrame:(CGRect)frame
215 return [self initWithFrame:frame configuration:adoptNS([[WKWebViewConfiguration alloc] init]).get()];
219 static int32_t deviceOrientationForUIInterfaceOrientation(UIInterfaceOrientation orientation)
221 switch (orientation) {
222 case UIInterfaceOrientationUnknown:
223 case UIInterfaceOrientationPortrait:
225 case UIInterfaceOrientationPortraitUpsideDown:
227 case UIInterfaceOrientationLandscapeLeft:
229 case UIInterfaceOrientationLandscapeRight:
234 static int32_t deviceOrientation()
236 return deviceOrientationForUIInterfaceOrientation([[UIApplication sharedApplication] statusBarOrientation]);
240 - (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration
242 if (!(self = [super initWithFrame:frame]))
245 _configuration = adoptNS([configuration copy]);
247 if (WKWebView *relatedWebView = [_configuration _relatedWebView]) {
248 WKProcessPool *processPool = [_configuration processPool];
249 WKProcessPool *relatedWebViewProcessPool = [relatedWebView->_configuration processPool];
250 if (processPool && processPool != relatedWebViewProcessPool)
251 [NSException raise:NSInvalidArgumentException format:@"Related web view %@ has process pool %@ but configuration specifies a different process pool %@", relatedWebView, relatedWebViewProcessPool, configuration.processPool];
253 [_configuration setProcessPool:relatedWebViewProcessPool];
256 [_configuration _validate];
258 CGRect bounds = self.bounds;
260 WebKit::WebContext& context = *[_configuration processPool]->_context;
262 WebKit::WebPageConfiguration webPageConfiguration;
263 webPageConfiguration.preferences = [_configuration preferences]->_preferences.get();
264 if (WKWebView *relatedWebView = [_configuration _relatedWebView])
265 webPageConfiguration.relatedPage = relatedWebView->_page.get();
267 webPageConfiguration.userContentController = [_configuration userContentController]->_userContentControllerProxy.get();
268 webPageConfiguration.visitedLinkProvider = [_configuration _visitedLinkProvider]->_visitedLinkProvider.get();
269 webPageConfiguration.session = [_configuration _websiteDataStore]->_session.get();
271 RefPtr<WebKit::WebPageGroup> pageGroup;
272 NSString *groupIdentifier = configuration._groupIdentifier;
273 if (groupIdentifier.length) {
274 pageGroup = WebKit::WebPageGroup::create(configuration._groupIdentifier);
275 webPageConfiguration.pageGroup = pageGroup.get();
278 webPageConfiguration.preferenceValues.set(WebKit::WebPreferencesKey::suppressesIncrementalRenderingKey(), WebKit::WebPreferencesStore::Value(!![_configuration suppressesIncrementalRendering]));
281 webPageConfiguration.preferenceValues.set(WebKit::WebPreferencesKey::mediaPlaybackAllowsInlineKey(), WebKit::WebPreferencesStore::Value(!![_configuration allowsInlineMediaPlayback]));
282 webPageConfiguration.preferenceValues.set(WebKit::WebPreferencesKey::mediaPlaybackRequiresUserGestureKey(), WebKit::WebPreferencesStore::Value(!![_configuration mediaPlaybackRequiresUserAction]));
283 webPageConfiguration.preferenceValues.set(WebKit::WebPreferencesKey::mediaPlaybackAllowsAirPlayKey(), WebKit::WebPreferencesStore::Value(!![_configuration mediaPlaybackAllowsAirPlay]));
287 _scrollView = adoptNS([[WKScrollView alloc] initWithFrame:bounds]);
288 [_scrollView setInternalDelegate:self];
289 [_scrollView setBouncesZoom:YES];
291 [self addSubview:_scrollView.get()];
293 _contentView = adoptNS([[WKContentView alloc] initWithFrame:bounds context:context configuration:WTF::move(webPageConfiguration) webView:self]);
295 _page = [_contentView page];
296 _page->setApplicationNameForUserAgent([@"Mobile/" stringByAppendingString:[UIDevice currentDevice].buildVersion]);
297 _page->setDeviceOrientation(deviceOrientation());
299 [_contentView layer].anchorPoint = CGPointZero;
300 [_contentView setFrame:bounds];
301 [_scrollView addSubview:_contentView.get()];
302 [_scrollView addSubview:[_contentView unscaledView]];
303 [self _updateScrollViewBackground];
305 _viewportMetaTagWidth = -1;
307 [self _frameOrBoundsChanged];
309 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
310 [center addObserver:self selector:@selector(_keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];
311 [center addObserver:self selector:@selector(_keyboardDidChangeFrame:) name:UIKeyboardDidChangeFrameNotification object:nil];
312 [center addObserver:self selector:@selector(_keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
313 [center addObserver:self selector:@selector(_keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
314 [center addObserver:self selector:@selector(_windowDidRotate:) name:UIWindowDidRotateNotification object:nil];
315 [center addObserver:self selector:@selector(_contentSizeCategoryDidChange:) name:UIContentSizeCategoryDidChangeNotification object:nil];
316 _page->contentSizeCategoryDidChange([self _contentSizeCategory]);
318 [[_configuration _contentProviderRegistry] addPage:*_page];
322 _wkView = adoptNS([[WKView alloc] initWithFrame:bounds context:context configuration:WTF::move(webPageConfiguration) webView:self]);
323 [self addSubview:_wkView.get()];
324 _page = WebKit::toImpl([_wkView pageRef]);
326 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
327 [_wkView _setAutomaticallyAdjustsContentInsets:YES];
331 _page->setBackgroundExtendsBeyondPage(true);
333 _navigationState = std::make_unique<WebKit::NavigationState>(self);
334 _page->setPolicyClient(_navigationState->createPolicyClient());
335 _page->setLoaderClient(_navigationState->createLoaderClient());
337 _uiDelegate = std::make_unique<WebKit::UIDelegate>(self);
338 _page->setUIClient(_uiDelegate->createUIClient());
340 _page->setFindClient(std::make_unique<WebKit::FindClient>(self));
342 pageToViewMap().add(_page.get(), self);
347 - (instancetype)initWithCoder:(NSCoder *)coder
357 [_remoteObjectRegistry _invalidate];
359 [[_configuration _contentProviderRegistry] removePage:*_page];
360 [[NSNotificationCenter defaultCenter] removeObserver:self];
363 pageToViewMap().remove(_page.get());
368 - (WKWebViewConfiguration *)configuration
370 return [[_configuration copy] autorelease];
373 - (WKBackForwardList *)backForwardList
375 return wrapper(_page->backForwardList());
378 - (id <WKNavigationDelegate>)navigationDelegate
380 return _navigationState->navigationDelegate().autorelease();
383 - (void)setNavigationDelegate:(id <WKNavigationDelegate>)navigationDelegate
385 _navigationState->setNavigationDelegate(navigationDelegate);
388 - (id <WKUIDelegate>)UIDelegate
390 return _uiDelegate->delegate().autorelease();
393 - (void)setUIDelegate:(id<WKUIDelegate>)UIDelegate
395 _uiDelegate->setDelegate(UIDelegate);
398 - (WKNavigation *)loadRequest:(NSURLRequest *)request
400 uint64_t navigationID = _page->loadRequest(request);
401 auto navigation = _navigationState->createLoadRequestNavigation(navigationID, request);
403 return navigation.autorelease();
406 - (WKNavigation *)loadHTMLString:(NSString *)string baseURL:(NSURL *)baseURL
408 uint64_t navigationID = _page->loadHTMLString(string, baseURL.absoluteString);
412 auto navigation = _navigationState->createLoadDataNavigation(navigationID);
414 return navigation.autorelease();
417 - (WKNavigation *)goToBackForwardListItem:(WKBackForwardListItem *)item
419 uint64_t navigationID = _page->goToBackForwardItem(&item._item);
421 auto navigation = _navigationState->createBackForwardNavigation(navigationID, item._item);
423 return navigation.autorelease();
428 return _page->pageLoadState().title();
433 return [NSURL _web_URLWithWTFString:_page->pageLoadState().activeURL()];
438 return _page->pageLoadState().isLoading();
441 - (double)estimatedProgress
443 return _page->pageLoadState().estimatedProgress();
446 - (BOOL)hasOnlySecureContent
448 return _page->pageLoadState().hasOnlySecureContent();
453 return _page->pageLoadState().canGoBack();
458 return _page->pageLoadState().canGoForward();
461 - (WKNavigation *)goBack
463 uint64_t navigationID = _page->goBack();
467 ASSERT(_page->backForwardList().currentItem());
468 auto navigation = _navigationState->createBackForwardNavigation(navigationID, *_page->backForwardList().currentItem());
470 return navigation.autorelease();
473 - (WKNavigation *)goForward
475 uint64_t navigationID = _page->goForward();
479 ASSERT(_page->backForwardList().currentItem());
480 auto navigation = _navigationState->createBackForwardNavigation(navigationID, *_page->backForwardList().currentItem());
482 return navigation.autorelease();
485 - (WKNavigation *)reload
487 uint64_t navigationID = _page->reload(false);
491 auto navigation = _navigationState->createReloadNavigation(navigationID);
492 return navigation.autorelease();
495 - (WKNavigation *)reloadFromOrigin
497 uint64_t navigationID = _page->reload(true);
501 auto navigation = _navigationState->createReloadNavigation(navigationID);
502 return navigation.autorelease();
507 _page->stopLoading();
510 static WKErrorCode callbackErrorCode(WebKit::CallbackBase::Error error)
513 case WebKit::CallbackBase::Error::None:
514 ASSERT_NOT_REACHED();
515 return WKErrorUnknown;
517 case WebKit::CallbackBase::Error::Unknown:
518 return WKErrorUnknown;
520 case WebKit::CallbackBase::Error::ProcessExited:
521 return WKErrorWebContentProcessTerminated;
523 case WebKit::CallbackBase::Error::OwnerWasInvalidated:
524 return WKErrorWebViewInvalidated;
528 - (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^)(id, NSError *))completionHandler
530 auto handler = adoptNS([completionHandler copy]);
532 _page->runJavaScriptInMainFrame(javaScriptString, [handler](WebKit::WebSerializedScriptValue* serializedScriptValue, WebKit::ScriptValueCallback::Error errorCode) {
536 auto completionHandler = (void (^)(id, NSError *))handler.get();
538 if (errorCode != WebKit::ScriptValueCallback::Error::None) {
539 auto error = createNSError(callbackErrorCode(errorCode));
540 if (errorCode == WebKit::ScriptValueCallback::Error::OwnerWasInvalidated) {
541 // The OwnerWasInvalidated callback is synchronous. We don't want to call the block from within it
542 // because that can trigger re-entrancy bugs in WebKit.
543 // FIXME: It would be even better if GenericCallback did this for us.
544 dispatch_async(dispatch_get_main_queue(), [completionHandler, error] {
545 completionHandler(nil, error.get());
550 completionHandler(nil, error.get());
554 if (!serializedScriptValue) {
555 completionHandler(nil, createNSError(WKErrorJavaScriptExceptionOccurred).get());
559 auto context = adoptNS([[JSContext alloc] init]);
560 JSValueRef valueRef = serializedScriptValue->deserialize([context JSGlobalContextRef], 0);
561 JSValue *value = [JSValue valueWithJSValueRef:valueRef inContext:context.get()];
563 completionHandler([value toObject], nil);
567 #pragma mark iOS-specific methods
570 - (void)setFrame:(CGRect)frame
572 CGRect oldFrame = self.frame;
573 [super setFrame:frame];
575 if (!CGSizeEqualToSize(oldFrame.size, frame.size))
576 [self _frameOrBoundsChanged];
579 - (void)setBounds:(CGRect)bounds
581 CGRect oldBounds = self.bounds;
582 [super setBounds:bounds];
583 [_customContentFixedOverlayView setFrame:self.bounds];
585 if (!CGSizeEqualToSize(oldBounds.size, bounds.size))
586 [self _frameOrBoundsChanged];
589 - (UIScrollView *)scrollView
591 return _scrollView.get();
594 - (WKBrowsingContextController *)browsingContextController
596 return [_contentView browsingContextController];
599 static inline CGFloat floorToDevicePixel(CGFloat input, float deviceScaleFactor)
601 return CGFloor(input * deviceScaleFactor) / deviceScaleFactor;
604 static CGSize roundScrollViewContentSize(const WebKit::WebPageProxy& page, CGSize contentSize)
606 float deviceScaleFactor = page.deviceScaleFactor();
607 return CGSizeMake(floorToDevicePixel(contentSize.width, deviceScaleFactor), floorToDevicePixel(contentSize.height, deviceScaleFactor));
610 - (UIView *)_currentContentView
612 return _customContentView ? _customContentView.get() : _contentView.get();
615 - (void)_setHasCustomContentView:(BOOL)pageHasCustomContentView loadedMIMEType:(const WTF::String&)mimeType
617 if (pageHasCustomContentView) {
618 [_customContentView removeFromSuperview];
619 [_customContentFixedOverlayView removeFromSuperview];
621 Class representationClass = [[_configuration _contentProviderRegistry] providerForMIMEType:mimeType];
622 ASSERT(representationClass);
623 _customContentView = adoptNS([[representationClass alloc] web_initWithFrame:self.bounds webView:self]);
624 _customContentFixedOverlayView = adoptNS([[UIView alloc] initWithFrame:self.bounds]);
625 [_customContentFixedOverlayView setUserInteractionEnabled:NO];
627 [[_contentView unscaledView] removeFromSuperview];
628 [_contentView removeFromSuperview];
629 [_scrollView addSubview:_customContentView.get()];
630 [self addSubview:_customContentFixedOverlayView.get()];
632 [_customContentView web_setMinimumSize:self.bounds.size];
633 [_customContentView web_setFixedOverlayView:_customContentFixedOverlayView.get()];
634 } else if (_customContentView) {
635 [_customContentView removeFromSuperview];
636 _customContentView = nullptr;
638 [_customContentFixedOverlayView removeFromSuperview];
639 _customContentFixedOverlayView = nullptr;
641 [_scrollView addSubview:_contentView.get()];
642 [_scrollView addSubview:[_contentView unscaledView]];
643 [_scrollView setContentSize:roundScrollViewContentSize(*_page, [_contentView frame].size)];
645 [_customContentFixedOverlayView setFrame:self.bounds];
646 [self addSubview:_customContentFixedOverlayView.get()];
650 - (void)_didFinishLoadingDataForCustomContentProviderWithSuggestedFilename:(const String&)suggestedFilename data:(NSData *)data
652 ASSERT(_customContentView);
653 [_customContentView web_setContentProviderData:data suggestedFilename:suggestedFilename];
656 - (void)_setViewportMetaTagWidth:(float)newWidth
658 _viewportMetaTagWidth = newWidth;
661 - (void)_willInvokeUIScrollViewDelegateCallback
663 _delayUpdateVisibleContentRects = YES;
666 - (void)_didInvokeUIScrollViewDelegateCallback
668 _delayUpdateVisibleContentRects = NO;
669 if (_hadDelayedUpdateVisibleContentRects) {
670 _hadDelayedUpdateVisibleContentRects = NO;
671 [self _updateVisibleContentRects];
675 static CGFloat contentZoomScale(WKWebView *webView)
677 CGFloat scale = webView._currentContentView.layer.affineTransform.a;
678 ASSERT(scale == [webView->_scrollView zoomScale]);
682 static WebCore::Color scrollViewBackgroundColor(WKWebView *webView)
685 return WebCore::Color::transparent;
687 WebCore::Color color;
689 if (webView->_customContentView)
690 color = [webView->_customContentView backgroundColor].CGColor;
692 color = webView->_page->pageExtendedBackgroundColor();
694 if (!color.isValid())
695 color = WebCore::Color::white;
697 CGFloat zoomScale = contentZoomScale(webView);
698 CGFloat minimumZoomScale = [webView->_scrollView minimumZoomScale];
699 if (zoomScale < minimumZoomScale) {
701 CGFloat opacity = std::max<CGFloat>(1 - slope * (minimumZoomScale - zoomScale), 0);
702 color = WebCore::colorWithOverrideAlpha(color.rgb(), opacity);
708 - (void)_updateScrollViewBackground
710 WebCore::Color color = scrollViewBackgroundColor(self);
712 if (_scrollViewBackgroundColor == color)
715 _scrollViewBackgroundColor = color;
717 auto uiBackgroundColor = adoptNS([[UIColor alloc] initWithCGColor:cachedCGColor(color, WebCore::ColorSpaceDeviceRGB)]);
718 [_scrollView setBackgroundColor:uiBackgroundColor.get()];
721 - (void)_setUsesMinimalUI:(BOOL)usesMinimalUI
723 _usesMinimalUI = usesMinimalUI;
724 _needsToNotifyDelegateAboutMinimalUI = YES;
727 - (BOOL)_usesMinimalUI
729 return _usesMinimalUI;
732 - (CGPoint)_adjustedContentOffset:(CGPoint)point
734 CGPoint result = point;
735 UIEdgeInsets contentInset = [self _computedContentInset];
737 result.x -= contentInset.left;
738 result.y -= contentInset.top;
743 - (UIEdgeInsets)_computedContentInset
745 if (_haveSetObscuredInsets)
746 return _obscuredInsets;
748 return [_scrollView contentInset];
751 - (void)_processDidExit
753 if (!_customContentView && _dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing) {
754 NSUInteger indexOfResizeAnimationView = [[_scrollView subviews] indexOfObject:_resizeAnimationView.get()];
755 [_scrollView insertSubview:_contentView.get() atIndex:indexOfResizeAnimationView];
756 [_scrollView insertSubview:[_contentView unscaledView] atIndex:indexOfResizeAnimationView + 1];
757 [_resizeAnimationView removeFromSuperview];
758 _resizeAnimationView = nil;
760 _resizeAnimationTransformAdjustments = CATransform3DIdentity;
762 [_contentView setFrame:self.bounds];
763 [_scrollView setBackgroundColor:[UIColor whiteColor]];
764 [_scrollView setContentOffset:[self _adjustedContentOffset:CGPointZero]];
765 [_scrollView setZoomScale:1];
767 _viewportMetaTagWidth = -1;
768 _hasCommittedLoadForMainFrame = NO;
769 _needsResetViewStateAfterCommitLoadForMainFrame = NO;
770 _dynamicViewportUpdateMode = DynamicViewportUpdateMode::NotResizing;
771 [_contentView setHidden:NO];
772 _needsToRestoreExposedRect = NO;
773 _needsToRestoreUnobscuredCenter = NO;
774 _scrollViewBackgroundColor = WebCore::Color();
775 _delayUpdateVisibleContentRects = NO;
776 _hadDelayedUpdateVisibleContentRects = NO;
779 - (void)_didCommitLoadForMainFrame
781 _firstPaintAfterCommitLoadTransactionID = toRemoteLayerTreeDrawingAreaProxy(_page->drawingArea())->nextLayerTreeTransactionID();
783 _hasCommittedLoadForMainFrame = YES;
784 _needsResetViewStateAfterCommitLoadForMainFrame = YES;
788 static CGPoint contentOffsetBoundedInValidRange(UIScrollView *scrollView, CGPoint contentOffset)
790 UIEdgeInsets contentInsets = scrollView.contentInset;
791 CGSize contentSize = scrollView.contentSize;
792 CGSize scrollViewSize = scrollView.bounds.size;
794 CGFloat maxHorizontalOffset = contentSize.width + contentInsets.right - scrollViewSize.width;
795 contentOffset.x = std::min(maxHorizontalOffset, contentOffset.x);
796 contentOffset.x = std::max(-contentInsets.left, contentOffset.x);
798 CGFloat maxVerticalOffset = contentSize.height + contentInsets.bottom - scrollViewSize.height;
799 contentOffset.y = std::min(maxVerticalOffset, contentOffset.y);
800 contentOffset.y = std::max(-contentInsets.top, contentOffset.y);
801 return contentOffset;
804 static void changeContentOffsetBoundedInValidRange(UIScrollView *scrollView, WebCore::FloatPoint contentOffset)
806 scrollView.contentOffset = contentOffsetBoundedInValidRange(scrollView, contentOffset);
809 // WebCore stores the page scale factor as float instead of double. When we get a scale from WebCore,
810 // we need to ignore differences that are within a small rounding error on floats.
811 template <typename TypeA, typename TypeB>
812 static inline bool withinEpsilon(TypeA a, TypeB b)
814 return std::abs(a - b) < std::numeric_limits<float>::epsilon();
817 - (void)_didCommitLayerTree:(const WebKit::RemoteLayerTreeTransaction&)layerTreeTransaction
819 if (_customContentView)
822 if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing) {
823 if (layerTreeTransaction.transactionID() >= _resizeAnimationTransformTransactionID) {
824 [_resizeAnimationView layer].sublayerTransform = _resizeAnimationTransformAdjustments;
825 if (_dynamicViewportUpdateMode == DynamicViewportUpdateMode::ResizingWithDocumentHidden) {
826 [_contentView setHidden:NO];
827 [self _endAnimatedResize];
833 CGSize newContentSize = roundScrollViewContentSize(*_page, [_contentView frame].size);
834 [_scrollView _setContentSizePreservingContentOffsetDuringRubberband:newContentSize];
836 [_scrollView setMinimumZoomScale:layerTreeTransaction.minimumScaleFactor()];
837 [_scrollView setMaximumZoomScale:layerTreeTransaction.maximumScaleFactor()];
838 [_scrollView setZoomEnabled:layerTreeTransaction.allowsUserScaling()];
839 if (!layerTreeTransaction.scaleWasSetByUIProcess() && ![_scrollView isZooming] && ![_scrollView isZoomBouncing] && ![_scrollView _isAnimatingZoom])
840 [_scrollView setZoomScale:layerTreeTransaction.pageScaleFactor()];
842 [self _updateScrollViewBackground];
844 if (_gestureController)
845 _gestureController->setRenderTreeSize(layerTreeTransaction.renderTreeSize());
847 if (_needsToNotifyDelegateAboutMinimalUI || _needsResetViewStateAfterCommitLoadForMainFrame) {
848 _needsToNotifyDelegateAboutMinimalUI = NO;
850 auto delegate = _uiDelegate->delegate();
851 if ([delegate respondsToSelector:@selector(_webView:usesMinimalUI:)])
852 [static_cast<id <WKUIDelegatePrivate>>(delegate.get()) _webView:self usesMinimalUI:_usesMinimalUI];
855 if (_needsResetViewStateAfterCommitLoadForMainFrame && layerTreeTransaction.transactionID() >= _firstPaintAfterCommitLoadTransactionID) {
856 _needsResetViewStateAfterCommitLoadForMainFrame = NO;
857 [_scrollView setContentOffset:[self _adjustedContentOffset:CGPointZero]];
858 [self _updateVisibleContentRects];
861 if (_needsToRestoreExposedRect && layerTreeTransaction.transactionID() >= _firstTransactionIDAfterPageRestore) {
862 _needsToRestoreExposedRect = NO;
864 if (withinEpsilon(contentZoomScale(self), _scaleToRestore)) {
865 WebCore::FloatPoint exposedPosition = _exposedRectToRestore.location();
866 exposedPosition.scale(_scaleToRestore, _scaleToRestore);
868 changeContentOffsetBoundedInValidRange(_scrollView.get(), exposedPosition);
870 [self _updateVisibleContentRects];
873 if (_needsToRestoreUnobscuredCenter && layerTreeTransaction.transactionID() >= _firstTransactionIDAfterPageRestore) {
874 _needsToRestoreUnobscuredCenter = NO;
876 if (withinEpsilon(contentZoomScale(self), _scaleToRestore)) {
877 CGRect unobscuredRect = UIEdgeInsetsInsetRect(self.bounds, _obscuredInsets);
878 WebCore::FloatSize unobscuredContentSizeAtNewScale(unobscuredRect.size.width / _scaleToRestore, unobscuredRect.size.height / _scaleToRestore);
879 WebCore::FloatPoint topLeftInDocumentCoordinate(_unobscuredCenterToRestore.x() - unobscuredContentSizeAtNewScale.width() / 2, _unobscuredCenterToRestore.y() - unobscuredContentSizeAtNewScale.height() / 2);
881 topLeftInDocumentCoordinate.scale(_scaleToRestore, _scaleToRestore);
882 topLeftInDocumentCoordinate.moveBy(WebCore::FloatPoint(-_obscuredInsets.left, -_obscuredInsets.top));
884 changeContentOffsetBoundedInValidRange(_scrollView.get(), topLeftInDocumentCoordinate);
886 [self _updateVisibleContentRects];
890 - (void)_dynamicViewportUpdateChangedTargetToScale:(double)newScale position:(CGPoint)newScrollPosition nextValidLayerTreeTransactionID:(uint64_t)nextValidLayerTreeTransactionID
892 if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing) {
893 CGFloat animatingScaleTarget = [[_resizeAnimationView layer] transform].m11;
894 double currentTargetScale = animatingScaleTarget * [[_contentView layer] transform].m11;
895 double scale = newScale / currentTargetScale;
896 _resizeAnimationTransformAdjustments = CATransform3DMakeScale(scale, scale, 1);
898 CGPoint newContentOffset = [self _adjustedContentOffset:CGPointMake(newScrollPosition.x * newScale, newScrollPosition.y * newScale)];
899 CGPoint currentContentOffset = [_scrollView contentOffset];
901 _resizeAnimationTransformAdjustments.m41 = (currentContentOffset.x - newContentOffset.x) / animatingScaleTarget;
902 _resizeAnimationTransformAdjustments.m42 = (currentContentOffset.y - newContentOffset.y) / animatingScaleTarget;
903 _resizeAnimationTransformTransactionID = nextValidLayerTreeTransactionID;
907 - (void)_restorePageStateToExposedRect:(WebCore::FloatRect)exposedRect scale:(double)scale
909 if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing)
912 if (_customContentView)
915 _needsToRestoreUnobscuredCenter = NO;
916 _needsToRestoreExposedRect = YES;
917 _firstTransactionIDAfterPageRestore = toRemoteLayerTreeDrawingAreaProxy(_page->drawingArea())->nextLayerTreeTransactionID();
918 _exposedRectToRestore = exposedRect;
919 _scaleToRestore = scale;
922 - (void)_restorePageStateToUnobscuredCenter:(WebCore::FloatPoint)center scale:(double)scale
924 if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing)
927 if (_customContentView)
930 _needsToRestoreExposedRect = NO;
931 _needsToRestoreUnobscuredCenter = YES;
932 _firstTransactionIDAfterPageRestore = toRemoteLayerTreeDrawingAreaProxy(_page->drawingArea())->nextLayerTreeTransactionID();
933 _unobscuredCenterToRestore = center;
934 _scaleToRestore = scale;
937 - (PassRefPtr<WebKit::ViewSnapshot>)_takeViewSnapshot
939 float deviceScale = WKGetScreenScaleFactor();
940 CGSize snapshotSize = self.bounds.size;
941 snapshotSize.width *= deviceScale;
942 snapshotSize.height *= deviceScale;
944 uint32_t slotID = [WebKit::ViewSnapshotStore::snapshottingContext() createImageSlot:snapshotSize hasAlpha:YES];
949 CATransform3D transform = CATransform3DMakeScale(deviceScale, deviceScale, 1);
950 CARenderServerCaptureLayerWithTransform(MACH_PORT_NULL, self.layer.context.contextId, (uint64_t)self.layer, slotID, 0, 0, &transform);
952 WebCore::IntSize imageSize = WebCore::expandedIntSize(WebCore::FloatSize(snapshotSize));
953 return WebKit::ViewSnapshot::create(slotID, imageSize, imageSize.width() * imageSize.height() * 4);
956 - (void)_zoomToPoint:(WebCore::FloatPoint)point atScale:(double)scale
958 double maximumZoomDuration = 0.4;
959 double minimumZoomDuration = 0.1;
960 double zoomDurationFactor = 0.3;
962 CGFloat zoomScale = contentZoomScale(self);
963 CFTimeInterval duration = std::min(fabs(log(zoomScale) - log(scale)) * zoomDurationFactor + minimumZoomDuration, maximumZoomDuration);
965 if (scale != zoomScale)
966 _page->willStartUserTriggeredZooming();
968 [_scrollView _zoomToCenter:point scale:scale duration:duration];
971 - (void)_zoomToRect:(WebCore::FloatRect)targetRect atScale:(double)scale origin:(WebCore::FloatPoint)origin
973 // FIMXE: Some of this could be shared with _scrollToRect.
974 const double visibleRectScaleChange = contentZoomScale(self) / scale;
975 const WebCore::FloatRect visibleRect([self convertRect:self.bounds toView:self._currentContentView]);
976 const WebCore::FloatRect unobscuredRect([self _contentRectForUserInteraction]);
978 const WebCore::FloatSize topLeftObscuredInsetAfterZoom((unobscuredRect.minXMinYCorner() - visibleRect.minXMinYCorner()) * visibleRectScaleChange);
979 const WebCore::FloatSize bottomRightObscuredInsetAfterZoom((visibleRect.maxXMaxYCorner() - unobscuredRect.maxXMaxYCorner()) * visibleRectScaleChange);
981 const WebCore::FloatSize unobscuredRectSizeAfterZoom(unobscuredRect.size() * visibleRectScaleChange);
983 // Center to the target rect.
984 WebCore::FloatPoint unobscuredRectLocationAfterZoom = targetRect.location() - (unobscuredRectSizeAfterZoom - targetRect.size()) * 0.5;
986 // Center to the tap point instead in case the target rect won't fit in a direction.
987 if (targetRect.width() > unobscuredRectSizeAfterZoom.width())
988 unobscuredRectLocationAfterZoom.setX(origin.x() - unobscuredRectSizeAfterZoom.width() / 2);
989 if (targetRect.height() > unobscuredRectSizeAfterZoom.height())
990 unobscuredRectLocationAfterZoom.setY(origin.y() - unobscuredRectSizeAfterZoom.height() / 2);
992 // We have computed where we want the unobscured rect to be. Now adjust for the obscuring insets.
993 WebCore::FloatRect visibleRectAfterZoom(unobscuredRectLocationAfterZoom, unobscuredRectSizeAfterZoom);
994 visibleRectAfterZoom.move(-topLeftObscuredInsetAfterZoom);
995 visibleRectAfterZoom.expand(topLeftObscuredInsetAfterZoom + bottomRightObscuredInsetAfterZoom);
997 [self _zoomToPoint:visibleRectAfterZoom.center() atScale:scale];
1000 static WebCore::FloatPoint constrainContentOffset(WebCore::FloatPoint contentOffset, WebCore::FloatSize contentSize, WebCore::FloatSize unobscuredContentSize)
1002 WebCore::FloatSize maximumContentOffset = contentSize - unobscuredContentSize;
1003 contentOffset = contentOffset.shrunkTo(WebCore::FloatPoint(maximumContentOffset.width(), maximumContentOffset.height()));
1004 contentOffset = contentOffset.expandedTo(WebCore::FloatPoint());
1005 return contentOffset;
1008 - (void)_scrollToContentOffset:(WebCore::FloatPoint)contentOffsetInPageCoordinates
1010 if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing)
1013 WebCore::FloatPoint scaledOffset = contentOffsetInPageCoordinates;
1014 CGFloat zoomScale = contentZoomScale(self);
1015 scaledOffset.scale(zoomScale, zoomScale);
1017 CGPoint contentOffsetInScrollViewCoordinates = [self _adjustedContentOffset:scaledOffset];
1018 contentOffsetInScrollViewCoordinates = contentOffsetBoundedInValidRange(_scrollView.get(), contentOffsetInScrollViewCoordinates);
1020 [_scrollView _stopScrollingAndZoomingAnimations];
1022 if (!CGPointEqualToPoint(contentOffsetInScrollViewCoordinates, [_scrollView contentOffset]))
1023 [_scrollView setContentOffset:contentOffsetInScrollViewCoordinates];
1025 // If we haven't changed anything, there would not be any VisibleContentRect update sent to the content.
1026 // The WebProcess would keep the invalid contentOffset as its scroll position.
1027 // To synchronize the WebProcess with what is on screen, we send the VisibleContentRect again.
1028 _page->resendLastVisibleContentRects();
1032 - (BOOL)_scrollToRect:(WebCore::FloatRect)targetRect origin:(WebCore::FloatPoint)origin minimumScrollDistance:(float)minimumScrollDistance
1034 WebCore::FloatRect unobscuredContentRect([self _contentRectForUserInteraction]);
1035 WebCore::FloatPoint unobscuredContentOffset = unobscuredContentRect.location();
1036 WebCore::FloatSize contentSize([self._currentContentView bounds].size);
1038 // Center the target rect in the scroll view.
1039 // If the target doesn't fit in the scroll view, center on the gesture location instead.
1040 WebCore::FloatPoint newUnobscuredContentOffset;
1041 if (targetRect.width() <= unobscuredContentRect.width())
1042 newUnobscuredContentOffset.setX(targetRect.x() - (unobscuredContentRect.width() - targetRect.width()) / 2);
1044 newUnobscuredContentOffset.setX(origin.x() - unobscuredContentRect.width() / 2);
1045 if (targetRect.height() <= unobscuredContentRect.height())
1046 newUnobscuredContentOffset.setY(targetRect.y() - (unobscuredContentRect.height() - targetRect.height()) / 2);
1048 newUnobscuredContentOffset.setY(origin.y() - unobscuredContentRect.height() / 2);
1049 newUnobscuredContentOffset = constrainContentOffset(newUnobscuredContentOffset, contentSize, unobscuredContentRect.size());
1051 if (unobscuredContentOffset == newUnobscuredContentOffset) {
1052 if (targetRect.width() > unobscuredContentRect.width())
1053 newUnobscuredContentOffset.setX(origin.x() - unobscuredContentRect.width() / 2);
1054 if (targetRect.height() > unobscuredContentRect.height())
1055 newUnobscuredContentOffset.setY(origin.y() - unobscuredContentRect.height() / 2);
1056 newUnobscuredContentOffset = constrainContentOffset(newUnobscuredContentOffset, contentSize, unobscuredContentRect.size());
1059 WebCore::FloatSize scrollViewOffsetDelta = newUnobscuredContentOffset - unobscuredContentOffset;
1060 scrollViewOffsetDelta.scale(contentZoomScale(self));
1062 float scrollDistance = scrollViewOffsetDelta.diagonalLength();
1063 if (scrollDistance < minimumScrollDistance)
1066 [_scrollView setContentOffset:([_scrollView contentOffset] + scrollViewOffsetDelta) animated:YES];
1070 - (void)_zoomOutWithOrigin:(WebCore::FloatPoint)origin
1072 [self _zoomToPoint:origin atScale:[_scrollView minimumZoomScale]];
1075 // focusedElementRect and selectionRect are both in document coordinates.
1076 - (void)_zoomToFocusRect:(WebCore::FloatRect)focusedElementRectInDocumentCoordinates selectionRect:(WebCore::FloatRect)selectionRectInDocumentCoordinates fontSize:(float)fontSize minimumScale:(double)minimumScale maximumScale:(double)maximumScale allowScaling:(BOOL)allowScaling forceScroll:(BOOL)forceScroll
1078 const double WKWebViewStandardFontSize = 16;
1079 const double kMinimumHeightToShowContentAboveKeyboard = 106;
1080 const CFTimeInterval UIWebFormAnimationDuration = 0.25;
1081 const double CaretOffsetFromWindowEdge = 20;
1083 // Zoom around the element's bounding frame. We use a "standard" size to determine the proper frame.
1084 double scale = allowScaling ? std::min(std::max(WKWebViewStandardFontSize / fontSize, minimumScale), maximumScale) : contentZoomScale(self);
1085 CGFloat documentWidth = [_contentView bounds].size.width;
1086 scale = CGRound(documentWidth * scale) / documentWidth;
1088 UIWindow *window = [_scrollView window];
1090 WebCore::FloatRect focusedElementRectInNewScale = focusedElementRectInDocumentCoordinates;
1091 focusedElementRectInNewScale.scale(scale);
1092 focusedElementRectInNewScale.moveBy([_contentView frame].origin);
1094 // Find the portion of the view that is visible on the screen.
1095 UIViewController *topViewController = [[[_scrollView _viewControllerForAncestor] _rootAncestorViewController] _viewControllerForSupportedInterfaceOrientations];
1096 UIView *fullScreenView = topViewController.view;
1097 if (!fullScreenView)
1098 fullScreenView = window;
1100 CGRect unobscuredScrollViewRectInWebViewCoordinates = UIEdgeInsetsInsetRect([self bounds], _obscuredInsets);
1101 CGRect visibleScrollViewBoundsInWebViewCoordinates = CGRectIntersection(unobscuredScrollViewRectInWebViewCoordinates, [fullScreenView convertRect:[fullScreenView bounds] toView:self]);
1102 CGRect formAssistantFrameInWebViewCoordinates = [window convertRect:_inputViewBounds toView:self];
1103 CGRect intersectionBetweenScrollViewAndFormAssistant = CGRectIntersection(visibleScrollViewBoundsInWebViewCoordinates, formAssistantFrameInWebViewCoordinates);
1104 CGSize visibleSize = visibleScrollViewBoundsInWebViewCoordinates.size;
1106 CGFloat visibleOffsetFromTop = 0;
1107 if (!CGRectIsEmpty(intersectionBetweenScrollViewAndFormAssistant)) {
1108 CGFloat heightVisibleAboveFormAssistant = CGRectGetMinY(intersectionBetweenScrollViewAndFormAssistant) - CGRectGetMinY(visibleScrollViewBoundsInWebViewCoordinates);
1109 CGFloat heightVisibleBelowFormAssistant = CGRectGetMaxY(visibleScrollViewBoundsInWebViewCoordinates) - CGRectGetMaxY(intersectionBetweenScrollViewAndFormAssistant);
1111 if (heightVisibleAboveFormAssistant >= kMinimumHeightToShowContentAboveKeyboard || heightVisibleBelowFormAssistant < heightVisibleAboveFormAssistant)
1112 visibleSize.height = heightVisibleAboveFormAssistant;
1114 visibleSize.height = heightVisibleBelowFormAssistant;
1115 visibleOffsetFromTop = CGRectGetMaxY(intersectionBetweenScrollViewAndFormAssistant) - CGRectGetMinY(visibleScrollViewBoundsInWebViewCoordinates);
1119 BOOL selectionRectIsNotNull = !selectionRectInDocumentCoordinates.isZero();
1121 CGRect currentlyVisibleRegionInWebViewCoordinates;
1122 currentlyVisibleRegionInWebViewCoordinates.origin = unobscuredScrollViewRectInWebViewCoordinates.origin;
1123 currentlyVisibleRegionInWebViewCoordinates.origin.y += visibleOffsetFromTop;
1124 currentlyVisibleRegionInWebViewCoordinates.size = visibleSize;
1126 // Don't bother scrolling if the entire node is already visible, whether or not we got a selectionRect.
1127 if (CGRectContainsRect(currentlyVisibleRegionInWebViewCoordinates, [self convertRect:focusedElementRectInDocumentCoordinates fromView:_contentView.get()]))
1130 // Don't bother scrolling if we have a valid selectionRect and it is already visible.
1131 if (selectionRectIsNotNull && CGRectContainsRect(currentlyVisibleRegionInWebViewCoordinates, [self convertRect:selectionRectInDocumentCoordinates fromView:_contentView.get()]))
1135 // We want to zoom to the left/top corner of the DOM node, with as much spacing on all sides as we
1136 // can get based on the visible area after zooming (workingFrame). The spacing in either dimension is half the
1137 // difference between the size of the DOM node and the size of the visible frame.
1138 CGFloat horizontalSpaceInWebViewCoordinates = std::max((visibleSize.width - focusedElementRectInNewScale.width()) / 2.0, 0.0);
1139 CGFloat verticalSpaceInWebViewCoordinates = std::max((visibleSize.height - focusedElementRectInNewScale.height()) / 2.0, 0.0);
1142 topLeft.x = focusedElementRectInNewScale.x() - horizontalSpaceInWebViewCoordinates;
1143 topLeft.y = focusedElementRectInNewScale.y() - verticalSpaceInWebViewCoordinates - visibleOffsetFromTop;
1145 CGFloat minimumAllowableHorizontalOffsetInWebViewCoordinates = -INFINITY;
1146 CGFloat minimumAllowableVerticalOffsetInWebViewCoordinates = -INFINITY;
1147 if (selectionRectIsNotNull) {
1148 WebCore::FloatRect selectionRectInNewScale = selectionRectInDocumentCoordinates;
1149 selectionRectInNewScale.scale(scale);
1150 selectionRectInNewScale.moveBy([_contentView frame].origin);
1151 minimumAllowableHorizontalOffsetInWebViewCoordinates = CGRectGetMaxX(selectionRectInNewScale) + CaretOffsetFromWindowEdge - visibleSize.width;
1152 minimumAllowableVerticalOffsetInWebViewCoordinates = CGRectGetMaxY(selectionRectInNewScale) + CaretOffsetFromWindowEdge - visibleSize.height - visibleOffsetFromTop;
1155 WebCore::FloatRect documentBoundsInNewScale = [_contentView bounds];
1156 documentBoundsInNewScale.scale(scale);
1157 documentBoundsInNewScale.moveBy([_contentView frame].origin);
1159 // Constrain the left edge in document coordinates so that:
1160 // - it isn't so small that the scrollVisibleRect isn't visible on the screen
1161 // - it isn't so great that the document's right edge is less than the right edge of the screen
1162 if (selectionRectIsNotNull && topLeft.x < minimumAllowableHorizontalOffsetInWebViewCoordinates)
1163 topLeft.x = minimumAllowableHorizontalOffsetInWebViewCoordinates;
1165 CGFloat maximumAllowableHorizontalOffset = CGRectGetMaxX(documentBoundsInNewScale) - visibleSize.width;
1166 if (topLeft.x > maximumAllowableHorizontalOffset)
1167 topLeft.x = maximumAllowableHorizontalOffset;
1170 // Constrain the top edge in document coordinates so that:
1171 // - it isn't so small that the scrollVisibleRect isn't visible on the screen
1172 // - it isn't so great that the document's bottom edge is higher than the top of the form assistant
1173 if (selectionRectIsNotNull && topLeft.y < minimumAllowableVerticalOffsetInWebViewCoordinates)
1174 topLeft.y = minimumAllowableVerticalOffsetInWebViewCoordinates;
1176 CGFloat maximumAllowableVerticalOffset = CGRectGetMaxY(documentBoundsInNewScale) - visibleSize.height;
1177 if (topLeft.y > maximumAllowableVerticalOffset)
1178 topLeft.y = maximumAllowableVerticalOffset;
1181 WebCore::FloatPoint newCenter = CGPointMake(topLeft.x + unobscuredScrollViewRectInWebViewCoordinates.size.width / 2.0, topLeft.y + unobscuredScrollViewRectInWebViewCoordinates.size.height / 2.0);
1183 if (scale != contentZoomScale(self))
1184 _page->willStartUserTriggeredZooming();
1186 // The newCenter has been computed in the new scale, but _zoomToCenter expected the center to be in the original scale.
1187 newCenter.scale(1 / scale, 1 / scale);
1188 [_scrollView _zoomToCenter:newCenter
1190 duration:UIWebFormAnimationDuration
1194 - (BOOL)_zoomToRect:(WebCore::FloatRect)targetRect withOrigin:(WebCore::FloatPoint)origin fitEntireRect:(BOOL)fitEntireRect minimumScale:(double)minimumScale maximumScale:(double)maximumScale minimumScrollDistance:(float)minimumScrollDistance
1196 const float maximumScaleFactorDeltaForPanScroll = 0.02;
1198 double currentScale = contentZoomScale(self);
1200 WebCore::FloatSize unobscuredContentSize([self _contentRectForUserInteraction].size);
1201 double horizontalScale = unobscuredContentSize.width() * currentScale / targetRect.width();
1202 double verticalScale = unobscuredContentSize.height() * currentScale / targetRect.height();
1204 horizontalScale = std::min(std::max(horizontalScale, minimumScale), maximumScale);
1205 verticalScale = std::min(std::max(verticalScale, minimumScale), maximumScale);
1207 double targetScale = fitEntireRect ? std::min(horizontalScale, verticalScale) : horizontalScale;
1208 if (fabs(targetScale - currentScale) < maximumScaleFactorDeltaForPanScroll) {
1209 if ([self _scrollToRect:targetRect origin:origin minimumScrollDistance:minimumScrollDistance])
1211 } else if (targetScale != currentScale) {
1212 [self _zoomToRect:targetRect atScale:targetScale origin:origin];
1219 - (void)didMoveToWindow
1221 _page->viewStateDidChange(WebCore::ViewState::IsInWindow);
1224 - (void)setOpaque:(BOOL)opaque
1226 BOOL oldOpaque = self.opaque;
1228 [super setOpaque:opaque];
1229 [_contentView setOpaque:opaque];
1231 if (oldOpaque == opaque)
1234 _page->setDrawsBackground(opaque);
1235 [self _updateScrollViewBackground];
1238 - (void)setBackgroundColor:(UIColor *)backgroundColor
1240 [super setBackgroundColor:backgroundColor];
1241 [_contentView setBackgroundColor:backgroundColor];
1244 #pragma mark - UIScrollViewDelegate
1246 - (BOOL)usesStandardContentView
1248 return !_customContentView;
1251 - (CGSize)scrollView:(UIScrollView*)scrollView contentSizeForZoomScale:(CGFloat)scale withProposedSize:(CGSize)proposedSize
1253 return roundScrollViewContentSize(*_page, proposedSize);
1256 - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
1258 ASSERT(_scrollView == scrollView);
1260 if (_customContentView)
1261 return _customContentView.get();
1263 return _contentView.get();
1266 - (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view
1268 if (![self usesStandardContentView])
1271 if (scrollView.pinchGestureRecognizer.state == UIGestureRecognizerStateBegan) {
1272 _page->willStartUserTriggeredZooming();
1273 [_contentView scrollViewWillStartPanOrPinchGesture];
1275 [_contentView willStartZoomOrScroll];
1278 - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
1280 if (![self usesStandardContentView])
1283 if (scrollView.panGestureRecognizer.state == UIGestureRecognizerStateBegan)
1284 [_contentView scrollViewWillStartPanOrPinchGesture];
1285 [_contentView willStartZoomOrScroll];
1286 #if ENABLE(CSS_SCROLL_SNAP) && ENABLE(ASYNC_SCROLLING)
1287 // FIXME: We will want to detect whether snapping will occur before beginning to drag. See WebPageProxy::didCommitLayerTree.
1288 WebKit::RemoteScrollingCoordinatorProxy* coordinator = _page->scrollingCoordinatorProxy();
1289 scrollView.decelerationRate = (coordinator && coordinator->shouldSetScrollViewDecelerationRateFast()) ? UIScrollViewDecelerationRateFast : UIScrollViewDecelerationRateNormal;
1293 - (void)_didFinishScrolling
1295 if (![self usesStandardContentView])
1298 [self _updateVisibleContentRects];
1299 [_contentView didFinishScrolling];
1302 - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
1304 // Work around <rdar://problem/16374753> by avoiding deceleration while
1305 // zooming. We'll animate to the right place once the zoom finishes.
1306 if ([scrollView isZooming])
1307 *targetContentOffset = [scrollView contentOffset];
1308 #if ENABLE(CSS_SCROLL_SNAP) && ENABLE(ASYNC_SCROLLING)
1309 if (WebKit::RemoteScrollingCoordinatorProxy* coordinator = _page->scrollingCoordinatorProxy()) {
1310 // FIXME: Here, I'm finding the maximum horizontal/vertical scroll offsets. There's probably a better way to do this.
1311 CGSize maxScrollOffsets = CGSizeMake(scrollView.contentSize.width - scrollView.bounds.size.width, scrollView.contentSize.height - scrollView.bounds.size.height);
1312 coordinator->adjustTargetContentOffsetForSnapping(maxScrollOffsets, velocity, targetContentOffset);
1317 - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
1319 // If we're decelerating, scroll offset will be updated when scrollViewDidFinishDecelerating: is called.
1321 [self _didFinishScrolling];
1324 - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
1326 [self _didFinishScrolling];
1329 - (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView
1331 [self _didFinishScrolling];
1334 - (void)scrollViewDidScroll:(UIScrollView *)scrollView
1336 if (![self usesStandardContentView])
1337 [_customContentView scrollViewDidScroll:(UIScrollView *)scrollView];
1339 [self _updateVisibleContentRects];
1342 - (void)scrollViewDidZoom:(UIScrollView *)scrollView
1344 [self _updateScrollViewBackground];
1345 [self _updateVisibleContentRects];
1348 - (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale
1350 ASSERT(scrollView == _scrollView);
1351 [self _updateVisibleContentRects];
1352 [_contentView didZoomToScale:scale];
1355 - (void)_frameOrBoundsChanged
1357 CGRect bounds = self.bounds;
1358 [_scrollView setFrame:bounds];
1360 if (_dynamicViewportUpdateMode == DynamicViewportUpdateMode::NotResizing) {
1361 if (!_overridesMinimumLayoutSize)
1362 _page->setViewportConfigurationMinimumLayoutSize(WebCore::FloatSize(bounds.size));
1363 if (!_overridesMinimumLayoutSizeForMinimalUI)
1364 _page->setViewportConfigurationMinimumLayoutSizeForMinimalUI(WebCore::FloatSize(bounds.size));
1365 if (!_overridesMaximumUnobscuredSize)
1366 _page->setMaximumUnobscuredSize(WebCore::FloatSize(bounds.size));
1367 if (WebKit::DrawingAreaProxy* drawingArea = _page->drawingArea())
1368 drawingArea->setSize(WebCore::IntSize(bounds.size), WebCore::IntSize(), WebCore::IntSize());
1371 [_customContentView web_setMinimumSize:bounds.size];
1372 [self _updateVisibleContentRects];
1375 // 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.
1376 - (CGRect)_contentRectForUserInteraction
1378 // FIXME: handle split keyboard.
1379 UIEdgeInsets obscuredInsets = _obscuredInsets;
1380 obscuredInsets.bottom = std::max(_obscuredInsets.bottom, _inputViewBounds.size.height);
1381 CGRect unobscuredRect = UIEdgeInsetsInsetRect(self.bounds, obscuredInsets);
1382 return [self convertRect:unobscuredRect toView:self._currentContentView];
1385 - (void)_updateVisibleContentRects
1387 if (![self usesStandardContentView]) {
1388 [_customContentView web_computedContentInsetDidChange];
1392 if (_delayUpdateVisibleContentRects) {
1393 _hadDelayedUpdateVisibleContentRects = YES;
1397 if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing)
1400 if (_needsResetViewStateAfterCommitLoadForMainFrame)
1403 CGRect fullViewRect = self.bounds;
1404 CGRect visibleRectInContentCoordinates = [self convertRect:fullViewRect toView:_contentView.get()];
1406 CGRect unobscuredRect = UIEdgeInsetsInsetRect(fullViewRect, [self _computedContentInset]);
1407 CGRect unobscuredRectInContentCoordinates = [self convertRect:unobscuredRect toView:_contentView.get()];
1409 CGFloat scaleFactor = contentZoomScale(self);
1411 BOOL isStableState = !(_isChangingObscuredInsetsInteractively || [_scrollView isDragging] || [_scrollView isDecelerating] || [_scrollView isZooming] || [_scrollView isZoomBouncing] || [_scrollView _isAnimatingZoom] || [_scrollView _isScrollingToTop]);
1412 [_contentView didUpdateVisibleRect:visibleRectInContentCoordinates
1413 unobscuredRect:unobscuredRectInContentCoordinates
1414 unobscuredRectInScrollViewCoordinates:unobscuredRect
1415 scale:scaleFactor minimumScale:[_scrollView minimumZoomScale]
1416 inStableState:isStableState isChangingObscuredInsetsInteractively:_isChangingObscuredInsetsInteractively];
1419 - (void)_keyboardChangedWithInfo:(NSDictionary *)keyboardInfo adjustScrollView:(BOOL)adjustScrollView
1421 NSValue *endFrameValue = [keyboardInfo objectForKey:UIKeyboardFrameEndUserInfoKey];
1425 // The keyboard rect is always in screen coordinates. In the view services case the window does not
1426 // have the interface orientation rotation transformation; its host does. So, it makes no sense to
1427 // clip the keyboard rect against its screen.
1428 if ([[self window] _isHostedInAnotherProcess])
1429 _inputViewBounds = [self.window convertRect:[endFrameValue CGRectValue] fromWindow:nil];
1431 _inputViewBounds = [self.window convertRect:CGRectIntersection([endFrameValue CGRectValue], self.window.screen.bounds) fromWindow:nil];
1433 [self _updateVisibleContentRects];
1435 if (adjustScrollView)
1436 [_scrollView _adjustForAutomaticKeyboardInfo:keyboardInfo animated:YES lastAdjustment:&_lastAdjustmentForScroller];
1439 - (void)_keyboardWillChangeFrame:(NSNotification *)notification
1441 if ([_contentView isAssistingNode])
1442 [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES];
1445 - (void)_keyboardDidChangeFrame:(NSNotification *)notification
1447 [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:NO];
1450 - (void)_keyboardWillShow:(NSNotification *)notification
1452 if ([_contentView isAssistingNode])
1453 [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES];
1456 - (void)_keyboardWillHide:(NSNotification *)notification
1458 // Ignore keyboard will hide notifications sent during rotation. They're just there for
1459 // backwards compatibility reasons and processing the will hide notification would
1460 // temporarily screw up the the unobscured view area.
1461 if ([[UIPeripheralHost sharedInstance] rotationState])
1464 [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES];
1467 - (void)_windowDidRotate:(NSNotification *)notification
1469 if (!_overridesInterfaceOrientation)
1470 _page->setDeviceOrientation(deviceOrientation());
1473 - (void)_contentSizeCategoryDidChange:(NSNotification *)notification
1475 _page->contentSizeCategoryDidChange([self _contentSizeCategory]);
1478 - (NSString *)_contentSizeCategory
1480 return [[UIApplication sharedApplication] preferredContentSizeCategory];
1483 - (void)setAllowsBackForwardNavigationGestures:(BOOL)allowsBackForwardNavigationGestures
1485 if (_allowsBackForwardNavigationGestures == allowsBackForwardNavigationGestures)
1488 _allowsBackForwardNavigationGestures = allowsBackForwardNavigationGestures;
1490 if (allowsBackForwardNavigationGestures) {
1491 if (!_gestureController) {
1492 _gestureController = std::make_unique<WebKit::ViewGestureController>(*_page);
1493 _gestureController->installSwipeHandler(self, [self scrollView]);
1494 _gestureController->setAlternateBackForwardListSourceView([_configuration _alternateWebViewForNavigationGestures]);
1497 _gestureController = nullptr;
1499 _page->setShouldRecordNavigationSnapshots(allowsBackForwardNavigationGestures);
1502 - (BOOL)allowsBackForwardNavigationGestures
1504 return _allowsBackForwardNavigationGestures;
1509 #pragma mark OS X-specific methods
1513 - (void)resizeSubviewsWithOldSize:(NSSize)oldSize
1515 [_wkView setFrame:self.bounds];
1518 - (void)setAllowsBackForwardNavigationGestures:(BOOL)allowsBackForwardNavigationGestures
1520 [_wkView setAllowsBackForwardNavigationGestures:allowsBackForwardNavigationGestures];
1523 - (BOOL)allowsBackForwardNavigationGestures
1525 return [_wkView allowsBackForwardNavigationGestures];
1528 - (void)setAllowsMagnification:(BOOL)allowsMagnification
1530 [_wkView setAllowsMagnification:allowsMagnification];
1533 - (BOOL)allowsMagnification
1535 return [_wkView allowsMagnification];
1538 - (void)setMagnification:(CGFloat)magnification
1540 [_wkView setMagnification:magnification];
1543 - (CGFloat)magnification
1545 return [_wkView magnification];
1548 - (void)setMagnification:(CGFloat)magnification centeredAtPoint:(CGPoint)point
1550 [_wkView setMagnification:magnification centeredAtPoint:NSPointFromCGPoint(point)];
1557 @implementation WKWebView (WKPrivate)
1559 - (_WKRemoteObjectRegistry *)_remoteObjectRegistry
1561 if (!_remoteObjectRegistry) {
1562 _remoteObjectRegistry = adoptNS([[_WKRemoteObjectRegistry alloc] _initWithMessageSender:*_page]);
1563 _page->process().context().addMessageReceiver(Messages::RemoteObjectRegistry::messageReceiverName(), _page->pageID(), [_remoteObjectRegistry remoteObjectRegistry]);
1566 return _remoteObjectRegistry.get();
1569 - (WKBrowsingContextHandle *)_handle
1571 return [[[WKBrowsingContextHandle alloc] _initWithPageID:_page->pageID()] autorelease];
1574 - (_WKRenderingProgressEvents)_observedRenderingProgressEvents
1576 return _observedRenderingProgressEvents;
1579 - (id <WKHistoryDelegatePrivate>)_historyDelegate
1581 return _navigationState->historyDelegate().autorelease();
1584 - (void)_setHistoryDelegate:(id <WKHistoryDelegatePrivate>)historyDelegate
1586 _navigationState->setHistoryDelegate(historyDelegate);
1589 - (NSURL *)_unreachableURL
1591 return [NSURL _web_URLWithWTFString:_page->pageLoadState().unreachableURL()];
1594 - (void)_loadAlternateHTMLString:(NSString *)string baseURL:(NSURL *)baseURL forUnreachableURL:(NSURL *)unreachableURL
1596 _page->loadAlternateHTMLString(string, [baseURL _web_originalDataAsWTFString], [unreachableURL _web_originalDataAsWTFString]);
1599 - (NSArray *)_certificateChain
1601 if (WebKit::WebFrameProxy* mainFrame = _page->mainFrame())
1602 return mainFrame->certificateInfo() ? (NSArray *)mainFrame->certificateInfo()->certificateInfo().certificateChain() : nil;
1607 - (NSURL *)_committedURL
1609 return [NSURL _web_URLWithWTFString:_page->pageLoadState().url()];
1612 - (NSString *)_MIMEType
1614 if (_page->mainFrame())
1615 return _page->mainFrame()->mimeType();
1620 - (NSString *)_userAgent
1622 return _page->userAgent();
1625 - (NSString *)_applicationNameForUserAgent
1627 return _page->applicationNameForUserAgent();
1630 - (void)_setApplicationNameForUserAgent:(NSString *)applicationNameForUserAgent
1632 _page->setApplicationNameForUserAgent(applicationNameForUserAgent);
1635 - (NSString *)_customUserAgent
1637 return _page->customUserAgent();
1640 - (void)_setCustomUserAgent:(NSString *)_customUserAgent
1642 _page->setCustomUserAgent(_customUserAgent);
1645 - (pid_t)_webProcessIdentifier
1647 return _page->isValid() ? _page->processIdentifier() : 0;
1650 - (void)_killWebContentProcess
1652 if (!_page->isValid())
1655 _page->process().terminate();
1659 static WebCore::FloatSize activeMinimumLayoutSize(WKWebView *webView, const CGRect& bounds)
1661 return WebCore::FloatSize(webView->_overridesMinimumLayoutSize ? webView->_minimumLayoutSizeOverride : bounds.size);
1664 static WebCore::FloatSize activeMinimumLayoutSizeForMinimalUI(WKWebView *webView, WebCore::FloatSize minimumLayoutSize)
1666 return webView->_overridesMinimumLayoutSizeForMinimalUI ? WebCore::FloatSize(webView->_minimumLayoutSizeOverrideForMinimalUI) : minimumLayoutSize;
1669 static WebCore::FloatSize activeMaximumUnobscuredSize(WKWebView *webView, const CGRect& bounds)
1671 return WebCore::FloatSize(webView->_overridesMaximumUnobscuredSize ? webView->_maximumUnobscuredSizeOverride : bounds.size);
1674 static int32_t activeOrientation(WKWebView *webView)
1676 return webView->_overridesInterfaceOrientation ? deviceOrientationForUIInterfaceOrientation(webView->_interfaceOrientationOverride) : webView->_page->deviceOrientation();
1680 - (void)_didRelaunchProcess
1683 CGRect bounds = self.bounds;
1684 WebCore::FloatSize minimalLayoutSize = activeMinimumLayoutSize(self, bounds);
1685 _page->setViewportConfigurationMinimumLayoutSize(minimalLayoutSize);
1686 _page->setViewportConfigurationMinimumLayoutSizeForMinimalUI(activeMinimumLayoutSizeForMinimalUI(self, minimalLayoutSize));
1687 _page->setMaximumUnobscuredSize(activeMaximumUnobscuredSize(self, bounds));
1691 - (NSData *)_sessionStateData
1693 WebKit::SessionState sessionState = _page->sessionState();
1695 // FIXME: This should not use the legacy session state encoder.
1696 return [wrapper(*WebKit::encodeLegacySessionState(sessionState).release().leakRef()) autorelease];
1699 - (_WKSessionState *)_sessionState
1701 return adoptNS([[_WKSessionState alloc] _initWithSessionState:_page->sessionState()]).autorelease();
1704 - (void)_restoreFromSessionStateData:(NSData *)sessionStateData
1706 // FIXME: This should not use the legacy session state decoder.
1707 WebKit::SessionState sessionState;
1708 if (!WebKit::decodeLegacySessionState(static_cast<const uint8_t*>(sessionStateData.bytes), sessionStateData.length, sessionState))
1711 if (uint64_t navigationID = _page->restoreFromSessionState(WTF::move(sessionState), true)) {
1712 // FIXME: This is not necessarily always a reload navigation.
1713 _navigationState->createReloadNavigation(navigationID);
1717 - (WKNavigation *)_restoreSessionState:(_WKSessionState *)sessionState andNavigate:(BOOL)navigate
1719 if (uint64_t navigationID = _page->restoreFromSessionState(sessionState->_sessionState, navigate)) {
1720 // FIXME: This is not necessarily always a reload navigation.
1721 return _navigationState->createReloadNavigation(navigationID).autorelease();
1732 - (BOOL)_allowsRemoteInspection
1734 #if ENABLE(REMOTE_INSPECTOR)
1735 return _page->allowsRemoteInspection();
1741 - (void)_setAllowsRemoteInspection:(BOOL)allow
1743 #if ENABLE(REMOTE_INSPECTOR)
1744 _page->setAllowsRemoteInspection(allow);
1748 - (BOOL)_addsVisitedLinks
1750 return _page->addsVisitedLinks();
1753 - (void)_setAddsVisitedLinks:(BOOL)addsVisitedLinks
1755 _page->setAddsVisitedLinks(addsVisitedLinks);
1758 - (BOOL)_networkRequestsInProgress
1760 return _page->pageLoadState().networkRequestsInProgress();
1763 static inline WebCore::LayoutMilestones layoutMilestones(_WKRenderingProgressEvents events)
1765 WebCore::LayoutMilestones milestones = 0;
1767 if (events & _WKRenderingProgressEventFirstLayout)
1768 milestones |= WebCore::DidFirstLayout;
1770 if (events & _WKRenderingProgressEventFirstPaintWithSignificantArea)
1771 milestones |= WebCore::DidHitRelevantRepaintedObjectsAreaThreshold;
1776 - (void)_setObservedRenderingProgressEvents:(_WKRenderingProgressEvents)observedRenderingProgressEvents
1778 _observedRenderingProgressEvents = observedRenderingProgressEvents;
1779 _page->listenForLayoutMilestones(layoutMilestones(observedRenderingProgressEvents));
1782 - (void)_getMainResourceDataWithCompletionHandler:(void (^)(NSData *, NSError *))completionHandler
1784 auto handler = adoptNS([completionHandler copy]);
1786 _page->getMainResourceDataOfFrame(_page->mainFrame(), [handler](API::Data* data, WebKit::CallbackBase::Error error) {
1787 void (^completionHandlerBlock)(NSData *, NSError *) = (void (^)(NSData *, NSError *))handler.get();
1788 if (error != WebKit::CallbackBase::Error::None) {
1789 // FIXME: Pipe a proper error in from the WebPageProxy.
1790 RetainPtr<NSError> error = adoptNS([[NSError alloc] init]);
1791 completionHandlerBlock(nil, error.get());
1793 completionHandlerBlock(wrapper(*data), nil);
1797 - (void)_getWebArchiveDataWithCompletionHandler:(void (^)(NSData *, NSError *))completionHandler
1799 auto handler = adoptNS([completionHandler copy]);
1801 _page->getWebArchiveOfFrame(_page->mainFrame(), [handler](API::Data* data, WebKit::CallbackBase::Error error) {
1802 void (^completionHandlerBlock)(NSData *, NSError *) = (void (^)(NSData *, NSError *))handler.get();
1803 if (error != WebKit::CallbackBase::Error::None) {
1804 // FIXME: Pipe a proper error in from the WebPageProxy.
1805 RetainPtr<NSError> error = adoptNS([[NSError alloc] init]);
1806 completionHandlerBlock(nil, error.get());
1808 completionHandlerBlock(wrapper(*data), nil);
1812 - (_WKPaginationMode)_paginationMode
1814 switch (_page->paginationMode()) {
1815 case WebCore::Pagination::Unpaginated:
1816 return _WKPaginationModeUnpaginated;
1817 case WebCore::Pagination::LeftToRightPaginated:
1818 return _WKPaginationModeLeftToRight;
1819 case WebCore::Pagination::RightToLeftPaginated:
1820 return _WKPaginationModeRightToLeft;
1821 case WebCore::Pagination::TopToBottomPaginated:
1822 return _WKPaginationModeTopToBottom;
1823 case WebCore::Pagination::BottomToTopPaginated:
1824 return _WKPaginationModeBottomToTop;
1827 ASSERT_NOT_REACHED();
1828 return _WKPaginationModeUnpaginated;
1831 - (void)_setPaginationMode:(_WKPaginationMode)paginationMode
1833 WebCore::Pagination::Mode mode;
1834 switch (paginationMode) {
1835 case _WKPaginationModeUnpaginated:
1836 mode = WebCore::Pagination::Unpaginated;
1838 case _WKPaginationModeLeftToRight:
1839 mode = WebCore::Pagination::LeftToRightPaginated;
1841 case _WKPaginationModeRightToLeft:
1842 mode = WebCore::Pagination::RightToLeftPaginated;
1844 case _WKPaginationModeTopToBottom:
1845 mode = WebCore::Pagination::TopToBottomPaginated;
1847 case _WKPaginationModeBottomToTop:
1848 mode = WebCore::Pagination::BottomToTopPaginated;
1854 _page->setPaginationMode(mode);
1857 - (BOOL)_paginationBehavesLikeColumns
1859 return _page->paginationBehavesLikeColumns();
1862 - (void)_setPaginationBehavesLikeColumns:(BOOL)behavesLikeColumns
1864 _page->setPaginationBehavesLikeColumns(behavesLikeColumns);
1867 - (CGFloat)_pageLength
1869 return _page->pageLength();
1872 - (void)_setPageLength:(CGFloat)pageLength
1874 _page->setPageLength(pageLength);
1877 - (CGFloat)_gapBetweenPages
1879 return _page->gapBetweenPages();
1882 - (void)_setGapBetweenPages:(CGFloat)gapBetweenPages
1884 _page->setGapBetweenPages(gapBetweenPages);
1887 - (NSUInteger)_pageCount
1889 return _page->pageCount();
1892 - (BOOL)_supportsTextZoom
1894 return _page->supportsTextZoom();
1897 - (double)_textZoomFactor
1899 return _page->textZoomFactor();
1902 - (void)_setTextZoomFactor:(double)zoomFactor
1904 _page->setTextZoomFactor(zoomFactor);
1907 - (double)_pageZoomFactor
1909 return _page->pageZoomFactor();
1912 - (void)_setPageZoomFactor:(double)zoomFactor
1914 _page->setPageZoomFactor(zoomFactor);
1917 - (id <_WKFindDelegate>)_findDelegate
1919 return [static_cast<WebKit::FindClient&>(_page->findClient()).delegate().leakRef() autorelease];
1922 - (void)_setFindDelegate:(id<_WKFindDelegate>)findDelegate
1924 static_cast<WebKit::FindClient&>(_page->findClient()).setDelegate(findDelegate);
1927 static inline WebKit::FindOptions toFindOptions(_WKFindOptions wkFindOptions)
1929 unsigned findOptions = 0;
1931 if (wkFindOptions & _WKFindOptionsCaseInsensitive)
1932 findOptions |= WebKit::FindOptionsCaseInsensitive;
1933 if (wkFindOptions & _WKFindOptionsAtWordStarts)
1934 findOptions |= WebKit::FindOptionsAtWordStarts;
1935 if (wkFindOptions & _WKFindOptionsTreatMedialCapitalAsWordStart)
1936 findOptions |= WebKit::FindOptionsTreatMedialCapitalAsWordStart;
1937 if (wkFindOptions & _WKFindOptionsBackwards)
1938 findOptions |= WebKit::FindOptionsBackwards;
1939 if (wkFindOptions & _WKFindOptionsWrapAround)
1940 findOptions |= WebKit::FindOptionsWrapAround;
1941 if (wkFindOptions & _WKFindOptionsShowOverlay)
1942 findOptions |= WebKit::FindOptionsShowOverlay;
1943 if (wkFindOptions & _WKFindOptionsShowFindIndicator)
1944 findOptions |= WebKit::FindOptionsShowFindIndicator;
1945 if (wkFindOptions & _WKFindOptionsShowHighlight)
1946 findOptions |= WebKit::FindOptionsShowHighlight;
1947 if (wkFindOptions & _WKFindOptionsDetermineMatchIndex)
1948 findOptions |= WebKit::FindOptionsDetermineMatchIndex;
1950 return static_cast<WebKit::FindOptions>(findOptions);
1953 - (void)_countStringMatches:(NSString *)string options:(_WKFindOptions)options maxCount:(NSUInteger)maxCount
1955 _page->countStringMatches(string, toFindOptions(options), maxCount);
1958 - (void)_findString:(NSString *)string options:(_WKFindOptions)options maxCount:(NSUInteger)maxCount
1960 _page->findString(string, toFindOptions(options), maxCount);
1965 _page->hideFindUI();
1968 - (id <_WKFormDelegate>)_formDelegate
1970 return _formDelegate.getAutoreleased();
1973 - (void)_setFormDelegate:(id <_WKFormDelegate>)formDelegate
1975 _formDelegate = formDelegate;
1977 class FormClient : public API::FormClient {
1979 explicit FormClient(WKWebView *webView)
1980 : m_webView(webView)
1984 virtual ~FormClient() { }
1986 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
1988 if (userData && userData->type() != API::Object::Type::Data) {
1989 ASSERT(!userData || userData->type() == API::Object::Type::Data);
1990 m_webView->_page->process().connection()->markCurrentlyDispatchedMessageAsInvalid();
1994 auto formDelegate = m_webView->_formDelegate.get();
1996 if (![formDelegate respondsToSelector:@selector(_webView:willSubmitFormValues:userObject:submissionHandler:)])
1999 auto valueMap = adoptNS([[NSMutableDictionary alloc] initWithCapacity:textFieldValues.size()]);
2000 for (const auto& pair : textFieldValues)
2001 [valueMap setObject:pair.second forKey:pair.first];
2003 NSObject <NSSecureCoding> *userObject = nil;
2004 if (API::Data* data = static_cast<API::Data*>(userData)) {
2005 auto nsData = adoptNS([[NSData alloc] initWithBytesNoCopy:const_cast<void*>(static_cast<const void*>(data->bytes())) length:data->size() freeWhenDone:NO]);
2006 auto unarchiver = adoptNS([[NSKeyedUnarchiver alloc] initForReadingWithData:nsData.get()]);
2007 [unarchiver setRequiresSecureCoding:YES];
2009 userObject = [unarchiver decodeObjectOfClass:[NSObject class] forKey:@"userObject"];
2010 } @catch (NSException *exception) {
2011 LOG_ERROR("Failed to decode user data: %@", exception);
2015 RefPtr<WebKit::CompletionHandlerCallChecker> checker = WebKit::CompletionHandlerCallChecker::create(formDelegate.get(), @selector(_webView:willSubmitFormValues:userObject:submissionHandler:));
2016 [formDelegate _webView:m_webView willSubmitFormValues:valueMap.get() userObject:userObject submissionHandler:[listener, checker] {
2017 checker->didCallCompletionHandler();
2018 listener->continueSubmission();
2024 WKWebView *m_webView;
2028 _page->setFormClient(std::make_unique<FormClient>(self));
2030 _page->setFormClient(nullptr);
2033 - (BOOL)_isDisplayingStandaloneImageDocument
2035 if (auto* mainFrame = _page->mainFrame())
2036 return mainFrame->isDisplayingStandaloneImageDocument();
2040 - (BOOL)_isShowingNavigationGestureSnapshot
2042 return _page->isShowingNavigationGestureSnapshot();
2045 #pragma mark iOS-specific methods
2049 - (CGSize)_minimumLayoutSizeOverride
2051 ASSERT(_overridesMinimumLayoutSize);
2052 return _minimumLayoutSizeOverride;
2055 - (void)_setMinimumLayoutSizeOverride:(CGSize)minimumLayoutSizeOverride
2057 _overridesMinimumLayoutSize = YES;
2058 if (CGSizeEqualToSize(_minimumLayoutSizeOverride, minimumLayoutSizeOverride))
2061 _minimumLayoutSizeOverride = minimumLayoutSizeOverride;
2062 if (_dynamicViewportUpdateMode == DynamicViewportUpdateMode::NotResizing)
2063 _page->setViewportConfigurationMinimumLayoutSize(WebCore::FloatSize(minimumLayoutSizeOverride));
2066 - (CGSize)_minimumLayoutSizeOverrideForMinimalUI
2068 ASSERT(_overridesMinimumLayoutSizeForMinimalUI);
2069 return _minimumLayoutSizeOverrideForMinimalUI;
2072 - (void)_setMinimumLayoutSizeOverrideForMinimalUI:(CGSize)size
2074 _overridesMinimumLayoutSizeForMinimalUI = YES;
2075 if (CGSizeEqualToSize(_minimumLayoutSizeOverrideForMinimalUI, size))
2078 _minimumLayoutSizeOverrideForMinimalUI = size;
2079 if (_dynamicViewportUpdateMode == DynamicViewportUpdateMode::NotResizing)
2080 _page->setViewportConfigurationMinimumLayoutSizeForMinimalUI(WebCore::FloatSize(size));
2083 - (UIEdgeInsets)_obscuredInsets
2085 return _obscuredInsets;
2088 - (void)_setObscuredInsets:(UIEdgeInsets)obscuredInsets
2090 ASSERT(obscuredInsets.top >= 0);
2091 ASSERT(obscuredInsets.left >= 0);
2092 ASSERT(obscuredInsets.bottom >= 0);
2093 ASSERT(obscuredInsets.right >= 0);
2095 _haveSetObscuredInsets = YES;
2097 if (UIEdgeInsetsEqualToEdgeInsets(_obscuredInsets, obscuredInsets))
2100 _obscuredInsets = obscuredInsets;
2102 [self _updateVisibleContentRects];
2105 - (void)_setInterfaceOrientationOverride:(UIInterfaceOrientation)interfaceOrientation
2107 if (!_overridesInterfaceOrientation)
2108 [[NSNotificationCenter defaultCenter] removeObserver:self name:UIWindowDidRotateNotification object:nil];
2110 _overridesInterfaceOrientation = YES;
2112 if (interfaceOrientation == _interfaceOrientationOverride)
2115 _interfaceOrientationOverride = interfaceOrientation;
2117 if (_dynamicViewportUpdateMode == DynamicViewportUpdateMode::NotResizing)
2118 _page->setDeviceOrientation(deviceOrientationForUIInterfaceOrientation(_interfaceOrientationOverride));
2121 - (UIInterfaceOrientation)_interfaceOrientationOverride
2123 ASSERT(_overridesInterfaceOrientation);
2124 return _interfaceOrientationOverride;
2127 - (CGSize)_maximumUnobscuredSizeOverride
2129 ASSERT(_overridesMaximumUnobscuredSize);
2130 return _maximumUnobscuredSizeOverride;
2133 - (void)_setMaximumUnobscuredSizeOverride:(CGSize)size
2135 ASSERT(size.width <= self.bounds.size.width && size.height <= self.bounds.size.height);
2136 _overridesMaximumUnobscuredSize = YES;
2137 if (CGSizeEqualToSize(_maximumUnobscuredSizeOverride, size))
2140 _maximumUnobscuredSizeOverride = size;
2141 if (_dynamicViewportUpdateMode == DynamicViewportUpdateMode::NotResizing)
2142 _page->setMaximumUnobscuredSize(WebCore::FloatSize(size));
2145 - (void)_setBackgroundExtendsBeyondPage:(BOOL)backgroundExtends
2147 _page->setBackgroundExtendsBeyondPage(backgroundExtends);
2150 - (BOOL)_backgroundExtendsBeyondPage
2152 return _page->backgroundExtendsBeyondPage();
2155 - (void)_beginInteractiveObscuredInsetsChange
2157 ASSERT(!_isChangingObscuredInsetsInteractively);
2158 _isChangingObscuredInsetsInteractively = YES;
2161 - (void)_endInteractiveObscuredInsetsChange
2163 ASSERT(_isChangingObscuredInsetsInteractively);
2164 _isChangingObscuredInsetsInteractively = NO;
2165 [self _updateVisibleContentRects];
2168 - (void)_beginAnimatedResizeWithUpdates:(void (^)(void))updateBlock
2170 if (_customContentView || !_hasCommittedLoadForMainFrame) {
2175 _dynamicViewportUpdateMode = DynamicViewportUpdateMode::ResizingWithAnimation;
2177 CGRect oldBounds = self.bounds;
2178 WebCore::FloatSize oldMinimumLayoutSize = activeMinimumLayoutSize(self, oldBounds);
2179 WebCore::FloatSize oldMinimumLayoutSizeForMinimalUI = activeMinimumLayoutSizeForMinimalUI(self, oldMinimumLayoutSize);
2180 WebCore::FloatSize oldMaximumUnobscuredSize = activeMaximumUnobscuredSize(self, oldBounds);
2181 int32_t oldOrientation = activeOrientation(self);
2182 UIEdgeInsets oldObscuredInsets = _obscuredInsets;
2183 WebCore::FloatRect oldUnobscuredContentRect = _page->unobscuredContentRect();
2187 CGRect newBounds = self.bounds;
2188 WebCore::FloatSize newMinimumLayoutSize = activeMinimumLayoutSize(self, newBounds);
2189 WebCore::FloatSize newMinimumLayoutSizeForMinimalUI = activeMinimumLayoutSizeForMinimalUI(self, newMinimumLayoutSize);
2190 WebCore::FloatSize newMaximumUnobscuredSize = activeMaximumUnobscuredSize(self, newBounds);
2191 int32_t newOrientation = activeOrientation(self);
2192 UIEdgeInsets newObscuredInsets = _obscuredInsets;
2194 if (CGRectEqualToRect(oldBounds, newBounds)
2195 && oldMinimumLayoutSize == newMinimumLayoutSize
2196 && oldMinimumLayoutSizeForMinimalUI == newMinimumLayoutSizeForMinimalUI
2197 && oldMaximumUnobscuredSize == newMaximumUnobscuredSize
2198 && oldOrientation == newOrientation
2199 && UIEdgeInsetsEqualToEdgeInsets(oldObscuredInsets, newObscuredInsets)) {
2200 _dynamicViewportUpdateMode = DynamicViewportUpdateMode::NotResizing;
2201 [self _updateVisibleContentRects];
2205 _resizeAnimationTransformAdjustments = CATransform3DIdentity;
2207 NSUInteger indexOfContentView = [[_scrollView subviews] indexOfObject:_contentView.get()];
2208 _resizeAnimationView = adoptNS([[UIView alloc] init]);
2209 [_scrollView insertSubview:_resizeAnimationView.get() atIndex:indexOfContentView];
2210 [_resizeAnimationView addSubview:_contentView.get()];
2211 [_resizeAnimationView addSubview:[_contentView unscaledView]];
2213 CGSize contentSizeInContentViewCoordinates = [_contentView bounds].size;
2214 [_scrollView setMinimumZoomScale:std::min(newMinimumLayoutSize.width() / contentSizeInContentViewCoordinates.width, [_scrollView minimumZoomScale])];
2215 [_scrollView setMaximumZoomScale:std::max(newMinimumLayoutSize.width() / contentSizeInContentViewCoordinates.width, [_scrollView maximumZoomScale])];
2217 // Compute the new scale to keep the current content width in the scrollview.
2218 CGFloat oldWebViewWidthInContentViewCoordinates = oldUnobscuredContentRect.width();
2219 CGFloat visibleContentViewWidthInContentCoordinates = std::min(contentSizeInContentViewCoordinates.width, oldWebViewWidthInContentViewCoordinates);
2220 CGFloat targetScale = newMinimumLayoutSize.width() / visibleContentViewWidthInContentCoordinates;
2221 CGFloat resizeAnimationViewAnimationScale = targetScale / contentZoomScale(self);
2222 [_resizeAnimationView setTransform:CGAffineTransformMakeScale(resizeAnimationViewAnimationScale, resizeAnimationViewAnimationScale)];
2224 // Compute a new position to keep the content centered.
2225 CGPoint originalContentCenter = oldUnobscuredContentRect.center();
2226 CGPoint originalContentCenterInSelfCoordinates = [self convertPoint:originalContentCenter fromView:_contentView.get()];
2227 CGRect futureUnobscuredRectInSelfCoordinates = UIEdgeInsetsInsetRect(newBounds, _obscuredInsets);
2228 CGPoint futureUnobscuredRectCenterInSelfCoordinates = CGPointMake(futureUnobscuredRectInSelfCoordinates.origin.x + futureUnobscuredRectInSelfCoordinates.size.width / 2, futureUnobscuredRectInSelfCoordinates.origin.y + futureUnobscuredRectInSelfCoordinates.size.height / 2);
2230 CGPoint originalContentOffset = [_scrollView contentOffset];
2231 CGPoint contentOffset = originalContentOffset;
2232 contentOffset.x += (originalContentCenterInSelfCoordinates.x - futureUnobscuredRectCenterInSelfCoordinates.x);
2233 contentOffset.y += (originalContentCenterInSelfCoordinates.y - futureUnobscuredRectCenterInSelfCoordinates.y);
2235 // Limit the new offset within the scrollview, we do not want to rubber band programmatically.
2236 CGSize futureContentSizeInSelfCoordinates = CGSizeMake(contentSizeInContentViewCoordinates.width * targetScale, contentSizeInContentViewCoordinates.height * targetScale);
2237 CGFloat maxHorizontalOffset = futureContentSizeInSelfCoordinates.width - newBounds.size.width + _obscuredInsets.right;
2238 contentOffset.x = std::min(contentOffset.x, maxHorizontalOffset);
2239 CGFloat maxVerticalOffset = futureContentSizeInSelfCoordinates.height - newBounds.size.height + _obscuredInsets.bottom;
2240 contentOffset.y = std::min(contentOffset.y, maxVerticalOffset);
2242 contentOffset.x = std::max(contentOffset.x, -_obscuredInsets.left);
2243 contentOffset.y = std::max(contentOffset.y, -_obscuredInsets.top);
2245 // Make the top/bottom edges "sticky" within 1 pixel.
2246 if (oldUnobscuredContentRect.maxY() > contentSizeInContentViewCoordinates.height - 1)
2247 contentOffset.y = maxVerticalOffset;
2248 if (oldUnobscuredContentRect.y() < 1)
2249 contentOffset.y = -_obscuredInsets.top;
2251 // FIXME: if we have content centered after double tap to zoom, we should also try to keep that rect in view.
2252 [_scrollView setContentSize:roundScrollViewContentSize(*_page, futureContentSizeInSelfCoordinates)];
2253 [_scrollView setContentOffset:contentOffset];
2255 CGRect visibleRectInContentCoordinates = [self convertRect:newBounds toView:_contentView.get()];
2256 CGRect unobscuredRectInContentCoordinates = [self convertRect:futureUnobscuredRectInSelfCoordinates toView:_contentView.get()];
2258 _page->dynamicViewportSizeUpdate(newMinimumLayoutSize, newMinimumLayoutSizeForMinimalUI, newMaximumUnobscuredSize, visibleRectInContentCoordinates, unobscuredRectInContentCoordinates, futureUnobscuredRectInSelfCoordinates, targetScale, newOrientation);
2259 if (WebKit::DrawingAreaProxy* drawingArea = _page->drawingArea())
2260 drawingArea->setSize(WebCore::IntSize(newBounds.size), WebCore::IntSize(), WebCore::IntSize());
2263 - (void)_endAnimatedResize
2265 if (_dynamicViewportUpdateMode == DynamicViewportUpdateMode::NotResizing)
2268 _page->synchronizeDynamicViewportUpdate();
2270 NSUInteger indexOfResizeAnimationView = [[_scrollView subviews] indexOfObject:_resizeAnimationView.get()];
2271 [_scrollView insertSubview:_contentView.get() atIndex:indexOfResizeAnimationView];
2272 [_scrollView insertSubview:[_contentView unscaledView] atIndex:indexOfResizeAnimationView + 1];
2274 CALayer *contentViewLayer = [_contentView layer];
2275 CGFloat adjustmentScale = _resizeAnimationTransformAdjustments.m11;
2276 contentViewLayer.sublayerTransform = CATransform3DIdentity;
2278 CGFloat animatingScaleTarget = [[_resizeAnimationView layer] transform].m11;
2279 CALayer *contentLayer = [_contentView layer];
2280 CATransform3D contentLayerTransform = contentLayer.transform;
2281 CGFloat currentScale = [[_resizeAnimationView layer] transform].m11 * contentLayerTransform.m11;
2283 // We cannot use [UIScrollView setZoomScale:] directly because the UIScrollView delegate would get a callback with
2284 // an invalid contentOffset. The real content offset is only set below.
2285 // Since there is no public API for setting both the zoomScale and the contentOffset, we set the zoomScale manually
2286 // on the zoom layer and then only change the contentOffset.
2287 CGFloat adjustedScale = adjustmentScale * currentScale;
2288 contentLayerTransform.m11 = adjustedScale;
2289 contentLayerTransform.m22 = adjustedScale;
2290 contentLayer.transform = contentLayerTransform;
2292 CGPoint currentScrollOffset = [_scrollView contentOffset];
2293 double horizontalScrollAdjustement = _resizeAnimationTransformAdjustments.m41 * animatingScaleTarget;
2294 double verticalScrollAdjustment = _resizeAnimationTransformAdjustments.m42 * animatingScaleTarget;
2296 [_scrollView setContentSize:roundScrollViewContentSize(*_page, [_contentView frame].size)];
2297 [_scrollView setContentOffset:CGPointMake(currentScrollOffset.x - horizontalScrollAdjustement, currentScrollOffset.y - verticalScrollAdjustment)];
2299 [_resizeAnimationView removeFromSuperview];
2300 _resizeAnimationView = nil;
2301 _resizeAnimationTransformAdjustments = CATransform3DIdentity;
2303 _dynamicViewportUpdateMode = DynamicViewportUpdateMode::NotResizing;
2304 [_contentView setHidden:NO];
2305 [self _updateVisibleContentRects];
2308 - (void)_resizeWhileHidingContentWithUpdates:(void (^)(void))updateBlock
2310 [self _beginAnimatedResizeWithUpdates:updateBlock];
2311 if (_dynamicViewportUpdateMode == DynamicViewportUpdateMode::ResizingWithAnimation) {
2312 [_contentView setHidden:YES];
2313 _dynamicViewportUpdateMode = DynamicViewportUpdateMode::ResizingWithDocumentHidden;
2317 - (void)_setOverlaidAccessoryViewsInset:(CGSize)inset
2319 [_customContentView web_setOverlaidAccessoryViewsInset:inset];
2322 - (void)_snapshotRect:(CGRect)rectInViewCoordinates intoImageOfWidth:(CGFloat)imageWidth completionHandler:(void(^)(CGImageRef))completionHandler
2324 CGRect snapshotRectInContentCoordinates = [self convertRect:rectInViewCoordinates toView:_contentView.get()];
2325 CGFloat imageHeight = imageWidth / snapshotRectInContentCoordinates.size.width * snapshotRectInContentCoordinates.size.height;
2326 CGSize imageSize = CGSizeMake(imageWidth, imageHeight);
2328 void(^copiedCompletionHandler)(CGImageRef) = [completionHandler copy];
2329 _page->takeSnapshot(WebCore::enclosingIntRect(snapshotRectInContentCoordinates), WebCore::expandedIntSize(WebCore::FloatSize(imageSize)), WebKit::SnapshotOptionsExcludeDeviceScaleFactor, [=](const WebKit::ShareableBitmap::Handle& imageHandle, WebKit::CallbackBase::Error) {
2330 if (imageHandle.isNull()) {
2331 copiedCompletionHandler(nullptr);
2332 [copiedCompletionHandler release];
2336 RefPtr<WebKit::ShareableBitmap> bitmap = WebKit::ShareableBitmap::create(imageHandle, WebKit::SharedMemory::ReadOnly);
2339 copiedCompletionHandler(nullptr);
2340 [copiedCompletionHandler release];
2344 RetainPtr<CGImageRef> cgImage;
2345 cgImage = bitmap->makeCGImage();
2346 copiedCompletionHandler(cgImage.get());
2347 [copiedCompletionHandler release];
2351 - (void)_overrideLayoutParametersWithMinimumLayoutSize:(CGSize)minimumLayoutSize minimumLayoutSizeForMinimalUI:(CGSize)minimumLayoutSizeForMinimalUI maximumUnobscuredSizeOverride:(CGSize)maximumUnobscuredSizeOverride
2353 // FIXME: After Safari is updated to use this function instead of setting the parameters separately, we should remove
2354 // the individual setters and send a single message to send everything at once to the WebProcess.
2355 self._minimumLayoutSizeOverride = minimumLayoutSize;
2356 self._minimumLayoutSizeOverrideForMinimalUI = minimumLayoutSizeForMinimalUI;
2357 self._maximumUnobscuredSizeOverride = maximumUnobscuredSizeOverride;
2360 - (UIView *)_viewForFindUI
2362 return [self viewForZoomingInScrollView:[self scrollView]];
2365 - (BOOL)_isDisplayingPDF
2367 return [_customContentView isKindOfClass:[WKPDFView class]];
2370 - (NSData *)_dataForDisplayedPDF
2372 if (![self _isDisplayingPDF])
2374 CGPDFDocumentRef pdfDocument = [(WKPDFView *)_customContentView pdfDocument];
2375 return [(NSData *)CGDataProviderCopyData(CGPDFDocumentGetDataProvider(pdfDocument)) autorelease];
2378 - (NSString *)_suggestedFilenameForDisplayedPDF
2380 if (![self _isDisplayingPDF])
2382 return [(WKPDFView *)_customContentView.get() suggestedFilename];
2385 - (CGFloat)_viewportMetaTagWidth
2387 return _viewportMetaTagWidth;
2390 - (_WKWebViewPrintFormatter *)_webViewPrintFormatter
2392 UIViewPrintFormatter *viewPrintFormatter = self.viewPrintFormatter;
2393 ASSERT([viewPrintFormatter isKindOfClass:[_WKWebViewPrintFormatter class]]);
2394 return (_WKWebViewPrintFormatter *)viewPrintFormatter;
2399 #pragma mark - OS X-specific methods
2401 - (NSColor *)_pageExtendedBackgroundColor
2403 WebCore::Color color = _page->pageExtendedBackgroundColor();
2404 if (!color.isValid())
2407 return nsColor(color);
2410 - (BOOL)_drawsTransparentBackground
2412 return _page->drawsTransparentBackground();
2415 - (void)_setDrawsTransparentBackground:(BOOL)drawsTransparentBackground
2417 _page->setDrawsTransparentBackground(drawsTransparentBackground);
2420 - (void)_setTopContentInset:(CGFloat)contentInset
2422 [_wkView _setTopContentInset:contentInset];
2425 - (CGFloat)_topContentInset
2427 return [_wkView _topContentInset];
2430 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
2432 - (void)_setAutomaticallyAdjustsContentInsets:(BOOL)automaticallyAdjustsContentInsets
2434 [_wkView _setAutomaticallyAdjustsContentInsets:automaticallyAdjustsContentInsets];
2437 - (BOOL)_automaticallyAdjustsContentInsets
2439 return [_wkView _automaticallyAdjustsContentInsets];
2448 #if !TARGET_OS_IPHONE
2450 @implementation WKWebView (WKIBActions)
2452 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
2454 SEL action = item.action;
2456 if (action == @selector(goBack:))
2457 return !!_page->backForwardList().backItem();
2459 if (action == @selector(goForward:))
2460 return !!_page->backForwardList().forwardItem();
2462 if (action == @selector(stopLoading:)) {
2463 // FIXME: Return no if we're stopped.
2467 if (action == @selector(reload:) || action == @selector(reloadFromOrigin:)) {
2468 // FIXME: Return no if we're loading.
2475 - (IBAction)goBack:(id)sender
2480 - (IBAction)goForward:(id)sender
2485 - (IBAction)reload:(id)sender
2490 - (IBAction)reloadFromOrigin:(id)sender
2492 [self reloadFromOrigin];
2495 - (IBAction)stopLoading:(id)sender
2497 _page->stopLoading();
2505 @implementation WKWebView (_WKWebViewPrintFormatter)
2507 - (Class)_printFormatterClass
2509 return [_WKWebViewPrintFormatter class];
2512 - (NSInteger)_computePageCountAndStartDrawingToPDFForFrame:(_WKFrameHandle *)frame printInfo:(const WebKit::PrintInfo&)printInfo firstPage:(uint32_t)firstPage computedTotalScaleFactor:(double&)totalScaleFactor
2514 if ([self _isDisplayingPDF])
2515 return CGPDFDocumentGetNumberOfPages([(WKPDFView *)_customContentView pdfDocument]);
2517 _pageIsPrintingToPDF = YES;
2518 Vector<WebCore::IntRect> pageRects;
2519 uint64_t frameID = frame ? frame._frameID : _page->mainFrame()->frameID();
2520 if (!_page->sendSync(Messages::WebPage::ComputePagesForPrintingAndStartDrawingToPDF(frameID, printInfo, firstPage), Messages::WebPage::ComputePagesForPrintingAndStartDrawingToPDF::Reply(pageRects, totalScaleFactor)))
2522 return pageRects.size();
2525 - (void)_endPrinting
2527 _pageIsPrintingToPDF = NO;
2528 _printedDocument = nullptr;
2529 _page->send(Messages::WebPage::EndPrinting());
2532 // FIXME: milliseconds::max() overflows when converted to nanoseconds, causing condition_variable::wait_for() to believe
2533 // a timeout occurred on any spurious wakeup. Use nanoseconds::max() (converted to ms) to avoid this. We should perhaps
2534 // change waitForAndDispatchImmediately() to take nanoseconds to avoid this issue.
2535 static constexpr std::chrono::milliseconds didFinishLoadingTimeout = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::nanoseconds::max());
2537 - (CGPDFDocumentRef)_printedDocument
2539 if ([self _isDisplayingPDF]) {
2540 ASSERT(!_pageIsPrintingToPDF);
2541 return [(WKPDFView *)_customContentView pdfDocument];
2544 if (_pageIsPrintingToPDF) {
2545 if (!_page->process().connection()->waitForAndDispatchImmediately<Messages::WebPageProxy::DidFinishDrawingPagesToPDF>(_page->pageID(), didFinishLoadingTimeout)) {
2546 ASSERT_NOT_REACHED();
2549 ASSERT(!_pageIsPrintingToPDF);
2551 return _printedDocument.get();
2554 - (void)_setPrintedDocument:(CGPDFDocumentRef)printedDocument
2556 if (!_pageIsPrintingToPDF)
2558 ASSERT(![self _isDisplayingPDF]);
2559 _printedDocument = printedDocument;
2560 _pageIsPrintingToPDF = NO;
2566 #endif // WK_API_ENABLED