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