WKWebView snapshots have the wrong scale after rotation
[WebKit-https.git] / Source / WebKit2 / UIProcess / API / Cocoa / WKWebView.mm
1 /*
2  * Copyright (C) 2014, 2015 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
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.
12  *
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.
24  */
25
26 #import "config.h"
27 #import "WKWebViewInternal.h"
28
29 #if WK_API_ENABLED
30
31 #import "APIFormClient.h"
32 #import "APISerializedScriptValue.h"
33 #import "CompletionHandlerCallChecker.h"
34 #import "DiagnosticLoggingClient.h"
35 #import "FindClient.h"
36 #import "LegacySessionStateCoding.h"
37 #import "NavigationState.h"
38 #import "RemoteLayerTreeScrollingPerformanceData.h"
39 #import "RemoteLayerTreeTransaction.h"
40 #import "RemoteObjectRegistry.h"
41 #import "RemoteObjectRegistryMessages.h"
42 #import "UIDelegate.h"
43 #import "ViewGestureController.h"
44 #import "ViewSnapshotStore.h"
45 #import "WKBackForwardListInternal.h"
46 #import "WKBackForwardListItemInternal.h"
47 #import "WKBrowsingContextHandleInternal.h"
48 #import "WKErrorInternal.h"
49 #import "WKHistoryDelegatePrivate.h"
50 #import "WKLayoutMode.h"
51 #import "WKNSData.h"
52 #import "WKNSURLExtras.h"
53 #import "WKNavigationDelegate.h"
54 #import "WKNavigationInternal.h"
55 #import "WKPreferencesInternal.h"
56 #import "WKProcessPoolInternal.h"
57 #import "WKSharedAPICast.h"
58 #import "WKUIDelegate.h"
59 #import "WKUIDelegatePrivate.h"
60 #import "WKUserContentControllerInternal.h"
61 #import "WKWebViewConfigurationInternal.h"
62 #import "WKWebViewContentProvider.h"
63 #import "WKWebsiteDataStoreInternal.h"
64 #import "WebBackForwardList.h"
65 #import "WebCertificateInfo.h"
66 #import "WebFormSubmissionListenerProxy.h"
67 #import "WebKitSystemInterface.h"
68 #import "WebPageGroup.h"
69 #import "WebPageProxy.h"
70 #import "WebPreferencesKeys.h"
71 #import "WebProcessPool.h"
72 #import "WebProcessProxy.h"
73 #import "_WKDiagnosticLoggingDelegate.h"
74 #import "_WKFindDelegate.h"
75 #import "_WKFormDelegate.h"
76 #import "_WKRemoteObjectRegistryInternal.h"
77 #import "_WKSessionStateInternal.h"
78 #import "_WKVisitedLinkProviderInternal.h"
79 #import <JavaScriptCore/JSContext.h>
80 #import <JavaScriptCore/JSValue.h>
81 #import <WebCore/IOSurface.h>
82 #import <wtf/HashMap.h>
83 #import <wtf/MathExtras.h>
84 #import <wtf/NeverDestroyed.h>
85 #import <wtf/Optional.h>
86 #import <wtf/RetainPtr.h>
87
88 #if PLATFORM(IOS)
89 #import "_WKFrameHandleInternal.h"
90 #import "_WKWebViewPrintFormatter.h"
91 #import "PrintInfo.h"
92 #import "ProcessThrottler.h"
93 #import "RemoteLayerTreeDrawingAreaProxy.h"
94 #import "RemoteScrollingCoordinatorProxy.h"
95 #import "UIKitSPI.h"
96 #import "WKContentViewInteraction.h"
97 #import "WKPDFView.h"
98 #import "WKScrollView.h"
99 #import "WKWebViewContentProviderRegistry.h"
100 #import "WebPageMessages.h"
101 #import "WebVideoFullscreenManagerProxy.h"
102 #import <UIKit/UIApplication.h>
103 #import <WebCore/CoreGraphicsSPI.h>
104 #import <WebCore/FrameLoaderTypes.h>
105 #import <WebCore/InspectorOverlay.h>
106 #import <WebCore/QuartzCoreSPI.h>
107
108 @interface UIScrollView (UIScrollViewInternal)
109 - (void)_adjustForAutomaticKeyboardInfo:(NSDictionary*)info animated:(BOOL)animated lastAdjustment:(CGFloat*)lastAdjustment;
110 - (BOOL)_isScrollingToTop;
111 - (BOOL)_isInterruptingDeceleration;
112 - (CGPoint)_animatedTargetOffset;
113 @end
114
115 @interface UIPeripheralHost(UIKitInternal)
116 - (CGFloat)getVerticalOverlapForView:(UIView *)view usingKeyboardInfo:(NSDictionary *)info;
117 @end
118
119 @interface UIView (UIViewInternal)
120 - (UIViewController *)_viewControllerForAncestor;
121 @end
122
123 @interface UIWindow (UIWindowInternal)
124 - (BOOL)_isHostedInAnotherProcess;
125 @end
126
127 @interface UIViewController (UIViewControllerInternal)
128 - (UIViewController *)_rootAncestorViewController;
129 - (UIViewController *)_viewControllerForSupportedInterfaceOrientations;
130 @end
131
132 enum class DynamicViewportUpdateMode {
133     NotResizing,
134     ResizingWithAnimation,
135     ResizingWithDocumentHidden,
136 };
137
138 #endif
139
140 #if PLATFORM(MAC)
141 #import "WKViewInternal.h"
142 #import <WebCore/ColorMac.h>
143 #endif
144
145 NSString * const _WKShouldOpenExternalURLsKey = @"_WKShouldOpenExternalURLsKey";
146
147 static HashMap<WebKit::WebPageProxy*, WKWebView *>& pageToViewMap()
148 {
149     static NeverDestroyed<HashMap<WebKit::WebPageProxy*, WKWebView *>> map;
150     return map;
151 }
152
153 WKWebView* fromWebPageProxy(WebKit::WebPageProxy& page)
154 {
155     return pageToViewMap().get(&page);
156 }
157
158 @implementation WKWebView {
159     std::unique_ptr<WebKit::NavigationState> _navigationState;
160     std::unique_ptr<WebKit::UIDelegate> _uiDelegate;
161
162     RetainPtr<_WKRemoteObjectRegistry> _remoteObjectRegistry;
163     _WKRenderingProgressEvents _observedRenderingProgressEvents;
164
165     WebKit::WeakObjCPtr<id <_WKFormDelegate>> _formDelegate;
166 #if PLATFORM(IOS)
167     RetainPtr<WKScrollView> _scrollView;
168     RetainPtr<WKContentView> _contentView;
169
170     BOOL _overridesMinimumLayoutSize;
171     CGSize _minimumLayoutSizeOverride;
172     BOOL _overridesMaximumUnobscuredSize;
173     CGSize _maximumUnobscuredSizeOverride;
174     CGRect _inputViewBounds;
175     CGFloat _viewportMetaTagWidth;
176     BOOL _allowsLinkPreview;
177
178     UIEdgeInsets _obscuredInsets;
179     BOOL _haveSetObscuredInsets;
180     BOOL _isChangingObscuredInsetsInteractively;
181
182     UIInterfaceOrientation _interfaceOrientationOverride;
183     BOOL _overridesInterfaceOrientation;
184
185     BOOL _hasCommittedLoadForMainFrame;
186     BOOL _needsResetViewStateAfterCommitLoadForMainFrame;
187     uint64_t _firstPaintAfterCommitLoadTransactionID;
188     DynamicViewportUpdateMode _dynamicViewportUpdateMode;
189     CATransform3D _resizeAnimationTransformAdjustments;
190     uint64_t _resizeAnimationTransformTransactionID;
191     RetainPtr<UIView> _resizeAnimationView;
192     CGFloat _lastAdjustmentForScroller;
193     Optional<CGRect> _frozenVisibleContentRect;
194     Optional<CGRect> _frozenUnobscuredContentRect;
195
196     BOOL _needsToRestoreExposedRect;
197     WebCore::FloatRect _exposedRectToRestore;
198     BOOL _needsToRestoreUnobscuredCenter;
199     WebCore::FloatPoint _unobscuredCenterToRestore;
200     uint64_t _firstTransactionIDAfterPageRestore;
201     double _scaleToRestore;
202
203     std::unique_ptr<WebKit::ViewGestureController> _gestureController;
204     BOOL _allowsBackForwardNavigationGestures;
205
206     RetainPtr<UIView <WKWebViewContentProvider>> _customContentView;
207     RetainPtr<UIView> _customContentFixedOverlayView;
208
209     WebCore::Color _scrollViewBackgroundColor;
210
211     BOOL _delayUpdateVisibleContentRects;
212     BOOL _hadDelayedUpdateVisibleContentRects;
213
214     BOOL _pageIsPrintingToPDF;
215     RetainPtr<CGPDFDocumentRef> _printedDocument;
216     Vector<std::function<void ()>> _snapshotsDeferredDuringResize;
217 #endif
218 #if PLATFORM(MAC)
219     RetainPtr<WKView> _wkView;
220 #endif
221 }
222
223 - (instancetype)initWithFrame:(CGRect)frame
224 {
225     return [self initWithFrame:frame configuration:adoptNS([[WKWebViewConfiguration alloc] init]).get()];
226 }
227
228 #if PLATFORM(IOS)
229 static int32_t deviceOrientationForUIInterfaceOrientation(UIInterfaceOrientation orientation)
230 {
231     switch (orientation) {
232     case UIInterfaceOrientationUnknown:
233     case UIInterfaceOrientationPortrait:
234         return 0;
235     case UIInterfaceOrientationPortraitUpsideDown:
236         return 180;
237     case UIInterfaceOrientationLandscapeLeft:
238         return -90;
239     case UIInterfaceOrientationLandscapeRight:
240         return 90;
241     }
242 }
243
244 static int32_t deviceOrientation()
245 {
246     return deviceOrientationForUIInterfaceOrientation([[UIApplication sharedApplication] statusBarOrientation]);
247 }
248
249 - (BOOL)_isShowingVideoPictureInPicture
250 {
251     if (!_page || !_page->videoFullscreenManager())
252         return false;
253     
254     return _page->videoFullscreenManager()->hasMode(WebCore::HTMLMediaElementEnums::VideoFullscreenModePictureInPicture);
255 }
256
257 - (BOOL)_mayAutomaticallyShowVideoPictureInPicture
258 {
259 #if (__IPHONE_OS_VERSION_MIN_REQUIRED <= 80200) || !HAVE(AVKIT)
260     return false;
261 #else
262     if (!_page || !_page->videoFullscreenManager())
263         return false;
264
265     return _page->videoFullscreenManager()->mayAutomaticallyShowVideoPictureInPicture();
266 #endif
267 }
268
269 static bool shouldAllowPictureInPictureMediaPlayback()
270 {
271 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 90000
272     static bool shouldAllowPictureInPictureMediaPlayback = iosExecutableWasLinkedOnOrAfterVersion(wkIOSSystemVersion_9_0);
273     return shouldAllowPictureInPictureMediaPlayback;
274 #else
275     return false;
276 #endif
277 }
278
279 #endif
280
281 - (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration
282 {
283     if (!(self = [super initWithFrame:frame]))
284         return nil;
285
286     if (!configuration)
287         [NSException raise:NSInvalidArgumentException format:@"Configuration cannot be nil"];
288
289     _configuration = adoptNS([configuration copy]);
290
291     if (WKWebView *relatedWebView = [_configuration _relatedWebView]) {
292         WKProcessPool *processPool = [_configuration processPool];
293         WKProcessPool *relatedWebViewProcessPool = [relatedWebView->_configuration processPool];
294         if (processPool && processPool != relatedWebViewProcessPool)
295             [NSException raise:NSInvalidArgumentException format:@"Related web view %@ has process pool %@ but configuration specifies a different process pool %@", relatedWebView, relatedWebViewProcessPool, configuration.processPool];
296
297         [_configuration setProcessPool:relatedWebViewProcessPool];
298     }
299
300     [_configuration _validate];
301
302     CGRect bounds = self.bounds;
303
304     WebKit::WebProcessPool& processPool = *[_configuration processPool]->_processPool;
305
306     WebKit::WebPageConfiguration webPageConfiguration;
307     webPageConfiguration.preferences = [_configuration preferences]->_preferences.get();
308     if (WKWebView *relatedWebView = [_configuration _relatedWebView])
309         webPageConfiguration.relatedPage = relatedWebView->_page.get();
310
311     webPageConfiguration.userContentController = [_configuration userContentController]->_userContentControllerProxy.get();
312     webPageConfiguration.visitedLinkProvider = [_configuration _visitedLinkProvider]->_visitedLinkProvider.get();
313     webPageConfiguration.websiteDataStore = &[_configuration websiteDataStore]->_websiteDataStore->websiteDataStore();
314     webPageConfiguration.sessionID = webPageConfiguration.websiteDataStore->sessionID();
315     webPageConfiguration.treatsSHA1SignedCertificatesAsInsecure = [_configuration _treatsSHA1SignedCertificatesAsInsecure];
316
317     RefPtr<WebKit::WebPageGroup> pageGroup;
318     NSString *groupIdentifier = configuration._groupIdentifier;
319     if (groupIdentifier.length) {
320         pageGroup = WebKit::WebPageGroup::create(configuration._groupIdentifier);
321         webPageConfiguration.pageGroup = pageGroup.get();
322     }
323
324     webPageConfiguration.preferenceValues.set(WebKit::WebPreferencesKey::suppressesIncrementalRenderingKey(), WebKit::WebPreferencesStore::Value(!![_configuration suppressesIncrementalRendering]));
325
326 #if PLATFORM(IOS)
327     webPageConfiguration.alwaysRunsAtForegroundPriority = [_configuration _alwaysRunsAtForegroundPriority];
328
329     webPageConfiguration.preferenceValues.set(WebKit::WebPreferencesKey::allowsInlineMediaPlaybackKey(), WebKit::WebPreferencesStore::Value(!![_configuration allowsInlineMediaPlayback]));
330     webPageConfiguration.preferenceValues.set(WebKit::WebPreferencesKey::allowsPictureInPictureMediaPlaybackKey(), WebKit::WebPreferencesStore::Value(!![_configuration allowsPictureInPictureMediaPlayback] && shouldAllowPictureInPictureMediaPlayback()));
331     webPageConfiguration.preferenceValues.set(WebKit::WebPreferencesKey::requiresUserGestureForMediaPlaybackKey(), WebKit::WebPreferencesStore::Value(!![_configuration requiresUserActionForMediaPlayback]));
332 #endif
333 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
334     webPageConfiguration.preferenceValues.set(WebKit::WebPreferencesKey::allowsAirPlayForMediaPlaybackKey(), WebKit::WebPreferencesStore::Value(!![_configuration allowsAirPlayForMediaPlayback]));
335 #endif
336
337 #if PLATFORM(IOS)
338     _scrollView = adoptNS([[WKScrollView alloc] initWithFrame:bounds]);
339     [_scrollView setInternalDelegate:self];
340     [_scrollView setBouncesZoom:YES];
341
342     [self addSubview:_scrollView.get()];
343
344     _contentView = adoptNS([[WKContentView alloc] initWithFrame:bounds processPool:processPool configuration:WTF::move(webPageConfiguration) webView:self]);
345
346     _page = [_contentView page];
347     _page->setDeviceOrientation(deviceOrientation());
348
349     [_contentView layer].anchorPoint = CGPointZero;
350     [_contentView setFrame:bounds];
351     [_scrollView addSubview:_contentView.get()];
352     [_scrollView addSubview:[_contentView unscaledView]];
353     [self _updateScrollViewBackground];
354
355     _viewportMetaTagWidth = -1;
356     _allowsLinkPreview = YES;
357
358     [self _frameOrBoundsChanged];
359
360     NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
361     [center addObserver:self selector:@selector(_keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];
362     [center addObserver:self selector:@selector(_keyboardDidChangeFrame:) name:UIKeyboardDidChangeFrameNotification object:nil];
363     [center addObserver:self selector:@selector(_keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
364     [center addObserver:self selector:@selector(_keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
365     [center addObserver:self selector:@selector(_windowDidRotate:) name:UIWindowDidRotateNotification object:nil];
366     [center addObserver:self selector:@selector(_contentSizeCategoryDidChange:) name:UIContentSizeCategoryDidChangeNotification object:nil];
367     _page->contentSizeCategoryDidChange([self _contentSizeCategory]);
368
369     [[_configuration _contentProviderRegistry] addPage:*_page];
370 #endif
371
372 #if PLATFORM(MAC)
373     _wkView = adoptNS([[WKView alloc] initWithFrame:bounds processPool:processPool configuration:WTF::move(webPageConfiguration) webView:self]);
374     [self addSubview:_wkView.get()];
375     _page = WebKit::toImpl([_wkView pageRef]);
376
377 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
378     [_wkView _setAutomaticallyAdjustsContentInsets:YES];
379 #endif
380 #endif
381
382     _page->setBackgroundExtendsBeyondPage(true);
383
384     if (NSString *applicationNameForUserAgent = configuration.applicationNameForUserAgent)
385         _page->setApplicationNameForUserAgent(applicationNameForUserAgent);
386
387     _navigationState = std::make_unique<WebKit::NavigationState>(self);
388     _uiDelegate = std::make_unique<WebKit::UIDelegate>(self);
389     _page->setFindClient(std::make_unique<WebKit::FindClient>(self));
390     _page->setDiagnosticLoggingClient(std::make_unique<WebKit::DiagnosticLoggingClient>(self));
391
392     pageToViewMap().add(_page.get(), self);
393
394     return self;
395 }
396
397 - (instancetype)initWithCoder:(NSCoder *)coder
398 {
399     [self release];
400     return nil;
401 }
402
403 - (void)dealloc
404 {
405     if (_remoteObjectRegistry)
406         _page->process().processPool().removeMessageReceiver(Messages::RemoteObjectRegistry::messageReceiverName(), _page->pageID());
407
408     _page->close();
409
410     [_remoteObjectRegistry _invalidate];
411 #if PLATFORM(IOS)
412     [[_configuration _contentProviderRegistry] removePage:*_page];
413     [[NSNotificationCenter defaultCenter] removeObserver:self];
414     [_scrollView setInternalDelegate:nil];
415 #endif
416
417     pageToViewMap().remove(_page.get());
418
419     [super dealloc];
420 }
421
422 - (WKWebViewConfiguration *)configuration
423 {
424     return [[_configuration copy] autorelease];
425 }
426
427 - (WKBackForwardList *)backForwardList
428 {
429     return wrapper(_page->backForwardList());
430 }
431
432 - (id <WKNavigationDelegate>)navigationDelegate
433 {
434     return _navigationState->navigationDelegate().autorelease();
435 }
436
437 - (void)setNavigationDelegate:(id <WKNavigationDelegate>)navigationDelegate
438 {
439     _page->setNavigationClient(_navigationState->createNavigationClient());
440     _navigationState->setNavigationDelegate(navigationDelegate);
441 }
442
443 - (id <WKUIDelegate>)UIDelegate
444 {
445     return _uiDelegate->delegate().autorelease();
446 }
447
448 - (void)setUIDelegate:(id<WKUIDelegate>)UIDelegate
449 {
450     _page->setUIClient(_uiDelegate->createUIClient());
451     _uiDelegate->setDelegate(UIDelegate);
452 }
453
454 - (WKNavigation *)loadRequest:(NSURLRequest *)request
455 {
456     return [self _loadRequest:request withOptions:nil];
457 }
458
459 - (WKNavigation *)loadFileURL:(NSURL *)URL allowingReadAccessToURL:(NSURL *)readAccessURL
460 {
461     if (![URL isFileURL])
462         [NSException raise:NSInvalidArgumentException format:@"%@ is not a file URL", URL];
463
464     if (![readAccessURL isFileURL])
465         [NSException raise:NSInvalidArgumentException format:@"%@ is not a file URL", readAccessURL];
466
467     auto navigation = _page->loadFile([URL _web_originalDataAsWTFString], [readAccessURL _web_originalDataAsWTFString]);
468     if (!navigation)
469         return nil;
470
471     return [wrapper(*navigation.release().leakRef()) autorelease];
472 }
473
474 - (WKNavigation *)loadHTMLString:(NSString *)string baseURL:(NSURL *)baseURL
475 {
476     NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
477
478     return [self loadData:data MIMEType:@"text/html" characterEncodingName:@"UTF-8" baseURL:baseURL];
479 }
480
481 - (WKNavigation *)loadData:(NSData *)data MIMEType:(NSString *)MIMEType characterEncodingName:(NSString *)characterEncodingName baseURL:(NSURL *)baseURL
482 {
483     auto navigation = _page->loadData(API::Data::createWithoutCopying(data).ptr(), MIMEType, characterEncodingName, baseURL.absoluteString);
484     if (!navigation)
485         return nil;
486
487     return [wrapper(*navigation.release().leakRef()) autorelease];
488 }
489
490 - (WKNavigation *)goToBackForwardListItem:(WKBackForwardListItem *)item
491 {
492     auto navigation = _page->goToBackForwardItem(&item._item);
493     if (!navigation)
494         return nil;
495
496     return [wrapper(*navigation.release().leakRef()) autorelease];
497 }
498
499 - (NSString *)title
500 {
501     return _page->pageLoadState().title();
502 }
503
504 - (NSURL *)URL
505 {
506     return [NSURL _web_URLWithWTFString:_page->pageLoadState().activeURL()];
507 }
508
509 - (BOOL)isLoading
510 {
511     return _page->pageLoadState().isLoading();
512 }
513
514 - (double)estimatedProgress
515 {
516     return _page->pageLoadState().estimatedProgress();
517 }
518
519 - (BOOL)hasOnlySecureContent
520 {
521     return _page->pageLoadState().hasOnlySecureContent();
522 }
523
524 - (NSArray *)certificateChain
525 {
526     auto certificateInfo = _page->pageLoadState().certificateInfo();
527     if (!certificateInfo)
528         return @[ ];
529
530     return (NSArray *)certificateInfo->certificateInfo().certificateChain() ?: @[ ];
531 }
532
533 - (BOOL)canGoBack
534 {
535     return _page->pageLoadState().canGoBack();
536 }
537
538 - (BOOL)canGoForward
539 {
540     return _page->pageLoadState().canGoForward();
541 }
542
543 - (WKNavigation *)goBack
544 {
545     auto navigation = _page->goBack();
546     if (!navigation)
547         return nil;
548  
549     return [wrapper(*navigation.release().leakRef()) autorelease];
550 }
551
552 - (WKNavigation *)goForward
553 {
554     auto navigation = _page->goForward();
555     if (!navigation)
556         return nil;
557
558     return [wrapper(*navigation.release().leakRef()) autorelease];
559 }
560
561 - (WKNavigation *)reload
562 {
563     auto navigation = _page->reload(false);
564     if (!navigation)
565         return nil;
566
567     return [wrapper(*navigation.release().leakRef()) autorelease];
568 }
569
570 - (WKNavigation *)reloadFromOrigin
571 {
572     auto navigation = _page->reload(true);
573     if (!navigation)
574         return nil;
575
576     return [wrapper(*navigation.release().leakRef()) autorelease];
577 }
578
579 - (void)stopLoading
580 {
581     _page->stopLoading();
582 }
583
584 static WKErrorCode callbackErrorCode(WebKit::CallbackBase::Error error)
585 {
586     switch (error) {
587     case WebKit::CallbackBase::Error::None:
588         ASSERT_NOT_REACHED();
589         return WKErrorUnknown;
590
591     case WebKit::CallbackBase::Error::Unknown:
592         return WKErrorUnknown;
593
594     case WebKit::CallbackBase::Error::ProcessExited:
595         return WKErrorWebContentProcessTerminated;
596
597     case WebKit::CallbackBase::Error::OwnerWasInvalidated:
598         return WKErrorWebViewInvalidated;
599     }
600 }
601
602 - (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^)(id, NSError *))completionHandler
603 {
604     auto handler = adoptNS([completionHandler copy]);
605
606     _page->runJavaScriptInMainFrame(javaScriptString, [handler](API::SerializedScriptValue* serializedScriptValue, bool hadException, WebKit::ScriptValueCallback::Error errorCode) {
607         if (!handler)
608             return;
609
610         if (errorCode != WebKit::ScriptValueCallback::Error::None) {
611             auto error = createNSError(callbackErrorCode(errorCode));
612             if (errorCode == WebKit::ScriptValueCallback::Error::OwnerWasInvalidated) {
613                 // The OwnerWasInvalidated callback is synchronous. We don't want to call the block from within it
614                 // because that can trigger re-entrancy bugs in WebKit.
615                 // FIXME: It would be even better if GenericCallback did this for us.
616                 dispatch_async(dispatch_get_main_queue(), [handler, error] {
617                     auto rawHandler = (void (^)(id, NSError *))handler.get();
618                     rawHandler(nil, error.get());
619                 });
620                 return;
621             }
622
623             auto rawHandler = (void (^)(id, NSError *))handler.get();
624             rawHandler(nil, error.get());
625             return;
626         }
627
628         auto rawHandler = (void (^)(id, NSError *))handler.get();
629         if (hadException) {
630             ASSERT(!serializedScriptValue);
631             rawHandler(nil, createNSError(WKErrorJavaScriptExceptionOccurred).get());
632             return;
633         }
634
635         if (!serializedScriptValue) {
636             rawHandler(nil, createNSError(WKErrorJavaScriptResultTypeIsUnsupported).get());
637             return;
638         }
639
640         auto context = adoptNS([[JSContext alloc] init]);
641         JSValueRef valueRef = serializedScriptValue->deserialize([context JSGlobalContextRef], 0);
642         JSValue *value = [JSValue valueWithJSValueRef:valueRef inContext:context.get()];
643
644         rawHandler([value toObject], nil);
645     });
646 }
647
648 - (NSString *)customUserAgent
649 {
650     return _page->customUserAgent();
651 }
652
653 - (void)setCustomUserAgent:(NSString *)customUserAgent
654 {
655     _page->setCustomUserAgent(customUserAgent);
656 }
657
658 - (WKPageRef)_pageForTesting
659 {
660     return toAPI(_page.get());
661 }
662
663 #pragma mark iOS-specific methods
664
665 #if PLATFORM(IOS)
666 - (void)setFrame:(CGRect)frame
667 {
668     CGRect oldFrame = self.frame;
669     [super setFrame:frame];
670
671     if (!CGSizeEqualToSize(oldFrame.size, frame.size))
672         [self _frameOrBoundsChanged];
673 }
674
675 - (void)setBounds:(CGRect)bounds
676 {
677     CGRect oldBounds = self.bounds;
678     [super setBounds:bounds];
679     [_customContentFixedOverlayView setFrame:self.bounds];
680
681     if (!CGSizeEqualToSize(oldBounds.size, bounds.size))
682         [self _frameOrBoundsChanged];
683 }
684
685 - (UIScrollView *)scrollView
686 {
687     return _scrollView.get();
688 }
689
690 - (WKBrowsingContextController *)browsingContextController
691 {
692     return [_contentView browsingContextController];
693 }
694
695 - (BOOL)becomeFirstResponder
696 {
697     return [_contentView becomeFirstResponder] || [super becomeFirstResponder];
698 }
699
700 static inline CGFloat floorToDevicePixel(CGFloat input, float deviceScaleFactor)
701 {
702     return CGFloor(input * deviceScaleFactor) / deviceScaleFactor;
703 }
704
705 static inline bool pointsEqualInDevicePixels(CGPoint a, CGPoint b, float deviceScaleFactor)
706 {
707     return fabs(a.x * deviceScaleFactor - b.x * deviceScaleFactor) < std::numeric_limits<float>::epsilon()
708         && fabs(a.y * deviceScaleFactor - b.y * deviceScaleFactor) < std::numeric_limits<float>::epsilon();
709 }
710
711 static CGSize roundScrollViewContentSize(const WebKit::WebPageProxy& page, CGSize contentSize)
712 {
713     float deviceScaleFactor = page.deviceScaleFactor();
714     return CGSizeMake(floorToDevicePixel(contentSize.width, deviceScaleFactor), floorToDevicePixel(contentSize.height, deviceScaleFactor));
715 }
716
717 - (UIView *)_currentContentView
718 {
719     return _customContentView ? _customContentView.get() : _contentView.get();
720 }
721
722 - (void)_setHasCustomContentView:(BOOL)pageHasCustomContentView loadedMIMEType:(const WTF::String&)mimeType
723 {
724     if (pageHasCustomContentView) {
725         [_customContentView removeFromSuperview];
726         [_customContentFixedOverlayView removeFromSuperview];
727
728         Class representationClass = [[_configuration _contentProviderRegistry] providerForMIMEType:mimeType];
729         ASSERT(representationClass);
730         _customContentView = adoptNS([[representationClass alloc] web_initWithFrame:self.bounds webView:self]);
731         _customContentFixedOverlayView = adoptNS([[UIView alloc] initWithFrame:self.bounds]);
732         [_customContentFixedOverlayView setUserInteractionEnabled:NO];
733
734         [[_contentView unscaledView] removeFromSuperview];
735         [_contentView removeFromSuperview];
736         [_scrollView addSubview:_customContentView.get()];
737         [self addSubview:_customContentFixedOverlayView.get()];
738
739         [_customContentView web_setMinimumSize:self.bounds.size];
740         [_customContentView web_setFixedOverlayView:_customContentFixedOverlayView.get()];
741     } else if (_customContentView) {
742         [_customContentView removeFromSuperview];
743         _customContentView = nullptr;
744
745         [_customContentFixedOverlayView removeFromSuperview];
746         _customContentFixedOverlayView = nullptr;
747
748         [_scrollView addSubview:_contentView.get()];
749         [_scrollView addSubview:[_contentView unscaledView]];
750         [_scrollView setContentSize:roundScrollViewContentSize(*_page, [_contentView frame].size)];
751
752         [_customContentFixedOverlayView setFrame:self.bounds];
753         [self addSubview:_customContentFixedOverlayView.get()];
754     }
755 }
756
757 - (void)_didFinishLoadingDataForCustomContentProviderWithSuggestedFilename:(const String&)suggestedFilename data:(NSData *)data
758 {
759     ASSERT(_customContentView);
760     [_customContentView web_setContentProviderData:data suggestedFilename:suggestedFilename];
761
762     // FIXME: It may make more sense for custom content providers to invoke this when they're ready,
763     // because there's no guarantee that all custom content providers will lay out synchronously.
764     _page->didLayoutForCustomContentProvider();
765 }
766
767 - (void)_setViewportMetaTagWidth:(float)newWidth
768 {
769     _viewportMetaTagWidth = newWidth;
770 }
771
772 - (void)_willInvokeUIScrollViewDelegateCallback
773 {
774     _delayUpdateVisibleContentRects = YES;
775 }
776
777 - (void)_didInvokeUIScrollViewDelegateCallback
778 {
779     _delayUpdateVisibleContentRects = NO;
780     if (_hadDelayedUpdateVisibleContentRects) {
781         _hadDelayedUpdateVisibleContentRects = NO;
782         [self _updateVisibleContentRects];
783     }
784 }
785
786 static CGFloat contentZoomScale(WKWebView *webView)
787 {
788     CGFloat scale = webView._currentContentView.layer.affineTransform.a;
789     ASSERT(scale == [webView->_scrollView zoomScale]);
790     return scale;
791 }
792
793 static WebCore::Color baseScrollViewBackgroundColor(WKWebView *webView)
794 {
795     if (webView->_customContentView)
796         return [webView->_customContentView backgroundColor].CGColor;
797
798     if (webView->_gestureController) {
799         WebCore::Color color = webView->_gestureController->backgroundColorForCurrentSnapshot();
800         if (color.isValid())
801             return color;
802     }
803
804     return webView->_page->pageExtendedBackgroundColor();
805 }
806
807 static WebCore::Color scrollViewBackgroundColor(WKWebView *webView)
808 {
809     if (!webView.opaque)
810         return WebCore::Color::transparent;
811
812     WebCore::Color color = baseScrollViewBackgroundColor(webView);
813
814     if (!color.isValid())
815         color = WebCore::Color::white;
816
817     CGFloat zoomScale = contentZoomScale(webView);
818     CGFloat minimumZoomScale = [webView->_scrollView minimumZoomScale];
819     if (zoomScale < minimumZoomScale) {
820         CGFloat slope = 12;
821         CGFloat opacity = std::max<CGFloat>(1 - slope * (minimumZoomScale - zoomScale), 0);
822         color = WebCore::colorWithOverrideAlpha(color.rgb(), opacity);
823     }
824
825     return color;
826 }
827
828 - (void)_updateScrollViewBackground
829 {
830     WebCore::Color color = scrollViewBackgroundColor(self);
831
832     if (_scrollViewBackgroundColor == color)
833         return;
834
835     _scrollViewBackgroundColor = color;
836
837     auto uiBackgroundColor = adoptNS([[UIColor alloc] initWithCGColor:cachedCGColor(color, WebCore::ColorSpaceDeviceRGB)]);
838     [_scrollView setBackgroundColor:uiBackgroundColor.get()];
839
840     // Update the indicator style based on the lightness/darkness of the background color.
841     double hue, saturation, lightness;
842     color.getHSL(hue, saturation, lightness);
843     if (lightness <= .5 && color.alpha() > 0)
844         [_scrollView setIndicatorStyle:UIScrollViewIndicatorStyleWhite];
845     else
846         [_scrollView setIndicatorStyle:UIScrollViewIndicatorStyleDefault];
847 }
848
849 - (CGPoint)_adjustedContentOffset:(CGPoint)point
850 {
851     CGPoint result = point;
852     UIEdgeInsets contentInset = [self _computedContentInset];
853
854     result.x -= contentInset.left;
855     result.y -= contentInset.top;
856
857     return result;
858 }
859
860 - (UIEdgeInsets)_computedContentInset
861 {
862     if (_haveSetObscuredInsets)
863         return _obscuredInsets;
864
865     return [_scrollView contentInset];
866 }
867
868 - (void)_processDidExit
869 {
870     if (!_customContentView && _dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing) {
871         NSUInteger indexOfResizeAnimationView = [[_scrollView subviews] indexOfObject:_resizeAnimationView.get()];
872         [_scrollView insertSubview:_contentView.get() atIndex:indexOfResizeAnimationView];
873         [_scrollView insertSubview:[_contentView unscaledView] atIndex:indexOfResizeAnimationView + 1];
874         [_resizeAnimationView removeFromSuperview];
875         _resizeAnimationView = nil;
876
877         _resizeAnimationTransformAdjustments = CATransform3DIdentity;
878     }
879     [_contentView setFrame:self.bounds];
880     [_scrollView setBackgroundColor:[UIColor whiteColor]];
881     [_scrollView setContentOffset:[self _adjustedContentOffset:CGPointZero]];
882     [_scrollView setZoomScale:1];
883
884     _viewportMetaTagWidth = -1;
885     _hasCommittedLoadForMainFrame = NO;
886     _needsResetViewStateAfterCommitLoadForMainFrame = NO;
887     _dynamicViewportUpdateMode = DynamicViewportUpdateMode::NotResizing;
888     [_contentView setHidden:NO];
889     _needsToRestoreExposedRect = NO;
890     _needsToRestoreUnobscuredCenter = NO;
891     _scrollViewBackgroundColor = WebCore::Color();
892     _delayUpdateVisibleContentRects = NO;
893     _hadDelayedUpdateVisibleContentRects = NO;
894 }
895
896 - (void)_didCommitLoadForMainFrame
897 {
898     _firstPaintAfterCommitLoadTransactionID = downcast<WebKit::RemoteLayerTreeDrawingAreaProxy>(*_page->drawingArea()).nextLayerTreeTransactionID();
899
900     _hasCommittedLoadForMainFrame = YES;
901     _needsResetViewStateAfterCommitLoadForMainFrame = YES;
902 }
903
904 static CGPoint contentOffsetBoundedInValidRange(UIScrollView *scrollView, CGPoint contentOffset)
905 {
906     UIEdgeInsets contentInsets = scrollView.contentInset;
907     CGSize contentSize = scrollView.contentSize;
908     CGSize scrollViewSize = scrollView.bounds.size;
909
910     CGFloat maxHorizontalOffset = contentSize.width + contentInsets.right - scrollViewSize.width;
911     contentOffset.x = std::min(maxHorizontalOffset, contentOffset.x);
912     contentOffset.x = std::max(-contentInsets.left, contentOffset.x);
913
914     CGFloat maxVerticalOffset = contentSize.height + contentInsets.bottom - scrollViewSize.height;
915     contentOffset.y = std::min(maxVerticalOffset, contentOffset.y);
916     contentOffset.y = std::max(-contentInsets.top, contentOffset.y);
917     return contentOffset;
918 }
919
920 static void changeContentOffsetBoundedInValidRange(UIScrollView *scrollView, WebCore::FloatPoint contentOffset)
921 {
922     scrollView.contentOffset = contentOffsetBoundedInValidRange(scrollView, contentOffset);
923 }
924
925 - (WebCore::FloatRect)visibleRectInViewCoordinates
926 {
927     WebCore::FloatRect bounds = self.bounds;
928     bounds.moveBy([_scrollView contentOffset]);
929     WebCore::FloatRect contentViewBounds = [_contentView bounds];
930     bounds.intersect(contentViewBounds);
931     return bounds;
932 }
933
934 // WebCore stores the page scale factor as float instead of double. When we get a scale from WebCore,
935 // we need to ignore differences that are within a small rounding error on floats.
936 static inline bool areEssentiallyEqualAsFloat(float a, float b)
937 {
938     return WTF::areEssentiallyEqual(a, b);
939 }
940
941 - (void)_didCommitLayerTree:(const WebKit::RemoteLayerTreeTransaction&)layerTreeTransaction
942 {
943     if (_customContentView)
944         return;
945
946     if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing) {
947         if (layerTreeTransaction.transactionID() >= _resizeAnimationTransformTransactionID) {
948             [_resizeAnimationView layer].sublayerTransform = _resizeAnimationTransformAdjustments;
949             if (_dynamicViewportUpdateMode == DynamicViewportUpdateMode::ResizingWithDocumentHidden) {
950                 [_contentView setHidden:NO];
951                 [self _endAnimatedResize];
952             }
953         }
954         return;
955     }
956
957     CGSize newContentSize = roundScrollViewContentSize(*_page, [_contentView frame].size);
958     [_scrollView _setContentSizePreservingContentOffsetDuringRubberband:newContentSize];
959
960     [_scrollView setMinimumZoomScale:layerTreeTransaction.minimumScaleFactor()];
961     [_scrollView setMaximumZoomScale:layerTreeTransaction.maximumScaleFactor()];
962     [_scrollView setZoomEnabled:layerTreeTransaction.allowsUserScaling()];
963     if (!layerTreeTransaction.scaleWasSetByUIProcess() && ![_scrollView isZooming] && ![_scrollView isZoomBouncing] && ![_scrollView _isAnimatingZoom])
964         [_scrollView setZoomScale:layerTreeTransaction.pageScaleFactor()];
965
966     [self _updateScrollViewBackground];
967
968     if (_gestureController)
969         _gestureController->setRenderTreeSize(layerTreeTransaction.renderTreeSize());
970
971     if (_needsResetViewStateAfterCommitLoadForMainFrame && layerTreeTransaction.transactionID() >= _firstPaintAfterCommitLoadTransactionID) {
972         _needsResetViewStateAfterCommitLoadForMainFrame = NO;
973         [_scrollView setContentOffset:[self _adjustedContentOffset:CGPointZero]];
974         [self _updateVisibleContentRects];
975     }
976
977     bool isTransactionAfterPageRestore = layerTreeTransaction.transactionID() >= _firstTransactionIDAfterPageRestore;
978
979     if (_needsToRestoreExposedRect && isTransactionAfterPageRestore) {
980         _needsToRestoreExposedRect = NO;
981
982         if (areEssentiallyEqualAsFloat(contentZoomScale(self), _scaleToRestore)) {
983             WebCore::FloatPoint exposedPosition = _exposedRectToRestore.location();
984             exposedPosition.scale(_scaleToRestore, _scaleToRestore);
985
986             changeContentOffsetBoundedInValidRange(_scrollView.get(), exposedPosition);
987             if (_gestureController)
988                 _gestureController->didRestoreScrollPosition();
989         }
990         [self _updateVisibleContentRects];
991     }
992
993     if (_needsToRestoreUnobscuredCenter && isTransactionAfterPageRestore) {
994         _needsToRestoreUnobscuredCenter = NO;
995
996         if (areEssentiallyEqualAsFloat(contentZoomScale(self), _scaleToRestore)) {
997             CGRect unobscuredRect = UIEdgeInsetsInsetRect(self.bounds, _obscuredInsets);
998             WebCore::FloatSize unobscuredContentSizeAtNewScale(unobscuredRect.size.width / _scaleToRestore, unobscuredRect.size.height / _scaleToRestore);
999             WebCore::FloatPoint topLeftInDocumentCoordinates(_unobscuredCenterToRestore.x() - unobscuredContentSizeAtNewScale.width() / 2, _unobscuredCenterToRestore.y() - unobscuredContentSizeAtNewScale.height() / 2);
1000
1001             topLeftInDocumentCoordinates.scale(_scaleToRestore, _scaleToRestore);
1002             topLeftInDocumentCoordinates.moveBy(WebCore::FloatPoint(-_obscuredInsets.left, -_obscuredInsets.top));
1003
1004             changeContentOffsetBoundedInValidRange(_scrollView.get(), topLeftInDocumentCoordinates);
1005             if (_gestureController)
1006                 _gestureController->didRestoreScrollPosition();
1007         }
1008         [self _updateVisibleContentRects];
1009     }
1010     
1011     if (WebKit::RemoteLayerTreeScrollingPerformanceData* scrollPerfData = _page->scrollingPerformanceData())
1012         scrollPerfData->didCommitLayerTree([self visibleRectInViewCoordinates]);
1013 }
1014
1015 - (void)_dynamicViewportUpdateChangedTargetToScale:(double)newScale position:(CGPoint)newScrollPosition nextValidLayerTreeTransactionID:(uint64_t)nextValidLayerTreeTransactionID
1016 {
1017     if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing) {
1018         CGFloat animatingScaleTarget = [[_resizeAnimationView layer] transform].m11;
1019         double currentTargetScale = animatingScaleTarget * [[_contentView layer] transform].m11;
1020         double scale = newScale / currentTargetScale;
1021         _resizeAnimationTransformAdjustments = CATransform3DMakeScale(scale, scale, 1);
1022
1023         CGPoint newContentOffset = [self _adjustedContentOffset:CGPointMake(newScrollPosition.x * newScale, newScrollPosition.y * newScale)];
1024         CGPoint currentContentOffset = [_scrollView contentOffset];
1025
1026         _resizeAnimationTransformAdjustments.m41 = (currentContentOffset.x - newContentOffset.x) / animatingScaleTarget;
1027         _resizeAnimationTransformAdjustments.m42 = (currentContentOffset.y - newContentOffset.y) / animatingScaleTarget;
1028         _resizeAnimationTransformTransactionID = nextValidLayerTreeTransactionID;
1029     }
1030 }
1031
1032 - (void)_couldNotRestorePageState
1033 {
1034     // The gestureController may be waiting for the scroll position to be restored
1035     // in order to remove the swipe snapshot. Since the scroll position could not be
1036     // restored, tell the gestureController it was restored so that it no longer waits
1037     // for it.
1038     if (_gestureController)
1039         _gestureController->didRestoreScrollPosition();
1040 }
1041
1042 - (void)_restorePageStateToExposedRect:(WebCore::FloatRect)exposedRect scale:(double)scale
1043 {
1044     if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing)
1045         return;
1046
1047     if (_customContentView)
1048         return;
1049
1050     _needsToRestoreUnobscuredCenter = NO;
1051     _needsToRestoreExposedRect = YES;
1052     _firstTransactionIDAfterPageRestore = downcast<WebKit::RemoteLayerTreeDrawingAreaProxy>(*_page->drawingArea()).nextLayerTreeTransactionID();
1053     _exposedRectToRestore = exposedRect;
1054     _scaleToRestore = scale;
1055 }
1056
1057 - (void)_restorePageStateToUnobscuredCenter:(WebCore::FloatPoint)center scale:(double)scale
1058 {
1059     if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing)
1060         return;
1061
1062     if (_customContentView)
1063         return;
1064
1065     _needsToRestoreExposedRect = NO;
1066     _needsToRestoreUnobscuredCenter = YES;
1067     _firstTransactionIDAfterPageRestore = downcast<WebKit::RemoteLayerTreeDrawingAreaProxy>(*_page->drawingArea()).nextLayerTreeTransactionID();
1068     _unobscuredCenterToRestore = center;
1069     _scaleToRestore = scale;
1070 }
1071
1072 - (PassRefPtr<WebKit::ViewSnapshot>)_takeViewSnapshot
1073 {
1074     float deviceScale = WKGetScreenScaleFactor();
1075     CGSize snapshotSize = self.bounds.size;
1076     snapshotSize.width *= deviceScale;
1077     snapshotSize.height *= deviceScale;
1078
1079     uint32_t slotID = [WebKit::ViewSnapshotStore::snapshottingContext() createImageSlot:snapshotSize hasAlpha:YES];
1080
1081     if (!slotID)
1082         return nullptr;
1083
1084     CATransform3D transform = CATransform3DMakeScale(deviceScale, deviceScale, 1);
1085     CARenderServerCaptureLayerWithTransform(MACH_PORT_NULL, self.layer.context.contextId, (uint64_t)self.layer, slotID, 0, 0, &transform);
1086
1087     WebCore::IntSize imageSize = WebCore::expandedIntSize(WebCore::FloatSize(snapshotSize));
1088     return WebKit::ViewSnapshot::create(slotID, imageSize, imageSize.width() * imageSize.height() * 4);
1089 }
1090
1091 - (void)_zoomToPoint:(WebCore::FloatPoint)point atScale:(double)scale animated:(BOOL)animated
1092 {
1093     CFTimeInterval duration = 0;
1094     CGFloat zoomScale = contentZoomScale(self);
1095
1096     if (animated) {
1097         const double maximumZoomDuration = 0.4;
1098         const double minimumZoomDuration = 0.1;
1099         const double zoomDurationFactor = 0.3;
1100
1101         duration = std::min(fabs(log(zoomScale) - log(scale)) * zoomDurationFactor + minimumZoomDuration, maximumZoomDuration);
1102     }
1103
1104     if (scale != zoomScale)
1105         _page->willStartUserTriggeredZooming();
1106
1107     [_scrollView _zoomToCenter:point scale:scale duration:duration];
1108 }
1109
1110 - (void)_zoomToRect:(WebCore::FloatRect)targetRect atScale:(double)scale origin:(WebCore::FloatPoint)origin animated:(BOOL)animated
1111 {
1112     // FIXME: Some of this could be shared with _scrollToRect.
1113     const double visibleRectScaleChange = contentZoomScale(self) / scale;
1114     const WebCore::FloatRect visibleRect([self convertRect:self.bounds toView:self._currentContentView]);
1115     const WebCore::FloatRect unobscuredRect([self _contentRectForUserInteraction]);
1116
1117     const WebCore::FloatSize topLeftObscuredInsetAfterZoom((unobscuredRect.minXMinYCorner() - visibleRect.minXMinYCorner()) * visibleRectScaleChange);
1118     const WebCore::FloatSize bottomRightObscuredInsetAfterZoom((visibleRect.maxXMaxYCorner() - unobscuredRect.maxXMaxYCorner()) * visibleRectScaleChange);
1119
1120     const WebCore::FloatSize unobscuredRectSizeAfterZoom(unobscuredRect.size() * visibleRectScaleChange);
1121
1122     // Center to the target rect.
1123     WebCore::FloatPoint unobscuredRectLocationAfterZoom = targetRect.location() - (unobscuredRectSizeAfterZoom - targetRect.size()) * 0.5;
1124
1125     // Center to the tap point instead in case the target rect won't fit in a direction.
1126     if (targetRect.width() > unobscuredRectSizeAfterZoom.width())
1127         unobscuredRectLocationAfterZoom.setX(origin.x() - unobscuredRectSizeAfterZoom.width() / 2);
1128     if (targetRect.height() > unobscuredRectSizeAfterZoom.height())
1129         unobscuredRectLocationAfterZoom.setY(origin.y() - unobscuredRectSizeAfterZoom.height() / 2);
1130
1131     // We have computed where we want the unobscured rect to be. Now adjust for the obscuring insets.
1132     WebCore::FloatRect visibleRectAfterZoom(unobscuredRectLocationAfterZoom, unobscuredRectSizeAfterZoom);
1133     visibleRectAfterZoom.move(-topLeftObscuredInsetAfterZoom);
1134     visibleRectAfterZoom.expand(topLeftObscuredInsetAfterZoom + bottomRightObscuredInsetAfterZoom);
1135
1136     [self _zoomToPoint:visibleRectAfterZoom.center() atScale:scale animated:animated];
1137 }
1138
1139 static WebCore::FloatPoint constrainContentOffset(WebCore::FloatPoint contentOffset, WebCore::FloatSize contentSize, WebCore::FloatSize unobscuredContentSize)
1140 {
1141     WebCore::FloatSize maximumContentOffset = contentSize - unobscuredContentSize;
1142     contentOffset = contentOffset.shrunkTo(WebCore::FloatPoint(maximumContentOffset.width(), maximumContentOffset.height()));
1143     contentOffset = contentOffset.expandedTo(WebCore::FloatPoint());
1144     return contentOffset;
1145 }
1146
1147 - (void)_scrollToContentOffset:(WebCore::FloatPoint)contentOffsetInPageCoordinates
1148 {
1149     if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing)
1150         return;
1151
1152     WebCore::FloatPoint scaledOffset = contentOffsetInPageCoordinates;
1153     CGFloat zoomScale = contentZoomScale(self);
1154     scaledOffset.scale(zoomScale, zoomScale);
1155
1156     CGPoint contentOffsetInScrollViewCoordinates = [self _adjustedContentOffset:scaledOffset];
1157     contentOffsetInScrollViewCoordinates = contentOffsetBoundedInValidRange(_scrollView.get(), contentOffsetInScrollViewCoordinates);
1158
1159     [_scrollView _stopScrollingAndZoomingAnimations];
1160
1161     if (!CGPointEqualToPoint(contentOffsetInScrollViewCoordinates, [_scrollView contentOffset]))
1162         [_scrollView setContentOffset:contentOffsetInScrollViewCoordinates];
1163     else {
1164         // If we haven't changed anything, there would not be any VisibleContentRect update sent to the content.
1165         // The WebProcess would keep the invalid contentOffset as its scroll position.
1166         // To synchronize the WebProcess with what is on screen, we send the VisibleContentRect again.
1167         _page->resendLastVisibleContentRects();
1168     }
1169 }
1170
1171 - (BOOL)_scrollToRect:(WebCore::FloatRect)targetRect origin:(WebCore::FloatPoint)origin minimumScrollDistance:(float)minimumScrollDistance
1172 {
1173     WebCore::FloatRect unobscuredContentRect([self _contentRectForUserInteraction]);
1174     WebCore::FloatPoint unobscuredContentOffset = unobscuredContentRect.location();
1175     WebCore::FloatSize contentSize([self._currentContentView bounds].size);
1176
1177     // Center the target rect in the scroll view.
1178     // If the target doesn't fit in the scroll view, center on the gesture location instead.
1179     WebCore::FloatPoint newUnobscuredContentOffset;
1180     if (targetRect.width() <= unobscuredContentRect.width())
1181         newUnobscuredContentOffset.setX(targetRect.x() - (unobscuredContentRect.width() - targetRect.width()) / 2);
1182     else
1183         newUnobscuredContentOffset.setX(origin.x() - unobscuredContentRect.width() / 2);
1184     if (targetRect.height() <= unobscuredContentRect.height())
1185         newUnobscuredContentOffset.setY(targetRect.y() - (unobscuredContentRect.height() - targetRect.height()) / 2);
1186     else
1187         newUnobscuredContentOffset.setY(origin.y() - unobscuredContentRect.height() / 2);
1188     newUnobscuredContentOffset = constrainContentOffset(newUnobscuredContentOffset, contentSize, unobscuredContentRect.size());
1189
1190     if (unobscuredContentOffset == newUnobscuredContentOffset) {
1191         if (targetRect.width() > unobscuredContentRect.width())
1192             newUnobscuredContentOffset.setX(origin.x() - unobscuredContentRect.width() / 2);
1193         if (targetRect.height() > unobscuredContentRect.height())
1194             newUnobscuredContentOffset.setY(origin.y() - unobscuredContentRect.height() / 2);
1195         newUnobscuredContentOffset = constrainContentOffset(newUnobscuredContentOffset, contentSize, unobscuredContentRect.size());
1196     }
1197
1198     WebCore::FloatSize scrollViewOffsetDelta = newUnobscuredContentOffset - unobscuredContentOffset;
1199     scrollViewOffsetDelta.scale(contentZoomScale(self));
1200
1201     float scrollDistance = scrollViewOffsetDelta.diagonalLength();
1202     if (scrollDistance < minimumScrollDistance)
1203         return false;
1204
1205     [_contentView willStartZoomOrScroll];
1206
1207     [_scrollView setContentOffset:([_scrollView contentOffset] + scrollViewOffsetDelta) animated:YES];
1208     return true;
1209 }
1210
1211 - (void)_scrollByOffset:(WebCore::FloatPoint)offset
1212 {
1213     CGPoint currentOffset = ([_scrollView _isAnimatingScroll]) ? [_scrollView _animatedTargetOffset] : [_scrollView contentOffset];
1214
1215     CGPoint boundedOffset = contentOffsetBoundedInValidRange(_scrollView.get(), currentOffset + offset);
1216     
1217     if (CGPointEqualToPoint(boundedOffset, currentOffset))
1218         return;
1219     [_contentView willStartZoomOrScroll];
1220     [_scrollView setContentOffset:boundedOffset animated:YES];
1221 }
1222
1223 - (void)_zoomOutWithOrigin:(WebCore::FloatPoint)origin animated:(BOOL)animated
1224 {
1225     [self _zoomToPoint:origin atScale:[_scrollView minimumZoomScale] animated:animated];
1226 }
1227
1228 // focusedElementRect and selectionRect are both in document coordinates.
1229 - (void)_zoomToFocusRect:(WebCore::FloatRect)focusedElementRectInDocumentCoordinates selectionRect:(WebCore::FloatRect)selectionRectInDocumentCoordinates fontSize:(float)fontSize minimumScale:(double)minimumScale maximumScale:(double)maximumScale allowScaling:(BOOL)allowScaling forceScroll:(BOOL)forceScroll
1230 {
1231     const double WKWebViewStandardFontSize = 16;
1232     const double kMinimumHeightToShowContentAboveKeyboard = 106;
1233     const CFTimeInterval UIWebFormAnimationDuration = 0.25;
1234     const double CaretOffsetFromWindowEdge = 20;
1235
1236     // Zoom around the element's bounding frame. We use a "standard" size to determine the proper frame.
1237     double scale = allowScaling ? std::min(std::max(WKWebViewStandardFontSize / fontSize, minimumScale), maximumScale) : contentZoomScale(self);
1238     CGFloat documentWidth = [_contentView bounds].size.width;
1239     scale = CGRound(documentWidth * scale) / documentWidth;
1240
1241     UIWindow *window = [_scrollView window];
1242
1243     WebCore::FloatRect focusedElementRectInNewScale = focusedElementRectInDocumentCoordinates;
1244     focusedElementRectInNewScale.scale(scale);
1245     focusedElementRectInNewScale.moveBy([_contentView frame].origin);
1246
1247     // Find the portion of the view that is visible on the screen.
1248     UIViewController *topViewController = [[[_scrollView _viewControllerForAncestor] _rootAncestorViewController] _viewControllerForSupportedInterfaceOrientations];
1249     UIView *fullScreenView = topViewController.view;
1250     if (!fullScreenView)
1251         fullScreenView = window;
1252
1253     CGRect unobscuredScrollViewRectInWebViewCoordinates = UIEdgeInsetsInsetRect([self bounds], _obscuredInsets);
1254     CGRect visibleScrollViewBoundsInWebViewCoordinates = CGRectIntersection(unobscuredScrollViewRectInWebViewCoordinates, [fullScreenView convertRect:[fullScreenView bounds] toView:self]);
1255     CGRect formAssistantFrameInWebViewCoordinates = [window convertRect:_inputViewBounds toView:self];
1256     CGRect intersectionBetweenScrollViewAndFormAssistant = CGRectIntersection(visibleScrollViewBoundsInWebViewCoordinates, formAssistantFrameInWebViewCoordinates);
1257     CGSize visibleSize = visibleScrollViewBoundsInWebViewCoordinates.size;
1258
1259     CGFloat visibleOffsetFromTop = 0;
1260     if (!CGRectIsEmpty(intersectionBetweenScrollViewAndFormAssistant)) {
1261         CGFloat heightVisibleAboveFormAssistant = CGRectGetMinY(intersectionBetweenScrollViewAndFormAssistant) - CGRectGetMinY(visibleScrollViewBoundsInWebViewCoordinates);
1262         CGFloat heightVisibleBelowFormAssistant = CGRectGetMaxY(visibleScrollViewBoundsInWebViewCoordinates) - CGRectGetMaxY(intersectionBetweenScrollViewAndFormAssistant);
1263
1264         if (heightVisibleAboveFormAssistant >= kMinimumHeightToShowContentAboveKeyboard || heightVisibleBelowFormAssistant < heightVisibleAboveFormAssistant)
1265             visibleSize.height = heightVisibleAboveFormAssistant;
1266         else {
1267             visibleSize.height = heightVisibleBelowFormAssistant;
1268             visibleOffsetFromTop = CGRectGetMaxY(intersectionBetweenScrollViewAndFormAssistant) - CGRectGetMinY(visibleScrollViewBoundsInWebViewCoordinates);
1269         }
1270     }
1271
1272     BOOL selectionRectIsNotNull = !selectionRectInDocumentCoordinates.isZero();
1273     if (!forceScroll) {
1274         CGRect currentlyVisibleRegionInWebViewCoordinates;
1275         currentlyVisibleRegionInWebViewCoordinates.origin = unobscuredScrollViewRectInWebViewCoordinates.origin;
1276         currentlyVisibleRegionInWebViewCoordinates.origin.y += visibleOffsetFromTop;
1277         currentlyVisibleRegionInWebViewCoordinates.size = visibleSize;
1278
1279         // Don't bother scrolling if the entire node is already visible, whether or not we got a selectionRect.
1280         if (CGRectContainsRect(currentlyVisibleRegionInWebViewCoordinates, [self convertRect:focusedElementRectInDocumentCoordinates fromView:_contentView.get()]))
1281             return;
1282
1283         // Don't bother scrolling if we have a valid selectionRect and it is already visible.
1284         if (selectionRectIsNotNull && CGRectContainsRect(currentlyVisibleRegionInWebViewCoordinates, [self convertRect:selectionRectInDocumentCoordinates fromView:_contentView.get()]))
1285             return;
1286     }
1287
1288     // We want to zoom to the left/top corner of the DOM node, with as much spacing on all sides as we
1289     // can get based on the visible area after zooming (workingFrame).  The spacing in either dimension is half the
1290     // difference between the size of the DOM node and the size of the visible frame.
1291     CGFloat horizontalSpaceInWebViewCoordinates = std::max((visibleSize.width - focusedElementRectInNewScale.width()) / 2.0, 0.0);
1292     CGFloat verticalSpaceInWebViewCoordinates = std::max((visibleSize.height - focusedElementRectInNewScale.height()) / 2.0, 0.0);
1293
1294     CGPoint topLeft;
1295     topLeft.x = focusedElementRectInNewScale.x() - horizontalSpaceInWebViewCoordinates;
1296     topLeft.y = focusedElementRectInNewScale.y() - verticalSpaceInWebViewCoordinates - visibleOffsetFromTop;
1297
1298     CGFloat minimumAllowableHorizontalOffsetInWebViewCoordinates = -INFINITY;
1299     CGFloat minimumAllowableVerticalOffsetInWebViewCoordinates = -INFINITY;
1300     if (selectionRectIsNotNull) {
1301         WebCore::FloatRect selectionRectInNewScale = selectionRectInDocumentCoordinates;
1302         selectionRectInNewScale.scale(scale);
1303         selectionRectInNewScale.moveBy([_contentView frame].origin);
1304         minimumAllowableHorizontalOffsetInWebViewCoordinates = CGRectGetMaxX(selectionRectInNewScale) + CaretOffsetFromWindowEdge - visibleSize.width;
1305         minimumAllowableVerticalOffsetInWebViewCoordinates = CGRectGetMaxY(selectionRectInNewScale) + CaretOffsetFromWindowEdge - visibleSize.height - visibleOffsetFromTop;
1306     }
1307
1308     WebCore::FloatRect documentBoundsInNewScale = [_contentView bounds];
1309     documentBoundsInNewScale.scale(scale);
1310     documentBoundsInNewScale.moveBy([_contentView frame].origin);
1311
1312     // Constrain the left edge in document coordinates so that:
1313     //  - it isn't so small that the scrollVisibleRect isn't visible on the screen
1314     //  - it isn't so great that the document's right edge is less than the right edge of the screen
1315     if (selectionRectIsNotNull && topLeft.x < minimumAllowableHorizontalOffsetInWebViewCoordinates)
1316         topLeft.x = minimumAllowableHorizontalOffsetInWebViewCoordinates;
1317     else {
1318         CGFloat maximumAllowableHorizontalOffset = CGRectGetMaxX(documentBoundsInNewScale) - visibleSize.width;
1319         if (topLeft.x > maximumAllowableHorizontalOffset)
1320             topLeft.x = maximumAllowableHorizontalOffset;
1321     }
1322
1323     // Constrain the top edge in document coordinates so that:
1324     //  - it isn't so small that the scrollVisibleRect isn't visible on the screen
1325     //  - it isn't so great that the document's bottom edge is higher than the top of the form assistant
1326     if (selectionRectIsNotNull && topLeft.y < minimumAllowableVerticalOffsetInWebViewCoordinates)
1327         topLeft.y = minimumAllowableVerticalOffsetInWebViewCoordinates;
1328     else {
1329         CGFloat maximumAllowableVerticalOffset = CGRectGetMaxY(documentBoundsInNewScale) - visibleSize.height;
1330         if (topLeft.y > maximumAllowableVerticalOffset)
1331             topLeft.y = maximumAllowableVerticalOffset;
1332     }
1333
1334     WebCore::FloatPoint newCenter = CGPointMake(topLeft.x + unobscuredScrollViewRectInWebViewCoordinates.size.width / 2.0, topLeft.y + unobscuredScrollViewRectInWebViewCoordinates.size.height / 2.0);
1335
1336     if (scale != contentZoomScale(self))
1337         _page->willStartUserTriggeredZooming();
1338
1339     // The newCenter has been computed in the new scale, but _zoomToCenter expected the center to be in the original scale.
1340     newCenter.scale(1 / scale, 1 / scale);
1341     [_scrollView _zoomToCenter:newCenter
1342                         scale:scale
1343                      duration:UIWebFormAnimationDuration
1344                         force:YES];
1345 }
1346
1347 - (BOOL)_zoomToRect:(WebCore::FloatRect)targetRect withOrigin:(WebCore::FloatPoint)origin fitEntireRect:(BOOL)fitEntireRect minimumScale:(double)minimumScale maximumScale:(double)maximumScale minimumScrollDistance:(float)minimumScrollDistance
1348 {
1349     const float maximumScaleFactorDeltaForPanScroll = 0.02;
1350
1351     double currentScale = contentZoomScale(self);
1352
1353     WebCore::FloatSize unobscuredContentSize([self _contentRectForUserInteraction].size);
1354     double horizontalScale = unobscuredContentSize.width() * currentScale / targetRect.width();
1355     double verticalScale = unobscuredContentSize.height() * currentScale / targetRect.height();
1356
1357     horizontalScale = std::min(std::max(horizontalScale, minimumScale), maximumScale);
1358     verticalScale = std::min(std::max(verticalScale, minimumScale), maximumScale);
1359
1360     double targetScale = fitEntireRect ? std::min(horizontalScale, verticalScale) : horizontalScale;
1361     if (fabs(targetScale - currentScale) < maximumScaleFactorDeltaForPanScroll) {
1362         if ([self _scrollToRect:targetRect origin:origin minimumScrollDistance:minimumScrollDistance])
1363             return true;
1364     } else if (targetScale != currentScale) {
1365         [self _zoomToRect:targetRect atScale:targetScale origin:origin animated:YES];
1366         return true;
1367     }
1368     
1369     return false;
1370 }
1371
1372 - (void)didMoveToWindow
1373 {
1374     _page->viewStateDidChange(WebCore::ViewState::AllFlags);
1375 }
1376
1377 - (void)setOpaque:(BOOL)opaque
1378 {
1379     BOOL oldOpaque = self.opaque;
1380
1381     [super setOpaque:opaque];
1382     [_contentView setOpaque:opaque];
1383
1384     if (oldOpaque == opaque)
1385         return;
1386
1387     _page->setDrawsBackground(opaque);
1388     [self _updateScrollViewBackground];
1389 }
1390
1391 - (void)setBackgroundColor:(UIColor *)backgroundColor
1392 {
1393     [super setBackgroundColor:backgroundColor];
1394     [_contentView setBackgroundColor:backgroundColor];
1395 }
1396
1397 #pragma mark - UIScrollViewDelegate
1398
1399 - (BOOL)usesStandardContentView
1400 {
1401     return !_customContentView;
1402 }
1403
1404 - (CGSize)scrollView:(UIScrollView*)scrollView contentSizeForZoomScale:(CGFloat)scale withProposedSize:(CGSize)proposedSize
1405 {
1406     return roundScrollViewContentSize(*_page, proposedSize);
1407 }
1408
1409 - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
1410 {
1411     ASSERT(_scrollView == scrollView);
1412
1413     if (_customContentView)
1414         return _customContentView.get();
1415
1416     return _contentView.get();
1417 }
1418
1419 - (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view
1420 {
1421     if (![self usesStandardContentView])
1422         return;
1423
1424     if (scrollView.pinchGestureRecognizer.state == UIGestureRecognizerStateBegan) {
1425         _page->willStartUserTriggeredZooming();
1426         [_contentView scrollViewWillStartPanOrPinchGesture];
1427     }
1428     [_contentView willStartZoomOrScroll];
1429 }
1430
1431 - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
1432 {
1433     if (![self usesStandardContentView])
1434         return;
1435
1436     if (scrollView.panGestureRecognizer.state == UIGestureRecognizerStateBegan)
1437         [_contentView scrollViewWillStartPanOrPinchGesture];
1438
1439     [_contentView willStartZoomOrScroll];
1440 #if ENABLE(CSS_SCROLL_SNAP) && ENABLE(ASYNC_SCROLLING)
1441     // FIXME: We will want to detect whether snapping will occur before beginning to drag. See WebPageProxy::didCommitLayerTree.
1442     WebKit::RemoteScrollingCoordinatorProxy* coordinator = _page->scrollingCoordinatorProxy();
1443     ASSERT(scrollView == _scrollView.get());
1444     scrollView.decelerationRate = (coordinator && coordinator->shouldSetScrollViewDecelerationRateFast()) ? UIScrollViewDecelerationRateFast : [_scrollView preferredScrollDecelerationFactor];
1445 #endif
1446 }
1447
1448 - (void)_didFinishScrolling
1449 {
1450     if (![self usesStandardContentView])
1451         return;
1452
1453     [self _updateVisibleContentRects];
1454     [_contentView didFinishScrolling];
1455 }
1456
1457 - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
1458 {
1459     // Work around <rdar://problem/16374753> by avoiding deceleration while
1460     // zooming. We'll animate to the right place once the zoom finishes.
1461     if ([scrollView isZooming])
1462         *targetContentOffset = [scrollView contentOffset];
1463 #if ENABLE(CSS_SCROLL_SNAP) && ENABLE(ASYNC_SCROLLING)
1464     if (WebKit::RemoteScrollingCoordinatorProxy* coordinator = _page->scrollingCoordinatorProxy()) {
1465         // FIXME: Here, I'm finding the maximum horizontal/vertical scroll offsets. There's probably a better way to do this.
1466         CGSize maxScrollOffsets = CGSizeMake(scrollView.contentSize.width - scrollView.bounds.size.width, scrollView.contentSize.height - scrollView.bounds.size.height);
1467         
1468         CGRect fullViewRect = self.bounds;
1469         CGRect unobscuredRect = UIEdgeInsetsInsetRect(fullViewRect, [self _computedContentInset]);
1470         
1471         coordinator->adjustTargetContentOffsetForSnapping(maxScrollOffsets, velocity, unobscuredRect.origin.y, targetContentOffset);
1472     }
1473 #endif
1474 }
1475
1476 - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
1477 {
1478     // If we're decelerating, scroll offset will be updated when scrollViewDidFinishDecelerating: is called.
1479     if (!decelerate)
1480         [self _didFinishScrolling];
1481 }
1482
1483 - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
1484 {
1485     [self _didFinishScrolling];
1486 }
1487
1488 - (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView
1489 {
1490     [self _didFinishScrolling];
1491 }
1492
1493 - (void)scrollViewDidScroll:(UIScrollView *)scrollView
1494 {
1495     if (![self usesStandardContentView])
1496         [_customContentView scrollViewDidScroll:(UIScrollView *)scrollView];
1497
1498     [self _updateVisibleContentRects];
1499     
1500     if (WebKit::RemoteLayerTreeScrollingPerformanceData* scrollPerfData = _page->scrollingPerformanceData())
1501         scrollPerfData->didScroll([self visibleRectInViewCoordinates]);
1502 }
1503
1504 - (void)scrollViewDidZoom:(UIScrollView *)scrollView
1505 {
1506     [self _updateScrollViewBackground];
1507     [self _updateVisibleContentRects];
1508 }
1509
1510 - (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale
1511 {
1512     ASSERT(scrollView == _scrollView);
1513     [self _updateVisibleContentRects];
1514     [_contentView didZoomToScale:scale];
1515 }
1516
1517 - (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
1518 {
1519     [self _didFinishScrolling];
1520 }
1521
1522 - (void)_scrollViewDidInterruptDecelerating:(UIScrollView *)scrollView
1523 {
1524     if (![self usesStandardContentView])
1525         return;
1526
1527     [_contentView didInterruptScrolling];
1528     [self _updateVisibleContentRects];
1529 }
1530
1531 - (void)_frameOrBoundsChanged
1532 {
1533     CGRect bounds = self.bounds;
1534     [_scrollView setFrame:bounds];
1535
1536     if (_dynamicViewportUpdateMode == DynamicViewportUpdateMode::NotResizing) {
1537         if (!_overridesMinimumLayoutSize)
1538             _page->setViewportConfigurationMinimumLayoutSize(WebCore::FloatSize(bounds.size));
1539         if (!_overridesMaximumUnobscuredSize)
1540             _page->setMaximumUnobscuredSize(WebCore::FloatSize(bounds.size));
1541         if (WebKit::DrawingAreaProxy* drawingArea = _page->drawingArea())
1542             drawingArea->setSize(WebCore::IntSize(bounds.size), WebCore::IntSize(), WebCore::IntSize());
1543     }
1544
1545     [_customContentView web_setMinimumSize:bounds.size];
1546     [self _updateVisibleContentRects];
1547 }
1548
1549 // 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.
1550 - (CGRect)_contentRectForUserInteraction
1551 {
1552     // FIXME: handle split keyboard.
1553     UIEdgeInsets obscuredInsets = _obscuredInsets;
1554     obscuredInsets.bottom = std::max(_obscuredInsets.bottom, _inputViewBounds.size.height);
1555     CGRect unobscuredRect = UIEdgeInsetsInsetRect(self.bounds, obscuredInsets);
1556     return [self convertRect:unobscuredRect toView:self._currentContentView];
1557 }
1558
1559 // Ideally UIScrollView would expose this for us: <rdar://problem/21394567>.
1560 - (BOOL)_scrollViewIsRubberBanding
1561 {
1562     float deviceScaleFactor = _page->deviceScaleFactor();
1563
1564     CGPoint contentOffset = [_scrollView contentOffset];
1565     CGPoint boundedOffset = contentOffsetBoundedInValidRange(_scrollView.get(), contentOffset);
1566     return !pointsEqualInDevicePixels(contentOffset, boundedOffset, deviceScaleFactor);
1567 }
1568
1569 - (void)_updateVisibleContentRects
1570 {
1571     if (![self usesStandardContentView]) {
1572         [_customContentView web_computedContentInsetDidChange];
1573         return;
1574     }
1575
1576     if (_delayUpdateVisibleContentRects) {
1577         _hadDelayedUpdateVisibleContentRects = YES;
1578         return;
1579     }
1580
1581     if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing)
1582         return;
1583
1584     if (_needsResetViewStateAfterCommitLoadForMainFrame)
1585         return;
1586
1587     if ([_scrollView isZoomBouncing])
1588         return;
1589
1590     CGRect fullViewRect = self.bounds;
1591     CGRect visibleRectInContentCoordinates = _frozenVisibleContentRect ? _frozenVisibleContentRect.value() : [self convertRect:fullViewRect toView:_contentView.get()];
1592
1593     CGRect unobscuredRect = UIEdgeInsetsInsetRect(fullViewRect, [self _computedContentInset]);
1594     CGRect unobscuredRectInContentCoordinates = _frozenUnobscuredContentRect ? _frozenUnobscuredContentRect.value() : [self convertRect:unobscuredRect toView:_contentView.get()];
1595
1596     CGFloat scaleFactor = contentZoomScale(self);
1597
1598     BOOL isStableState = !(_isChangingObscuredInsetsInteractively || [_scrollView isDragging] || [_scrollView isDecelerating] || [_scrollView isZooming] || [_scrollView _isAnimatingZoom] || [_scrollView _isScrollingToTop] || [self _scrollViewIsRubberBanding]);
1599
1600     // FIXME: this can be made static after we stop supporting iOS 8.x.
1601     if (isStableState && [_scrollView respondsToSelector:@selector(_isInterruptingDeceleration)])
1602         isStableState = ![_scrollView performSelector:@selector(_isInterruptingDeceleration)];
1603
1604 #if ENABLE(CSS_SCROLL_SNAP) && ENABLE(ASYNC_SCROLLING)
1605     if (isStableState) {
1606         WebKit::RemoteScrollingCoordinatorProxy* coordinator = _page->scrollingCoordinatorProxy();
1607         if (coordinator && coordinator->hasActiveSnapPoint()) {
1608             CGRect fullViewRect = self.bounds;
1609             CGRect unobscuredRect = UIEdgeInsetsInsetRect(fullViewRect, [self _computedContentInset]);
1610             
1611             CGPoint currentPoint = [_scrollView contentOffset];
1612             CGPoint activePoint = coordinator->nearestActiveContentInsetAdjustedSnapPoint(unobscuredRect.origin.y, currentPoint);
1613
1614             if (!CGPointEqualToPoint(activePoint, currentPoint)) {
1615                 RetainPtr<WKScrollView> strongScrollView = _scrollView;
1616                 dispatch_async(dispatch_get_main_queue(), [strongScrollView, activePoint] {
1617                     [strongScrollView setContentOffset:activePoint animated:NO];
1618                 });
1619             }
1620         }
1621     }
1622 #endif
1623     
1624     [_contentView didUpdateVisibleRect:visibleRectInContentCoordinates
1625         unobscuredRect:unobscuredRectInContentCoordinates
1626         unobscuredRectInScrollViewCoordinates:unobscuredRect
1627         scale:scaleFactor minimumScale:[_scrollView minimumZoomScale]
1628         inStableState:isStableState isChangingObscuredInsetsInteractively:_isChangingObscuredInsetsInteractively];
1629 }
1630
1631 - (void)_didFinishLoadForMainFrame
1632 {
1633     if (_gestureController)
1634         _gestureController->didFinishLoadForMainFrame();
1635 }
1636
1637 - (void)_didFailLoadForMainFrame
1638 {
1639     if (_gestureController)
1640         _gestureController->didFailLoadForMainFrame();
1641 }
1642
1643 - (void)_didSameDocumentNavigationForMainFrame:(WebKit::SameDocumentNavigationType)navigationType
1644 {
1645     [_customContentView web_didSameDocumentNavigation:toAPI(navigationType)];
1646
1647     if (_gestureController)
1648         _gestureController->didSameDocumentNavigationForMainFrame(navigationType);
1649 }
1650
1651 - (void)_keyboardChangedWithInfo:(NSDictionary *)keyboardInfo adjustScrollView:(BOOL)adjustScrollView
1652 {
1653     NSValue *endFrameValue = [keyboardInfo objectForKey:UIKeyboardFrameEndUserInfoKey];
1654     if (!endFrameValue)
1655         return;
1656
1657     // The keyboard rect is always in screen coordinates. In the view services case the window does not
1658     // have the interface orientation rotation transformation; its host does. So, it makes no sense to
1659     // clip the keyboard rect against its screen.
1660     if ([[self window] _isHostedInAnotherProcess])
1661         _inputViewBounds = [self.window convertRect:[endFrameValue CGRectValue] fromWindow:nil];
1662     else
1663         _inputViewBounds = [self.window convertRect:CGRectIntersection([endFrameValue CGRectValue], self.window.screen.bounds) fromWindow:nil];
1664
1665     [self _updateVisibleContentRects];
1666
1667     if (adjustScrollView)
1668         [_scrollView _adjustForAutomaticKeyboardInfo:keyboardInfo animated:YES lastAdjustment:&_lastAdjustmentForScroller];
1669 }
1670
1671 - (BOOL)_shouldUpdateKeyboardWithInfo:(NSDictionary *)keyboardInfo
1672 {
1673     if ([_contentView isAssistingNode])
1674         return YES;
1675
1676     NSNumber *isLocalKeyboard = [keyboardInfo valueForKey:UIKeyboardIsLocalUserInfoKey];
1677     return isLocalKeyboard && !isLocalKeyboard.boolValue;
1678 }
1679
1680 - (void)_keyboardWillChangeFrame:(NSNotification *)notification
1681 {
1682     if ([self _shouldUpdateKeyboardWithInfo:notification.userInfo])
1683         [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES];
1684 }
1685
1686 - (void)_keyboardDidChangeFrame:(NSNotification *)notification
1687 {
1688     [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:NO];
1689 }
1690
1691 - (void)_keyboardWillShow:(NSNotification *)notification
1692 {
1693     if ([self _shouldUpdateKeyboardWithInfo:notification.userInfo])
1694         [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES];
1695 }
1696
1697 - (void)_keyboardWillHide:(NSNotification *)notification
1698 {
1699     // Ignore keyboard will hide notifications sent during rotation. They're just there for
1700     // backwards compatibility reasons and processing the will hide notification would
1701     // temporarily screw up the the unobscured view area.
1702     if ([[UIPeripheralHost sharedInstance] rotationState])
1703         return;
1704
1705     [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES];
1706 }
1707
1708 - (void)_windowDidRotate:(NSNotification *)notification
1709 {
1710     if (!_overridesInterfaceOrientation)
1711         _page->setDeviceOrientation(deviceOrientation());
1712 }
1713
1714 - (void)_contentSizeCategoryDidChange:(NSNotification *)notification
1715 {
1716     _page->contentSizeCategoryDidChange([self _contentSizeCategory]);
1717 }
1718
1719 - (NSString *)_contentSizeCategory
1720 {
1721     return [[UIApplication sharedApplication] preferredContentSizeCategory];
1722 }
1723
1724 - (void)setAllowsBackForwardNavigationGestures:(BOOL)allowsBackForwardNavigationGestures
1725 {
1726     if (_allowsBackForwardNavigationGestures == allowsBackForwardNavigationGestures)
1727         return;
1728
1729     _allowsBackForwardNavigationGestures = allowsBackForwardNavigationGestures;
1730
1731     if (allowsBackForwardNavigationGestures) {
1732         if (!_gestureController) {
1733             _gestureController = std::make_unique<WebKit::ViewGestureController>(*_page);
1734             _gestureController->installSwipeHandler(self, [self scrollView]);
1735             _gestureController->setAlternateBackForwardListSourceView([_configuration _alternateWebViewForNavigationGestures]);
1736         }
1737     } else
1738         _gestureController = nullptr;
1739
1740     _page->setShouldRecordNavigationSnapshots(allowsBackForwardNavigationGestures);
1741 }
1742
1743 - (BOOL)allowsBackForwardNavigationGestures
1744 {
1745     return _allowsBackForwardNavigationGestures;
1746 }
1747
1748 - (void)_navigationGestureDidBegin
1749 {
1750     // During a back/forward swipe, there's a view interposed between this view and the content view that has
1751     // an offset and animation on it, which results in computing incorrect rectangles. Work around by using
1752     // frozen rects during swipes.
1753     CGRect fullViewRect = self.bounds;
1754     CGRect unobscuredRect = UIEdgeInsetsInsetRect(fullViewRect, [self _computedContentInset]);
1755
1756     _frozenVisibleContentRect = [self convertRect:fullViewRect toView:_contentView.get()];
1757     _frozenUnobscuredContentRect = [self convertRect:unobscuredRect toView:_contentView.get()];
1758 }
1759
1760 - (void)_navigationGestureDidEnd
1761 {
1762     _frozenVisibleContentRect = Nullopt;
1763     _frozenUnobscuredContentRect = Nullopt;
1764 }
1765
1766 #endif // PLATFORM(IOS)
1767
1768 #pragma mark OS X-specific methods
1769
1770 #if PLATFORM(MAC)
1771
1772 - (BOOL)becomeFirstResponder
1773 {
1774     return [[self window] makeFirstResponder: _wkView.get()];
1775 }
1776
1777 - (BOOL)acceptsFirstResponder
1778 {
1779     return [_wkView acceptsFirstResponder];
1780 }
1781
1782 - (void)resizeSubviewsWithOldSize:(NSSize)oldSize
1783 {
1784     [_wkView setFrame:self.bounds];
1785 }
1786
1787 - (void)setAllowsBackForwardNavigationGestures:(BOOL)allowsBackForwardNavigationGestures
1788 {
1789     [_wkView setAllowsBackForwardNavigationGestures:allowsBackForwardNavigationGestures];
1790 }
1791
1792 - (BOOL)allowsBackForwardNavigationGestures
1793 {
1794     return [_wkView allowsBackForwardNavigationGestures];
1795 }
1796
1797 - (void)setAllowsMagnification:(BOOL)allowsMagnification
1798 {
1799     [_wkView setAllowsMagnification:allowsMagnification];
1800 }
1801
1802 - (BOOL)allowsMagnification
1803 {
1804     return [_wkView allowsMagnification];
1805 }
1806
1807 - (void)setMagnification:(CGFloat)magnification
1808 {
1809     [_wkView setMagnification:magnification];
1810 }
1811
1812 - (CGFloat)magnification
1813 {
1814     return [_wkView magnification];
1815 }
1816
1817 - (void)setMagnification:(CGFloat)magnification centeredAtPoint:(CGPoint)point
1818 {
1819     [_wkView setMagnification:magnification centeredAtPoint:NSPointFromCGPoint(point)];
1820 }
1821
1822 - (BOOL)_ignoresNonWheelEvents
1823 {
1824     return [_wkView _ignoresNonWheelEvents];
1825 }
1826
1827 - (void)_setIgnoresNonWheelEvents:(BOOL)ignoresNonWheelEvents
1828 {
1829     [_wkView _setIgnoresNonWheelEvents:ignoresNonWheelEvents];
1830 }
1831
1832 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
1833 {
1834     return [_wkView draggingEntered:sender];
1835 }
1836
1837 - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
1838 {
1839     return [_wkView draggingUpdated:sender];
1840 }
1841
1842 - (void)draggingExited:(id <NSDraggingInfo>)sender
1843 {
1844     [_wkView draggingExited:sender];
1845 }
1846
1847 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
1848 {
1849     return [_wkView prepareForDragOperation:sender];
1850 }
1851
1852 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
1853 {
1854     return [_wkView performDragOperation:sender];
1855 }
1856 #endif // PLATFORM(MAC)
1857
1858 @end
1859
1860 @implementation WKWebView (WKPrivate)
1861
1862 - (WKNavigation *)_loadRequest:(NSURLRequest *)request withOptions:(NSDictionary *)loadOptions
1863 {
1864     bool shouldOpenExternalURLs = [loadOptions[_WKShouldOpenExternalURLsKey] boolValue];
1865     WebCore::ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicy = shouldOpenExternalURLs ? WebCore::ShouldOpenExternalURLsPolicy::ShouldAllow : WebCore::ShouldOpenExternalURLsPolicy::ShouldNotAllow;
1866
1867     auto navigation = _page->loadRequest(request, shouldOpenExternalURLsPolicy);
1868     if (!navigation)
1869         return nil;
1870
1871     return [wrapper(*navigation.release().leakRef()) autorelease];
1872 }
1873
1874 - (BOOL)_isEditable
1875 {
1876     return _page->isEditable();
1877 }
1878
1879 - (void)_setEditable:(BOOL)editable
1880 {
1881     _page->setEditable(editable);
1882 #if !PLATFORM(IOS)
1883     [_wkView _addFontPanelObserver];
1884 #endif
1885 }
1886
1887 - (_WKRemoteObjectRegistry *)_remoteObjectRegistry
1888 {
1889     if (!_remoteObjectRegistry) {
1890         _remoteObjectRegistry = adoptNS([[_WKRemoteObjectRegistry alloc] _initWithMessageSender:*_page]);
1891         _page->process().processPool().addMessageReceiver(Messages::RemoteObjectRegistry::messageReceiverName(), _page->pageID(), [_remoteObjectRegistry remoteObjectRegistry]);
1892     }
1893
1894     return _remoteObjectRegistry.get();
1895 }
1896
1897 - (WKBrowsingContextHandle *)_handle
1898 {
1899     return [[[WKBrowsingContextHandle alloc] _initWithPageID:_page->pageID()] autorelease];
1900 }
1901
1902 - (_WKRenderingProgressEvents)_observedRenderingProgressEvents
1903 {
1904     return _observedRenderingProgressEvents;
1905 }
1906
1907 - (id <WKHistoryDelegatePrivate>)_historyDelegate
1908 {
1909     return _navigationState->historyDelegate().autorelease();
1910 }
1911
1912 - (void)_setHistoryDelegate:(id <WKHistoryDelegatePrivate>)historyDelegate
1913 {
1914     _page->setHistoryClient(_navigationState->createHistoryClient());
1915     _navigationState->setHistoryDelegate(historyDelegate);
1916 }
1917
1918 - (NSURL *)_unreachableURL
1919 {
1920     return [NSURL _web_URLWithWTFString:_page->pageLoadState().unreachableURL()];
1921 }
1922
1923 - (void)_loadAlternateHTMLString:(NSString *)string baseURL:(NSURL *)baseURL forUnreachableURL:(NSURL *)unreachableURL
1924 {
1925     _page->loadAlternateHTMLString(string, [baseURL _web_originalDataAsWTFString], [unreachableURL _web_originalDataAsWTFString]);
1926 }
1927
1928 - (NSArray *)_certificateChain
1929 {
1930     if (WebKit::WebFrameProxy* mainFrame = _page->mainFrame())
1931         return mainFrame->certificateInfo() ? (NSArray *)mainFrame->certificateInfo()->certificateInfo().certificateChain() : nil;
1932
1933     return nil;
1934 }
1935
1936 - (NSURL *)_committedURL
1937 {
1938     return [NSURL _web_URLWithWTFString:_page->pageLoadState().url()];
1939 }
1940
1941 - (NSString *)_MIMEType
1942 {
1943     if (_page->mainFrame())
1944         return _page->mainFrame()->mimeType();
1945
1946     return nil;
1947 }
1948
1949 - (NSString *)_userAgent
1950 {
1951     return _page->userAgent();
1952 }
1953
1954 - (NSString *)_applicationNameForUserAgent
1955 {
1956     return _page->applicationNameForUserAgent();
1957 }
1958
1959 - (void)_setApplicationNameForUserAgent:(NSString *)applicationNameForUserAgent
1960 {
1961     _page->setApplicationNameForUserAgent(applicationNameForUserAgent);
1962 }
1963
1964 - (NSString *)_customUserAgent
1965 {
1966     return self.customUserAgent;
1967 }
1968
1969 - (void)_setCustomUserAgent:(NSString *)customUserAgent
1970 {
1971     self.customUserAgent = customUserAgent;
1972 }
1973
1974 - (void)_setUserContentExtensionsEnabled:(BOOL)userContentExtensionsEnabled
1975 {
1976     _page->setUserContentExtensionsEnabled(userContentExtensionsEnabled);
1977 }
1978
1979 - (BOOL)_userContentExtensionsEnabled
1980 {
1981     return _page->userContentExtensionsEnabled();
1982 }
1983
1984 - (pid_t)_webProcessIdentifier
1985 {
1986     return _page->isValid() ? _page->processIdentifier() : 0;
1987 }
1988
1989 - (void)_killWebContentProcess
1990 {
1991     if (!_page->isValid())
1992         return;
1993
1994     _page->process().terminate();
1995 }
1996
1997 #if PLATFORM(IOS)
1998 static WebCore::FloatSize activeMinimumLayoutSize(WKWebView *webView, const CGRect& bounds)
1999 {
2000     return WebCore::FloatSize(webView->_overridesMinimumLayoutSize ? webView->_minimumLayoutSizeOverride : bounds.size);
2001 }
2002
2003 static WebCore::FloatSize activeMaximumUnobscuredSize(WKWebView *webView, const CGRect& bounds)
2004 {
2005     return WebCore::FloatSize(webView->_overridesMaximumUnobscuredSize ? webView->_maximumUnobscuredSizeOverride : bounds.size);
2006 }
2007
2008 static int32_t activeOrientation(WKWebView *webView)
2009 {
2010     return webView->_overridesInterfaceOrientation ? deviceOrientationForUIInterfaceOrientation(webView->_interfaceOrientationOverride) : webView->_page->deviceOrientation();
2011 }
2012
2013 - (void (^)(void))_retainActiveFocusedState
2014 {
2015     ++_activeFocusedStateRetainCount;
2016
2017     // FIXME: Use something like CompletionHandlerCallChecker to ensure that the returned block is called before it's released.
2018     return [[[self] {
2019         --_activeFocusedStateRetainCount;
2020     } copy] autorelease];
2021 }
2022
2023 - (void)_becomeFirstResponderWithSelectionMovingForward:(BOOL)selectingForward completionHandler:(void (^)(BOOL didBecomeFirstResponder))completionHandler
2024 {
2025     typeof(completionHandler) completionHandlerCopy = nil;
2026     if (completionHandler)
2027         completionHandlerCopy = Block_copy(completionHandler);
2028
2029     [_contentView _becomeFirstResponderWithSelectionMovingForward:selectingForward completionHandler:[completionHandlerCopy](BOOL didBecomeFirstResponder) {
2030         if (!completionHandlerCopy)
2031             return;
2032
2033         completionHandlerCopy(didBecomeFirstResponder);
2034         Block_release(completionHandlerCopy);
2035     }];
2036 }
2037
2038 - (id)_snapshotLayerContentsForBackForwardListItem:(WKBackForwardListItem *)item
2039 {
2040     if (_page->backForwardList().currentItem() == &item._item)
2041         _page->recordNavigationSnapshot(*_page->backForwardList().currentItem());
2042
2043     if (auto* viewSnapshot = item._item.snapshot())
2044         return viewSnapshot->asLayerContents();
2045
2046     return nil;
2047 }
2048
2049 #endif
2050
2051 - (void)_didRelaunchProcess
2052 {
2053 #if PLATFORM(IOS)
2054     CGRect bounds = self.bounds;
2055     WebCore::FloatSize minimalLayoutSize = activeMinimumLayoutSize(self, bounds);
2056     _page->setViewportConfigurationMinimumLayoutSize(minimalLayoutSize);
2057     _page->setMaximumUnobscuredSize(activeMaximumUnobscuredSize(self, bounds));
2058 #endif
2059 }
2060
2061 - (NSData *)_sessionStateData
2062 {
2063     WebKit::SessionState sessionState = _page->sessionState();
2064
2065     // FIXME: This should not use the legacy session state encoder.
2066     return [wrapper(*WebKit::encodeLegacySessionState(sessionState).release().leakRef()) autorelease];
2067 }
2068
2069 - (_WKSessionState *)_sessionState
2070 {
2071     return adoptNS([[_WKSessionState alloc] _initWithSessionState:_page->sessionState()]).autorelease();
2072 }
2073
2074 - (void)_restoreFromSessionStateData:(NSData *)sessionStateData
2075 {
2076     // FIXME: This should not use the legacy session state decoder.
2077     WebKit::SessionState sessionState;
2078     if (!WebKit::decodeLegacySessionState(static_cast<const uint8_t*>(sessionStateData.bytes), sessionStateData.length, sessionState))
2079         return;
2080
2081     _page->restoreFromSessionState(WTF::move(sessionState), true);
2082 }
2083
2084 - (WKNavigation *)_restoreSessionState:(_WKSessionState *)sessionState andNavigate:(BOOL)navigate
2085 {
2086     auto navigation = _page->restoreFromSessionState(sessionState->_sessionState, navigate);
2087     if (!navigation)
2088         return nil;
2089
2090     return [wrapper(*navigation.release().leakRef()) autorelease];
2091 }
2092
2093 - (void)_close
2094 {
2095     _page->close();
2096 }
2097
2098 - (BOOL)_allowsRemoteInspection
2099 {
2100 #if ENABLE(REMOTE_INSPECTOR)
2101     return _page->allowsRemoteInspection();
2102 #else
2103     return NO;
2104 #endif
2105 }
2106
2107 - (void)_setAllowsRemoteInspection:(BOOL)allow
2108 {
2109 #if ENABLE(REMOTE_INSPECTOR)
2110     _page->setAllowsRemoteInspection(allow);
2111 #endif
2112 }
2113
2114 - (BOOL)_addsVisitedLinks
2115 {
2116     return _page->addsVisitedLinks();
2117 }
2118
2119 - (void)_setAddsVisitedLinks:(BOOL)addsVisitedLinks
2120 {
2121     _page->setAddsVisitedLinks(addsVisitedLinks);
2122 }
2123
2124 - (BOOL)_networkRequestsInProgress
2125 {
2126     return _page->pageLoadState().networkRequestsInProgress();
2127 }
2128
2129 static inline WebCore::LayoutMilestones layoutMilestones(_WKRenderingProgressEvents events)
2130 {
2131     WebCore::LayoutMilestones milestones = 0;
2132
2133     if (events & _WKRenderingProgressEventFirstLayout)
2134         milestones |= WebCore::DidFirstLayout;
2135
2136     if (events & _WKRenderingProgressEventFirstVisuallyNonEmptyLayout)
2137         milestones |= WebCore::DidFirstVisuallyNonEmptyLayout;
2138
2139     if (events & _WKRenderingProgressEventFirstPaintWithSignificantArea)
2140         milestones |= WebCore::DidHitRelevantRepaintedObjectsAreaThreshold;
2141
2142     if (events & _WKRenderingProgressEventReachedSessionRestorationRenderTreeSizeThreshold)
2143         milestones |= WebCore::ReachedSessionRestorationRenderTreeSizeThreshold;
2144
2145     if (events & _WKRenderingProgressEventFirstLayoutAfterSuppressedIncrementalRendering)
2146         milestones |= WebCore::DidFirstLayoutAfterSuppressedIncrementalRendering;
2147
2148     if (events & _WKRenderingProgressEventFirstPaintAfterSuppressedIncrementalRendering)
2149         milestones |= WebCore::DidFirstPaintAfterSuppressedIncrementalRendering;
2150
2151     return milestones;
2152 }
2153
2154 - (void)_setObservedRenderingProgressEvents:(_WKRenderingProgressEvents)observedRenderingProgressEvents
2155 {
2156     _observedRenderingProgressEvents = observedRenderingProgressEvents;
2157     _page->listenForLayoutMilestones(layoutMilestones(observedRenderingProgressEvents));
2158 }
2159
2160 - (void)_getMainResourceDataWithCompletionHandler:(void (^)(NSData *, NSError *))completionHandler
2161 {
2162     auto handler = adoptNS([completionHandler copy]);
2163
2164     _page->getMainResourceDataOfFrame(_page->mainFrame(), [handler](API::Data* data, WebKit::CallbackBase::Error error) {
2165         void (^completionHandlerBlock)(NSData *, NSError *) = (void (^)(NSData *, NSError *))handler.get();
2166         if (error != WebKit::CallbackBase::Error::None) {
2167             // FIXME: Pipe a proper error in from the WebPageProxy.
2168             RetainPtr<NSError> error = adoptNS([[NSError alloc] init]);
2169             completionHandlerBlock(nil, error.get());
2170         } else
2171             completionHandlerBlock(wrapper(*data), nil);
2172     });
2173 }
2174
2175 - (void)_getWebArchiveDataWithCompletionHandler:(void (^)(NSData *, NSError *))completionHandler
2176 {
2177     auto handler = adoptNS([completionHandler copy]);
2178
2179     _page->getWebArchiveOfFrame(_page->mainFrame(), [handler](API::Data* data, WebKit::CallbackBase::Error error) {
2180         void (^completionHandlerBlock)(NSData *, NSError *) = (void (^)(NSData *, NSError *))handler.get();
2181         if (error != WebKit::CallbackBase::Error::None) {
2182             // FIXME: Pipe a proper error in from the WebPageProxy.
2183             RetainPtr<NSError> error = adoptNS([[NSError alloc] init]);
2184             completionHandlerBlock(nil, error.get());
2185         } else
2186             completionHandlerBlock(wrapper(*data), nil);
2187     });
2188 }
2189
2190 - (_WKPaginationMode)_paginationMode
2191 {
2192     switch (_page->paginationMode()) {
2193     case WebCore::Pagination::Unpaginated:
2194         return _WKPaginationModeUnpaginated;
2195     case WebCore::Pagination::LeftToRightPaginated:
2196         return _WKPaginationModeLeftToRight;
2197     case WebCore::Pagination::RightToLeftPaginated:
2198         return _WKPaginationModeRightToLeft;
2199     case WebCore::Pagination::TopToBottomPaginated:
2200         return _WKPaginationModeTopToBottom;
2201     case WebCore::Pagination::BottomToTopPaginated:
2202         return _WKPaginationModeBottomToTop;
2203     }
2204
2205     ASSERT_NOT_REACHED();
2206     return _WKPaginationModeUnpaginated;
2207 }
2208
2209 - (void)_setPaginationMode:(_WKPaginationMode)paginationMode
2210 {
2211     WebCore::Pagination::Mode mode;
2212     switch (paginationMode) {
2213     case _WKPaginationModeUnpaginated:
2214         mode = WebCore::Pagination::Unpaginated;
2215         break;
2216     case _WKPaginationModeLeftToRight:
2217         mode = WebCore::Pagination::LeftToRightPaginated;
2218         break;
2219     case _WKPaginationModeRightToLeft:
2220         mode = WebCore::Pagination::RightToLeftPaginated;
2221         break;
2222     case _WKPaginationModeTopToBottom:
2223         mode = WebCore::Pagination::TopToBottomPaginated;
2224         break;
2225     case _WKPaginationModeBottomToTop:
2226         mode = WebCore::Pagination::BottomToTopPaginated;
2227         break;
2228     default:
2229         return;
2230     }
2231
2232     _page->setPaginationMode(mode);
2233 }
2234
2235 - (BOOL)_paginationBehavesLikeColumns
2236 {
2237     return _page->paginationBehavesLikeColumns();
2238 }
2239
2240 - (void)_setPaginationBehavesLikeColumns:(BOOL)behavesLikeColumns
2241 {
2242     _page->setPaginationBehavesLikeColumns(behavesLikeColumns);
2243 }
2244
2245 - (CGFloat)_pageLength
2246 {
2247     return _page->pageLength();
2248 }
2249
2250 - (void)_setPageLength:(CGFloat)pageLength
2251 {
2252     _page->setPageLength(pageLength);
2253 }
2254
2255 - (CGFloat)_gapBetweenPages
2256 {
2257     return _page->gapBetweenPages();
2258 }
2259
2260 - (void)_setGapBetweenPages:(CGFloat)gapBetweenPages
2261 {
2262     _page->setGapBetweenPages(gapBetweenPages);
2263 }
2264
2265 - (NSUInteger)_pageCount
2266 {
2267     return _page->pageCount();
2268 }
2269
2270 - (BOOL)_supportsTextZoom
2271 {
2272     return _page->supportsTextZoom();
2273 }
2274
2275 - (double)_textZoomFactor
2276 {
2277     return _page->textZoomFactor();
2278 }
2279
2280 - (void)_setTextZoomFactor:(double)zoomFactor
2281 {
2282     _page->setTextZoomFactor(zoomFactor);
2283 }
2284
2285 - (double)_pageZoomFactor
2286 {
2287     return _page->pageZoomFactor();
2288 }
2289
2290 - (void)_setPageZoomFactor:(double)zoomFactor
2291 {
2292     _page->setPageZoomFactor(zoomFactor);
2293 }
2294
2295 - (id <_WKDiagnosticLoggingDelegate>)_diagnosticLoggingDelegate
2296 {
2297     return [static_cast<WebKit::DiagnosticLoggingClient&>(_page->diagnosticLoggingClient()).delegate().leakRef() autorelease];
2298 }
2299
2300 - (void)_setDiagnosticLoggingDelegate:(id<_WKDiagnosticLoggingDelegate>)diagnosticLoggingDelegate
2301 {
2302     static_cast<WebKit::DiagnosticLoggingClient&>(_page->diagnosticLoggingClient()).setDelegate(diagnosticLoggingDelegate);
2303 }
2304
2305 - (id <_WKFindDelegate>)_findDelegate
2306 {
2307     return [static_cast<WebKit::FindClient&>(_page->findClient()).delegate().leakRef() autorelease];
2308 }
2309
2310 - (void)_setFindDelegate:(id<_WKFindDelegate>)findDelegate
2311 {
2312     static_cast<WebKit::FindClient&>(_page->findClient()).setDelegate(findDelegate);
2313 }
2314
2315 static inline WebKit::FindOptions toFindOptions(_WKFindOptions wkFindOptions)
2316 {
2317     unsigned findOptions = 0;
2318
2319     if (wkFindOptions & _WKFindOptionsCaseInsensitive)
2320         findOptions |= WebKit::FindOptionsCaseInsensitive;
2321     if (wkFindOptions & _WKFindOptionsAtWordStarts)
2322         findOptions |= WebKit::FindOptionsAtWordStarts;
2323     if (wkFindOptions & _WKFindOptionsTreatMedialCapitalAsWordStart)
2324         findOptions |= WebKit::FindOptionsTreatMedialCapitalAsWordStart;
2325     if (wkFindOptions & _WKFindOptionsBackwards)
2326         findOptions |= WebKit::FindOptionsBackwards;
2327     if (wkFindOptions & _WKFindOptionsWrapAround)
2328         findOptions |= WebKit::FindOptionsWrapAround;
2329     if (wkFindOptions & _WKFindOptionsShowOverlay)
2330         findOptions |= WebKit::FindOptionsShowOverlay;
2331     if (wkFindOptions & _WKFindOptionsShowFindIndicator)
2332         findOptions |= WebKit::FindOptionsShowFindIndicator;
2333     if (wkFindOptions & _WKFindOptionsShowHighlight)
2334         findOptions |= WebKit::FindOptionsShowHighlight;
2335     if (wkFindOptions & _WKFindOptionsDetermineMatchIndex)
2336         findOptions |= WebKit::FindOptionsDetermineMatchIndex;
2337
2338     return static_cast<WebKit::FindOptions>(findOptions);
2339 }
2340
2341 - (void)_countStringMatches:(NSString *)string options:(_WKFindOptions)options maxCount:(NSUInteger)maxCount
2342 {
2343 #if PLATFORM(IOS)
2344     if (_customContentView) {
2345         [_customContentView web_countStringMatches:string options:options maxCount:maxCount];
2346         return;
2347     }
2348 #endif
2349     _page->countStringMatches(string, toFindOptions(options), maxCount);
2350 }
2351
2352 - (void)_findString:(NSString *)string options:(_WKFindOptions)options maxCount:(NSUInteger)maxCount
2353 {
2354 #if PLATFORM(IOS)
2355     if (_customContentView) {
2356         [_customContentView web_findString:string options:options maxCount:maxCount];
2357         return;
2358     }
2359 #endif
2360     _page->findString(string, toFindOptions(options), maxCount);
2361 }
2362
2363 - (void)_hideFindUI
2364 {
2365 #if PLATFORM(IOS)
2366     if (_customContentView) {
2367         [_customContentView web_hideFindUI];
2368         return;
2369     }
2370 #endif
2371     _page->hideFindUI();
2372 }
2373
2374 - (void)_saveBackForwardSnapshotForItem:(WKBackForwardListItem *)item
2375 {
2376     _page->recordNavigationSnapshot(item._item);
2377 }
2378
2379 - (id <_WKFormDelegate>)_formDelegate
2380 {
2381     return _formDelegate.getAutoreleased();
2382 }
2383
2384 - (void)_setFormDelegate:(id <_WKFormDelegate>)formDelegate
2385 {
2386     _formDelegate = formDelegate;
2387
2388     class FormClient : public API::FormClient {
2389     public:
2390         explicit FormClient(WKWebView *webView)
2391             : m_webView(webView)
2392         {
2393         }
2394
2395         virtual ~FormClient() { }
2396
2397         virtual void willSubmitForm(WebKit::WebPageProxy&, WebKit::WebFrameProxy&, WebKit::WebFrameProxy& sourceFrame, const Vector<std::pair<WTF::String, WTF::String>>& textFieldValues, API::Object* userData, Ref<WebKit::WebFormSubmissionListenerProxy>&& listener) override
2398         {
2399             if (userData && userData->type() != API::Object::Type::Data) {
2400                 ASSERT(!userData || userData->type() == API::Object::Type::Data);
2401                 m_webView->_page->process().connection()->markCurrentlyDispatchedMessageAsInvalid();
2402                 listener->continueSubmission();
2403                 return;
2404             }
2405
2406             auto formDelegate = m_webView->_formDelegate.get();
2407
2408             if (![formDelegate respondsToSelector:@selector(_webView:willSubmitFormValues:userObject:submissionHandler:)]) {
2409                 listener->continueSubmission();
2410                 return;
2411             }
2412
2413             auto valueMap = adoptNS([[NSMutableDictionary alloc] initWithCapacity:textFieldValues.size()]);
2414             for (const auto& pair : textFieldValues)
2415                 [valueMap setObject:pair.second forKey:pair.first];
2416
2417             NSObject <NSSecureCoding> *userObject = nil;
2418             if (API::Data* data = static_cast<API::Data*>(userData)) {
2419                 auto nsData = adoptNS([[NSData alloc] initWithBytesNoCopy:const_cast<void*>(static_cast<const void*>(data->bytes())) length:data->size() freeWhenDone:NO]);
2420                 auto unarchiver = adoptNS([[NSKeyedUnarchiver alloc] initForReadingWithData:nsData.get()]);
2421                 [unarchiver setRequiresSecureCoding:YES];
2422                 @try {
2423                     userObject = [unarchiver decodeObjectOfClass:[NSObject class] forKey:@"userObject"];
2424                 } @catch (NSException *exception) {
2425                     LOG_ERROR("Failed to decode user data: %@", exception);
2426                 }
2427             }
2428
2429             RefPtr<WebKit::WebFormSubmissionListenerProxy> localListener = WTF::move(listener);
2430             RefPtr<WebKit::CompletionHandlerCallChecker> checker = WebKit::CompletionHandlerCallChecker::create(formDelegate.get(), @selector(_webView:willSubmitFormValues:userObject:submissionHandler:));
2431             [formDelegate _webView:m_webView willSubmitFormValues:valueMap.get() userObject:userObject submissionHandler:[localListener, checker] {
2432                 checker->didCallCompletionHandler();
2433                 localListener->continueSubmission();
2434             }];
2435         }
2436
2437     private:
2438         WKWebView *m_webView;
2439     };
2440
2441     if (formDelegate)
2442         _page->setFormClient(std::make_unique<FormClient>(self));
2443     else
2444         _page->setFormClient(nullptr);
2445 }
2446
2447 - (BOOL)_isDisplayingStandaloneImageDocument
2448 {
2449     if (auto* mainFrame = _page->mainFrame())
2450         return mainFrame->isDisplayingStandaloneImageDocument();
2451     return NO;
2452 }
2453
2454 - (BOOL)_isShowingNavigationGestureSnapshot
2455 {
2456     return _page->isShowingNavigationGestureSnapshot();
2457 }
2458
2459 - (_WKLayoutMode)_layoutMode
2460 {
2461 #if PLATFORM(MAC)
2462     switch ([_wkView _layoutMode]) {
2463     case kWKLayoutModeFixedSize:
2464         return _WKLayoutModeFixedSize;
2465     case kWKLayoutModeDynamicSizeComputedFromViewScale:
2466         return _WKLayoutModeDynamicSizeComputedFromViewScale;
2467     case kWKLayoutModeDynamicSizeWithMinimumViewSize:
2468         return _WKLayoutModeDynamicSizeWithMinimumViewSize;
2469     case kWKLayoutModeDynamicSizeComputedFromMinimumDocumentSize:
2470         return _WKLayoutModeDynamicSizeComputedFromMinimumDocumentSize;
2471     case kWKLayoutModeViewSize:
2472     default:
2473         return _WKLayoutModeViewSize;
2474     }
2475 #else
2476     return _page->useFixedLayout() ? _WKLayoutModeFixedSize : _WKLayoutModeViewSize;
2477 #endif
2478 }
2479
2480 - (void)_setLayoutMode:(_WKLayoutMode)layoutMode
2481 {
2482 #if PLATFORM(MAC)
2483     WKLayoutMode wkViewLayoutMode;
2484     switch (layoutMode) {
2485     case _WKLayoutModeFixedSize:
2486         wkViewLayoutMode = kWKLayoutModeFixedSize;
2487         break;
2488     case _WKLayoutModeDynamicSizeComputedFromViewScale:
2489         wkViewLayoutMode = kWKLayoutModeDynamicSizeComputedFromViewScale;
2490         break;
2491     case _WKLayoutModeDynamicSizeWithMinimumViewSize:
2492         wkViewLayoutMode = kWKLayoutModeDynamicSizeWithMinimumViewSize;
2493         break;
2494     case _WKLayoutModeDynamicSizeComputedFromMinimumDocumentSize:
2495         wkViewLayoutMode = kWKLayoutModeDynamicSizeComputedFromMinimumDocumentSize;
2496         break;
2497     case _WKLayoutModeViewSize:
2498     default:
2499         wkViewLayoutMode = kWKLayoutModeViewSize;
2500         break;
2501     }
2502     [_wkView _setLayoutMode:wkViewLayoutMode];
2503 #else
2504     _page->setUseFixedLayout(layoutMode == _WKLayoutModeFixedSize || layoutMode == _WKLayoutModeDynamicSizeComputedFromViewScale);
2505 #endif
2506 }
2507
2508 - (CGSize)_fixedLayoutSize
2509 {
2510     return _page->fixedLayoutSize();
2511 }
2512
2513 - (void)_setFixedLayoutSize:(CGSize)fixedLayoutSize
2514 {
2515     _page->setFixedLayoutSize(WebCore::expandedIntSize(WebCore::FloatSize(fixedLayoutSize)));
2516 }
2517
2518 - (CGFloat)_viewScale
2519 {
2520     return _page->viewScaleFactor();
2521 }
2522
2523 - (void)_setViewScale:(CGFloat)viewScale
2524 {
2525 #if PLATFORM(MAC)
2526     [_wkView _setViewScale:viewScale];
2527 #else
2528     if (viewScale <= 0 || isnan(viewScale) || isinf(viewScale))
2529         [NSException raise:NSInvalidArgumentException format:@"View scale should be a positive number"];
2530
2531     _page->scaleView(viewScale);
2532 #endif
2533 }
2534
2535 - (void)_setMinimumViewSize:(CGSize)minimumViewSize
2536 {
2537 #if PLATFORM(MAC)
2538     [_wkView _setMinimumViewSize:minimumViewSize];
2539 #endif
2540 }
2541
2542 - (CGSize)_minimumViewSize
2543 {
2544 #if PLATFORM(MAC)
2545     return [_wkView _minimumViewSize];
2546 #else
2547     return CGSizeZero;
2548 #endif
2549 }
2550
2551 #pragma mark scrollperf methods
2552
2553 - (void)_setScrollPerformanceDataCollectionEnabled:(BOOL)enabled
2554 {
2555     _page->setScrollPerformanceDataCollectionEnabled(enabled);
2556 }
2557
2558 - (BOOL)_scrollPerformanceDataCollectionEnabled
2559 {
2560     return _page->scrollPerformanceDataCollectionEnabled();
2561 }
2562
2563 - (NSArray *)_scrollPerformanceData
2564 {
2565 #if PLATFORM(IOS)
2566     if (WebKit::RemoteLayerTreeScrollingPerformanceData* scrollPerfData = _page->scrollingPerformanceData())
2567         return scrollPerfData->data();
2568 #endif
2569     return nil;
2570 }
2571
2572 #pragma mark iOS-specific methods
2573
2574 #if PLATFORM(IOS)
2575
2576 - (CGSize)_minimumLayoutSizeOverride
2577 {
2578     ASSERT(_overridesMinimumLayoutSize);
2579     return _minimumLayoutSizeOverride;
2580 }
2581
2582 - (void)_setMinimumLayoutSizeOverride:(CGSize)minimumLayoutSizeOverride
2583 {
2584     _overridesMinimumLayoutSize = YES;
2585     if (CGSizeEqualToSize(_minimumLayoutSizeOverride, minimumLayoutSizeOverride))
2586         return;
2587
2588     _minimumLayoutSizeOverride = minimumLayoutSizeOverride;
2589     if (_dynamicViewportUpdateMode == DynamicViewportUpdateMode::NotResizing)
2590         _page->setViewportConfigurationMinimumLayoutSize(WebCore::FloatSize(minimumLayoutSizeOverride));
2591 }
2592
2593 - (UIEdgeInsets)_obscuredInsets
2594 {
2595     return _obscuredInsets;
2596 }
2597
2598 - (void)_setObscuredInsets:(UIEdgeInsets)obscuredInsets
2599 {
2600     ASSERT(obscuredInsets.top >= 0);
2601     ASSERT(obscuredInsets.left >= 0);
2602     ASSERT(obscuredInsets.bottom >= 0);
2603     ASSERT(obscuredInsets.right >= 0);
2604     
2605     _haveSetObscuredInsets = YES;
2606
2607     if (UIEdgeInsetsEqualToEdgeInsets(_obscuredInsets, obscuredInsets))
2608         return;
2609
2610     _obscuredInsets = obscuredInsets;
2611
2612     [self _updateVisibleContentRects];
2613 }
2614
2615 - (void)_setInterfaceOrientationOverride:(UIInterfaceOrientation)interfaceOrientation
2616 {
2617     if (!_overridesInterfaceOrientation)
2618         [[NSNotificationCenter defaultCenter] removeObserver:self name:UIWindowDidRotateNotification object:nil];
2619
2620     _overridesInterfaceOrientation = YES;
2621
2622     if (interfaceOrientation == _interfaceOrientationOverride)
2623         return;
2624
2625     _interfaceOrientationOverride = interfaceOrientation;
2626
2627     if (_dynamicViewportUpdateMode == DynamicViewportUpdateMode::NotResizing)
2628         _page->setDeviceOrientation(deviceOrientationForUIInterfaceOrientation(_interfaceOrientationOverride));
2629 }
2630
2631 - (UIInterfaceOrientation)_interfaceOrientationOverride
2632 {
2633     ASSERT(_overridesInterfaceOrientation);
2634     return _interfaceOrientationOverride;
2635 }
2636
2637 - (CGSize)_maximumUnobscuredSizeOverride
2638 {
2639     ASSERT(_overridesMaximumUnobscuredSize);
2640     return _maximumUnobscuredSizeOverride;
2641 }
2642
2643 - (void)_setMaximumUnobscuredSizeOverride:(CGSize)size
2644 {
2645     ASSERT(size.width <= self.bounds.size.width && size.height <= self.bounds.size.height);
2646     _overridesMaximumUnobscuredSize = YES;
2647     if (CGSizeEqualToSize(_maximumUnobscuredSizeOverride, size))
2648         return;
2649
2650     _maximumUnobscuredSizeOverride = size;
2651     if (_dynamicViewportUpdateMode == DynamicViewportUpdateMode::NotResizing)
2652         _page->setMaximumUnobscuredSize(WebCore::FloatSize(size));
2653 }
2654
2655 - (void)_setBackgroundExtendsBeyondPage:(BOOL)backgroundExtends
2656 {
2657     _page->setBackgroundExtendsBeyondPage(backgroundExtends);
2658 }
2659
2660 - (BOOL)_backgroundExtendsBeyondPage
2661 {
2662     return _page->backgroundExtendsBeyondPage();
2663 }
2664
2665 - (void)_beginInteractiveObscuredInsetsChange
2666 {
2667     ASSERT(!_isChangingObscuredInsetsInteractively);
2668     _isChangingObscuredInsetsInteractively = YES;
2669 }
2670
2671 - (void)_endInteractiveObscuredInsetsChange
2672 {
2673     ASSERT(_isChangingObscuredInsetsInteractively);
2674     _isChangingObscuredInsetsInteractively = NO;
2675     [self _updateVisibleContentRects];
2676 }
2677
2678 - (void)_hideContentUntilNextUpdate
2679 {
2680     if (auto* area = _page->drawingArea())
2681         area->hideContentUntilAnyUpdate();
2682 }
2683
2684 - (void)_beginAnimatedResizeWithUpdates:(void (^)(void))updateBlock
2685 {
2686     CGRect oldBounds = self.bounds;
2687     WebCore::FloatRect oldUnobscuredContentRect = _page->unobscuredContentRect();
2688
2689     if (_customContentView || !_hasCommittedLoadForMainFrame || CGRectIsEmpty(oldBounds) || oldUnobscuredContentRect.isEmpty()) {
2690         updateBlock();
2691         return;
2692     }
2693
2694     _dynamicViewportUpdateMode = DynamicViewportUpdateMode::ResizingWithAnimation;
2695
2696     WebCore::FloatSize oldMinimumLayoutSize = activeMinimumLayoutSize(self, oldBounds);
2697     WebCore::FloatSize oldMaximumUnobscuredSize = activeMaximumUnobscuredSize(self, oldBounds);
2698     int32_t oldOrientation = activeOrientation(self);
2699     UIEdgeInsets oldObscuredInsets = _obscuredInsets;
2700
2701     updateBlock();
2702
2703     CGRect newBounds = self.bounds;
2704     WebCore::FloatSize newMinimumLayoutSize = activeMinimumLayoutSize(self, newBounds);
2705     WebCore::FloatSize newMaximumUnobscuredSize = activeMaximumUnobscuredSize(self, newBounds);
2706     int32_t newOrientation = activeOrientation(self);
2707     UIEdgeInsets newObscuredInsets = _obscuredInsets;
2708     CGRect futureUnobscuredRectInSelfCoordinates = UIEdgeInsetsInsetRect(newBounds, _obscuredInsets);
2709     CGRect contentViewBounds = [_contentView bounds];
2710
2711     ASSERT_WITH_MESSAGE(!(_overridesMinimumLayoutSize && newMinimumLayoutSize.isEmpty()), "Clients controlling the layout size should maintain a valid layout size to minimize layouts.");
2712     if (CGRectIsEmpty(newBounds) || newMinimumLayoutSize.isEmpty() || CGRectIsEmpty(futureUnobscuredRectInSelfCoordinates) || CGRectIsEmpty(contentViewBounds)) {
2713         _dynamicViewportUpdateMode = DynamicViewportUpdateMode::NotResizing;
2714         [self _frameOrBoundsChanged];
2715         if (_overridesMinimumLayoutSize)
2716             _page->setViewportConfigurationMinimumLayoutSize(WebCore::FloatSize(newMinimumLayoutSize));
2717         if (_overridesMaximumUnobscuredSize)
2718             _page->setMaximumUnobscuredSize(WebCore::FloatSize(newMaximumUnobscuredSize));
2719         if (_overridesInterfaceOrientation)
2720             _page->setDeviceOrientation(newOrientation);
2721         [self _updateVisibleContentRects];
2722         return;
2723     }
2724
2725     if (CGRectEqualToRect(oldBounds, newBounds)
2726         && oldMinimumLayoutSize == newMinimumLayoutSize
2727         && oldMaximumUnobscuredSize == newMaximumUnobscuredSize
2728         && oldOrientation == newOrientation
2729         && UIEdgeInsetsEqualToEdgeInsets(oldObscuredInsets, newObscuredInsets)) {
2730         _dynamicViewportUpdateMode = DynamicViewportUpdateMode::NotResizing;
2731         [self _updateVisibleContentRects];
2732         return;
2733     }
2734
2735     _resizeAnimationTransformAdjustments = CATransform3DIdentity;
2736
2737     NSUInteger indexOfContentView = [[_scrollView subviews] indexOfObject:_contentView.get()];
2738     _resizeAnimationView = adoptNS([[UIView alloc] init]);
2739     [_scrollView insertSubview:_resizeAnimationView.get() atIndex:indexOfContentView];
2740     [_resizeAnimationView addSubview:_contentView.get()];
2741     [_resizeAnimationView addSubview:[_contentView unscaledView]];
2742
2743     CGSize contentSizeInContentViewCoordinates = contentViewBounds.size;
2744     [_scrollView setMinimumZoomScale:std::min(newMinimumLayoutSize.width() / contentSizeInContentViewCoordinates.width, [_scrollView minimumZoomScale])];
2745     [_scrollView setMaximumZoomScale:std::max(newMinimumLayoutSize.width() / contentSizeInContentViewCoordinates.width, [_scrollView maximumZoomScale])];
2746
2747     // Compute the new scale to keep the current content width in the scrollview.
2748     CGFloat oldWebViewWidthInContentViewCoordinates = oldUnobscuredContentRect.width();
2749     CGFloat visibleContentViewWidthInContentCoordinates = std::min(contentSizeInContentViewCoordinates.width, oldWebViewWidthInContentViewCoordinates);
2750     CGFloat targetScale = newMinimumLayoutSize.width() / visibleContentViewWidthInContentCoordinates;
2751     CGFloat resizeAnimationViewAnimationScale = targetScale / contentZoomScale(self);
2752     [_resizeAnimationView setTransform:CGAffineTransformMakeScale(resizeAnimationViewAnimationScale, resizeAnimationViewAnimationScale)];
2753
2754     // Compute a new position to keep the content centered.
2755     CGPoint originalContentCenter = oldUnobscuredContentRect.center();
2756     CGPoint originalContentCenterInSelfCoordinates = [self convertPoint:originalContentCenter fromView:_contentView.get()];
2757     CGPoint futureUnobscuredRectCenterInSelfCoordinates = CGPointMake(futureUnobscuredRectInSelfCoordinates.origin.x + futureUnobscuredRectInSelfCoordinates.size.width / 2, futureUnobscuredRectInSelfCoordinates.origin.y + futureUnobscuredRectInSelfCoordinates.size.height / 2);
2758
2759     CGPoint originalContentOffset = [_scrollView contentOffset];
2760     CGPoint contentOffset = originalContentOffset;
2761     contentOffset.x += (originalContentCenterInSelfCoordinates.x - futureUnobscuredRectCenterInSelfCoordinates.x);
2762     contentOffset.y += (originalContentCenterInSelfCoordinates.y - futureUnobscuredRectCenterInSelfCoordinates.y);
2763
2764     // Limit the new offset within the scrollview, we do not want to rubber band programmatically.
2765     CGSize futureContentSizeInSelfCoordinates = CGSizeMake(contentSizeInContentViewCoordinates.width * targetScale, contentSizeInContentViewCoordinates.height * targetScale);
2766     CGFloat maxHorizontalOffset = futureContentSizeInSelfCoordinates.width - newBounds.size.width + _obscuredInsets.right;
2767     contentOffset.x = std::min(contentOffset.x, maxHorizontalOffset);
2768     CGFloat maxVerticalOffset = futureContentSizeInSelfCoordinates.height - newBounds.size.height + _obscuredInsets.bottom;
2769     contentOffset.y = std::min(contentOffset.y, maxVerticalOffset);
2770
2771     contentOffset.x = std::max(contentOffset.x, -_obscuredInsets.left);
2772     contentOffset.y = std::max(contentOffset.y, -_obscuredInsets.top);
2773
2774     // Make the top/bottom edges "sticky" within 1 pixel.
2775     if (oldUnobscuredContentRect.maxY() > contentSizeInContentViewCoordinates.height - 1)
2776         contentOffset.y = maxVerticalOffset;
2777     if (oldUnobscuredContentRect.y() < 1)
2778         contentOffset.y = -_obscuredInsets.top;
2779
2780     // FIXME: if we have content centered after double tap to zoom, we should also try to keep that rect in view.
2781     [_scrollView setContentSize:roundScrollViewContentSize(*_page, futureContentSizeInSelfCoordinates)];
2782     [_scrollView setContentOffset:contentOffset];
2783
2784     CGRect visibleRectInContentCoordinates = [self convertRect:newBounds toView:_contentView.get()];
2785     CGRect unobscuredRectInContentCoordinates = [self convertRect:futureUnobscuredRectInSelfCoordinates toView:_contentView.get()];
2786
2787     _page->dynamicViewportSizeUpdate(newMinimumLayoutSize, newMaximumUnobscuredSize, visibleRectInContentCoordinates, unobscuredRectInContentCoordinates, futureUnobscuredRectInSelfCoordinates, targetScale, newOrientation);
2788     if (WebKit::DrawingAreaProxy* drawingArea = _page->drawingArea())
2789         drawingArea->setSize(WebCore::IntSize(newBounds.size), WebCore::IntSize(), WebCore::IntSize());
2790 }
2791
2792 - (void)_endAnimatedResize
2793 {
2794     if (_dynamicViewportUpdateMode == DynamicViewportUpdateMode::NotResizing)
2795         return;
2796
2797     _page->synchronizeDynamicViewportUpdate();
2798
2799     NSUInteger indexOfResizeAnimationView = [[_scrollView subviews] indexOfObject:_resizeAnimationView.get()];
2800     [_scrollView insertSubview:_contentView.get() atIndex:indexOfResizeAnimationView];
2801     [_scrollView insertSubview:[_contentView unscaledView] atIndex:indexOfResizeAnimationView + 1];
2802
2803     CALayer *contentViewLayer = [_contentView layer];
2804     CGFloat adjustmentScale = _resizeAnimationTransformAdjustments.m11;
2805     contentViewLayer.sublayerTransform = CATransform3DIdentity;
2806
2807     CGFloat animatingScaleTarget = [[_resizeAnimationView layer] transform].m11;
2808     CALayer *contentLayer = [_contentView layer];
2809     CATransform3D contentLayerTransform = contentLayer.transform;
2810     CGFloat currentScale = [[_resizeAnimationView layer] transform].m11 * contentLayerTransform.m11;
2811
2812     // We cannot use [UIScrollView setZoomScale:] directly because the UIScrollView delegate would get a callback with
2813     // an invalid contentOffset. The real content offset is only set below.
2814     // Since there is no public API for setting both the zoomScale and the contentOffset, we set the zoomScale manually
2815     // on the zoom layer and then only change the contentOffset.
2816     CGFloat adjustedScale = adjustmentScale * currentScale;
2817     contentLayerTransform.m11 = adjustedScale;
2818     contentLayerTransform.m22 = adjustedScale;
2819     contentLayer.transform = contentLayerTransform;
2820
2821     CGPoint currentScrollOffset = [_scrollView contentOffset];
2822     double horizontalScrollAdjustement = _resizeAnimationTransformAdjustments.m41 * animatingScaleTarget;
2823     double verticalScrollAdjustment = _resizeAnimationTransformAdjustments.m42 * animatingScaleTarget;
2824
2825     [_scrollView setContentSize:roundScrollViewContentSize(*_page, [_contentView frame].size)];
2826     [_scrollView setContentOffset:CGPointMake(currentScrollOffset.x - horizontalScrollAdjustement, currentScrollOffset.y - verticalScrollAdjustment)];
2827
2828     [_resizeAnimationView removeFromSuperview];
2829     _resizeAnimationView = nil;
2830     _resizeAnimationTransformAdjustments = CATransform3DIdentity;
2831
2832     _dynamicViewportUpdateMode = DynamicViewportUpdateMode::NotResizing;
2833     [_contentView setHidden:NO];
2834     [self _updateVisibleContentRects];
2835
2836     while (!_snapshotsDeferredDuringResize.isEmpty())
2837         _snapshotsDeferredDuringResize.takeLast()();
2838 }
2839
2840 - (void)_resizeWhileHidingContentWithUpdates:(void (^)(void))updateBlock
2841 {
2842     [self _beginAnimatedResizeWithUpdates:updateBlock];
2843     if (_dynamicViewportUpdateMode == DynamicViewportUpdateMode::ResizingWithAnimation) {
2844         [_contentView setHidden:YES];
2845         _dynamicViewportUpdateMode = DynamicViewportUpdateMode::ResizingWithDocumentHidden;
2846     }
2847 }
2848
2849 - (void)_setOverlaidAccessoryViewsInset:(CGSize)inset
2850 {
2851     [_customContentView web_setOverlaidAccessoryViewsInset:inset];
2852 }
2853
2854 - (void)_snapshotRect:(CGRect)rectInViewCoordinates intoImageOfWidth:(CGFloat)imageWidth completionHandler:(void(^)(CGImageRef))completionHandler
2855 {
2856     if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing) {
2857         // Defer snapshotting until after the current resize completes.
2858         void (^copiedCompletionHandler)(CGImageRef) = [completionHandler copy];
2859         RetainPtr<WKWebView> retainedSelf = self;
2860         _snapshotsDeferredDuringResize.append([retainedSelf, rectInViewCoordinates, imageWidth, copiedCompletionHandler] {
2861             [retainedSelf _snapshotRect:rectInViewCoordinates intoImageOfWidth:imageWidth completionHandler:copiedCompletionHandler];
2862             [copiedCompletionHandler release];
2863         });
2864         return;
2865     }
2866
2867     CGRect snapshotRectInContentCoordinates = [self convertRect:rectInViewCoordinates toView:self._currentContentView];
2868     CGFloat imageScale = imageWidth / snapshotRectInContentCoordinates.size.width;
2869     CGFloat imageHeight = imageScale * snapshotRectInContentCoordinates.size.height;
2870     CGSize imageSize = CGSizeMake(imageWidth, imageHeight);
2871
2872 #if USE(IOSURFACE)
2873     // If we are parented and thus won't incur a significant penalty from paging in tiles, snapshot the view hierarchy directly.
2874     if (self.window) {
2875         auto surface = WebCore::IOSurface::create(WebCore::expandedIntSize(WebCore::FloatSize(imageSize)), WebCore::ColorSpaceDeviceRGB);
2876         CGFloat imageScaleInViewCoordinates = imageWidth / rectInViewCoordinates.size.width;
2877         CATransform3D transform = CATransform3DMakeScale(imageScaleInViewCoordinates, imageScaleInViewCoordinates, 1);
2878         transform = CATransform3DTranslate(transform, -rectInViewCoordinates.origin.x, -rectInViewCoordinates.origin.y, 0);
2879         CARenderServerRenderLayerWithTransform(MACH_PORT_NULL, self.layer.context.contextId, reinterpret_cast<uint64_t>(self.layer), surface->surface(), 0, 0, &transform);
2880         completionHandler(surface->createImage().get());
2881
2882         return;
2883     }
2884 #endif
2885     
2886     if (_customContentView) {
2887         UIGraphicsBeginImageContextWithOptions(imageSize, YES, 1);
2888
2889         UIView *customContentView = _customContentView.get();
2890         [customContentView.backgroundColor set];
2891         UIRectFill(CGRectMake(0, 0, imageWidth, imageHeight));
2892
2893         CGContextRef context = UIGraphicsGetCurrentContext();
2894         CGContextTranslateCTM(context, -snapshotRectInContentCoordinates.origin.x * imageScale, -snapshotRectInContentCoordinates.origin.y * imageScale);
2895         CGContextScaleCTM(context, imageScale, imageScale);
2896         [customContentView.layer renderInContext:context];
2897
2898         completionHandler([UIGraphicsGetImageFromCurrentImageContext() CGImage]);
2899
2900         UIGraphicsEndImageContext();
2901         return;
2902     }
2903
2904     void(^copiedCompletionHandler)(CGImageRef) = [completionHandler copy];
2905     _page->takeSnapshot(WebCore::enclosingIntRect(snapshotRectInContentCoordinates), WebCore::expandedIntSize(WebCore::FloatSize(imageSize)), WebKit::SnapshotOptionsExcludeDeviceScaleFactor, [=](const WebKit::ShareableBitmap::Handle& imageHandle, WebKit::CallbackBase::Error) {
2906         if (imageHandle.isNull()) {
2907             copiedCompletionHandler(nullptr);
2908             [copiedCompletionHandler release];
2909             return;
2910         }
2911
2912         RefPtr<WebKit::ShareableBitmap> bitmap = WebKit::ShareableBitmap::create(imageHandle, WebKit::SharedMemory::Protection::ReadOnly);
2913
2914         if (!bitmap) {
2915             copiedCompletionHandler(nullptr);
2916             [copiedCompletionHandler release];
2917             return;
2918         }
2919
2920         RetainPtr<CGImageRef> cgImage;
2921         cgImage = bitmap->makeCGImage();
2922         copiedCompletionHandler(cgImage.get());
2923         [copiedCompletionHandler release];
2924     });
2925 }
2926
2927 - (void)_overrideLayoutParametersWithMinimumLayoutSize:(CGSize)minimumLayoutSize minimumLayoutSizeForMinimalUI:(CGSize)minimumLayoutSizeForMinimalUI maximumUnobscuredSizeOverride:(CGSize)maximumUnobscuredSizeOverride
2928 {
2929     UNUSED_PARAM(minimumLayoutSizeForMinimalUI);
2930     [self _overrideLayoutParametersWithMinimumLayoutSize:minimumLayoutSize maximumUnobscuredSizeOverride:maximumUnobscuredSizeOverride];
2931 }
2932
2933 - (void)_overrideLayoutParametersWithMinimumLayoutSize:(CGSize)minimumLayoutSize maximumUnobscuredSizeOverride:(CGSize)maximumUnobscuredSizeOverride
2934 {
2935     [self _setMinimumLayoutSizeOverride:minimumLayoutSize];
2936     [self _setMaximumUnobscuredSizeOverride:maximumUnobscuredSizeOverride];
2937 }
2938
2939 - (UIView *)_viewForFindUI
2940 {
2941     return [self viewForZoomingInScrollView:[self scrollView]];
2942 }
2943
2944 - (BOOL)_isDisplayingPDF
2945 {
2946     return [_customContentView isKindOfClass:[WKPDFView class]];
2947 }
2948
2949 - (NSData *)_dataForDisplayedPDF
2950 {
2951     if (![self _isDisplayingPDF])
2952         return nil;
2953     CGPDFDocumentRef pdfDocument = [(WKPDFView *)_customContentView pdfDocument];
2954     return [(NSData *)CGDataProviderCopyData(CGPDFDocumentGetDataProvider(pdfDocument)) autorelease];
2955 }
2956
2957 - (NSString *)_suggestedFilenameForDisplayedPDF
2958 {
2959     if (![self _isDisplayingPDF])
2960         return nil;
2961     return [(WKPDFView *)_customContentView.get() suggestedFilename];
2962 }
2963
2964 - (CGFloat)_viewportMetaTagWidth
2965 {
2966     return _viewportMetaTagWidth;
2967 }
2968
2969 - (_WKWebViewPrintFormatter *)_webViewPrintFormatter
2970 {
2971     UIViewPrintFormatter *viewPrintFormatter = self.viewPrintFormatter;
2972     ASSERT([viewPrintFormatter isKindOfClass:[_WKWebViewPrintFormatter class]]);
2973     return (_WKWebViewPrintFormatter *)viewPrintFormatter;
2974 }
2975
2976 - (BOOL)_allowsLinkPreview
2977 {
2978     return _allowsLinkPreview;
2979 }
2980
2981 - (void)_setAllowsLinkPreview:(BOOL)allowsLinkPreview
2982 {
2983     if (_allowsLinkPreview == allowsLinkPreview)
2984         return;
2985
2986     _allowsLinkPreview = allowsLinkPreview;
2987 #if HAVE(LINK_PREVIEW)
2988     if (_allowsLinkPreview)
2989         [_contentView _registerPreviewInWindow:[_contentView window]];
2990     else
2991         [_contentView _unregisterPreviewInWindow:[_contentView window]];
2992 #endif
2993 }
2994
2995 #else
2996
2997 #pragma mark - OS X-specific methods
2998
2999 - (NSColor *)_pageExtendedBackgroundColor
3000 {
3001     WebCore::Color color = _page->pageExtendedBackgroundColor();
3002     if (!color.isValid())
3003         return nil;
3004
3005     return nsColor(color);
3006 }
3007
3008 - (BOOL)_drawsTransparentBackground
3009 {
3010     return _page->drawsTransparentBackground();
3011 }
3012
3013 - (void)_setDrawsTransparentBackground:(BOOL)drawsTransparentBackground
3014 {
3015     _page->setDrawsTransparentBackground(drawsTransparentBackground);
3016 }
3017
3018 - (void)_setOverrideDeviceScaleFactor:(CGFloat)deviceScaleFactor
3019 {
3020     [_wkView _setOverrideDeviceScaleFactor:deviceScaleFactor];
3021 }
3022
3023 - (CGFloat)_overrideDeviceScaleFactor
3024 {
3025     return [_wkView _overrideDeviceScaleFactor];
3026 }
3027
3028 - (void)_setTopContentInset:(CGFloat)contentInset
3029 {
3030     [_wkView _setTopContentInset:contentInset];
3031 }
3032
3033 - (CGFloat)_topContentInset
3034 {
3035     return [_wkView _topContentInset];
3036 }
3037
3038 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
3039
3040 - (void)_setAutomaticallyAdjustsContentInsets:(BOOL)automaticallyAdjustsContentInsets
3041 {
3042     [_wkView _setAutomaticallyAdjustsContentInsets:automaticallyAdjustsContentInsets];
3043 }
3044
3045 - (BOOL)_automaticallyAdjustsContentInsets
3046 {
3047     return [_wkView _automaticallyAdjustsContentInsets];
3048 }
3049
3050 #endif
3051
3052 #endif
3053
3054 @end
3055
3056 #if !TARGET_OS_IPHONE
3057
3058 @implementation WKWebView (WKIBActions)
3059
3060 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
3061 {
3062     SEL action = item.action;
3063
3064     if (action == @selector(goBack:))
3065         return !!_page->backForwardList().backItem();
3066
3067     if (action == @selector(goForward:))
3068         return !!_page->backForwardList().forwardItem();
3069
3070     if (action == @selector(stopLoading:)) {
3071         // FIXME: Return no if we're stopped.
3072         return YES;
3073     }
3074
3075     if (action == @selector(reload:) || action == @selector(reloadFromOrigin:)) {
3076         // FIXME: Return no if we're loading.
3077         return YES;
3078     }
3079
3080     return NO;
3081 }
3082
3083 - (IBAction)goBack:(id)sender
3084 {
3085     [self goBack];
3086 }
3087
3088 - (IBAction)goForward:(id)sender
3089 {
3090     [self goForward];
3091 }
3092
3093 - (IBAction)reload:(id)sender
3094 {
3095     [self reload];
3096 }
3097
3098 - (IBAction)reloadFromOrigin:(id)sender
3099 {
3100     [self reloadFromOrigin];
3101 }
3102
3103 - (IBAction)stopLoading:(id)sender
3104 {
3105     _page->stopLoading();
3106 }
3107
3108 @end
3109
3110 #endif
3111
3112 #if PLATFORM(IOS)
3113 @implementation WKWebView (_WKWebViewPrintFormatter)
3114
3115 - (Class)_printFormatterClass
3116 {
3117     return [_WKWebViewPrintFormatter class];
3118 }
3119
3120 - (NSInteger)_computePageCountAndStartDrawingToPDFForFrame:(_WKFrameHandle *)frame printInfo:(const WebKit::PrintInfo&)printInfo firstPage:(uint32_t)firstPage computedTotalScaleFactor:(double&)totalScaleFactor
3121 {
3122     if ([self _isDisplayingPDF])
3123         return CGPDFDocumentGetNumberOfPages([(WKPDFView *)_customContentView pdfDocument]);
3124
3125     _pageIsPrintingToPDF = YES;
3126     Vector<WebCore::IntRect> pageRects;
3127     uint64_t frameID = frame ? frame._frameID : _page->mainFrame()->frameID();
3128     if (!_page->sendSync(Messages::WebPage::ComputePagesForPrintingAndStartDrawingToPDF(frameID, printInfo, firstPage), Messages::WebPage::ComputePagesForPrintingAndStartDrawingToPDF::Reply(pageRects, totalScaleFactor)))
3129         return 0;
3130     return pageRects.size();
3131 }
3132
3133 - (void)_endPrinting
3134 {
3135     _pageIsPrintingToPDF = NO;
3136     _printedDocument = nullptr;
3137     _page->send(Messages::WebPage::EndPrinting());
3138 }
3139
3140 // FIXME: milliseconds::max() overflows when converted to nanoseconds, causing condition_variable::wait_for() to believe
3141 // a timeout occurred on any spurious wakeup. Use nanoseconds::max() (converted to ms) to avoid this. We should perhaps
3142 // change waitForAndDispatchImmediately() to take nanoseconds to avoid this issue.
3143 static constexpr std::chrono::milliseconds didFinishLoadingTimeout = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::nanoseconds::max());
3144
3145 - (CGPDFDocumentRef)_printedDocument
3146 {
3147     if ([self _isDisplayingPDF]) {
3148         ASSERT(!_pageIsPrintingToPDF);
3149         return [(WKPDFView *)_customContentView pdfDocument];
3150     }
3151
3152     if (_pageIsPrintingToPDF) {
3153         if (!_page->process().connection()->waitForAndDispatchImmediately<Messages::WebPageProxy::DidFinishDrawingPagesToPDF>(_page->pageID(), didFinishLoadingTimeout)) {
3154             ASSERT_NOT_REACHED();
3155             return nullptr;
3156         }
3157         ASSERT(!_pageIsPrintingToPDF);
3158     }
3159     return _printedDocument.get();
3160 }
3161
3162 - (void)_setPrintedDocument:(CGPDFDocumentRef)printedDocument
3163 {
3164     if (!_pageIsPrintingToPDF)
3165         return;
3166     ASSERT(![self _isDisplayingPDF]);
3167     _printedDocument = printedDocument;
3168     _pageIsPrintingToPDF = NO;
3169 }
3170
3171 @end
3172 #endif
3173
3174 #endif // WK_API_ENABLED