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