[iOS] Documents without an explicit width should not get fast tapping
[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 "APIPageConfiguration.h"
33 #import "APISerializedScriptValue.h"
34 #import "CompletionHandlerCallChecker.h"
35 #import "DiagnosticLoggingClient.h"
36 #import "FindClient.h"
37 #import "LegacySessionStateCoding.h"
38 #import "NavigationState.h"
39 #import "ObjCObjectGraph.h"
40 #import "RemoteLayerTreeScrollingPerformanceData.h"
41 #import "RemoteLayerTreeTransaction.h"
42 #import "RemoteObjectRegistry.h"
43 #import "RemoteObjectRegistryMessages.h"
44 #import "UIDelegate.h"
45 #import "ViewGestureController.h"
46 #import "ViewSnapshotStore.h"
47 #import "WKBackForwardListInternal.h"
48 #import "WKBackForwardListItemInternal.h"
49 #import "WKBrowsingContextHandleInternal.h"
50 #import "WKErrorInternal.h"
51 #import "WKHistoryDelegatePrivate.h"
52 #import "WKLayoutMode.h"
53 #import "WKNSData.h"
54 #import "WKNSURLExtras.h"
55 #import "WKNavigationDelegate.h"
56 #import "WKNavigationInternal.h"
57 #import "WKPreferencesInternal.h"
58 #import "WKProcessPoolInternal.h"
59 #import "WKSharedAPICast.h"
60 #import "WKUIDelegate.h"
61 #import "WKUIDelegatePrivate.h"
62 #import "WKUserContentControllerInternal.h"
63 #import "WKWebViewConfigurationInternal.h"
64 #import "WKWebViewContentProvider.h"
65 #import "WKWebsiteDataStoreInternal.h"
66 #import "WebBackForwardList.h"
67 #import "WebCertificateInfo.h"
68 #import "WebFormSubmissionListenerProxy.h"
69 #import "WebKitSystemInterface.h"
70 #import "WebPageGroup.h"
71 #import "WebPageProxy.h"
72 #import "WebPreferencesKeys.h"
73 #import "WebProcessPool.h"
74 #import "WebProcessProxy.h"
75 #import "WebViewImpl.h"
76 #import "_WKDiagnosticLoggingDelegate.h"
77 #import "_WKFindDelegate.h"
78 #import "_WKFormDelegate.h"
79 #import "_WKFrameHandleInternal.h"
80 #import "_WKHitTestResultInternal.h"
81 #import "_WKInputDelegate.h"
82 #import "_WKRemoteObjectRegistryInternal.h"
83 #import "_WKSessionStateInternal.h"
84 #import "_WKVisitedLinkStoreInternal.h"
85 #import <WebCore/IOSurface.h>
86 #import <WebCore/JSDOMBinding.h>
87 #import <WebCore/NSTextFinderSPI.h>
88 #import <wtf/HashMap.h>
89 #import <wtf/MathExtras.h>
90 #import <wtf/NeverDestroyed.h>
91 #import <wtf/Optional.h>
92 #import <wtf/RetainPtr.h>
93 #import <wtf/TemporaryChange.h>
94
95 #if PLATFORM(IOS)
96 #import "_WKWebViewPrintFormatter.h"
97 #import "PrintInfo.h"
98 #import "ProcessThrottler.h"
99 #import "RemoteLayerTreeDrawingAreaProxy.h"
100 #import "RemoteScrollingCoordinatorProxy.h"
101 #import "UIKitSPI.h"
102 #import "WKContentViewInteraction.h"
103 #import "WKPDFView.h"
104 #import "WKScrollView.h"
105 #import "WKWebViewContentProviderRegistry.h"
106 #import "WebPageMessages.h"
107 #import "WebVideoFullscreenManagerProxy.h"
108 #import <UIKit/UIApplication.h>
109 #import <WebCore/CoreGraphicsSPI.h>
110 #import <WebCore/DynamicLinkerSPI.h>
111 #import <WebCore/FrameLoaderTypes.h>
112 #import <WebCore/InspectorOverlay.h>
113 #import <WebCore/QuartzCoreSPI.h>
114
115 @interface UIScrollView (UIScrollViewInternal)
116 - (void)_adjustForAutomaticKeyboardInfo:(NSDictionary*)info animated:(BOOL)animated lastAdjustment:(CGFloat*)lastAdjustment;
117 - (BOOL)_isScrollingToTop;
118 - (BOOL)_isInterruptingDeceleration;
119 - (CGPoint)_animatedTargetOffset;
120 @end
121
122 @interface UIPeripheralHost(UIKitInternal)
123 - (CGFloat)getVerticalOverlapForView:(UIView *)view usingKeyboardInfo:(NSDictionary *)info;
124 @end
125
126 @interface UIView (UIViewInternal)
127 - (UIViewController *)_viewControllerForAncestor;
128 @end
129
130 @interface UIWindow (UIWindowInternal)
131 - (BOOL)_isHostedInAnotherProcess;
132 @end
133
134 @interface UIViewController (UIViewControllerInternal)
135 - (UIViewController *)_rootAncestorViewController;
136 - (UIViewController *)_viewControllerForSupportedInterfaceOrientations;
137 @end
138
139 enum class DynamicViewportUpdateMode {
140     NotResizing,
141     ResizingWithAnimation,
142     ResizingWithDocumentHidden,
143 };
144
145 #if __has_include(<WebKitAdditions/RemoteLayerBackingStoreAdditions.mm>)
146 #import <WebKitAdditions/RemoteLayerBackingStoreAdditions.mm>
147 #else
148
149 namespace WebKit {
150
151 #if USE(IOSURFACE)
152 static WebCore::IOSurface::Format bufferFormat(bool)
153 {
154     return WebCore::IOSurface::Format::RGBA;
155 }
156 #endif // USE(IOSURFACE)
157
158 } // namespace WebKit
159
160 #endif
161
162 #endif // PLATFORM(IOS)
163
164 #if PLATFORM(MAC)
165 #import "WKTextFinderClient.h"
166 #import "WKViewInternal.h"
167 #import <WebCore/ColorMac.h>
168
169 @interface WKWebView () <WebViewImplDelegate>
170 @end
171 #endif
172
173 static HashMap<WebKit::WebPageProxy*, WKWebView *>& pageToViewMap()
174 {
175     static NeverDestroyed<HashMap<WebKit::WebPageProxy*, WKWebView *>> map;
176     return map;
177 }
178
179 WKWebView* fromWebPageProxy(WebKit::WebPageProxy& page)
180 {
181     return pageToViewMap().get(&page);
182 }
183
184 @implementation WKWebView {
185     std::unique_ptr<WebKit::NavigationState> _navigationState;
186     std::unique_ptr<WebKit::UIDelegate> _uiDelegate;
187
188     _WKRenderingProgressEvents _observedRenderingProgressEvents;
189
190     WebKit::WeakObjCPtr<id <_WKInputDelegate>> _inputDelegate;
191
192 #if PLATFORM(IOS)
193     RetainPtr<_WKRemoteObjectRegistry> _remoteObjectRegistry;
194
195     RetainPtr<WKScrollView> _scrollView;
196     RetainPtr<WKContentView> _contentView;
197
198     BOOL _overridesMinimumLayoutSize;
199     CGSize _minimumLayoutSizeOverride;
200     BOOL _overridesMaximumUnobscuredSize;
201     CGSize _maximumUnobscuredSizeOverride;
202     CGRect _inputViewBounds;
203     CGFloat _viewportMetaTagWidth;
204     BOOL _viewportMetaTagWidthWasExplicit;
205     BOOL _viewportMetaTagCameFromImageDocument;
206     CGFloat _initialScaleFactor;
207     BOOL _fastClickingIsDisabled;
208
209     BOOL _allowsLinkPreview;
210
211     UIEdgeInsets _obscuredInsets;
212     BOOL _haveSetObscuredInsets;
213     BOOL _isChangingObscuredInsetsInteractively;
214
215     UIInterfaceOrientation _interfaceOrientationOverride;
216     BOOL _overridesInterfaceOrientation;
217     
218     BOOL _allowsViewportShrinkToFit;
219
220     BOOL _hasCommittedLoadForMainFrame;
221     BOOL _needsResetViewStateAfterCommitLoadForMainFrame;
222     uint64_t _firstPaintAfterCommitLoadTransactionID;
223     DynamicViewportUpdateMode _dynamicViewportUpdateMode;
224     CATransform3D _resizeAnimationTransformAdjustments;
225     uint64_t _resizeAnimationTransformTransactionID;
226     RetainPtr<UIView> _resizeAnimationView;
227     CGFloat _lastAdjustmentForScroller;
228     Optional<CGRect> _frozenVisibleContentRect;
229     Optional<CGRect> _frozenUnobscuredContentRect;
230
231     BOOL _needsToRestoreExposedRect;
232     WebCore::FloatRect _exposedRectToRestore;
233     BOOL _needsToRestoreUnobscuredCenter;
234     WebCore::FloatPoint _unobscuredCenterToRestore;
235     uint64_t _firstTransactionIDAfterPageRestore;
236     double _scaleToRestore;
237
238     std::unique_ptr<WebKit::ViewGestureController> _gestureController;
239     BOOL _allowsBackForwardNavigationGestures;
240
241     RetainPtr<UIView <WKWebViewContentProvider>> _customContentView;
242     RetainPtr<UIView> _customContentFixedOverlayView;
243
244     WebCore::Color _scrollViewBackgroundColor;
245
246     // This value tracks the current adjustment added to the bottom inset due to the keyboard sliding out from the bottom
247     // when computing obscured content insets. This is used when updating the visible content rects where we should not
248     // include this adjustment.
249     CGFloat _totalScrollViewBottomInsetAdjustmentForKeyboard;
250     BOOL _currentlyAdjustingScrollViewInsetsForKeyboard;
251
252     BOOL _delayUpdateVisibleContentRects;
253     BOOL _hadDelayedUpdateVisibleContentRects;
254
255     BOOL _pageIsPrintingToPDF;
256     RetainPtr<CGPDFDocumentRef> _printedDocument;
257     Vector<std::function<void ()>> _snapshotsDeferredDuringResize;
258 #endif
259 #if PLATFORM(MAC)
260     std::unique_ptr<WebKit::WebViewImpl> _impl;
261     RetainPtr<WKTextFinderClient> _textFinderClient;
262 #endif
263 }
264
265 - (instancetype)initWithFrame:(CGRect)frame
266 {
267     return [self initWithFrame:frame configuration:adoptNS([[WKWebViewConfiguration alloc] init]).get()];
268 }
269
270 #if PLATFORM(IOS)
271 static int32_t deviceOrientationForUIInterfaceOrientation(UIInterfaceOrientation orientation)
272 {
273     switch (orientation) {
274     case UIInterfaceOrientationUnknown:
275     case UIInterfaceOrientationPortrait:
276         return 0;
277     case UIInterfaceOrientationPortraitUpsideDown:
278         return 180;
279     case UIInterfaceOrientationLandscapeLeft:
280         return -90;
281     case UIInterfaceOrientationLandscapeRight:
282         return 90;
283     }
284 }
285
286 static int32_t deviceOrientation()
287 {
288     return deviceOrientationForUIInterfaceOrientation([[UIApplication sharedApplication] statusBarOrientation]);
289 }
290
291 - (BOOL)_isShowingVideoPictureInPicture
292 {
293     if (!_page || !_page->videoFullscreenManager())
294         return false;
295     
296     return _page->videoFullscreenManager()->hasMode(WebCore::HTMLMediaElementEnums::VideoFullscreenModePictureInPicture);
297 }
298
299 - (BOOL)_mayAutomaticallyShowVideoPictureInPicture
300 {
301 #if !HAVE(AVKIT)
302     return false;
303 #else
304     if (!_page || !_page->videoFullscreenManager())
305         return false;
306
307     return _page->videoFullscreenManager()->mayAutomaticallyShowVideoPictureInPicture();
308 #endif
309 }
310
311 static bool shouldAllowPictureInPictureMediaPlayback()
312 {
313     static bool shouldAllowPictureInPictureMediaPlayback = dyld_get_program_sdk_version() >= DYLD_IOS_VERSION_9_0;
314     return shouldAllowPictureInPictureMediaPlayback;
315 }
316
317 #endif
318
319 #if ENABLE(DATA_DETECTION)
320 static WebCore::DataDetectorTypes fromWKDataDetectorTypes(uint64_t types)
321 {
322     if (static_cast<WKDataDetectorTypes>(types) == WKDataDetectorTypeNone)
323         return WebCore::DataDetectorTypeNone;
324     if (static_cast<WKDataDetectorTypes>(types) == WKDataDetectorTypeAll)
325         return WebCore::DataDetectorTypeAll;
326     
327     uint32_t value = WebCore::DataDetectorTypeNone;
328     if (types & WKDataDetectorTypePhoneNumber)
329         value |= WebCore::DataDetectorTypePhoneNumber;
330     if (types & WKDataDetectorTypeLink)
331         value |= WebCore::DataDetectorTypeLink;
332     if (types & WKDataDetectorTypeAddress)
333         value |= WebCore::DataDetectorTypeAddress;
334     if (types & WKDataDetectorTypeCalendarEvent)
335         value |= WebCore::DataDetectorTypeCalendarEvent;
336     
337     return static_cast<WebCore::DataDetectorTypes>(value);
338 }
339 #endif
340
341 - (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration
342 {
343     if (!(self = [super initWithFrame:frame]))
344         return nil;
345
346     if (!configuration)
347         [NSException raise:NSInvalidArgumentException format:@"Configuration cannot be nil"];
348
349     _configuration = adoptNS([configuration copy]);
350
351     if (WKWebView *relatedWebView = [_configuration _relatedWebView]) {
352         WKProcessPool *processPool = [_configuration processPool];
353         WKProcessPool *relatedWebViewProcessPool = [relatedWebView->_configuration processPool];
354         if (processPool && processPool != relatedWebViewProcessPool)
355             [NSException raise:NSInvalidArgumentException format:@"Related web view %@ has process pool %@ but configuration specifies a different process pool %@", relatedWebView, relatedWebViewProcessPool, configuration.processPool];
356
357         [_configuration setProcessPool:relatedWebViewProcessPool];
358     }
359
360     [_configuration _validate];
361
362
363     WebKit::WebProcessPool& processPool = *[_configuration processPool]->_processPool;
364     
365     auto pageConfiguration = API::PageConfiguration::create();
366
367     pageConfiguration->setProcessPool(&processPool);
368     pageConfiguration->setPreferences([_configuration preferences]->_preferences.get());
369     if (WKWebView *relatedWebView = [_configuration _relatedWebView])
370         pageConfiguration->setRelatedPage(relatedWebView->_page.get());
371
372     pageConfiguration->setUserContentController([_configuration userContentController]->_userContentControllerProxy.get());
373     pageConfiguration->setVisitedLinkStore([_configuration _visitedLinkStore]->_visitedLinkStore.get());
374     pageConfiguration->setWebsiteDataStore([_configuration websiteDataStore]->_websiteDataStore.get());
375     pageConfiguration->setTreatsSHA1SignedCertificatesAsInsecure([_configuration _treatsSHA1SignedCertificatesAsInsecure]);
376
377     RefPtr<WebKit::WebPageGroup> pageGroup;
378     NSString *groupIdentifier = configuration._groupIdentifier;
379     if (groupIdentifier.length) {
380         pageGroup = WebKit::WebPageGroup::create(configuration._groupIdentifier);
381         pageConfiguration->setPageGroup(pageGroup.get());
382     }
383
384     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::suppressesIncrementalRenderingKey(), WebKit::WebPreferencesStore::Value(!![_configuration suppressesIncrementalRendering]));
385
386     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::shouldRespectImageOrientationKey(), WebKit::WebPreferencesStore::Value(!![_configuration _respectsImageOrientation]));
387     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::shouldPrintBackgroundsKey(), WebKit::WebPreferencesStore::Value(!![_configuration _printsBackgrounds]));
388     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::incrementalRenderingSuppressionTimeoutKey(), WebKit::WebPreferencesStore::Value([_configuration _incrementalRenderingSuppressionTimeout]));
389     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::javaScriptMarkupEnabledKey(), WebKit::WebPreferencesStore::Value(!![_configuration _allowsJavaScriptMarkup]));
390     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::shouldConvertPositionStyleOnCopyKey(), WebKit::WebPreferencesStore::Value(!![_configuration _convertsPositionStyleOnCopy]));
391     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::httpEquivEnabledKey(), WebKit::WebPreferencesStore::Value(!![_configuration _allowsMetaRefresh]));
392
393 #if PLATFORM(MAC)
394     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::showsURLsInToolTipsEnabledKey(), WebKit::WebPreferencesStore::Value(!![_configuration _showsURLsInToolTips]));
395     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::serviceControlsEnabledKey(), WebKit::WebPreferencesStore::Value(!![_configuration _serviceControlsEnabled]));
396     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::imageControlsEnabledKey(), WebKit::WebPreferencesStore::Value(!![_configuration _imageControlsEnabled]));
397 #endif
398
399 #if PLATFORM(IOS)
400     pageConfiguration->setAlwaysRunsAtForegroundPriority([_configuration _alwaysRunsAtForegroundPriority]);
401
402     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::allowsInlineMediaPlaybackKey(), WebKit::WebPreferencesStore::Value(!![_configuration allowsInlineMediaPlayback]));
403     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::inlineMediaPlaybackRequiresPlaysInlineAttributeKey(), WebKit::WebPreferencesStore::Value(!![_configuration _inlineMediaPlaybackRequiresPlaysInlineAttribute]));
404     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::allowsPictureInPictureMediaPlaybackKey(), WebKit::WebPreferencesStore::Value(!![_configuration allowsPictureInPictureMediaPlayback] && shouldAllowPictureInPictureMediaPlayback()));
405     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::requiresUserGestureForMediaPlaybackKey(), WebKit::WebPreferencesStore::Value(!![_configuration requiresUserActionForMediaPlayback]));
406     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::requiresUserGestureForAudioPlaybackKey(), WebKit::WebPreferencesStore::Value(!![_configuration _requiresUserActionForAudioPlayback]));
407     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::invisibleAutoplayNotPermittedKey(), WebKit::WebPreferencesStore::Value(!![_configuration _invisibleAutoplayNotPermitted]));
408     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::mediaDataLoadsAutomaticallyKey(), WebKit::WebPreferencesStore::Value(!![_configuration _mediaDataLoadsAutomatically]));
409 #endif
410 #if ENABLE(DATA_DETECTION)
411     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::dataDetectorTypesKey(), WebKit::WebPreferencesStore::Value(static_cast<uint32_t>(fromWKDataDetectorTypes([_configuration dataDetectorTypes]))));
412 #endif
413 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
414     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::allowsAirPlayForMediaPlaybackKey(), WebKit::WebPreferencesStore::Value(!![_configuration allowsAirPlayForMediaPlayback]));
415 #endif
416
417 #if PLATFORM(IOS)
418     CGRect bounds = self.bounds;
419     _scrollView = adoptNS([[WKScrollView alloc] initWithFrame:bounds]);
420     [_scrollView setInternalDelegate:self];
421     [_scrollView setBouncesZoom:YES];
422
423     [self addSubview:_scrollView.get()];
424
425     _contentView = adoptNS([[WKContentView alloc] initWithFrame:bounds processPool:processPool configuration:WTFMove(pageConfiguration) webView:self]);
426
427     _page = [_contentView page];
428     _page->setDeviceOrientation(deviceOrientation());
429
430     [_contentView layer].anchorPoint = CGPointZero;
431     [_contentView setFrame:bounds];
432     [_scrollView addSubview:_contentView.get()];
433     [_scrollView addSubview:[_contentView unscaledView]];
434     [self _updateScrollViewBackground];
435
436     _viewportMetaTagWidth = WebCore::ViewportArguments::ValueAuto;
437     _initialScaleFactor = 1;
438     _fastClickingIsDisabled = [[NSUserDefaults standardUserDefaults] boolForKey:@"WebKitFastClickingDisabled"];
439
440     [self _frameOrBoundsChanged];
441
442     NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
443     [center addObserver:self selector:@selector(_keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];
444     [center addObserver:self selector:@selector(_keyboardDidChangeFrame:) name:UIKeyboardDidChangeFrameNotification object:nil];
445     [center addObserver:self selector:@selector(_keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
446     [center addObserver:self selector:@selector(_keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
447     [center addObserver:self selector:@selector(_windowDidRotate:) name:UIWindowDidRotateNotification object:nil];
448     [center addObserver:self selector:@selector(_contentSizeCategoryDidChange:) name:UIContentSizeCategoryDidChangeNotification object:nil];
449     _page->contentSizeCategoryDidChange([self _contentSizeCategory]);
450
451     [[_configuration _contentProviderRegistry] addPage:*_page];
452 #endif
453
454 #if PLATFORM(MAC)
455     _impl = std::make_unique<WebKit::WebViewImpl>(self, self, processPool, WTFMove(pageConfiguration));
456     _page = &_impl->page();
457
458     _impl->setAutomaticallyAdjustsContentInsets(true);
459 #endif
460
461     _page->setBackgroundExtendsBeyondPage(true);
462
463     if (NSString *applicationNameForUserAgent = configuration.applicationNameForUserAgent)
464         _page->setApplicationNameForUserAgent(applicationNameForUserAgent);
465
466     _navigationState = std::make_unique<WebKit::NavigationState>(self);
467     _uiDelegate = std::make_unique<WebKit::UIDelegate>(self);
468     _page->setFindClient(std::make_unique<WebKit::FindClient>(self));
469     _page->setDiagnosticLoggingClient(std::make_unique<WebKit::DiagnosticLoggingClient>(self));
470
471     pageToViewMap().add(_page.get(), self);
472
473     return self;
474 }
475
476 - (instancetype)initWithCoder:(NSCoder *)coder
477 {
478     [self release];
479     return nil;
480 }
481
482 - (void)dealloc
483 {
484 #if PLATFORM(MAC)
485     [_textFinderClient willDestroyView:self];
486 #endif
487
488 #if PLATFORM(IOS)
489     [_contentView _webViewDestroyed];
490
491     if (_remoteObjectRegistry)
492         _page->process().processPool().removeMessageReceiver(Messages::RemoteObjectRegistry::messageReceiverName(), _page->pageID());
493
494     _page->close();
495
496     [_remoteObjectRegistry _invalidate];
497     [[_configuration _contentProviderRegistry] removePage:*_page];
498     [[NSNotificationCenter defaultCenter] removeObserver:self];
499     [_scrollView setInternalDelegate:nil];
500 #endif
501
502     pageToViewMap().remove(_page.get());
503
504     [super dealloc];
505 }
506
507 - (WKWebViewConfiguration *)configuration
508 {
509     return [[_configuration copy] autorelease];
510 }
511
512 - (WKBackForwardList *)backForwardList
513 {
514     return wrapper(_page->backForwardList());
515 }
516
517 - (id <WKNavigationDelegate>)navigationDelegate
518 {
519     return _navigationState->navigationDelegate().autorelease();
520 }
521
522 - (void)setNavigationDelegate:(id <WKNavigationDelegate>)navigationDelegate
523 {
524     _page->setNavigationClient(_navigationState->createNavigationClient());
525     _navigationState->setNavigationDelegate(navigationDelegate);
526 }
527
528 - (id <WKUIDelegate>)UIDelegate
529 {
530     return _uiDelegate->delegate().autorelease();
531 }
532
533 - (void)setUIDelegate:(id<WKUIDelegate>)UIDelegate
534 {
535 #if ENABLE(CONTEXT_MENUS)
536     _page->setContextMenuClient(_uiDelegate->createContextMenuClient());
537 #endif
538     _page->setUIClient(_uiDelegate->createUIClient());
539     _uiDelegate->setDelegate(UIDelegate);
540 }
541
542 - (WKNavigation *)loadRequest:(NSURLRequest *)request
543 {
544     auto navigation = _page->loadRequest(request);
545     if (!navigation)
546         return nil;
547
548     return [wrapper(*navigation.release().leakRef()) autorelease];
549 }
550
551 - (WKNavigation *)loadFileURL:(NSURL *)URL allowingReadAccessToURL:(NSURL *)readAccessURL
552 {
553     if (![URL isFileURL])
554         [NSException raise:NSInvalidArgumentException format:@"%@ is not a file URL", URL];
555
556     if (![readAccessURL isFileURL])
557         [NSException raise:NSInvalidArgumentException format:@"%@ is not a file URL", readAccessURL];
558
559     auto navigation = _page->loadFile([URL _web_originalDataAsWTFString], [readAccessURL _web_originalDataAsWTFString]);
560     if (!navigation)
561         return nil;
562
563     return [wrapper(*navigation.release().leakRef()) autorelease];
564 }
565
566 - (WKNavigation *)loadHTMLString:(NSString *)string baseURL:(NSURL *)baseURL
567 {
568     NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
569
570     return [self loadData:data MIMEType:@"text/html" characterEncodingName:@"UTF-8" baseURL:baseURL];
571 }
572
573 - (WKNavigation *)loadData:(NSData *)data MIMEType:(NSString *)MIMEType characterEncodingName:(NSString *)characterEncodingName baseURL:(NSURL *)baseURL
574 {
575     auto navigation = _page->loadData(API::Data::createWithoutCopying(data).ptr(), MIMEType, characterEncodingName, baseURL.absoluteString);
576     if (!navigation)
577         return nil;
578
579     return [wrapper(*navigation.release().leakRef()) autorelease];
580 }
581
582 - (WKNavigation *)goToBackForwardListItem:(WKBackForwardListItem *)item
583 {
584     auto navigation = _page->goToBackForwardItem(&item._item);
585     if (!navigation)
586         return nil;
587
588     return [wrapper(*navigation.release().leakRef()) autorelease];
589 }
590
591 - (NSString *)title
592 {
593     return _page->pageLoadState().title();
594 }
595
596 - (NSURL *)URL
597 {
598     return [NSURL _web_URLWithWTFString:_page->pageLoadState().activeURL()];
599 }
600
601 - (BOOL)isLoading
602 {
603     return _page->pageLoadState().isLoading();
604 }
605
606 - (double)estimatedProgress
607 {
608     return _page->pageLoadState().estimatedProgress();
609 }
610
611 - (BOOL)hasOnlySecureContent
612 {
613     return _page->pageLoadState().hasOnlySecureContent();
614 }
615
616 - (NSArray *)certificateChain
617 {
618     auto certificateInfo = _page->pageLoadState().certificateInfo();
619     if (!certificateInfo)
620         return @[ ];
621
622     return (NSArray *)certificateInfo->certificateInfo().certificateChain() ?: @[ ];
623 }
624
625 - (BOOL)canGoBack
626 {
627     return _page->pageLoadState().canGoBack();
628 }
629
630 - (BOOL)canGoForward
631 {
632     return _page->pageLoadState().canGoForward();
633 }
634
635 - (WKNavigation *)goBack
636 {
637     auto navigation = _page->goBack();
638     if (!navigation)
639         return nil;
640  
641     return [wrapper(*navigation.release().leakRef()) autorelease];
642 }
643
644 - (WKNavigation *)goForward
645 {
646     auto navigation = _page->goForward();
647     if (!navigation)
648         return nil;
649
650     return [wrapper(*navigation.release().leakRef()) autorelease];
651 }
652
653 - (WKNavigation *)reload
654 {
655     const bool reloadFromOrigin = false;
656     const bool contentBlockersEnabled = true;
657     auto navigation = _page->reload(reloadFromOrigin, contentBlockersEnabled);
658     if (!navigation)
659         return nil;
660
661     return [wrapper(*navigation.release().leakRef()) autorelease];
662 }
663
664 - (WKNavigation *)reloadFromOrigin
665 {
666     const bool reloadFromOrigin = true;
667     const bool contentBlockersEnabled = true;
668     auto navigation = _page->reload(reloadFromOrigin, contentBlockersEnabled);
669     if (!navigation)
670         return nil;
671
672     return [wrapper(*navigation.release().leakRef()) autorelease];
673 }
674
675 - (void)stopLoading
676 {
677     _page->stopLoading();
678 }
679
680 static WKErrorCode callbackErrorCode(WebKit::CallbackBase::Error error)
681 {
682     switch (error) {
683     case WebKit::CallbackBase::Error::None:
684         ASSERT_NOT_REACHED();
685         return WKErrorUnknown;
686
687     case WebKit::CallbackBase::Error::Unknown:
688         return WKErrorUnknown;
689
690     case WebKit::CallbackBase::Error::ProcessExited:
691         return WKErrorWebContentProcessTerminated;
692
693     case WebKit::CallbackBase::Error::OwnerWasInvalidated:
694         return WKErrorWebViewInvalidated;
695     }
696 }
697
698 - (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^)(id, NSError *))completionHandler
699 {
700     auto handler = adoptNS([completionHandler copy]);
701
702     _page->runJavaScriptInMainFrame(javaScriptString, [handler](API::SerializedScriptValue* serializedScriptValue, bool hadException, const WebCore::ExceptionDetails& details, WebKit::ScriptValueCallback::Error errorCode) {
703         if (!handler)
704             return;
705
706         if (errorCode != WebKit::ScriptValueCallback::Error::None) {
707             auto error = createNSError(callbackErrorCode(errorCode));
708             if (errorCode == WebKit::ScriptValueCallback::Error::OwnerWasInvalidated) {
709                 // The OwnerWasInvalidated callback is synchronous. We don't want to call the block from within it
710                 // because that can trigger re-entrancy bugs in WebKit.
711                 // FIXME: It would be even better if GenericCallback did this for us.
712                 dispatch_async(dispatch_get_main_queue(), [handler, error] {
713                     auto rawHandler = (void (^)(id, NSError *))handler.get();
714                     rawHandler(nil, error.get());
715                 });
716                 return;
717             }
718
719             auto rawHandler = (void (^)(id, NSError *))handler.get();
720             rawHandler(nil, error.get());
721             return;
722         }
723
724         auto rawHandler = (void (^)(id, NSError *))handler.get();
725         if (hadException) {
726             ASSERT(!serializedScriptValue);
727
728             RetainPtr<NSMutableDictionary> userInfo = adoptNS([[NSMutableDictionary alloc] init]);
729
730             [userInfo setObject:localizedDescriptionForErrorCode(WKErrorJavaScriptExceptionOccurred) forKey:NSLocalizedDescriptionKey];
731             [userInfo setObject:static_cast<NSString *>(details.message) forKey:_WKJavaScriptExceptionMessageErrorKey];
732             [userInfo setObject:@(details.lineNumber) forKey:_WKJavaScriptExceptionLineNumberErrorKey];
733             [userInfo setObject:@(details.columnNumber) forKey:_WKJavaScriptExceptionColumnNumberErrorKey];
734
735             if (!details.sourceURL.isEmpty())
736                 [userInfo setObject:[NSURL _web_URLWithWTFString:details.sourceURL] forKey:_WKJavaScriptExceptionSourceURLErrorKey];
737
738             rawHandler(nil, adoptNS([[NSError alloc] initWithDomain:WKErrorDomain code:WKErrorJavaScriptExceptionOccurred userInfo:userInfo.get()]).get());
739             return;
740         }
741
742         if (!serializedScriptValue) {
743             rawHandler(nil, createNSError(WKErrorJavaScriptResultTypeIsUnsupported).get());
744             return;
745         }
746
747         id body = API::SerializedScriptValue::deserialize(*serializedScriptValue->internalRepresentation(), 0);
748         rawHandler(body, nil);
749     });
750 }
751
752 - (NSString *)customUserAgent
753 {
754     return _page->customUserAgent();
755 }
756
757 - (void)setCustomUserAgent:(NSString *)customUserAgent
758 {
759     _page->setCustomUserAgent(customUserAgent);
760 }
761
762 - (WKPageRef)_pageForTesting
763 {
764     return toAPI(_page.get());
765 }
766
767 - (BOOL)allowsLinkPreview
768 {
769 #if PLATFORM(MAC)
770     return _impl->allowsLinkPreview();
771 #elif PLATFORM(IOS)
772     return _allowsLinkPreview;
773 #endif
774 }
775
776 - (void)setAllowsLinkPreview:(BOOL)allowsLinkPreview
777 {
778 #if PLATFORM(MAC)
779     _impl->setAllowsLinkPreview(allowsLinkPreview);
780     return;
781 #elif PLATFORM(IOS)
782     if (_allowsLinkPreview == allowsLinkPreview)
783         return;
784
785     _allowsLinkPreview = allowsLinkPreview;
786
787 #if HAVE(LINK_PREVIEW)
788     if (_allowsLinkPreview)
789         [_contentView _registerPreview];
790     else
791         [_contentView _unregisterPreview];
792 #endif // HAVE(LINK_PREVIEW)
793 #endif // PLATFORM(IOS)
794 }
795
796 #pragma mark iOS-specific methods
797
798 #if PLATFORM(IOS)
799
800 - (BOOL)_isBackground
801 {
802     if ([self _isDisplayingPDF])
803         return [(WKPDFView *)_customContentView isBackground];
804
805     return [_contentView isBackground];
806 }
807
808 - (void)setFrame:(CGRect)frame
809 {
810     CGRect oldFrame = self.frame;
811     [super setFrame:frame];
812
813     if (!CGSizeEqualToSize(oldFrame.size, frame.size))
814         [self _frameOrBoundsChanged];
815 }
816
817 - (void)setBounds:(CGRect)bounds
818 {
819     CGRect oldBounds = self.bounds;
820     [super setBounds:bounds];
821     [_customContentFixedOverlayView setFrame:self.bounds];
822
823     if (!CGSizeEqualToSize(oldBounds.size, bounds.size))
824         [self _frameOrBoundsChanged];
825 }
826
827 - (UIScrollView *)scrollView
828 {
829     return _scrollView.get();
830 }
831
832 - (WKBrowsingContextController *)browsingContextController
833 {
834     return [_contentView browsingContextController];
835 }
836
837 - (BOOL)becomeFirstResponder
838 {
839     return [self._currentContentView becomeFirstResponder] || [super becomeFirstResponder];
840 }
841
842 - (BOOL)canBecomeFirstResponder
843 {
844     return YES;
845 }
846
847 static inline CGFloat floorToDevicePixel(CGFloat input, float deviceScaleFactor)
848 {
849     return CGFloor(input * deviceScaleFactor) / deviceScaleFactor;
850 }
851
852 static inline bool pointsEqualInDevicePixels(CGPoint a, CGPoint b, float deviceScaleFactor)
853 {
854     return fabs(a.x * deviceScaleFactor - b.x * deviceScaleFactor) < std::numeric_limits<float>::epsilon()
855         && fabs(a.y * deviceScaleFactor - b.y * deviceScaleFactor) < std::numeric_limits<float>::epsilon();
856 }
857
858 static CGSize roundScrollViewContentSize(const WebKit::WebPageProxy& page, CGSize contentSize)
859 {
860     float deviceScaleFactor = page.deviceScaleFactor();
861     return CGSizeMake(floorToDevicePixel(contentSize.width, deviceScaleFactor), floorToDevicePixel(contentSize.height, deviceScaleFactor));
862 }
863
864 - (UIView *)_currentContentView
865 {
866     return _customContentView ? _customContentView.get() : _contentView.get();
867 }
868
869 - (WKWebViewContentProviderRegistry *)_contentProviderRegistry
870 {
871     return [_configuration _contentProviderRegistry];
872 }
873
874 - (WKSelectionGranularity)_selectionGranularity
875 {
876     return [_configuration selectionGranularity];
877 }
878
879 - (void)_setHasCustomContentView:(BOOL)pageHasCustomContentView loadedMIMEType:(const WTF::String&)mimeType
880 {
881     if (pageHasCustomContentView) {
882         [_customContentView removeFromSuperview];
883         [_customContentFixedOverlayView removeFromSuperview];
884
885         Class representationClass = [[_configuration _contentProviderRegistry] providerForMIMEType:mimeType];
886         ASSERT(representationClass);
887         _customContentView = adoptNS([[representationClass alloc] web_initWithFrame:self.bounds webView:self]);
888         _customContentFixedOverlayView = adoptNS([[UIView alloc] initWithFrame:self.bounds]);
889         [_customContentFixedOverlayView setUserInteractionEnabled:NO];
890
891         [[_contentView unscaledView] removeFromSuperview];
892         [_contentView removeFromSuperview];
893         [_scrollView addSubview:_customContentView.get()];
894         [self addSubview:_customContentFixedOverlayView.get()];
895
896         [_customContentView web_setMinimumSize:self.bounds.size];
897         [_customContentView web_setFixedOverlayView:_customContentFixedOverlayView.get()];
898     } else if (_customContentView) {
899         [_customContentView removeFromSuperview];
900         _customContentView = nullptr;
901
902         [_customContentFixedOverlayView removeFromSuperview];
903         _customContentFixedOverlayView = nullptr;
904
905         [_scrollView addSubview:_contentView.get()];
906         [_scrollView addSubview:[_contentView unscaledView]];
907         [_scrollView setContentSize:roundScrollViewContentSize(*_page, [_contentView frame].size)];
908
909         [_customContentFixedOverlayView setFrame:self.bounds];
910         [self addSubview:_customContentFixedOverlayView.get()];
911     }
912
913     if (self.isFirstResponder && self._currentContentView.canBecomeFirstResponder)
914         [self._currentContentView becomeFirstResponder];
915 }
916
917 - (void)_didFinishLoadingDataForCustomContentProviderWithSuggestedFilename:(const String&)suggestedFilename data:(NSData *)data
918 {
919     ASSERT(_customContentView);
920     [_customContentView web_setContentProviderData:data suggestedFilename:suggestedFilename];
921
922     // FIXME: It may make more sense for custom content providers to invoke this when they're ready,
923     // because there's no guarantee that all custom content providers will lay out synchronously.
924     _page->didLayoutForCustomContentProvider();
925 }
926
927 - (void)_willInvokeUIScrollViewDelegateCallback
928 {
929     _delayUpdateVisibleContentRects = YES;
930 }
931
932 - (void)_didInvokeUIScrollViewDelegateCallback
933 {
934     _delayUpdateVisibleContentRects = NO;
935     if (_hadDelayedUpdateVisibleContentRects) {
936         _hadDelayedUpdateVisibleContentRects = NO;
937         [self _updateVisibleContentRects];
938     }
939 }
940
941 static CGFloat contentZoomScale(WKWebView *webView)
942 {
943     CGFloat scale = webView._currentContentView.layer.affineTransform.a;
944     ASSERT(scale == [webView->_scrollView zoomScale]);
945     return scale;
946 }
947
948 static WebCore::Color baseScrollViewBackgroundColor(WKWebView *webView)
949 {
950     if (webView->_customContentView)
951         return [webView->_customContentView backgroundColor].CGColor;
952
953     if (webView->_gestureController) {
954         WebCore::Color color = webView->_gestureController->backgroundColorForCurrentSnapshot();
955         if (color.isValid())
956             return color;
957     }
958
959     return webView->_page->pageExtendedBackgroundColor();
960 }
961
962 static WebCore::Color scrollViewBackgroundColor(WKWebView *webView)
963 {
964     if (!webView.opaque)
965         return WebCore::Color::transparent;
966
967     WebCore::Color color = baseScrollViewBackgroundColor(webView);
968
969     if (!color.isValid())
970         color = WebCore::Color::white;
971
972     CGFloat zoomScale = contentZoomScale(webView);
973     CGFloat minimumZoomScale = [webView->_scrollView minimumZoomScale];
974     if (zoomScale < minimumZoomScale) {
975         CGFloat slope = 12;
976         CGFloat opacity = std::max<CGFloat>(1 - slope * (minimumZoomScale - zoomScale), 0);
977         color = WebCore::colorWithOverrideAlpha(color.rgb(), opacity);
978     }
979
980     return color;
981 }
982
983 - (void)_updateScrollViewBackground
984 {
985     WebCore::Color color = scrollViewBackgroundColor(self);
986
987     if (_scrollViewBackgroundColor == color)
988         return;
989
990     _scrollViewBackgroundColor = color;
991
992     auto uiBackgroundColor = adoptNS([[UIColor alloc] initWithCGColor:cachedCGColor(color)]);
993     [_scrollView setBackgroundColor:uiBackgroundColor.get()];
994
995     // Update the indicator style based on the lightness/darkness of the background color.
996     double hue, saturation, lightness;
997     color.getHSL(hue, saturation, lightness);
998     if (lightness <= .5 && color.alpha() > 0)
999         [_scrollView setIndicatorStyle:UIScrollViewIndicatorStyleWhite];
1000     else
1001         [_scrollView setIndicatorStyle:UIScrollViewIndicatorStyleDefault];
1002 }
1003
1004 - (CGPoint)_adjustedContentOffset:(CGPoint)point
1005 {
1006     CGPoint result = point;
1007     UIEdgeInsets contentInset = [self _computedContentInset];
1008
1009     result.x -= contentInset.left;
1010     result.y -= contentInset.top;
1011
1012     return result;
1013 }
1014
1015 - (UIEdgeInsets)_computedContentInset
1016 {
1017     if (_haveSetObscuredInsets)
1018         return _obscuredInsets;
1019
1020     return [_scrollView contentInset];
1021 }
1022
1023 - (void)_processDidExit
1024 {
1025     if (!_customContentView && _dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing) {
1026         NSUInteger indexOfResizeAnimationView = [[_scrollView subviews] indexOfObject:_resizeAnimationView.get()];
1027         [_scrollView insertSubview:_contentView.get() atIndex:indexOfResizeAnimationView];
1028         [_scrollView insertSubview:[_contentView unscaledView] atIndex:indexOfResizeAnimationView + 1];
1029         [_resizeAnimationView removeFromSuperview];
1030         _resizeAnimationView = nil;
1031
1032         _resizeAnimationTransformAdjustments = CATransform3DIdentity;
1033     }
1034     [_contentView setFrame:self.bounds];
1035     [_scrollView setBackgroundColor:[UIColor whiteColor]];
1036     [_scrollView setContentOffset:[self _adjustedContentOffset:CGPointZero]];
1037     [_scrollView setZoomScale:1];
1038
1039     _viewportMetaTagWidth = WebCore::ViewportArguments::ValueAuto;
1040     _initialScaleFactor = 1;
1041     _hasCommittedLoadForMainFrame = NO;
1042     _needsResetViewStateAfterCommitLoadForMainFrame = NO;
1043     _dynamicViewportUpdateMode = DynamicViewportUpdateMode::NotResizing;
1044     [_contentView setHidden:NO];
1045     _needsToRestoreExposedRect = NO;
1046     _needsToRestoreUnobscuredCenter = NO;
1047     _scrollViewBackgroundColor = WebCore::Color();
1048     _delayUpdateVisibleContentRects = NO;
1049     _hadDelayedUpdateVisibleContentRects = NO;
1050 }
1051
1052 - (void)_didCommitLoadForMainFrame
1053 {
1054     _firstPaintAfterCommitLoadTransactionID = downcast<WebKit::RemoteLayerTreeDrawingAreaProxy>(*_page->drawingArea()).nextLayerTreeTransactionID();
1055
1056     _hasCommittedLoadForMainFrame = YES;
1057     _needsResetViewStateAfterCommitLoadForMainFrame = YES;
1058 }
1059
1060 static CGPoint contentOffsetBoundedInValidRange(UIScrollView *scrollView, CGPoint contentOffset)
1061 {
1062     UIEdgeInsets contentInsets = scrollView.contentInset;
1063     CGSize contentSize = scrollView.contentSize;
1064     CGSize scrollViewSize = scrollView.bounds.size;
1065
1066     CGFloat maxHorizontalOffset = contentSize.width + contentInsets.right - scrollViewSize.width;
1067     contentOffset.x = std::min(maxHorizontalOffset, contentOffset.x);
1068     contentOffset.x = std::max(-contentInsets.left, contentOffset.x);
1069
1070     CGFloat maxVerticalOffset = contentSize.height + contentInsets.bottom - scrollViewSize.height;
1071     contentOffset.y = std::min(maxVerticalOffset, contentOffset.y);
1072     contentOffset.y = std::max(-contentInsets.top, contentOffset.y);
1073     return contentOffset;
1074 }
1075
1076 static void changeContentOffsetBoundedInValidRange(UIScrollView *scrollView, WebCore::FloatPoint contentOffset)
1077 {
1078     scrollView.contentOffset = contentOffsetBoundedInValidRange(scrollView, contentOffset);
1079 }
1080
1081 - (WebCore::FloatRect)visibleRectInViewCoordinates
1082 {
1083     WebCore::FloatRect bounds = self.bounds;
1084     bounds.moveBy([_scrollView contentOffset]);
1085     WebCore::FloatRect contentViewBounds = [_contentView bounds];
1086     bounds.intersect(contentViewBounds);
1087     return bounds;
1088 }
1089
1090 // WebCore stores the page scale factor as float instead of double. When we get a scale from WebCore,
1091 // we need to ignore differences that are within a small rounding error on floats.
1092 static inline bool areEssentiallyEqualAsFloat(float a, float b)
1093 {
1094     return WTF::areEssentiallyEqual(a, b);
1095 }
1096
1097 - (void)_didCommitLayerTree:(const WebKit::RemoteLayerTreeTransaction&)layerTreeTransaction
1098 {
1099     if (_customContentView)
1100         return;
1101
1102     if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing) {
1103         if (layerTreeTransaction.transactionID() >= _resizeAnimationTransformTransactionID) {
1104             [_resizeAnimationView layer].sublayerTransform = _resizeAnimationTransformAdjustments;
1105             if (_dynamicViewportUpdateMode == DynamicViewportUpdateMode::ResizingWithDocumentHidden) {
1106                 [_contentView setHidden:NO];
1107                 [self _endAnimatedResize];
1108             }
1109         }
1110         return;
1111     }
1112
1113     CGSize newContentSize = roundScrollViewContentSize(*_page, [_contentView frame].size);
1114     [_scrollView _setContentSizePreservingContentOffsetDuringRubberband:newContentSize];
1115
1116     [_scrollView setMinimumZoomScale:layerTreeTransaction.minimumScaleFactor()];
1117     [_scrollView setMaximumZoomScale:layerTreeTransaction.maximumScaleFactor()];
1118     [_scrollView setZoomEnabled:layerTreeTransaction.allowsUserScaling()];
1119     if (!layerTreeTransaction.scaleWasSetByUIProcess() && ![_scrollView isZooming] && ![_scrollView isZoomBouncing] && ![_scrollView _isAnimatingZoom])
1120         [_scrollView setZoomScale:layerTreeTransaction.pageScaleFactor()];
1121
1122     _viewportMetaTagWidth = layerTreeTransaction.viewportMetaTagWidth();
1123     _viewportMetaTagWidthWasExplicit = layerTreeTransaction.viewportMetaTagWidthWasExplicit();
1124     _viewportMetaTagCameFromImageDocument = layerTreeTransaction.viewportMetaTagCameFromImageDocument();
1125     _initialScaleFactor = layerTreeTransaction.initialScaleFactor();
1126     if (![_contentView _mayDisableDoubleTapGesturesDuringSingleTap])
1127         [_contentView _setDoubleTapGesturesEnabled:self._allowsDoubleTapGestures];
1128
1129     [self _updateScrollViewBackground];
1130
1131     if (_gestureController)
1132         _gestureController->setRenderTreeSize(layerTreeTransaction.renderTreeSize());
1133
1134     if (_needsResetViewStateAfterCommitLoadForMainFrame && layerTreeTransaction.transactionID() >= _firstPaintAfterCommitLoadTransactionID) {
1135         _needsResetViewStateAfterCommitLoadForMainFrame = NO;
1136         [_scrollView setContentOffset:[self _adjustedContentOffset:CGPointZero]];
1137         [self _updateVisibleContentRects];
1138         if (_observedRenderingProgressEvents & _WKRenderingProgressEventFirstPaint)
1139             _navigationState->didFirstPaint();
1140     }
1141
1142     bool isTransactionAfterPageRestore = layerTreeTransaction.transactionID() >= _firstTransactionIDAfterPageRestore;
1143
1144     if (_needsToRestoreExposedRect && isTransactionAfterPageRestore) {
1145         _needsToRestoreExposedRect = NO;
1146
1147         if (areEssentiallyEqualAsFloat(contentZoomScale(self), _scaleToRestore)) {
1148             WebCore::FloatPoint exposedPosition = _exposedRectToRestore.location();
1149             exposedPosition.scale(_scaleToRestore, _scaleToRestore);
1150
1151             changeContentOffsetBoundedInValidRange(_scrollView.get(), exposedPosition);
1152             if (_gestureController)
1153                 _gestureController->didRestoreScrollPosition();
1154         }
1155         [self _updateVisibleContentRects];
1156     }
1157
1158     if (_needsToRestoreUnobscuredCenter && isTransactionAfterPageRestore) {
1159         _needsToRestoreUnobscuredCenter = NO;
1160
1161         if (areEssentiallyEqualAsFloat(contentZoomScale(self), _scaleToRestore)) {
1162             CGRect unobscuredRect = UIEdgeInsetsInsetRect(self.bounds, _obscuredInsets);
1163             WebCore::FloatSize unobscuredContentSizeAtNewScale(unobscuredRect.size.width / _scaleToRestore, unobscuredRect.size.height / _scaleToRestore);
1164             WebCore::FloatPoint topLeftInDocumentCoordinates(_unobscuredCenterToRestore.x() - unobscuredContentSizeAtNewScale.width() / 2, _unobscuredCenterToRestore.y() - unobscuredContentSizeAtNewScale.height() / 2);
1165
1166             topLeftInDocumentCoordinates.scale(_scaleToRestore, _scaleToRestore);
1167             topLeftInDocumentCoordinates.moveBy(WebCore::FloatPoint(-_obscuredInsets.left, -_obscuredInsets.top));
1168
1169             changeContentOffsetBoundedInValidRange(_scrollView.get(), topLeftInDocumentCoordinates);
1170             if (_gestureController)
1171                 _gestureController->didRestoreScrollPosition();
1172         }
1173         [self _updateVisibleContentRects];
1174     }
1175     
1176     if (WebKit::RemoteLayerTreeScrollingPerformanceData* scrollPerfData = _page->scrollingPerformanceData())
1177         scrollPerfData->didCommitLayerTree([self visibleRectInViewCoordinates]);
1178 }
1179
1180 - (void)_dynamicViewportUpdateChangedTargetToScale:(double)newScale position:(CGPoint)newScrollPosition nextValidLayerTreeTransactionID:(uint64_t)nextValidLayerTreeTransactionID
1181 {
1182     if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing) {
1183         CGFloat animatingScaleTarget = [[_resizeAnimationView layer] transform].m11;
1184         double currentTargetScale = animatingScaleTarget * [[_contentView layer] transform].m11;
1185         double scale = newScale / currentTargetScale;
1186         _resizeAnimationTransformAdjustments = CATransform3DMakeScale(scale, scale, 1);
1187
1188         CGPoint newContentOffset = [self _adjustedContentOffset:CGPointMake(newScrollPosition.x * newScale, newScrollPosition.y * newScale)];
1189         CGPoint currentContentOffset = [_scrollView contentOffset];
1190
1191         _resizeAnimationTransformAdjustments.m41 = (currentContentOffset.x - newContentOffset.x) / animatingScaleTarget;
1192         _resizeAnimationTransformAdjustments.m42 = (currentContentOffset.y - newContentOffset.y) / animatingScaleTarget;
1193         _resizeAnimationTransformTransactionID = nextValidLayerTreeTransactionID;
1194     }
1195 }
1196
1197 - (void)_couldNotRestorePageState
1198 {
1199     // The gestureController may be waiting for the scroll position to be restored
1200     // in order to remove the swipe snapshot. Since the scroll position could not be
1201     // restored, tell the gestureController it was restored so that it no longer waits
1202     // for it.
1203     if (_gestureController)
1204         _gestureController->didRestoreScrollPosition();
1205 }
1206
1207 - (void)_restorePageStateToExposedRect:(WebCore::FloatRect)exposedRect scale:(double)scale
1208 {
1209     if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing)
1210         return;
1211
1212     if (_customContentView)
1213         return;
1214
1215     _needsToRestoreUnobscuredCenter = NO;
1216     _needsToRestoreExposedRect = YES;
1217     _firstTransactionIDAfterPageRestore = downcast<WebKit::RemoteLayerTreeDrawingAreaProxy>(*_page->drawingArea()).nextLayerTreeTransactionID();
1218     _exposedRectToRestore = exposedRect;
1219     _scaleToRestore = scale;
1220 }
1221
1222 - (void)_restorePageStateToUnobscuredCenter:(WebCore::FloatPoint)center scale:(double)scale
1223 {
1224     if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing)
1225         return;
1226
1227     if (_customContentView)
1228         return;
1229
1230     _needsToRestoreExposedRect = NO;
1231     _needsToRestoreUnobscuredCenter = YES;
1232     _firstTransactionIDAfterPageRestore = downcast<WebKit::RemoteLayerTreeDrawingAreaProxy>(*_page->drawingArea()).nextLayerTreeTransactionID();
1233     _unobscuredCenterToRestore = center;
1234     _scaleToRestore = scale;
1235 }
1236
1237 - (PassRefPtr<WebKit::ViewSnapshot>)_takeViewSnapshot
1238 {
1239     float deviceScale = WebCore::screenScaleFactor();
1240     WebCore::FloatSize snapshotSize(self.bounds.size);
1241     snapshotSize.scale(deviceScale, deviceScale);
1242
1243     CATransform3D transform = CATransform3DMakeScale(deviceScale, deviceScale, 1);
1244
1245 #if USE(IOSURFACE)
1246     WebCore::IOSurface::Format snapshotFormat = WebKit::bufferFormat(true /* is opaque */);
1247     auto surface = WebCore::IOSurface::create(WebCore::expandedIntSize(snapshotSize), WebCore::ColorSpaceSRGB, snapshotFormat);
1248     CARenderServerRenderLayerWithTransform(MACH_PORT_NULL, self.layer.context.contextId, reinterpret_cast<uint64_t>(self.layer), surface->surface(), 0, 0, &transform);
1249
1250     WebCore::IOSurface::Format compressedFormat = WebCore::IOSurface::Format::YUV422;
1251     if (WebCore::IOSurface::allowConversionFromFormatToFormat(snapshotFormat, compressedFormat)) {
1252         RefPtr<WebKit::ViewSnapshot> viewSnapshot = WebKit::ViewSnapshot::create(nullptr);
1253         WebCore::IOSurface::convertToFormat(WTFMove(surface), WebCore::IOSurface::Format::YUV422, [viewSnapshot](std::unique_ptr<WebCore::IOSurface> convertedSurface) {
1254             viewSnapshot->setSurface(WTFMove(convertedSurface));
1255         });
1256
1257         return viewSnapshot;
1258     }
1259
1260     return WebKit::ViewSnapshot::create(WTFMove(surface));
1261 #else
1262     uint32_t slotID = [WebKit::ViewSnapshotStore::snapshottingContext() createImageSlot:snapshotSize hasAlpha:YES];
1263
1264     if (!slotID)
1265         return nullptr;
1266
1267     CARenderServerCaptureLayerWithTransform(MACH_PORT_NULL, self.layer.context.contextId, (uint64_t)self.layer, slotID, 0, 0, &transform);
1268     WebCore::IntSize imageSize = WebCore::expandedIntSize(WebCore::FloatSize(snapshotSize));
1269     return WebKit::ViewSnapshot::create(slotID, imageSize, imageSize.width() * imageSize.height() * 4);
1270 #endif
1271 }
1272
1273 - (void)_zoomToPoint:(WebCore::FloatPoint)point atScale:(double)scale animated:(BOOL)animated
1274 {
1275     CFTimeInterval duration = 0;
1276     CGFloat zoomScale = contentZoomScale(self);
1277
1278     if (animated) {
1279         const double maximumZoomDuration = 0.4;
1280         const double minimumZoomDuration = 0.1;
1281         const double zoomDurationFactor = 0.3;
1282
1283         duration = std::min(fabs(log(zoomScale) - log(scale)) * zoomDurationFactor + minimumZoomDuration, maximumZoomDuration);
1284     }
1285
1286     if (scale != zoomScale)
1287         _page->willStartUserTriggeredZooming();
1288
1289     [_scrollView _zoomToCenter:point scale:scale duration:duration];
1290 }
1291
1292 - (void)_zoomToRect:(WebCore::FloatRect)targetRect atScale:(double)scale origin:(WebCore::FloatPoint)origin animated:(BOOL)animated
1293 {
1294     // FIXME: Some of this could be shared with _scrollToRect.
1295     const double visibleRectScaleChange = contentZoomScale(self) / scale;
1296     const WebCore::FloatRect visibleRect([self convertRect:self.bounds toView:self._currentContentView]);
1297     const WebCore::FloatRect unobscuredRect([self _contentRectForUserInteraction]);
1298
1299     const WebCore::FloatSize topLeftObscuredInsetAfterZoom((unobscuredRect.minXMinYCorner() - visibleRect.minXMinYCorner()) * visibleRectScaleChange);
1300     const WebCore::FloatSize bottomRightObscuredInsetAfterZoom((visibleRect.maxXMaxYCorner() - unobscuredRect.maxXMaxYCorner()) * visibleRectScaleChange);
1301
1302     const WebCore::FloatSize unobscuredRectSizeAfterZoom(unobscuredRect.size() * visibleRectScaleChange);
1303
1304     // Center to the target rect.
1305     WebCore::FloatPoint unobscuredRectLocationAfterZoom = targetRect.location() - (unobscuredRectSizeAfterZoom - targetRect.size()) * 0.5;
1306
1307     // Center to the tap point instead in case the target rect won't fit in a direction.
1308     if (targetRect.width() > unobscuredRectSizeAfterZoom.width())
1309         unobscuredRectLocationAfterZoom.setX(origin.x() - unobscuredRectSizeAfterZoom.width() / 2);
1310     if (targetRect.height() > unobscuredRectSizeAfterZoom.height())
1311         unobscuredRectLocationAfterZoom.setY(origin.y() - unobscuredRectSizeAfterZoom.height() / 2);
1312
1313     // We have computed where we want the unobscured rect to be. Now adjust for the obscuring insets.
1314     WebCore::FloatRect visibleRectAfterZoom(unobscuredRectLocationAfterZoom, unobscuredRectSizeAfterZoom);
1315     visibleRectAfterZoom.move(-topLeftObscuredInsetAfterZoom);
1316     visibleRectAfterZoom.expand(topLeftObscuredInsetAfterZoom + bottomRightObscuredInsetAfterZoom);
1317
1318     [self _zoomToPoint:visibleRectAfterZoom.center() atScale:scale animated:animated];
1319 }
1320
1321 static WebCore::FloatPoint constrainContentOffset(WebCore::FloatPoint contentOffset, WebCore::FloatSize contentSize, WebCore::FloatSize unobscuredContentSize)
1322 {
1323     WebCore::FloatSize maximumContentOffset = contentSize - unobscuredContentSize;
1324     return contentOffset.constrainedBetween(WebCore::FloatPoint(), WebCore::FloatPoint(maximumContentOffset));
1325 }
1326
1327 - (void)_scrollToContentOffset:(WebCore::FloatPoint)contentOffsetInPageCoordinates scrollOrigin:(WebCore::IntPoint)scrollOrigin
1328 {
1329     if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing)
1330         return;
1331
1332     WebCore::FloatPoint contentOffsetRespectingOrigin = scrollOrigin + toFloatSize(contentOffsetInPageCoordinates);
1333
1334     WebCore::FloatPoint scaledOffset = contentOffsetRespectingOrigin;
1335     CGFloat zoomScale = contentZoomScale(self);
1336     scaledOffset.scale(zoomScale, zoomScale);
1337
1338     CGPoint contentOffsetInScrollViewCoordinates = [self _adjustedContentOffset:scaledOffset];
1339     contentOffsetInScrollViewCoordinates = contentOffsetBoundedInValidRange(_scrollView.get(), contentOffsetInScrollViewCoordinates);
1340
1341     [_scrollView _stopScrollingAndZoomingAnimations];
1342
1343     if (!CGPointEqualToPoint(contentOffsetInScrollViewCoordinates, [_scrollView contentOffset]))
1344         [_scrollView setContentOffset:contentOffsetInScrollViewCoordinates];
1345     else {
1346         // If we haven't changed anything, there would not be any VisibleContentRect update sent to the content.
1347         // The WebProcess would keep the invalid contentOffset as its scroll position.
1348         // To synchronize the WebProcess with what is on screen, we send the VisibleContentRect again.
1349         _page->resendLastVisibleContentRects();
1350     }
1351 }
1352
1353 - (BOOL)_scrollToRect:(WebCore::FloatRect)targetRect origin:(WebCore::FloatPoint)origin minimumScrollDistance:(float)minimumScrollDistance
1354 {
1355     WebCore::FloatRect unobscuredContentRect([self _contentRectForUserInteraction]);
1356     WebCore::FloatPoint unobscuredContentOffset = unobscuredContentRect.location();
1357     WebCore::FloatSize contentSize([self._currentContentView bounds].size);
1358
1359     // Center the target rect in the scroll view.
1360     // If the target doesn't fit in the scroll view, center on the gesture location instead.
1361     WebCore::FloatPoint newUnobscuredContentOffset;
1362     if (targetRect.width() <= unobscuredContentRect.width())
1363         newUnobscuredContentOffset.setX(targetRect.x() - (unobscuredContentRect.width() - targetRect.width()) / 2);
1364     else
1365         newUnobscuredContentOffset.setX(origin.x() - unobscuredContentRect.width() / 2);
1366     if (targetRect.height() <= unobscuredContentRect.height())
1367         newUnobscuredContentOffset.setY(targetRect.y() - (unobscuredContentRect.height() - targetRect.height()) / 2);
1368     else
1369         newUnobscuredContentOffset.setY(origin.y() - unobscuredContentRect.height() / 2);
1370     newUnobscuredContentOffset = constrainContentOffset(newUnobscuredContentOffset, contentSize, unobscuredContentRect.size());
1371
1372     if (unobscuredContentOffset == newUnobscuredContentOffset) {
1373         if (targetRect.width() > unobscuredContentRect.width())
1374             newUnobscuredContentOffset.setX(origin.x() - unobscuredContentRect.width() / 2);
1375         if (targetRect.height() > unobscuredContentRect.height())
1376             newUnobscuredContentOffset.setY(origin.y() - unobscuredContentRect.height() / 2);
1377         newUnobscuredContentOffset = constrainContentOffset(newUnobscuredContentOffset, contentSize, unobscuredContentRect.size());
1378     }
1379
1380     WebCore::FloatSize scrollViewOffsetDelta = newUnobscuredContentOffset - unobscuredContentOffset;
1381     scrollViewOffsetDelta.scale(contentZoomScale(self));
1382
1383     float scrollDistance = scrollViewOffsetDelta.diagonalLength();
1384     if (scrollDistance < minimumScrollDistance)
1385         return false;
1386
1387     [_contentView willStartZoomOrScroll];
1388
1389     [_scrollView setContentOffset:([_scrollView contentOffset] + scrollViewOffsetDelta) animated:YES];
1390     return true;
1391 }
1392
1393 - (void)_scrollByContentOffset:(WebCore::FloatPoint)contentOffsetDelta
1394 {
1395     WebCore::FloatPoint scaledOffsetDelta = contentOffsetDelta;
1396     CGFloat zoomScale = contentZoomScale(self);
1397     scaledOffsetDelta.scale(zoomScale, zoomScale);
1398
1399     CGPoint currentOffset = [_scrollView _isAnimatingScroll] ? [_scrollView _animatedTargetOffset] : [_scrollView contentOffset];
1400     CGPoint boundedOffset = contentOffsetBoundedInValidRange(_scrollView.get(), currentOffset + scaledOffsetDelta);
1401
1402     if (CGPointEqualToPoint(boundedOffset, currentOffset))
1403         return;
1404     [_contentView willStartZoomOrScroll];
1405     [_scrollView setContentOffset:boundedOffset animated:YES];
1406 }
1407
1408 - (void)_zoomOutWithOrigin:(WebCore::FloatPoint)origin animated:(BOOL)animated
1409 {
1410     [self _zoomToPoint:origin atScale:[_scrollView minimumZoomScale] animated:animated];
1411 }
1412
1413 - (void)_zoomToInitialScaleWithOrigin:(WebCore::FloatPoint)origin animated:(BOOL)animated
1414 {
1415     ASSERT(_initialScaleFactor > 0);
1416     [self _zoomToPoint:origin atScale:_initialScaleFactor animated:animated];
1417 }
1418
1419 // focusedElementRect and selectionRect are both in document coordinates.
1420 - (void)_zoomToFocusRect:(WebCore::FloatRect)focusedElementRectInDocumentCoordinates selectionRect:(WebCore::FloatRect)selectionRectInDocumentCoordinates fontSize:(float)fontSize minimumScale:(double)minimumScale maximumScale:(double)maximumScale allowScaling:(BOOL)allowScaling forceScroll:(BOOL)forceScroll
1421 {
1422     const double WKWebViewStandardFontSize = 16;
1423     const double kMinimumHeightToShowContentAboveKeyboard = 106;
1424     const CFTimeInterval UIWebFormAnimationDuration = 0.25;
1425     const double CaretOffsetFromWindowEdge = 20;
1426
1427     // Zoom around the element's bounding frame. We use a "standard" size to determine the proper frame.
1428     double scale = allowScaling ? std::min(std::max(WKWebViewStandardFontSize / fontSize, minimumScale), maximumScale) : contentZoomScale(self);
1429     CGFloat documentWidth = [_contentView bounds].size.width;
1430     scale = CGRound(documentWidth * scale) / documentWidth;
1431
1432     UIWindow *window = [_scrollView window];
1433
1434     WebCore::FloatRect focusedElementRectInNewScale = focusedElementRectInDocumentCoordinates;
1435     focusedElementRectInNewScale.scale(scale);
1436     focusedElementRectInNewScale.moveBy([_contentView frame].origin);
1437
1438     // Find the portion of the view that is visible on the screen.
1439     UIViewController *topViewController = [[[_scrollView _viewControllerForAncestor] _rootAncestorViewController] _viewControllerForSupportedInterfaceOrientations];
1440     UIView *fullScreenView = topViewController.view;
1441     if (!fullScreenView)
1442         fullScreenView = window;
1443
1444     CGRect unobscuredScrollViewRectInWebViewCoordinates = UIEdgeInsetsInsetRect([self bounds], _obscuredInsets);
1445     CGRect visibleScrollViewBoundsInWebViewCoordinates = CGRectIntersection(unobscuredScrollViewRectInWebViewCoordinates, [fullScreenView convertRect:[fullScreenView bounds] toView:self]);
1446     CGRect formAssistantFrameInWebViewCoordinates = [window convertRect:_inputViewBounds toView:self];
1447     CGRect intersectionBetweenScrollViewAndFormAssistant = CGRectIntersection(visibleScrollViewBoundsInWebViewCoordinates, formAssistantFrameInWebViewCoordinates);
1448     CGSize visibleSize = visibleScrollViewBoundsInWebViewCoordinates.size;
1449
1450     CGFloat visibleOffsetFromTop = 0;
1451     if (!CGRectIsEmpty(intersectionBetweenScrollViewAndFormAssistant)) {
1452         CGFloat heightVisibleAboveFormAssistant = CGRectGetMinY(intersectionBetweenScrollViewAndFormAssistant) - CGRectGetMinY(visibleScrollViewBoundsInWebViewCoordinates);
1453         CGFloat heightVisibleBelowFormAssistant = CGRectGetMaxY(visibleScrollViewBoundsInWebViewCoordinates) - CGRectGetMaxY(intersectionBetweenScrollViewAndFormAssistant);
1454
1455         if (heightVisibleAboveFormAssistant >= kMinimumHeightToShowContentAboveKeyboard || heightVisibleBelowFormAssistant < heightVisibleAboveFormAssistant)
1456             visibleSize.height = heightVisibleAboveFormAssistant;
1457         else {
1458             visibleSize.height = heightVisibleBelowFormAssistant;
1459             visibleOffsetFromTop = CGRectGetMaxY(intersectionBetweenScrollViewAndFormAssistant) - CGRectGetMinY(visibleScrollViewBoundsInWebViewCoordinates);
1460         }
1461     }
1462
1463     BOOL selectionRectIsNotNull = !selectionRectInDocumentCoordinates.isZero();
1464     if (!forceScroll) {
1465         CGRect currentlyVisibleRegionInWebViewCoordinates;
1466         currentlyVisibleRegionInWebViewCoordinates.origin = unobscuredScrollViewRectInWebViewCoordinates.origin;
1467         currentlyVisibleRegionInWebViewCoordinates.origin.y += visibleOffsetFromTop;
1468         currentlyVisibleRegionInWebViewCoordinates.size = visibleSize;
1469
1470         // Don't bother scrolling if the entire node is already visible, whether or not we got a selectionRect.
1471         if (CGRectContainsRect(currentlyVisibleRegionInWebViewCoordinates, [self convertRect:focusedElementRectInDocumentCoordinates fromView:_contentView.get()]))
1472             return;
1473
1474         // Don't bother scrolling if we have a valid selectionRect and it is already visible.
1475         if (selectionRectIsNotNull && CGRectContainsRect(currentlyVisibleRegionInWebViewCoordinates, [self convertRect:selectionRectInDocumentCoordinates fromView:_contentView.get()]))
1476             return;
1477     }
1478
1479     // We want to zoom to the left/top corner of the DOM node, with as much spacing on all sides as we
1480     // can get based on the visible area after zooming (workingFrame).  The spacing in either dimension is half the
1481     // difference between the size of the DOM node and the size of the visible frame.
1482     CGFloat horizontalSpaceInWebViewCoordinates = std::max((visibleSize.width - focusedElementRectInNewScale.width()) / 2.0, 0.0);
1483     CGFloat verticalSpaceInWebViewCoordinates = std::max((visibleSize.height - focusedElementRectInNewScale.height()) / 2.0, 0.0);
1484
1485     CGPoint topLeft;
1486     topLeft.x = focusedElementRectInNewScale.x() - horizontalSpaceInWebViewCoordinates;
1487     topLeft.y = focusedElementRectInNewScale.y() - verticalSpaceInWebViewCoordinates - visibleOffsetFromTop;
1488
1489     CGFloat minimumAllowableHorizontalOffsetInWebViewCoordinates = -INFINITY;
1490     CGFloat minimumAllowableVerticalOffsetInWebViewCoordinates = -INFINITY;
1491     if (selectionRectIsNotNull) {
1492         WebCore::FloatRect selectionRectInNewScale = selectionRectInDocumentCoordinates;
1493         selectionRectInNewScale.scale(scale);
1494         selectionRectInNewScale.moveBy([_contentView frame].origin);
1495         minimumAllowableHorizontalOffsetInWebViewCoordinates = CGRectGetMaxX(selectionRectInNewScale) + CaretOffsetFromWindowEdge - visibleSize.width;
1496         minimumAllowableVerticalOffsetInWebViewCoordinates = CGRectGetMaxY(selectionRectInNewScale) + CaretOffsetFromWindowEdge - visibleSize.height - visibleOffsetFromTop;
1497     }
1498
1499     WebCore::FloatRect documentBoundsInNewScale = [_contentView bounds];
1500     documentBoundsInNewScale.scale(scale);
1501     documentBoundsInNewScale.moveBy([_contentView frame].origin);
1502
1503     // Constrain the left edge in document coordinates so that:
1504     //  - it isn't so small that the scrollVisibleRect isn't visible on the screen
1505     //  - it isn't so great that the document's right edge is less than the right edge of the screen
1506     if (selectionRectIsNotNull && topLeft.x < minimumAllowableHorizontalOffsetInWebViewCoordinates)
1507         topLeft.x = minimumAllowableHorizontalOffsetInWebViewCoordinates;
1508     else {
1509         CGFloat maximumAllowableHorizontalOffset = CGRectGetMaxX(documentBoundsInNewScale) - visibleSize.width;
1510         if (topLeft.x > maximumAllowableHorizontalOffset)
1511             topLeft.x = maximumAllowableHorizontalOffset;
1512     }
1513
1514     // Constrain the top edge in document coordinates so that:
1515     //  - it isn't so small that the scrollVisibleRect isn't visible on the screen
1516     //  - it isn't so great that the document's bottom edge is higher than the top of the form assistant
1517     if (selectionRectIsNotNull && topLeft.y < minimumAllowableVerticalOffsetInWebViewCoordinates)
1518         topLeft.y = minimumAllowableVerticalOffsetInWebViewCoordinates;
1519     else {
1520         CGFloat maximumAllowableVerticalOffset = CGRectGetMaxY(documentBoundsInNewScale) - visibleSize.height;
1521         if (topLeft.y > maximumAllowableVerticalOffset)
1522             topLeft.y = maximumAllowableVerticalOffset;
1523     }
1524
1525     WebCore::FloatPoint newCenter = CGPointMake(topLeft.x + unobscuredScrollViewRectInWebViewCoordinates.size.width / 2.0, topLeft.y + unobscuredScrollViewRectInWebViewCoordinates.size.height / 2.0);
1526
1527     if (scale != contentZoomScale(self))
1528         _page->willStartUserTriggeredZooming();
1529
1530     // The newCenter has been computed in the new scale, but _zoomToCenter expected the center to be in the original scale.
1531     newCenter.scale(1 / scale, 1 / scale);
1532     [_scrollView _zoomToCenter:newCenter
1533                         scale:scale
1534                      duration:UIWebFormAnimationDuration
1535                         force:YES];
1536 }
1537
1538 - (CGFloat)_targetContentZoomScaleForRect:(const WebCore::FloatRect&)targetRect currentScale:(double)currentScale fitEntireRect:(BOOL)fitEntireRect minimumScale:(double)minimumScale maximumScale:(double)maximumScale
1539 {
1540     WebCore::FloatSize unobscuredContentSize([self _contentRectForUserInteraction].size);
1541     double horizontalScale = unobscuredContentSize.width() * currentScale / targetRect.width();
1542     double verticalScale = unobscuredContentSize.height() * currentScale / targetRect.height();
1543
1544     horizontalScale = std::min(std::max(horizontalScale, minimumScale), maximumScale);
1545     verticalScale = std::min(std::max(verticalScale, minimumScale), maximumScale);
1546
1547     return fitEntireRect ? std::min(horizontalScale, verticalScale) : horizontalScale;
1548 }
1549
1550 - (BOOL)_zoomToRect:(WebCore::FloatRect)targetRect withOrigin:(WebCore::FloatPoint)origin fitEntireRect:(BOOL)fitEntireRect minimumScale:(double)minimumScale maximumScale:(double)maximumScale minimumScrollDistance:(float)minimumScrollDistance
1551 {
1552     const float maximumScaleFactorDeltaForPanScroll = 0.02;
1553
1554     double currentScale = contentZoomScale(self);
1555     double targetScale = [self _targetContentZoomScaleForRect:targetRect currentScale:currentScale fitEntireRect:fitEntireRect minimumScale:minimumScale maximumScale:maximumScale];
1556
1557     if (fabs(targetScale - currentScale) < maximumScaleFactorDeltaForPanScroll) {
1558         if ([self _scrollToRect:targetRect origin:origin minimumScrollDistance:minimumScrollDistance])
1559             return true;
1560     } else if (targetScale != currentScale) {
1561         [self _zoomToRect:targetRect atScale:targetScale origin:origin animated:YES];
1562         return true;
1563     }
1564     
1565     return false;
1566 }
1567
1568 - (void)didMoveToWindow
1569 {
1570     _page->viewStateDidChange(WebCore::ViewState::AllFlags);
1571 }
1572
1573 - (void)setOpaque:(BOOL)opaque
1574 {
1575     BOOL oldOpaque = self.opaque;
1576
1577     [super setOpaque:opaque];
1578     [_contentView setOpaque:opaque];
1579
1580     if (oldOpaque == opaque)
1581         return;
1582
1583     _page->setDrawsBackground(opaque);
1584     [self _updateScrollViewBackground];
1585 }
1586
1587 - (void)setBackgroundColor:(UIColor *)backgroundColor
1588 {
1589     [super setBackgroundColor:backgroundColor];
1590     [_contentView setBackgroundColor:backgroundColor];
1591 }
1592
1593 - (BOOL)_allowsDoubleTapGestures
1594 {
1595     if (_fastClickingIsDisabled)
1596         return YES;
1597
1598     // If the page is not user scalable, we don't allow double tap gestures.
1599     if (![_scrollView isZoomEnabled] || [_scrollView minimumZoomScale] >= [_scrollView maximumZoomScale])
1600         return NO;
1601
1602     // If the viewport width was not explicit, we allow double tap gestures.
1603     if (!_viewportMetaTagWidthWasExplicit || _viewportMetaTagCameFromImageDocument)
1604         return YES;
1605
1606     // For scalable viewports, only disable double tap gestures if the viewport width is device width.
1607     if (_viewportMetaTagWidth != WebCore::ViewportArguments::ValueDeviceWidth)
1608         return YES;
1609
1610     return !areEssentiallyEqualAsFloat(contentZoomScale(self), _initialScaleFactor);
1611 }
1612
1613 #pragma mark - UIScrollViewDelegate
1614
1615 - (BOOL)usesStandardContentView
1616 {
1617     return !_customContentView;
1618 }
1619
1620 - (CGSize)scrollView:(UIScrollView*)scrollView contentSizeForZoomScale:(CGFloat)scale withProposedSize:(CGSize)proposedSize
1621 {
1622     return roundScrollViewContentSize(*_page, proposedSize);
1623 }
1624
1625 - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
1626 {
1627     ASSERT(_scrollView == scrollView);
1628
1629     if (_customContentView)
1630         return _customContentView.get();
1631
1632     return _contentView.get();
1633 }
1634
1635 - (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view
1636 {
1637     if (![self usesStandardContentView])
1638         return;
1639
1640     if (scrollView.pinchGestureRecognizer.state == UIGestureRecognizerStateBegan) {
1641         _page->willStartUserTriggeredZooming();
1642         [_contentView scrollViewWillStartPanOrPinchGesture];
1643     }
1644     [_contentView willStartZoomOrScroll];
1645 }
1646
1647 - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
1648 {
1649     if (![self usesStandardContentView])
1650         return;
1651
1652     if (scrollView.panGestureRecognizer.state == UIGestureRecognizerStateBegan)
1653         [_contentView scrollViewWillStartPanOrPinchGesture];
1654
1655     [_contentView willStartZoomOrScroll];
1656 #if ENABLE(CSS_SCROLL_SNAP) && ENABLE(ASYNC_SCROLLING)
1657     // FIXME: We will want to detect whether snapping will occur before beginning to drag. See WebPageProxy::didCommitLayerTree.
1658     WebKit::RemoteScrollingCoordinatorProxy* coordinator = _page->scrollingCoordinatorProxy();
1659     ASSERT(scrollView == _scrollView.get());
1660     CGFloat scrollDecelerationFactor = (coordinator && coordinator->shouldSetScrollViewDecelerationRateFast()) ? UIScrollViewDecelerationRateFast : [_scrollView preferredScrollDecelerationFactor];
1661     scrollView.horizontalScrollDecelerationFactor = scrollDecelerationFactor;
1662     scrollView.verticalScrollDecelerationFactor = scrollDecelerationFactor;
1663 #endif
1664 }
1665
1666 - (void)_didFinishScrolling
1667 {
1668     if (![self usesStandardContentView])
1669         return;
1670
1671     [self _updateVisibleContentRects];
1672     [_contentView didFinishScrolling];
1673 }
1674
1675 - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
1676 {
1677     // Work around <rdar://problem/16374753> by avoiding deceleration while
1678     // zooming. We'll animate to the right place once the zoom finishes.
1679     if ([scrollView isZooming])
1680         *targetContentOffset = [scrollView contentOffset];
1681 #if ENABLE(CSS_SCROLL_SNAP) && ENABLE(ASYNC_SCROLLING)
1682     if (WebKit::RemoteScrollingCoordinatorProxy* coordinator = _page->scrollingCoordinatorProxy()) {
1683         // FIXME: Here, I'm finding the maximum horizontal/vertical scroll offsets. There's probably a better way to do this.
1684         CGSize maxScrollOffsets = CGSizeMake(scrollView.contentSize.width - scrollView.bounds.size.width, scrollView.contentSize.height - scrollView.bounds.size.height);
1685         
1686         CGRect fullViewRect = self.bounds;
1687
1688         UIEdgeInsets contentInset;
1689
1690         id<WKUIDelegatePrivate> uiDelegatePrivate = static_cast<id <WKUIDelegatePrivate>>([self UIDelegate]);
1691         if ([uiDelegatePrivate respondsToSelector:@selector(_webView:finalObscuredInsetsForScrollView:withVelocity:targetContentOffset:)])
1692             contentInset = [uiDelegatePrivate _webView:self finalObscuredInsetsForScrollView:scrollView withVelocity:velocity targetContentOffset:targetContentOffset];
1693         else
1694             contentInset = [self _computedContentInset];
1695
1696         CGRect unobscuredRect = UIEdgeInsetsInsetRect(fullViewRect, contentInset);
1697         
1698         coordinator->adjustTargetContentOffsetForSnapping(maxScrollOffsets, velocity, unobscuredRect.origin.y, targetContentOffset);
1699     }
1700 #endif
1701 }
1702
1703 - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
1704 {
1705     // If we're decelerating, scroll offset will be updated when scrollViewDidFinishDecelerating: is called.
1706     if (!decelerate)
1707         [self _didFinishScrolling];
1708 }
1709
1710 - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
1711 {
1712     [self _didFinishScrolling];
1713 }
1714
1715 - (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView
1716 {
1717     [self _didFinishScrolling];
1718 }
1719
1720 - (void)scrollViewDidScroll:(UIScrollView *)scrollView
1721 {
1722     if (![self usesStandardContentView])
1723         [_customContentView scrollViewDidScroll:(UIScrollView *)scrollView];
1724
1725     [self _updateVisibleContentRects];
1726     
1727     if (WebKit::RemoteLayerTreeScrollingPerformanceData* scrollPerfData = _page->scrollingPerformanceData())
1728         scrollPerfData->didScroll([self visibleRectInViewCoordinates]);
1729 }
1730
1731 - (void)scrollViewDidZoom:(UIScrollView *)scrollView
1732 {
1733     [self _updateScrollViewBackground];
1734     [self _updateVisibleContentRects];
1735 }
1736
1737 - (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale
1738 {
1739     ASSERT(scrollView == _scrollView);
1740     [self _updateVisibleContentRects];
1741     [_contentView didZoomToScale:scale];
1742 }
1743
1744 - (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
1745 {
1746     [self _didFinishScrolling];
1747 }
1748
1749 - (void)_scrollViewDidInterruptDecelerating:(UIScrollView *)scrollView
1750 {
1751     if (![self usesStandardContentView])
1752         return;
1753
1754     [_contentView didInterruptScrolling];
1755     [self _updateVisibleContentRects];
1756 }
1757
1758 - (void)_frameOrBoundsChanged
1759 {
1760     CGRect bounds = self.bounds;
1761     [_scrollView setFrame:bounds];
1762
1763     if (_dynamicViewportUpdateMode == DynamicViewportUpdateMode::NotResizing) {
1764         if (!_overridesMinimumLayoutSize)
1765             _page->setViewportConfigurationMinimumLayoutSize(WebCore::FloatSize(bounds.size));
1766         if (!_overridesMaximumUnobscuredSize)
1767             _page->setMaximumUnobscuredSize(WebCore::FloatSize(bounds.size));
1768         if (WebKit::DrawingAreaProxy* drawingArea = _page->drawingArea())
1769             drawingArea->setSize(WebCore::IntSize(bounds.size), WebCore::IntSize(), WebCore::IntSize());
1770     }
1771
1772     [_customContentView web_setMinimumSize:bounds.size];
1773     [self _updateVisibleContentRects];
1774 }
1775
1776 // Unobscured content rect where the user can interact. When the keyboard is up, this should be the area above or below the keyboard, wherever there is enough space.
1777 - (CGRect)_contentRectForUserInteraction
1778 {
1779     // FIXME: handle split keyboard.
1780     UIEdgeInsets obscuredInsets = _obscuredInsets;
1781     obscuredInsets.bottom = std::max(_obscuredInsets.bottom, _inputViewBounds.size.height);
1782     CGRect unobscuredRect = UIEdgeInsetsInsetRect(self.bounds, obscuredInsets);
1783     return [self convertRect:unobscuredRect toView:self._currentContentView];
1784 }
1785
1786 // Ideally UIScrollView would expose this for us: <rdar://problem/21394567>.
1787 - (BOOL)_scrollViewIsRubberBanding
1788 {
1789     float deviceScaleFactor = _page->deviceScaleFactor();
1790
1791     CGPoint contentOffset = [_scrollView contentOffset];
1792     CGPoint boundedOffset = contentOffsetBoundedInValidRange(_scrollView.get(), contentOffset);
1793     return !pointsEqualInDevicePixels(contentOffset, boundedOffset, deviceScaleFactor);
1794 }
1795
1796 - (void)_updateVisibleContentRects
1797 {
1798     if (![self usesStandardContentView]) {
1799         [_customContentView web_computedContentInsetDidChange];
1800         return;
1801     }
1802
1803     if (_delayUpdateVisibleContentRects) {
1804         _hadDelayedUpdateVisibleContentRects = YES;
1805         return;
1806     }
1807
1808     if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing
1809         || _needsResetViewStateAfterCommitLoadForMainFrame
1810         || [_scrollView isZoomBouncing]
1811         || _currentlyAdjustingScrollViewInsetsForKeyboard)
1812         return;
1813
1814     CGRect fullViewRect = self.bounds;
1815     CGRect visibleRectInContentCoordinates = _frozenVisibleContentRect ? _frozenVisibleContentRect.value() : [self convertRect:fullViewRect toView:_contentView.get()];
1816
1817     UIEdgeInsets computedContentInsetUnadjustedForKeyboard = [self _computedContentInset];
1818     if (!_haveSetObscuredInsets)
1819         computedContentInsetUnadjustedForKeyboard.bottom -= _totalScrollViewBottomInsetAdjustmentForKeyboard;
1820
1821     CGRect unobscuredRect = UIEdgeInsetsInsetRect(fullViewRect, computedContentInsetUnadjustedForKeyboard);
1822     CGRect unobscuredRectInContentCoordinates = _frozenUnobscuredContentRect ? _frozenUnobscuredContentRect.value() : [self convertRect:unobscuredRect toView:_contentView.get()];
1823
1824     CGFloat scaleFactor = contentZoomScale(self);
1825
1826     BOOL isStableState = !(_isChangingObscuredInsetsInteractively || [_scrollView isDragging] || [_scrollView isDecelerating] || [_scrollView isZooming] || [_scrollView _isAnimatingZoom] || [_scrollView _isScrollingToTop] || [self _scrollViewIsRubberBanding]);
1827
1828     // FIXME: this can be made static after we stop supporting iOS 8.x.
1829     if (isStableState && [_scrollView respondsToSelector:@selector(_isInterruptingDeceleration)])
1830         isStableState = ![_scrollView performSelector:@selector(_isInterruptingDeceleration)];
1831
1832 #if ENABLE(CSS_SCROLL_SNAP) && ENABLE(ASYNC_SCROLLING)
1833     if (isStableState) {
1834         WebKit::RemoteScrollingCoordinatorProxy* coordinator = _page->scrollingCoordinatorProxy();
1835         if (coordinator && coordinator->hasActiveSnapPoint()) {
1836             CGRect fullViewRect = self.bounds;
1837             CGRect unobscuredRect = UIEdgeInsetsInsetRect(fullViewRect, computedContentInsetUnadjustedForKeyboard);
1838             
1839             CGPoint currentPoint = [_scrollView contentOffset];
1840             CGPoint activePoint = coordinator->nearestActiveContentInsetAdjustedSnapPoint(unobscuredRect.origin.y, currentPoint);
1841
1842             if (!CGPointEqualToPoint(activePoint, currentPoint)) {
1843                 RetainPtr<WKScrollView> strongScrollView = _scrollView;
1844                 dispatch_async(dispatch_get_main_queue(), [strongScrollView, activePoint] {
1845                     [strongScrollView setContentOffset:activePoint animated:NO];
1846                 });
1847             }
1848         }
1849     }
1850 #endif
1851     
1852     [_contentView didUpdateVisibleRect:visibleRectInContentCoordinates
1853         unobscuredRect:unobscuredRectInContentCoordinates
1854         unobscuredRectInScrollViewCoordinates:unobscuredRect
1855         scale:scaleFactor minimumScale:[_scrollView minimumZoomScale]
1856         inStableState:isStableState
1857         isChangingObscuredInsetsInteractively:_isChangingObscuredInsetsInteractively];
1858 }
1859
1860 - (void)_didFinishLoadForMainFrame
1861 {
1862     if (_gestureController)
1863         _gestureController->didFinishLoadForMainFrame();
1864 }
1865
1866 - (void)_didFailLoadForMainFrame
1867 {
1868     if (_gestureController)
1869         _gestureController->didFailLoadForMainFrame();
1870 }
1871
1872 - (void)_didSameDocumentNavigationForMainFrame:(WebKit::SameDocumentNavigationType)navigationType
1873 {
1874     [_customContentView web_didSameDocumentNavigation:toAPI(navigationType)];
1875
1876     if (_gestureController)
1877         _gestureController->didSameDocumentNavigationForMainFrame(navigationType);
1878 }
1879
1880 - (void)_keyboardChangedWithInfo:(NSDictionary *)keyboardInfo adjustScrollView:(BOOL)adjustScrollView
1881 {
1882     NSValue *endFrameValue = [keyboardInfo objectForKey:UIKeyboardFrameEndUserInfoKey];
1883     if (!endFrameValue)
1884         return;
1885
1886     // The keyboard rect is always in screen coordinates. In the view services case the window does not
1887     // have the interface orientation rotation transformation; its host does. So, it makes no sense to
1888     // clip the keyboard rect against its screen.
1889     if ([[self window] _isHostedInAnotherProcess])
1890         _inputViewBounds = [self.window convertRect:[endFrameValue CGRectValue] fromWindow:nil];
1891     else
1892         _inputViewBounds = [self.window convertRect:CGRectIntersection([endFrameValue CGRectValue], self.window.screen.bounds) fromWindow:nil];
1893
1894     if (adjustScrollView) {
1895         CGFloat bottomInsetBeforeAdjustment = [_scrollView contentInset].bottom;
1896         TemporaryChange<BOOL> insetAdjustmentGuard(_currentlyAdjustingScrollViewInsetsForKeyboard, YES);
1897         [_scrollView _adjustForAutomaticKeyboardInfo:keyboardInfo animated:YES lastAdjustment:&_lastAdjustmentForScroller];
1898         CGFloat bottomInsetAfterAdjustment = [_scrollView contentInset].bottom;
1899         if (bottomInsetBeforeAdjustment != bottomInsetAfterAdjustment)
1900             _totalScrollViewBottomInsetAdjustmentForKeyboard += bottomInsetAfterAdjustment - bottomInsetBeforeAdjustment;
1901     }
1902
1903     [self _updateVisibleContentRects];
1904 }
1905
1906 - (BOOL)_shouldUpdateKeyboardWithInfo:(NSDictionary *)keyboardInfo
1907 {
1908     if ([_contentView isAssistingNode])
1909         return YES;
1910
1911     NSNumber *isLocalKeyboard = [keyboardInfo valueForKey:UIKeyboardIsLocalUserInfoKey];
1912     return isLocalKeyboard && !isLocalKeyboard.boolValue;
1913 }
1914
1915 - (void)_keyboardWillChangeFrame:(NSNotification *)notification
1916 {
1917     if ([self _shouldUpdateKeyboardWithInfo:notification.userInfo])
1918         [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES];
1919 }
1920
1921 - (void)_keyboardDidChangeFrame:(NSNotification *)notification
1922 {
1923     [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:NO];
1924 }
1925
1926 - (void)_keyboardWillShow:(NSNotification *)notification
1927 {
1928     if ([self _shouldUpdateKeyboardWithInfo:notification.userInfo])
1929         [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES];
1930 }
1931
1932 - (void)_keyboardWillHide:(NSNotification *)notification
1933 {
1934     // Ignore keyboard will hide notifications sent during rotation. They're just there for
1935     // backwards compatibility reasons and processing the will hide notification would
1936     // temporarily screw up the the unobscured view area.
1937     if ([[UIPeripheralHost sharedInstance] rotationState])
1938         return;
1939
1940     [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES];
1941 }
1942
1943 - (void)_windowDidRotate:(NSNotification *)notification
1944 {
1945     if (!_overridesInterfaceOrientation)
1946         _page->setDeviceOrientation(deviceOrientation());
1947 }
1948
1949 - (void)_contentSizeCategoryDidChange:(NSNotification *)notification
1950 {
1951     _page->contentSizeCategoryDidChange([self _contentSizeCategory]);
1952 }
1953
1954 - (NSString *)_contentSizeCategory
1955 {
1956     return [[UIApplication sharedApplication] preferredContentSizeCategory];
1957 }
1958
1959 - (void)setAllowsBackForwardNavigationGestures:(BOOL)allowsBackForwardNavigationGestures
1960 {
1961     if (_allowsBackForwardNavigationGestures == allowsBackForwardNavigationGestures)
1962         return;
1963
1964     _allowsBackForwardNavigationGestures = allowsBackForwardNavigationGestures;
1965
1966     if (allowsBackForwardNavigationGestures) {
1967         if (!_gestureController) {
1968             _gestureController = std::make_unique<WebKit::ViewGestureController>(*_page);
1969             _gestureController->installSwipeHandler(self, [self scrollView]);
1970             _gestureController->setAlternateBackForwardListSourceView([_configuration _alternateWebViewForNavigationGestures]);
1971         }
1972     } else
1973         _gestureController = nullptr;
1974
1975     _page->setShouldRecordNavigationSnapshots(allowsBackForwardNavigationGestures);
1976 }
1977
1978 - (BOOL)allowsBackForwardNavigationGestures
1979 {
1980     return _allowsBackForwardNavigationGestures;
1981 }
1982
1983 - (void)_navigationGestureDidBegin
1984 {
1985     // During a back/forward swipe, there's a view interposed between this view and the content view that has
1986     // an offset and animation on it, which results in computing incorrect rectangles. Work around by using
1987     // frozen rects during swipes.
1988     CGRect fullViewRect = self.bounds;
1989     CGRect unobscuredRect = UIEdgeInsetsInsetRect(fullViewRect, [self _computedContentInset]);
1990
1991     _frozenVisibleContentRect = [self convertRect:fullViewRect toView:_contentView.get()];
1992     _frozenUnobscuredContentRect = [self convertRect:unobscuredRect toView:_contentView.get()];
1993 }
1994
1995 - (void)_navigationGestureDidEnd
1996 {
1997     _frozenVisibleContentRect = Nullopt;
1998     _frozenUnobscuredContentRect = Nullopt;
1999 }
2000
2001 #endif // PLATFORM(IOS)
2002
2003 #pragma mark OS X-specific methods
2004
2005 #if PLATFORM(MAC)
2006
2007 - (BOOL)acceptsFirstResponder
2008 {
2009     return _impl->acceptsFirstResponder();
2010 }
2011
2012 - (BOOL)becomeFirstResponder
2013 {
2014     return _impl->becomeFirstResponder();
2015 }
2016
2017 - (BOOL)resignFirstResponder
2018 {
2019     return _impl->resignFirstResponder();
2020 }
2021
2022 - (void)viewWillStartLiveResize
2023 {
2024     _impl->viewWillStartLiveResize();
2025 }
2026
2027 - (void)viewDidEndLiveResize
2028 {
2029     _impl->viewDidEndLiveResize();
2030 }
2031
2032 - (BOOL)isFlipped
2033 {
2034     return YES;
2035 }
2036
2037 - (NSSize)intrinsicContentSize
2038 {
2039     return NSSizeFromCGSize(_impl->intrinsicContentSize());
2040 }
2041
2042 - (void)prepareContentInRect:(NSRect)rect
2043 {
2044     _impl->prepareContentInRect(NSRectToCGRect(rect));
2045 }
2046
2047 - (void)setFrameSize:(NSSize)size
2048 {
2049     [super setFrameSize:size];
2050     _impl->setFrameSize(NSSizeToCGSize(size));
2051 }
2052
2053 - (void)renewGState
2054 {
2055     _impl->renewGState();
2056     [super renewGState];
2057 }
2058
2059 #define WEBCORE_COMMAND(command) - (void)command:(id)sender { _impl->executeEditCommandForSelector(_cmd); }
2060
2061 WEBCORE_COMMAND(alignCenter)
2062 WEBCORE_COMMAND(alignJustified)
2063 WEBCORE_COMMAND(alignLeft)
2064 WEBCORE_COMMAND(alignRight)
2065 WEBCORE_COMMAND(copy)
2066 WEBCORE_COMMAND(cut)
2067 WEBCORE_COMMAND(delete)
2068 WEBCORE_COMMAND(deleteBackward)
2069 WEBCORE_COMMAND(deleteBackwardByDecomposingPreviousCharacter)
2070 WEBCORE_COMMAND(deleteForward)
2071 WEBCORE_COMMAND(deleteToBeginningOfLine)
2072 WEBCORE_COMMAND(deleteToBeginningOfParagraph)
2073 WEBCORE_COMMAND(deleteToEndOfLine)
2074 WEBCORE_COMMAND(deleteToEndOfParagraph)
2075 WEBCORE_COMMAND(deleteToMark)
2076 WEBCORE_COMMAND(deleteWordBackward)
2077 WEBCORE_COMMAND(deleteWordForward)
2078 WEBCORE_COMMAND(ignoreSpelling)
2079 WEBCORE_COMMAND(indent)
2080 WEBCORE_COMMAND(insertBacktab)
2081 WEBCORE_COMMAND(insertLineBreak)
2082 WEBCORE_COMMAND(insertNewline)
2083 WEBCORE_COMMAND(insertNewlineIgnoringFieldEditor)
2084 WEBCORE_COMMAND(insertParagraphSeparator)
2085 WEBCORE_COMMAND(insertTab)
2086 WEBCORE_COMMAND(insertTabIgnoringFieldEditor)
2087 WEBCORE_COMMAND(makeTextWritingDirectionLeftToRight)
2088 WEBCORE_COMMAND(makeTextWritingDirectionNatural)
2089 WEBCORE_COMMAND(makeTextWritingDirectionRightToLeft)
2090 WEBCORE_COMMAND(moveBackward)
2091 WEBCORE_COMMAND(moveBackwardAndModifySelection)
2092 WEBCORE_COMMAND(moveDown)
2093 WEBCORE_COMMAND(moveDownAndModifySelection)
2094 WEBCORE_COMMAND(moveForward)
2095 WEBCORE_COMMAND(moveForwardAndModifySelection)
2096 WEBCORE_COMMAND(moveLeft)
2097 WEBCORE_COMMAND(moveLeftAndModifySelection)
2098 WEBCORE_COMMAND(moveParagraphBackwardAndModifySelection)
2099 WEBCORE_COMMAND(moveParagraphForwardAndModifySelection)
2100 WEBCORE_COMMAND(moveRight)
2101 WEBCORE_COMMAND(moveRightAndModifySelection)
2102 WEBCORE_COMMAND(moveToBeginningOfDocument)
2103 WEBCORE_COMMAND(moveToBeginningOfDocumentAndModifySelection)
2104 WEBCORE_COMMAND(moveToBeginningOfLine)
2105 WEBCORE_COMMAND(moveToBeginningOfLineAndModifySelection)
2106 WEBCORE_COMMAND(moveToBeginningOfParagraph)
2107 WEBCORE_COMMAND(moveToBeginningOfParagraphAndModifySelection)
2108 WEBCORE_COMMAND(moveToBeginningOfSentence)
2109 WEBCORE_COMMAND(moveToBeginningOfSentenceAndModifySelection)
2110 WEBCORE_COMMAND(moveToEndOfDocument)
2111 WEBCORE_COMMAND(moveToEndOfDocumentAndModifySelection)
2112 WEBCORE_COMMAND(moveToEndOfLine)
2113 WEBCORE_COMMAND(moveToEndOfLineAndModifySelection)
2114 WEBCORE_COMMAND(moveToEndOfParagraph)
2115 WEBCORE_COMMAND(moveToEndOfParagraphAndModifySelection)
2116 WEBCORE_COMMAND(moveToEndOfSentence)
2117 WEBCORE_COMMAND(moveToEndOfSentenceAndModifySelection)
2118 WEBCORE_COMMAND(moveToLeftEndOfLine)
2119 WEBCORE_COMMAND(moveToLeftEndOfLineAndModifySelection)
2120 WEBCORE_COMMAND(moveToRightEndOfLine)
2121 WEBCORE_COMMAND(moveToRightEndOfLineAndModifySelection)
2122 WEBCORE_COMMAND(moveUp)
2123 WEBCORE_COMMAND(moveUpAndModifySelection)
2124 WEBCORE_COMMAND(moveWordBackward)
2125 WEBCORE_COMMAND(moveWordBackwardAndModifySelection)
2126 WEBCORE_COMMAND(moveWordForward)
2127 WEBCORE_COMMAND(moveWordForwardAndModifySelection)
2128 WEBCORE_COMMAND(moveWordLeft)
2129 WEBCORE_COMMAND(moveWordLeftAndModifySelection)
2130 WEBCORE_COMMAND(moveWordRight)
2131 WEBCORE_COMMAND(moveWordRightAndModifySelection)
2132 WEBCORE_COMMAND(outdent)
2133 WEBCORE_COMMAND(pageDown)
2134 WEBCORE_COMMAND(pageDownAndModifySelection)
2135 WEBCORE_COMMAND(pageUp)
2136 WEBCORE_COMMAND(pageUpAndModifySelection)
2137 WEBCORE_COMMAND(paste)
2138 WEBCORE_COMMAND(pasteAsPlainText)
2139 WEBCORE_COMMAND(scrollPageDown)
2140 WEBCORE_COMMAND(scrollPageUp)
2141 WEBCORE_COMMAND(scrollLineDown)
2142 WEBCORE_COMMAND(scrollLineUp)
2143 WEBCORE_COMMAND(scrollToBeginningOfDocument)
2144 WEBCORE_COMMAND(scrollToEndOfDocument)
2145 WEBCORE_COMMAND(selectAll)
2146 WEBCORE_COMMAND(selectLine)
2147 WEBCORE_COMMAND(selectParagraph)
2148 WEBCORE_COMMAND(selectSentence)
2149 WEBCORE_COMMAND(selectToMark)
2150 WEBCORE_COMMAND(selectWord)
2151 WEBCORE_COMMAND(setMark)
2152 WEBCORE_COMMAND(subscript)
2153 WEBCORE_COMMAND(superscript)
2154 WEBCORE_COMMAND(swapWithMark)
2155 WEBCORE_COMMAND(takeFindStringFromSelection)
2156 WEBCORE_COMMAND(transpose)
2157 WEBCORE_COMMAND(underline)
2158 WEBCORE_COMMAND(unscript)
2159 WEBCORE_COMMAND(yank)
2160 WEBCORE_COMMAND(yankAndSelect)
2161
2162 #undef WEBCORE_COMMAND
2163
2164 - (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pasteboard types:(NSArray *)types
2165 {
2166     return _impl->writeSelectionToPasteboard(pasteboard, types);
2167 }
2168
2169 - (void)centerSelectionInVisibleArea:(id)sender
2170 {
2171     _impl->centerSelectionInVisibleArea();
2172 }
2173
2174 - (id)validRequestorForSendType:(NSString *)sendType returnType:(NSString *)returnType
2175 {
2176     return _impl->validRequestorForSendAndReturnTypes(sendType, returnType);
2177 }
2178
2179 - (BOOL)readSelectionFromPasteboard:(NSPasteboard *)pasteboard
2180 {
2181     return _impl->readSelectionFromPasteboard(pasteboard);
2182 }
2183
2184 - (void)changeFont:(id)sender
2185 {
2186     _impl->changeFontFromFontPanel();
2187 }
2188
2189 - (IBAction)startSpeaking:(id)sender
2190 {
2191     _impl->startSpeaking();
2192 }
2193
2194 - (IBAction)stopSpeaking:(id)sender
2195 {
2196     _impl->stopSpeaking(sender);
2197 }
2198
2199 - (IBAction)showGuessPanel:(id)sender
2200 {
2201     _impl->showGuessPanel(sender);
2202 }
2203
2204 - (IBAction)checkSpelling:(id)sender
2205 {
2206     _impl->checkSpelling();
2207 }
2208
2209 - (void)changeSpelling:(id)sender
2210 {
2211     _impl->changeSpelling(sender);
2212 }
2213
2214 - (IBAction)toggleContinuousSpellChecking:(id)sender
2215 {
2216     _impl->toggleContinuousSpellChecking();
2217 }
2218
2219 - (BOOL)isGrammarCheckingEnabled
2220 {
2221     return _impl->isGrammarCheckingEnabled();
2222 }
2223
2224 - (void)setGrammarCheckingEnabled:(BOOL)flag
2225 {
2226     _impl->setGrammarCheckingEnabled(flag);
2227 }
2228
2229 - (IBAction)toggleGrammarChecking:(id)sender
2230 {
2231     _impl->toggleGrammarChecking();
2232 }
2233
2234 - (IBAction)toggleAutomaticSpellingCorrection:(id)sender
2235 {
2236     _impl->toggleAutomaticSpellingCorrection();
2237 }
2238
2239 - (void)orderFrontSubstitutionsPanel:(id)sender
2240 {
2241     _impl->orderFrontSubstitutionsPanel(sender);
2242 }
2243
2244 - (IBAction)toggleSmartInsertDelete:(id)sender
2245 {
2246     _impl->toggleSmartInsertDelete();
2247 }
2248
2249 - (BOOL)isAutomaticQuoteSubstitutionEnabled
2250 {
2251     return _impl->isAutomaticQuoteSubstitutionEnabled();
2252 }
2253
2254 - (void)setAutomaticQuoteSubstitutionEnabled:(BOOL)flag
2255 {
2256     _impl->setAutomaticQuoteSubstitutionEnabled(flag);
2257 }
2258
2259 - (void)toggleAutomaticQuoteSubstitution:(id)sender
2260 {
2261     _impl->toggleAutomaticQuoteSubstitution();
2262 }
2263
2264 - (BOOL)isAutomaticDashSubstitutionEnabled
2265 {
2266     return _impl->isAutomaticDashSubstitutionEnabled();
2267 }
2268
2269 - (void)setAutomaticDashSubstitutionEnabled:(BOOL)flag
2270 {
2271     _impl->setAutomaticDashSubstitutionEnabled(flag);
2272 }
2273
2274 - (void)toggleAutomaticDashSubstitution:(id)sender
2275 {
2276     _impl->toggleAutomaticDashSubstitution();
2277 }
2278
2279 - (BOOL)isAutomaticLinkDetectionEnabled
2280 {
2281     return _impl->isAutomaticLinkDetectionEnabled();
2282 }
2283
2284 - (void)setAutomaticLinkDetectionEnabled:(BOOL)flag
2285 {
2286     _impl->setAutomaticLinkDetectionEnabled(flag);
2287 }
2288
2289 - (void)toggleAutomaticLinkDetection:(id)sender
2290 {
2291     _impl->toggleAutomaticLinkDetection();
2292 }
2293
2294 - (BOOL)isAutomaticTextReplacementEnabled
2295 {
2296     return _impl->isAutomaticTextReplacementEnabled();
2297 }
2298
2299 - (void)setAutomaticTextReplacementEnabled:(BOOL)flag
2300 {
2301     _impl->setAutomaticTextReplacementEnabled(flag);
2302 }
2303
2304 - (void)toggleAutomaticTextReplacement:(id)sender
2305 {
2306     _impl->toggleAutomaticTextReplacement();
2307 }
2308
2309 - (void)uppercaseWord:(id)sender
2310 {
2311     _impl->uppercaseWord();
2312 }
2313
2314 - (void)lowercaseWord:(id)sender
2315 {
2316     _impl->lowercaseWord();
2317 }
2318
2319 - (void)capitalizeWord:(id)sender
2320 {
2321     _impl->capitalizeWord();
2322 }
2323
2324 - (BOOL)_wantsKeyDownForEvent:(NSEvent *)event
2325 {
2326     return _impl->wantsKeyDownForEvent(event);
2327 }
2328
2329 - (void)scrollWheel:(NSEvent *)event
2330 {
2331     _impl->scrollWheel(event);
2332 }
2333
2334 - (void)swipeWithEvent:(NSEvent *)event
2335 {
2336     _impl->swipeWithEvent(event);
2337 }
2338
2339 - (void)mouseMoved:(NSEvent *)event
2340 {
2341     _impl->mouseMoved(event);
2342 }
2343
2344 - (void)mouseDown:(NSEvent *)event
2345 {
2346     _impl->mouseDown(event);
2347 }
2348
2349 - (void)mouseUp:(NSEvent *)event
2350 {
2351     _impl->mouseUp(event);
2352 }
2353
2354 - (void)mouseDragged:(NSEvent *)event
2355 {
2356     _impl->mouseDragged(event);
2357 }
2358
2359 - (void)mouseEntered:(NSEvent *)event
2360 {
2361     _impl->mouseEntered(event);
2362 }
2363
2364 - (void)mouseExited:(NSEvent *)event
2365 {
2366     _impl->mouseExited(event);
2367 }
2368
2369 - (void)otherMouseDown:(NSEvent *)event
2370 {
2371     _impl->otherMouseDown(event);
2372 }
2373
2374 - (void)otherMouseDragged:(NSEvent *)event
2375 {
2376     _impl->otherMouseDragged(event);
2377 }
2378
2379 - (void)otherMouseUp:(NSEvent *)event
2380 {
2381     _impl->otherMouseUp(event);
2382 }
2383
2384 - (void)rightMouseDown:(NSEvent *)event
2385 {
2386     _impl->rightMouseDown(event);
2387 }
2388
2389 - (void)rightMouseDragged:(NSEvent *)event
2390 {
2391     _impl->rightMouseDragged(event);
2392 }
2393
2394 - (void)rightMouseUp:(NSEvent *)event
2395 {
2396     _impl->rightMouseUp(event);
2397 }
2398
2399 - (void)pressureChangeWithEvent:(NSEvent *)event
2400 {
2401     _impl->pressureChangeWithEvent(event);
2402 }
2403
2404 - (BOOL)acceptsFirstMouse:(NSEvent *)event
2405 {
2406     return _impl->acceptsFirstMouse(event);
2407 }
2408
2409 - (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent *)event
2410 {
2411     return _impl->shouldDelayWindowOrderingForEvent(event);
2412 }
2413
2414 - (void)doCommandBySelector:(SEL)selector
2415 {
2416     _impl->doCommandBySelector(selector);
2417 }
2418
2419 - (void)insertText:(id)string
2420 {
2421     _impl->insertText(string);
2422 }
2423
2424 - (void)insertText:(id)string replacementRange:(NSRange)replacementRange
2425 {
2426     _impl->insertText(string, replacementRange);
2427 }
2428
2429 - (NSTextInputContext *)inputContext
2430 {
2431     return _impl->inputContext();
2432 }
2433
2434 - (BOOL)performKeyEquivalent:(NSEvent *)event
2435 {
2436     return _impl->performKeyEquivalent(event);
2437 }
2438
2439 - (void)keyUp:(NSEvent *)theEvent
2440 {
2441     _impl->keyUp(theEvent);
2442 }
2443
2444 - (void)keyDown:(NSEvent *)theEvent
2445 {
2446     _impl->keyDown(theEvent);
2447 }
2448
2449 - (void)flagsChanged:(NSEvent *)theEvent
2450 {
2451     _impl->flagsChanged(theEvent);
2452 }
2453
2454 - (void)setMarkedText:(id)string selectedRange:(NSRange)newSelectedRange replacementRange:(NSRange)replacementRange
2455 {
2456     _impl->setMarkedText(string, newSelectedRange, replacementRange);
2457 }
2458
2459 - (void)unmarkText
2460 {
2461     _impl->unmarkText();
2462 }
2463
2464 - (NSRange)selectedRange
2465 {
2466     return _impl->selectedRange();
2467 }
2468
2469 - (BOOL)hasMarkedText
2470 {
2471     return _impl->hasMarkedText();
2472 }
2473
2474 - (NSRange)markedRange
2475 {
2476     return _impl->markedRange();
2477 }
2478
2479 - (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)nsRange actualRange:(NSRangePointer)actualRange
2480 {
2481     return _impl->attributedSubstringForProposedRange(nsRange, actualRange);
2482 }
2483
2484 - (NSUInteger)characterIndexForPoint:(NSPoint)thePoint
2485 {
2486     return _impl->characterIndexForPoint(thePoint);
2487 }
2488
2489 - (NSRect)firstRectForCharacterRange:(NSRange)theRange actualRange:(NSRangePointer)actualRange
2490 {
2491     return _impl->firstRectForCharacterRange(theRange, actualRange);
2492 }
2493
2494 - (void)selectedRangeWithCompletionHandler:(void(^)(NSRange selectedRange))completionHandlerPtr
2495 {
2496     _impl->selectedRangeWithCompletionHandler(completionHandlerPtr);
2497 }
2498
2499 - (void)markedRangeWithCompletionHandler:(void(^)(NSRange markedRange))completionHandlerPtr
2500 {
2501     _impl->markedRangeWithCompletionHandler(completionHandlerPtr);
2502 }
2503
2504 - (void)hasMarkedTextWithCompletionHandler:(void(^)(BOOL hasMarkedText))completionHandlerPtr
2505 {
2506     _impl->hasMarkedTextWithCompletionHandler(completionHandlerPtr);
2507 }
2508
2509 - (void)attributedSubstringForProposedRange:(NSRange)nsRange completionHandler:(void(^)(NSAttributedString *attrString, NSRange actualRange))completionHandlerPtr
2510 {
2511     _impl->attributedSubstringForProposedRange(nsRange, completionHandlerPtr);
2512 }
2513
2514 - (void)firstRectForCharacterRange:(NSRange)theRange completionHandler:(void(^)(NSRect firstRect, NSRange actualRange))completionHandlerPtr
2515 {
2516     _impl->firstRectForCharacterRange(theRange, completionHandlerPtr);
2517 }
2518
2519 - (void)characterIndexForPoint:(NSPoint)thePoint completionHandler:(void(^)(NSUInteger))completionHandlerPtr
2520 {
2521     _impl->characterIndexForPoint(thePoint, completionHandlerPtr);
2522 }
2523
2524 - (NSArray *)validAttributesForMarkedText
2525 {
2526     return _impl->validAttributesForMarkedText();
2527 }
2528
2529 #if ENABLE(DRAG_SUPPORT)
2530 - (void)draggedImage:(NSImage *)image endedAt:(NSPoint)endPoint operation:(NSDragOperation)operation
2531 {
2532     _impl->draggedImage(image, NSPointToCGPoint(endPoint), operation);
2533 }
2534
2535 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)draggingInfo
2536 {
2537     return _impl->draggingEntered(draggingInfo);
2538 }
2539
2540 - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)draggingInfo
2541 {
2542     return _impl->draggingUpdated(draggingInfo);
2543 }
2544
2545 - (void)draggingExited:(id <NSDraggingInfo>)draggingInfo
2546 {
2547     _impl->draggingExited(draggingInfo);
2548 }
2549
2550 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)draggingInfo
2551 {
2552     return _impl->prepareForDragOperation(draggingInfo);
2553 }
2554
2555 - (BOOL)performDragOperation:(id <NSDraggingInfo>)draggingInfo
2556 {
2557     return _impl->performDragOperation(draggingInfo);
2558 }
2559
2560 - (NSView *)_hitTest:(NSPoint *)point dragTypes:(NSSet *)types
2561 {
2562     return _impl->hitTestForDragTypes(NSPointToCGPoint(*point), types);
2563 }
2564 #endif // ENABLE(DRAG_SUPPORT)
2565
2566 - (BOOL)_windowResizeMouseLocationIsInVisibleScrollerThumb:(NSPoint)point
2567 {
2568     return _impl->windowResizeMouseLocationIsInVisibleScrollerThumb(NSPointToCGPoint(point));
2569 }
2570
2571 - (void)viewWillMoveToWindow:(NSWindow *)window
2572 {
2573     _impl->viewWillMoveToWindow(window);
2574 }
2575
2576 - (void)viewDidMoveToWindow
2577 {
2578     _impl->viewDidMoveToWindow();
2579 }
2580
2581 - (void)drawRect:(NSRect)rect
2582 {
2583     _impl->drawRect(NSRectToCGRect(rect));
2584 }
2585
2586 - (BOOL)isOpaque
2587 {
2588     return _impl->isOpaque();
2589 }
2590
2591 - (BOOL)mouseDownCanMoveWindow
2592 {
2593     return WebKit::WebViewImpl::mouseDownCanMoveWindow();
2594 }
2595
2596 - (void)viewDidHide
2597 {
2598     _impl->viewDidHide();
2599 }
2600
2601 - (void)viewDidUnhide
2602 {
2603     _impl->viewDidUnhide();
2604 }
2605
2606 - (void)viewDidChangeBackingProperties
2607 {
2608     _impl->viewDidChangeBackingProperties();
2609 }
2610
2611 - (void)_activeSpaceDidChange:(NSNotification *)notification
2612 {
2613     _impl->activeSpaceDidChange();
2614 }
2615
2616 - (id)accessibilityFocusedUIElement
2617 {
2618     return _impl->accessibilityFocusedUIElement();
2619 }
2620
2621 - (BOOL)accessibilityIsIgnored
2622 {
2623     return _impl->accessibilityIsIgnored();
2624 }
2625
2626 - (id)accessibilityHitTest:(NSPoint)point
2627 {
2628     return _impl->accessibilityHitTest(NSPointToCGPoint(point));
2629 }
2630
2631 - (id)accessibilityAttributeValue:(NSString *)attribute
2632 {
2633     return _impl->accessibilityAttributeValue(attribute);
2634 }
2635
2636 - (NSView *)hitTest:(NSPoint)point
2637 {
2638     if (!_impl)
2639         return [super hitTest:point];
2640     return _impl->hitTest(NSPointToCGPoint(point));
2641 }
2642
2643 - (NSInteger)conversationIdentifier
2644 {
2645     return (NSInteger)self;
2646 }
2647
2648 - (void)quickLookWithEvent:(NSEvent *)event
2649 {
2650     _impl->quickLookWithEvent(event);
2651 }
2652
2653 - (NSTrackingRectTag)addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside
2654 {
2655     return _impl->addTrackingRect(NSRectToCGRect(rect), owner, data, assumeInside);
2656 }
2657
2658 - (NSTrackingRectTag)_addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside useTrackingNum:(int)tag
2659 {
2660     return _impl->addTrackingRectWithTrackingNum(NSRectToCGRect(rect), owner, data, assumeInside, tag);
2661 }
2662
2663 - (void)_addTrackingRects:(NSRect *)rects owner:(id)owner userDataList:(void **)userDataList assumeInsideList:(BOOL *)assumeInsideList trackingNums:(NSTrackingRectTag *)trackingNums count:(int)count
2664 {
2665     CGRect *cgRects = (CGRect *)calloc(1, sizeof(CGRect));
2666     for (int i = 0; i < count; i++)
2667         cgRects[i] = NSRectToCGRect(rects[i]);
2668     _impl->addTrackingRectsWithTrackingNums(cgRects, owner, userDataList, assumeInsideList, trackingNums, count);
2669     free(cgRects);
2670 }
2671
2672 - (void)removeTrackingRect:(NSTrackingRectTag)tag
2673 {
2674     if (!_impl)
2675         return;
2676     _impl->removeTrackingRect(tag);
2677 }
2678
2679 - (void)_removeTrackingRects:(NSTrackingRectTag *)tags count:(int)count
2680 {
2681     if (!_impl)
2682         return;
2683     _impl->removeTrackingRects(tags, count);
2684 }
2685
2686 - (NSString *)view:(NSView *)view stringForToolTip:(NSToolTipTag)tag point:(NSPoint)point userData:(void *)data
2687 {
2688     return _impl->stringForToolTip(tag);
2689 }
2690
2691 - (void)pasteboardChangedOwner:(NSPasteboard *)pasteboard
2692 {
2693     _impl->pasteboardChangedOwner(pasteboard);
2694 }
2695
2696 - (void)pasteboard:(NSPasteboard *)pasteboard provideDataForType:(NSString *)type
2697 {
2698     _impl->provideDataForPasteboard(pasteboard, type);
2699 }
2700
2701 - (NSArray *)namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination
2702 {
2703     return _impl->namesOfPromisedFilesDroppedAtDestination(dropDestination);
2704 }
2705
2706 - (BOOL)wantsUpdateLayer
2707 {
2708     return WebKit::WebViewImpl::wantsUpdateLayer();
2709 }
2710
2711 - (void)updateLayer
2712 {
2713     _impl->updateLayer();
2714 }
2715
2716 - (void)setAllowsBackForwardNavigationGestures:(BOOL)allowsBackForwardNavigationGestures
2717 {
2718     _impl->setAllowsBackForwardNavigationGestures(allowsBackForwardNavigationGestures);
2719 }
2720
2721 - (BOOL)allowsBackForwardNavigationGestures
2722 {
2723     return _impl->allowsBackForwardNavigationGestures();
2724 }
2725
2726 - (void)smartMagnifyWithEvent:(NSEvent *)event
2727 {
2728     _impl->smartMagnifyWithEvent(event);
2729 }
2730
2731 - (void)setMagnification:(double)magnification centeredAtPoint:(NSPoint)point
2732 {
2733     _impl->setMagnification(magnification, NSPointToCGPoint(point));
2734 }
2735
2736 - (void)setMagnification:(double)magnification
2737 {
2738     _impl->setMagnification(magnification);
2739 }
2740
2741 - (double)magnification
2742 {
2743     return _impl->magnification();
2744 }
2745
2746 - (void)setAllowsMagnification:(BOOL)allowsMagnification
2747 {
2748     _impl->setAllowsMagnification(allowsMagnification);
2749 }
2750
2751 - (BOOL)allowsMagnification
2752 {
2753     return _impl->allowsMagnification();
2754 }
2755
2756 - (void)magnifyWithEvent:(NSEvent *)event
2757 {
2758     _impl->magnifyWithEvent(event);
2759 }
2760
2761 #if ENABLE(MAC_GESTURE_EVENTS)
2762 - (void)rotateWithEvent:(NSEvent *)event
2763 {
2764     _impl->rotateWithEvent(event);
2765 }
2766 #endif
2767
2768 - (WKTextFinderClient *)_ensureTextFinderClient
2769 {
2770     if (!_textFinderClient)
2771         _textFinderClient = adoptNS([[WKTextFinderClient alloc] initWithPage:*_page view:self]);
2772     return _textFinderClient.get();
2773 }
2774
2775 - (void)findMatchesForString:(NSString *)targetString relativeToMatch:(id <NSTextFinderAsynchronousDocumentFindMatch>)relativeMatch findOptions:(NSTextFinderAsynchronousDocumentFindOptions)findOptions maxResults:(NSUInteger)maxResults resultCollector:(void (^)(NSArray *matches, BOOL didWrap))resultCollector
2776 {
2777     [[self _ensureTextFinderClient] findMatchesForString:targetString relativeToMatch:relativeMatch findOptions:findOptions maxResults:maxResults resultCollector:resultCollector];
2778 }
2779
2780 - (NSView *)documentContainerView
2781 {
2782     return self;
2783 }
2784
2785 - (void)getSelectedText:(void (^)(NSString *selectedTextString))completionHandler
2786 {
2787     [[self _ensureTextFinderClient] getSelectedText:completionHandler];
2788 }
2789
2790 - (void)selectFindMatch:(id <NSTextFinderAsynchronousDocumentFindMatch>)findMatch completionHandler:(void (^)(void))completionHandler
2791 {
2792     [[self _ensureTextFinderClient] selectFindMatch:findMatch completionHandler:completionHandler];
2793 }
2794
2795 - (void)touchesBeganWithEvent:(NSEvent *)event
2796 {
2797     _impl->touchesBeganWithEvent(event);
2798 }
2799
2800 - (void)touchesMovedWithEvent:(NSEvent *)event
2801 {
2802     _impl->touchesMovedWithEvent(event);
2803 }
2804
2805 - (void)touchesEndedWithEvent:(NSEvent *)event
2806 {
2807     _impl->touchesEndedWithEvent(event);
2808 }
2809
2810 - (void)touchesCancelledWithEvent:(NSEvent *)event
2811 {
2812     _impl->touchesCancelledWithEvent(event);
2813 }
2814
2815 - (NSTextInputContext *)_web_superInputContext
2816 {
2817     return [super inputContext];
2818 }
2819
2820 - (void)_web_superQuickLookWithEvent:(NSEvent *)event
2821 {
2822     [super quickLookWithEvent:event];
2823 }
2824
2825 - (void)_web_superSwipeWithEvent:(NSEvent *)event
2826 {
2827     [super swipeWithEvent:event];
2828 }
2829
2830 - (void)_web_superMagnifyWithEvent:(NSEvent *)event
2831 {
2832     [super magnifyWithEvent:event];
2833 }
2834
2835 - (void)_web_superSmartMagnifyWithEvent:(NSEvent *)event
2836 {
2837     [super smartMagnifyWithEvent:event];
2838 }
2839
2840 - (void)_web_superRemoveTrackingRect:(NSTrackingRectTag)tag
2841 {
2842     [super removeTrackingRect:tag];
2843 }
2844
2845 - (id)_web_superAccessibilityAttributeValue:(NSString *)attribute
2846 {
2847 #pragma clang diagnostic push
2848 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
2849     return [super accessibilityAttributeValue:attribute];
2850 #pragma clang diagnostic pop
2851 }
2852
2853 - (void)_web_superDoCommandBySelector:(SEL)selector
2854 {
2855     [super doCommandBySelector:selector];
2856 }
2857
2858 - (BOOL)_web_superPerformKeyEquivalent:(NSEvent *)event
2859 {
2860     return [super performKeyEquivalent:event];
2861 }
2862
2863 - (void)_web_superKeyDown:(NSEvent *)event
2864 {
2865     [super keyDown:event];
2866 }
2867
2868 - (NSView *)_web_superHitTest:(NSPoint)point
2869 {
2870     return [super hitTest:point];
2871 }
2872
2873 - (id)_web_immediateActionAnimationControllerForHitTestResultInternal:(API::HitTestResult*)hitTestResult withType:(uint32_t)type userData:(API::Object*)userData
2874 {
2875     id<NSSecureCoding> data = userData ? static_cast<id<NSSecureCoding>>(userData->wrapper()) : nil;
2876     return [self _immediateActionAnimationControllerForHitTestResult:wrapper(*hitTestResult) withType:(_WKImmediateActionType)type userData:data];
2877 }
2878
2879 // We don't expose these various bits of SPI like WKView does,
2880 // so have these internal methods just do the work (or do nothing):
2881 - (void)_web_prepareForImmediateActionAnimation
2882 {
2883 }
2884
2885 - (void)_web_cancelImmediateActionAnimation
2886 {
2887 }
2888
2889 - (void)_web_completeImmediateActionAnimation
2890 {
2891 }
2892
2893 - (void)_web_didChangeContentSize:(NSSize)newSize
2894 {
2895 }
2896
2897 - (void)_web_dismissContentRelativeChildWindows
2898 {
2899     _impl->dismissContentRelativeChildWindowsFromViewOnly();
2900 }
2901
2902 - (void)_web_dismissContentRelativeChildWindowsWithAnimation:(BOOL)withAnimation
2903 {
2904     _impl->dismissContentRelativeChildWindowsWithAnimationFromViewOnly(withAnimation);
2905 }
2906
2907 - (void)_web_gestureEventWasNotHandledByWebCore:(NSEvent *)event
2908 {
2909     _impl->gestureEventWasNotHandledByWebCoreFromViewOnly(event);
2910 }
2911
2912 #endif // PLATFORM(MAC)
2913
2914 @end
2915
2916 @implementation WKWebView (WKPrivate)
2917
2918 - (BOOL)_isEditable
2919 {
2920     return _page->isEditable();
2921 }
2922
2923 - (void)_setEditable:(BOOL)editable
2924 {
2925     _page->setEditable(editable);
2926 #if PLATFORM(MAC)
2927     _impl->startObservingFontPanel();
2928 #endif
2929 }
2930
2931 - (_WKRemoteObjectRegistry *)_remoteObjectRegistry
2932 {
2933 #if PLATFORM(MAC)
2934     return _impl->remoteObjectRegistry();
2935 #else
2936     if (!_remoteObjectRegistry) {
2937         _remoteObjectRegistry = adoptNS([[_WKRemoteObjectRegistry alloc] _initWithMessageSender:*_page]);
2938         _page->process().processPool().addMessageReceiver(Messages::RemoteObjectRegistry::messageReceiverName(), _page->pageID(), [_remoteObjectRegistry remoteObjectRegistry]);
2939     }
2940
2941     return _remoteObjectRegistry.get();
2942 #endif
2943 }
2944
2945 - (WKBrowsingContextHandle *)_handle
2946 {
2947     return [[[WKBrowsingContextHandle alloc] _initWithPageID:_page->pageID()] autorelease];
2948 }
2949
2950 - (_WKRenderingProgressEvents)_observedRenderingProgressEvents
2951 {
2952     return _observedRenderingProgressEvents;
2953 }
2954
2955 - (id <WKHistoryDelegatePrivate>)_historyDelegate
2956 {
2957     return _navigationState->historyDelegate().autorelease();
2958 }
2959
2960 - (void)_setHistoryDelegate:(id <WKHistoryDelegatePrivate>)historyDelegate
2961 {
2962     _page->setHistoryClient(_navigationState->createHistoryClient());
2963     _navigationState->setHistoryDelegate(historyDelegate);
2964 }
2965
2966 - (NSURL *)_unreachableURL
2967 {
2968     return [NSURL _web_URLWithWTFString:_page->pageLoadState().unreachableURL()];
2969 }
2970
2971 - (void)_loadAlternateHTMLString:(NSString *)string baseURL:(NSURL *)baseURL forUnreachableURL:(NSURL *)unreachableURL
2972 {
2973     _page->loadAlternateHTMLString(string, [baseURL _web_originalDataAsWTFString], [unreachableURL _web_originalDataAsWTFString]);
2974 }
2975
2976 - (WKNavigation *)_loadData:(NSData *)data MIMEType:(NSString *)MIMEType characterEncodingName:(NSString *)characterEncodingName baseURL:(NSURL *)baseURL userData:(id)userData
2977 {
2978     auto navigation = _page->loadData(API::Data::createWithoutCopying(data).ptr(), MIMEType, characterEncodingName, baseURL.absoluteString, WebKit::ObjCObjectGraph::create(userData).ptr());
2979     if (!navigation)
2980         return nil;
2981
2982     return [wrapper(*navigation.release().leakRef()) autorelease];
2983 }
2984
2985 - (NSArray *)_certificateChain
2986 {
2987     if (WebKit::WebFrameProxy* mainFrame = _page->mainFrame())
2988         return mainFrame->certificateInfo() ? (NSArray *)mainFrame->certificateInfo()->certificateInfo().certificateChain() : nil;
2989
2990     return nil;
2991 }
2992
2993 - (NSURL *)_committedURL
2994 {
2995     return [NSURL _web_URLWithWTFString:_page->pageLoadState().url()];
2996 }
2997
2998 - (NSString *)_MIMEType
2999 {
3000     if (_page->mainFrame())
3001         return _page->mainFrame()->mimeType();
3002
3003     return nil;
3004 }
3005
3006 - (NSString *)_userAgent
3007 {
3008     return _page->userAgent();
3009 }
3010
3011 - (NSString *)_applicationNameForUserAgent
3012 {
3013     return _page->applicationNameForUserAgent();
3014 }
3015
3016 - (void)_setApplicationNameForUserAgent:(NSString *)applicationNameForUserAgent
3017 {
3018     _page->setApplicationNameForUserAgent(applicationNameForUserAgent);
3019 }
3020
3021 - (NSString *)_customUserAgent
3022 {
3023     return self.customUserAgent;
3024 }
3025
3026 - (void)_setCustomUserAgent:(NSString *)customUserAgent
3027 {
3028     self.customUserAgent = customUserAgent;
3029 }
3030
3031 - (void)_setUserContentExtensionsEnabled:(BOOL)userContentExtensionsEnabled
3032 {
3033     // This is kept for binary compatibility with iOS 9.
3034 }
3035
3036 - (BOOL)_userContentExtensionsEnabled
3037 {
3038     // This is kept for binary compatibility with iOS 9.
3039     return true;
3040 }
3041
3042 - (pid_t)_webProcessIdentifier
3043 {
3044     return _page->isValid() ? _page->processIdentifier() : 0;
3045 }
3046
3047 - (void)_killWebContentProcess
3048 {
3049     if (!_page->isValid())
3050         return;
3051
3052     _page->process().terminate();
3053 }
3054
3055 - (WKNavigation *)_reloadWithoutContentBlockers
3056 {
3057     const bool reloadFromOrigin = false;
3058     const bool contentBlockersEnabled = false;
3059     auto navigation = _page->reload(reloadFromOrigin, contentBlockersEnabled);
3060     if (!navigation)
3061         return nil;
3062     
3063     return [wrapper(*navigation.release().leakRef()) autorelease];
3064 }
3065
3066 - (void)_killWebContentProcessAndResetState
3067 {
3068     _page->terminateProcess();
3069 }
3070
3071 #if PLATFORM(IOS)
3072 static WebCore::FloatSize activeMinimumLayoutSize(WKWebView *webView, const CGRect& bounds)
3073 {
3074     return WebCore::FloatSize(webView->_overridesMinimumLayoutSize ? webView->_minimumLayoutSizeOverride : bounds.size);
3075 }
3076
3077 static WebCore::FloatSize activeMaximumUnobscuredSize(WKWebView *webView, const CGRect& bounds)
3078 {
3079     return WebCore::FloatSize(webView->_overridesMaximumUnobscuredSize ? webView->_maximumUnobscuredSizeOverride : bounds.size);
3080 }
3081
3082 static int32_t activeOrientation(WKWebView *webView)
3083 {
3084     return webView->_overridesInterfaceOrientation ? deviceOrientationForUIInterfaceOrientation(webView->_interfaceOrientationOverride) : webView->_page->deviceOrientation();
3085 }
3086
3087 - (void (^)(void))_retainActiveFocusedState
3088 {
3089     ++_activeFocusedStateRetainCount;
3090
3091     // FIXME: Use something like CompletionHandlerCallChecker to ensure that the returned block is called before it's released.
3092     return [[[self] {
3093         --_activeFocusedStateRetainCount;
3094     } copy] autorelease];
3095 }
3096
3097 - (void)_becomeFirstResponderWithSelectionMovingForward:(BOOL)selectingForward completionHandler:(void (^)(BOOL didBecomeFirstResponder))completionHandler
3098 {
3099     typeof(completionHandler) completionHandlerCopy = nil;
3100     if (completionHandler)
3101         completionHandlerCopy = Block_copy(completionHandler);
3102
3103     [_contentView _becomeFirstResponderWithSelectionMovingForward:selectingForward completionHandler:[completionHandlerCopy](BOOL didBecomeFirstResponder) {
3104         if (!completionHandlerCopy)
3105             return;
3106
3107         completionHandlerCopy(didBecomeFirstResponder);
3108         Block_release(completionHandlerCopy);
3109     }];
3110 }
3111
3112 - (id)_snapshotLayerContentsForBackForwardListItem:(WKBackForwardListItem *)item
3113 {
3114     if (_page->backForwardList().currentItem() == &item._item)
3115         _page->recordNavigationSnapshot(*_page->backForwardList().currentItem());
3116
3117     if (auto* viewSnapshot = item._item.snapshot())
3118         return viewSnapshot->asLayerContents();
3119
3120     return nil;
3121 }
3122
3123 #endif
3124
3125 - (void)_didRelaunchProcess
3126 {
3127 #if PLATFORM(IOS)
3128     CGRect bounds = self.bounds;
3129     WebCore::FloatSize minimalLayoutSize = activeMinimumLayoutSize(self, bounds);
3130     _page->setViewportConfigurationMinimumLayoutSize(minimalLayoutSize);
3131     _page->setMaximumUnobscuredSize(activeMaximumUnobscuredSize(self, bounds));
3132 #endif
3133 }
3134
3135 - (NSData *)_sessionStateData
3136 {
3137     WebKit::SessionState sessionState = _page->sessionState();
3138
3139     // FIXME: This should not use the legacy session state encoder.
3140     return [wrapper(*WebKit::encodeLegacySessionState(sessionState).release().leakRef()) autorelease];
3141 }
3142
3143 - (_WKSessionState *)_sessionState
3144 {
3145     return adoptNS([[_WKSessionState alloc] _initWithSessionState:_page->sessionState()]).autorelease();
3146 }
3147
3148 - (void)_restoreFromSessionStateData:(NSData *)sessionStateData
3149 {
3150     // FIXME: This should not use the legacy session state decoder.
3151     WebKit::SessionState sessionState;
3152     if (!WebKit::decodeLegacySessionState(static_cast<const uint8_t*>(sessionStateData.bytes), sessionStateData.length, sessionState))
3153         return;
3154
3155     _page->restoreFromSessionState(WTFMove(sessionState), true);
3156 }
3157
3158 - (WKNavigation *)_restoreSessionState:(_WKSessionState *)sessionState andNavigate:(BOOL)navigate
3159 {
3160     auto navigation = _page->restoreFromSessionState(sessionState->_sessionState, navigate);
3161     if (!navigation)
3162         return nil;
3163
3164     return [wrapper(*navigation.release().leakRef()) autorelease];
3165 }
3166
3167 - (void)_close
3168 {
3169     _page->close();
3170 }
3171
3172 - (BOOL)_allowsRemoteInspection
3173 {
3174 #if ENABLE(REMOTE_INSPECTOR)
3175     return _page->allowsRemoteInspection();
3176 #else
3177     return NO;
3178 #endif
3179 }
3180
3181 - (void)_setAllowsRemoteInspection:(BOOL)allow
3182 {
3183 #if ENABLE(REMOTE_INSPECTOR)
3184     _page->setAllowsRemoteInspection(allow);
3185 #endif
3186 }
3187
3188 - (NSString *)_remoteInspectionNameOverride
3189 {
3190 #if ENABLE(REMOTE_INSPECTOR)
3191     return _page->remoteInspectionNameOverride();
3192 #else
3193     return nil;
3194 #endif
3195 }
3196
3197 - (void)_setRemoteInspectionNameOverride:(NSString *)name
3198 {
3199 #if ENABLE(REMOTE_INSPECTOR)
3200     _page->setRemoteInspectionNameOverride(name);
3201 #endif
3202 }
3203
3204 - (BOOL)_addsVisitedLinks
3205 {
3206     return _page->addsVisitedLinks();
3207 }
3208
3209 - (void)_setAddsVisitedLinks:(BOOL)addsVisitedLinks
3210 {
3211     _page->setAddsVisitedLinks(addsVisitedLinks);
3212 }
3213
3214 - (BOOL)_networkRequestsInProgress
3215 {
3216     return _page->pageLoadState().networkRequestsInProgress();
3217 }
3218
3219 static inline WebCore::LayoutMilestones layoutMilestones(_WKRenderingProgressEvents events)
3220 {
3221     WebCore::LayoutMilestones milestones = 0;
3222
3223     if (events & _WKRenderingProgressEventFirstLayout)
3224         milestones |= WebCore::DidFirstLayout;
3225
3226     if (events & _WKRenderingProgressEventFirstVisuallyNonEmptyLayout)
3227         milestones |= WebCore::DidFirstVisuallyNonEmptyLayout;
3228
3229     if (events & _WKRenderingProgressEventFirstPaintWithSignificantArea)
3230         milestones |= WebCore::DidHitRelevantRepaintedObjectsAreaThreshold;
3231
3232     if (events & _WKRenderingProgressEventReachedSessionRestorationRenderTreeSizeThreshold)
3233         milestones |= WebCore::ReachedSessionRestorationRenderTreeSizeThreshold;
3234
3235     if (events & _WKRenderingProgressEventFirstLayoutAfterSuppressedIncrementalRendering)
3236         milestones |= WebCore::DidFirstLayoutAfterSuppressedIncrementalRendering;
3237
3238     if (events & _WKRenderingProgressEventFirstPaintAfterSuppressedIncrementalRendering)
3239         milestones |= WebCore::DidFirstPaintAfterSuppressedIncrementalRendering;
3240
3241     return milestones;
3242 }
3243
3244 - (void)_setObservedRenderingProgressEvents:(_WKRenderingProgressEvents)observedRenderingProgressEvents
3245 {
3246     _observedRenderingProgressEvents = observedRenderingProgressEvents;
3247     _page->listenForLayoutMilestones(layoutMilestones(observedRenderingProgressEvents));
3248 }
3249
3250 - (void)_getMainResourceDataWithCompletionHandler:(void (^)(NSData *, NSError *))completionHandler
3251 {
3252     auto handler = adoptNS([completionHandler copy]);
3253
3254     _page->getMainResourceDataOfFrame(_page->mainFrame(), [handler](API::Data* data, WebKit::CallbackBase::Error error) {
3255         void (^completionHandlerBlock)(NSData *, NSError *) = (void (^)(NSData *, NSError *))handler.get();
3256         if (error != WebKit::CallbackBase::Error::None) {
3257             // FIXME: Pipe a proper error in from the WebPageProxy.
3258             RetainPtr<NSError> error = adoptNS([[NSError alloc] init]);
3259             completionHandlerBlock(nil, error.get());
3260         } else
3261             completionHandlerBlock(wrapper(*data), nil);
3262     });
3263 }
3264
3265 - (void)_getWebArchiveDataWithCompletionHandler:(void (^)(NSData *, NSError *))completionHandler
3266 {
3267     auto handler = adoptNS([completionHandler copy]);
3268
3269     _page->getWebArchiveOfFrame(_page->mainFrame(), [handler](API::Data* data, WebKit::CallbackBase::Error error) {
3270         void (^completionHandlerBlock)(NSData *, NSError *) = (void (^)(NSData *, NSError *))handler.get();
3271         if (error != WebKit::CallbackBase::Error::None) {
3272             // FIXME: Pipe a proper error in from the WebPageProxy.
3273             RetainPtr<NSError> error = adoptNS([[NSError alloc] init]);
3274             completionHandlerBlock(nil, error.get());
3275         } else
3276             completionHandlerBlock(wrapper(*data), nil);
3277     });
3278 }
3279
3280 - (_WKPaginationMode)_paginationMode
3281 {
3282     switch (_page->paginationMode()) {
3283     case WebCore::Pagination::Unpaginated:
3284         return _WKPaginationModeUnpaginated;
3285     case WebCore::Pagination::LeftToRightPaginated:
3286         return _WKPaginationModeLeftToRight;
3287     case WebCore::Pagination::RightToLeftPaginated:
3288         return _WKPaginationModeRightToLeft;
3289     case WebCore::Pagination::TopToBottomPaginated:
3290         return _WKPaginationModeTopToBottom;
3291     case WebCore::Pagination::BottomToTopPaginated:
3292         return _WKPaginationModeBottomToTop;
3293     }
3294
3295     ASSERT_NOT_REACHED();
3296     return _WKPaginationModeUnpaginated;
3297 }
3298
3299 - (void)_setPaginationMode:(_WKPaginationMode)paginationMode
3300 {
3301     WebCore::Pagination::Mode mode;
3302     switch (paginationMode) {
3303     case _WKPaginationModeUnpaginated:
3304         mode = WebCore::Pagination::Unpaginated;
3305         break;
3306     case _WKPaginationModeLeftToRight:
3307         mode = WebCore::Pagination::LeftToRightPaginated;
3308         break;
3309     case _WKPaginationModeRightToLeft:
3310         mode = WebCore::Pagination::RightToLeftPaginated;
3311         break;
3312     case _WKPaginationModeTopToBottom:
3313         mode = WebCore::Pagination::TopToBottomPaginated;
3314         break;
3315     case _WKPaginationModeBottomToTop:
3316         mode = WebCore::Pagination::BottomToTopPaginated;
3317         break;
3318     default:
3319         return;
3320     }
3321
3322     _page->setPaginationMode(mode);
3323 }
3324
3325 - (BOOL)_paginationBehavesLikeColumns
3326 {
3327     return _page->paginationBehavesLikeColumns();
3328 }
3329
3330 - (void)_setPaginationBehavesLikeColumns:(BOOL)behavesLikeColumns
3331 {
3332     _page->setPaginationBehavesLikeColumns(behavesLikeColumns);
3333 }
3334
3335 - (CGFloat)_pageLength
3336 {
3337     return _page->pageLength();
3338 }
3339
3340 - (void)_setPageLength:(CGFloat)pageLength
3341 {
3342     _page->setPageLength(pageLength);
3343 }
3344
3345 - (CGFloat)_gapBetweenPages
3346 {
3347     return _page->gapBetweenPages();
3348 }
3349
3350 - (void)_setGapBetweenPages:(CGFloat)gapBetweenPages
3351 {
3352     _page->setGapBetweenPages(gapBetweenPages);
3353 }
3354
3355 - (NSUInteger)_pageCount
3356 {
3357     return _page->pageCount();
3358 }
3359
3360 - (BOOL)_supportsTextZoom
3361 {
3362     return _page->supportsTextZoom();
3363 }
3364
3365 - (double)_textZoomFactor
3366 {
3367     return _page->textZoomFactor();
3368 }
3369
3370 - (void)_setTextZoomFactor:(double)zoomFactor
3371 {
3372     _page->setTextZoomFactor(zoomFactor);
3373 }
3374
3375 - (double)_pageZoomFactor
3376 {
3377     return _page->pageZoomFactor();
3378 }
3379
3380 - (void)_setPageZoomFactor:(double)zoomFactor
3381 {
3382     _page->setPageZoomFactor(zoomFactor);
3383 }
3384
3385 - (id <_WKDiagnosticLoggingDelegate>)_diagnosticLoggingDelegate
3386 {
3387     return [static_cast<WebKit::DiagnosticLoggingClient&>(_page->diagnosticLoggingClient()).delegate().leakRef() autorelease];
3388 }
3389
3390 - (void)_setDiagnosticLoggingDelegate:(id<_WKDiagnosticLoggingDelegate>)diagnosticLoggingDelegate
3391 {
3392     static_cast<WebKit::DiagnosticLoggingClient&>(_page->diagnosticLoggingClient()).setDelegate(diagnosticLoggingDelegate);
3393 }
3394
3395 - (id <_WKFindDelegate>)_findDelegate
3396 {
3397     return [static_cast<WebKit::FindClient&>(_page->findClient()).delegate().leakRef() autorelease];
3398 }
3399
3400 - (void)_setFindDelegate:(id<_WKFindDelegate>)findDelegate
3401 {
3402     static_cast<WebKit::FindClient&>(_page->findClient()).setDelegate(findDelegate);
3403 }
3404
3405 static inline WebKit::FindOptions toFindOptions(_WKFindOptions wkFindOptions)
3406 {
3407     unsigned findOptions = 0;
3408
3409     if (wkFindOptions & _WKFindOptionsCaseInsensitive)
3410         findOptions |= WebKit::FindOptionsCaseInsensitive;
3411     if (wkFindOptions & _WKFindOptionsAtWordStarts)
3412         findOptions |= WebKit::FindOptionsAtWordStarts;
3413     if (wkFindOptions & _WKFindOptionsTreatMedialCapitalAsWordStart)
3414         findOptions |= WebKit::FindOptionsTreatMedialCapitalAsWordStart;
3415     if (wkFindOptions & _WKFindOptionsBackwards)
3416         findOptions |= WebKit::FindOptionsBackwards;
3417     if (wkFindOptions & _WKFindOptionsWrapAround)
3418         findOptions |= WebKit::FindOptionsWrapAround;
3419     if (wkFindOptions & _WKFindOptionsShowOverlay)
3420         findOptions |= WebKit::FindOptionsShowOverlay;
3421     if (wkFindOptions & _WKFindOptionsShowFindIndicator)
3422         findOptions |= WebKit::FindOptionsShowFindIndicator;
3423     if (wkFindOptions & _WKFindOptionsShowHighlight)
3424         findOptions |= WebKit::FindOptionsShowHighlight;
3425     if (wkFindOptions & _WKFindOptionsDetermineMatchIndex)
3426         findOptions |= WebKit::FindOptionsDetermineMatchIndex;
3427
3428     return static_cast<WebKit::FindOptions>(findOptions);
3429 }
3430
3431 - (void)_countStringMatches:(NSString *)string options:(_WKFindOptions)options maxCount:(NSUInteger)maxCount
3432 {
3433 #if PLATFORM(IOS)
3434     if (_customContentView) {
3435         [_customContentView web_countStringMatches:string options:options maxCount:maxCount];
3436         return;
3437     }
3438 #endif
3439     _page->countStringMatches(string, toFindOptions(options), maxCount);
3440 }
3441
3442 - (void)_findString:(NSString *)string options:(_WKFindOptions)options maxCount:(NSUInteger)maxCount
3443 {
3444 #if PLATFORM(IOS)
3445     if (_customContentView) {
3446         [_customContentView web_findString:string options:options maxCount:maxCount];
3447         return;
3448     }
3449 #endif
3450     _page->findString(string, toFindOptions(options), maxCount);
3451 }
3452
3453 - (void)_hideFindUI
3454 {
3455 #if PLATFORM(IOS)
3456     if (_customContentView) {
3457         [_customContentView web_hideFindUI];
3458         return;
3459     }
3460 #endif
3461     _page->hideFindUI();
3462 }
3463
3464 - (void)_saveBackForwardSnapshotForItem:(WKBackForwardListItem *)item
3465 {
3466     _page->recordNavigationSnapshot(item._item);
3467 }
3468
3469 - (id <_WKInputDelegate>)_inputDelegate
3470 {
3471     return _inputDelegate.getAutoreleased();
3472 }
3473
3474 - (id <_WKFormDelegate>)_formDelegate
3475 {
3476     return (id <_WKFormDelegate>)[self _inputDelegate];
3477 }
3478
3479 - (void)_setInputDelegate:(id <_WKInputDelegate>)inputDelegate
3480 {
3481     _inputDelegate = inputDelegate;
3482
3483     class FormClient : public API::FormClient {
3484     public:
3485         explicit FormClient(WKWebView *webView)
3486             : m_webView(webView)
3487         {
3488         }
3489
3490         virtual ~FormClient() { }
3491
3492         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
3493         {
3494             if (userData && userData->type() != API::Object::Type::Data) {
3495                 ASSERT(!userData || userData->type() == API::Object::Type::Data);
3496                 m_webView->_page->process().connection()->markCurrentlyDispatchedMessageAsInvalid();
3497                 listener->continueSubmission();
3498                 return;
3499             }
3500
3501             auto inputDelegate = m_webView->_inputDelegate.get();
3502
3503             if (![inputDelegate respondsToSelector:@selector(_webView:willSubmitFormValues:userObject:submissionHandler:)]) {
3504                 listener->continueSubmission();
3505                 return;
3506             }
3507
3508             auto valueMap = adoptNS([[NSMutableDictionary alloc] initWithCapacity:textFieldValues.size()]);
3509             for (const auto& pair : textFieldValues)
3510                 [valueMap setObject:pair.second forKey:pair.first];
3511
3512             NSObject <NSSecureCoding> *userObject = nil;
3513             if (API::Data* data = static_cast<API::Data*>(userData)) {
3514                 auto nsData = adoptNS([[NSData alloc] initWithBytesNoCopy:const_cast<void*>(static_cast<const void*>(data->bytes())) length:data->size() freeWhenDone:NO]);
3515                 auto unarchiver = adoptNS([[NSKeyedUnarchiver alloc] initForReadingWithData:nsData.get()]);
3516                 [unarchiver setRequiresSecureCoding:YES];
3517                 @try {
3518                     userObject = [unarchiver decodeObjectOfClass:[NSObject class] forKey:@"userObject"];
3519                 } @catch (NSException *exception) {
3520                     LOG_ERROR("Failed to decode user data: %@", exception);
3521                 }
3522             }
3523
3524             RefPtr<WebKit::WebFormSubmissionListenerProxy> localListener = WTFMove(listener);
3525             RefPtr<WebKit::CompletionHandlerCallChecker> checker = WebKit::CompletionHandlerCallChecker::create(inputDelegate.get(), @selector(_webView:willSubmitFormValues:userObject:submissionHandler:));
3526             [inputDelegate _webView:m_webView willSubmitFormValues:valueMap.get() userObject:userObject submissionHandler:[localListener, checker] {
3527                 checker->didCallCompletionHandler();
3528                 localListener->continueSubmission();
3529             }];
3530         }
3531
3532     private:
3533         WKWebView *m_webView;
3534     };
3535
3536     if (inputDelegate)
3537         _page->setFormClient(std::make_unique<FormClient>(self));
3538     else
3539         _page->setFormClient(nullptr);
3540 }
3541
3542 - (void)_setFormDelegate:(id <_WKFormDelegate>)formDelegate
3543 {
3544     [self _setInputDelegate:(id <_WKInputDelegate>)formDelegate];
3545 }
3546
3547 - (BOOL)_isDisplayingStandaloneImageDocument
3548 {
3549     if (auto* mainFrame = _page->mainFrame())
3550         return mainFrame->isDisplayingStandaloneImageDocument();
3551     return NO;
3552 }
3553
3554 - (BOOL)_isDisplayingStandaloneMediaDocument
3555 {
3556     if (auto* mainFrame = _page->mainFrame())
3557         return mainFrame->isDisplayingStandaloneMediaDocument();
3558     return NO;
3559 }
3560
3561 - (BOOL)_isShowingNavigationGestureSnapshot
3562 {
3563     return _page->isShowingNavigationGestureSnapshot();
3564 }
3565
3566 - (_WKLayoutMode)_layoutMode
3567 {
3568 #if PLATFORM(MAC)
3569     switch (_impl->layoutMode()) {
3570     case kWKLayoutModeFixedSize:
3571         return _WKLayoutModeFixedSize;
3572     case kWKLayoutModeDynamicSizeComputedFromViewScale:
3573         return _WKLayoutModeDynamicSizeComputedFromViewScale;
3574     case kWKLayoutModeDynamicSizeComputedFromMinimumDocumentSize:
3575         return _WKLayoutModeDynamicSizeComputedFromMinimumDocumentSize;
3576     case kWKLayoutModeViewSize:
3577     default:
3578         return _WKLayoutModeViewSize;
3579     }
3580 #else
3581     return _page->useFixedLayout() ? _WKLayoutModeFixedSize : _WKLayoutModeViewSize;
3582 #endif
3583 }
3584
3585 - (void)_setLayoutMode:(_WKLayoutMode)layoutMode
3586 {
3587 #if PLATFORM(MAC)
3588     WKLayoutMode wkViewLayoutMode;
3589     switch (layoutMode) {
3590     case _WKLayoutModeFixedSize:
3591         wkViewLayoutMode = kWKLayoutModeFixedSize;
3592         break;
3593     case _WKLayoutModeDynamicSizeComputedFromViewScale:
3594         wkViewLayoutMode = kWKLayoutModeDynamicSizeComputedFromViewScale;
3595         break;
3596     case _WKLayoutModeDynamicSizeComputedFromMinimumDocumentSize:
3597         wkViewLayoutMode = kWKLayoutModeDynamicSizeComputedFromMinimumDocumentSize;
3598         break;
3599     case _WKLayoutModeViewSize:
3600     default:
3601         wkViewLayoutMode = kWKLayoutModeViewSize;
3602         break;
3603     }
3604     _impl->setLayoutMode(wkViewLayoutMode);
3605 #else
3606     _page->setUseFixedLayout(layoutMode == _WKLayoutModeFixedSize || layoutMode == _WKLayoutModeDynamicSizeComputedFromViewScale);
3607 #endif
3608 }
3609
3610 - (CGSize)_fixedLayoutSize
3611 {
3612     return _page->fixedLayoutSize();
3613 }
3614
3615 - (void)_setFixedLayoutSize:(CGSize)fixedLayoutSize
3616 {
3617     _page->setFixedLayoutSize(WebCore::expandedIntSize(WebCore::FloatSize(fixedLayoutSize)));
3618 }
3619
3620 - (CGFloat)_viewScale
3621 {
3622     return _page->viewScaleFactor();
3623 }
3624
3625 - (void)_setViewScale:(CGFloat)viewScale
3626 {
3627 #if PLATFORM(MAC)
3628     _impl->setViewScale(viewScale);
3629 #else
3630     if (viewScale <= 0 || isnan(viewScale) || isinf(viewScale))
3631         [NSException raise:NSInvalidArgumentException format:@"View scale should be a positive number"];
3632
3633     _page->scaleView(viewScale);
3634 #endif
3635 }
3636
3637 #pragma mark scrollperf methods
3638
3639 - (void)_setScrollPerformanceDataCollectionEnabled:(BOOL)enabled
3640 {
3641     _page->setScrollPerformanceDataCollectionEnabled(enabled);
3642 }
3643
3644 - (BOOL)_scrollPerformanceDataCollectionEnabled
3645 {
3646     return _page->scrollPerformanceDataCollectionEnabled();
3647 }
3648
3649 - (NSArray *)_scrollPerformanceData
3650 {
3651 #if PLATFORM(IOS)
3652     if (WebKit::RemoteLayerTreeScrollingPerformanceData* scrollPerfData = _page->scrollingPerformanceData())
3653         return scrollPerfData->data();
3654 #endif
3655     return nil;
3656 }
3657
3658 #pragma mark media playback restrictions
3659
3660 - (BOOL)_allowsMediaDocumentInlinePlayback
3661 {
3662 #if PLATFORM(IOS)
3663     return _page->allowsMediaDocumentInlinePlayback();
3664 #else
3665     return NO;
3666 #endif
3667 }
3668
3669 - (void)_setAllowsMediaDocumentInlinePlayback:(BOOL)flag
3670 {
3671 #if PLATFORM(IOS)
3672     _page->setAllowsMediaDocumentInlinePlayback(flag);
3673 #endif
3674 }
3675
3676 - (BOOL)_webProcessIsResponsive
3677 {
3678     return _page->process().responsivenessTimer().isResponsive();
3679 }
3680
3681 #pragma mark iOS-specific methods
3682
3683 #if PLATFORM(IOS)
3684
3685 - (CGSize)_minimumLayoutSizeOverride
3686 {
3687     ASSERT(_overridesMinimumLayoutSize);
3688     return _minimumLayoutSizeOverride;
3689 }
3690
3691 - (void)_setMinimumLayoutSizeOverride:(CGSize)minimumLayoutSizeOverride
3692 {
3693     _overridesMinimumLayoutSize = YES;
3694     if (CGSizeEqualToSize(_minimumLayoutSizeOverride, minimumLayoutSizeOverride))
3695         return;
3696
3697     _minimumLayoutSizeOverride = minimumLayoutSizeOverride;
3698     if (_dynamicViewportUpdateMode == DynamicViewportUpdateMode::NotResizing)
3699         _page->setViewportConfigurationMinimumLayoutSize(WebCore::FloatSize(minimumLayoutSizeOverride));
3700 }
3701
3702 - (UIEdgeInsets)_obscuredInsets
3703 {
3704     return _obscuredInsets;
3705 }
3706
3707 - (void)_setObscuredInsets:(UIEdgeInsets)obscuredInsets
3708 {
3709     ASSERT(obscuredInsets.top >= 0);
3710     ASSERT(obscuredInsets.left >= 0);
3711     ASSERT(obscuredInsets.bottom >= 0);
3712     ASSERT(obscuredInsets.right >= 0);
3713     
3714     _haveSetObscuredInsets = YES;
3715
3716     if (UIEdgeInsetsEqualToEdgeInsets(_obscuredInsets, obscuredInsets))
3717         return;
3718
3719     _obscuredInsets = obscuredInsets;
3720
3721     [self _updateVisibleContentRects];
3722 }
3723
3724 - (void)_setInterfaceOrientationOverride:(UIInterfaceOrientation)interfaceOrientation
3725 {
3726     if (!_overridesInterfaceOrientation)
3727         [[NSNotificationCenter defaultCenter] removeObserver:self name:UIWindowDidRotateNotification object:nil];
3728
3729     _overridesInterfaceOrientation = YES;
3730
3731     if (interfaceOrientation == _interfaceOrientationOverride)
3732         return;
3733
3734     _interfaceOrientationOverride = interfaceOrientation;
3735
3736     if (_dynamicViewportUpdateMode == DynamicViewportUpdateMode::NotResizing)
3737         _page->setDeviceOrientation(deviceOrientationForUIInterfaceOrientation(_interfaceOrientationOverride));
3738 }
3739
3740 - (UIInterfaceOrientation)_interfaceOrientationOverride
3741 {
3742     ASSERT(_overridesInterfaceOrientation);
3743     return _interfaceOrientationOverride;
3744 }
3745
3746 - (CGSize)_maximumUnobscuredSizeOverride
3747 {
3748     ASSERT(_overridesMaximumUnobscuredSize);
3749     return _maximumUnobscuredSizeOverride;
3750 }
3751
3752 - (void)_setMaximumUnobscuredSizeOverride:(CGSize)size
3753 {
3754     ASSERT(size.width <= self.bounds.size.width && size.height <= self.bounds.size.height);
3755     _overridesMaximumUnobscuredSize = YES;
3756     if (CGSizeEqualToSize(_maximumUnobscuredSizeOverride, size))
3757         return;
3758
3759     _maximumUnobscuredSizeOverride = size;
3760     if (_dynamicViewportUpdateMode == DynamicViewportUpdateMode::NotResizing)
3761         _page->setMaximumUnobscuredSize(WebCore::FloatSize(size));
3762 }
3763
3764 - (void)_setBackgroundExtendsBeyondPage:(BOOL)backgroundExtends
3765 {
3766     _page->setBackgroundExtendsBeyondPage(backgroundExtends);
3767 }
3768
3769 - (BOOL)_backgroundExtendsBeyondPage
3770 {
3771     return _page->backgroundExtendsBeyondPage();
3772 }
3773
3774 - (void)_setAllowsViewportShrinkToFit:(BOOL)allowShrinkToFit
3775 {
3776     _allowsViewportShrinkToFit = allowShrinkToFit;
3777 }
3778
3779 - (BOOL)_allowsViewportShrinkToFit
3780 {
3781     return _allowsViewportShrinkToFit;
3782 }
3783
3784 - (void)_beginInteractiveObscuredInsetsChange
3785 {
3786     ASSERT(!_isChangingObscuredInsetsInteractively);
3787     _isChangingObscuredInsetsInteractively = YES;
3788 }
3789
3790 - (void)_endInteractiveObscuredInsetsChange
3791 {
3792     ASSERT(_isChangingObscuredInsetsInteractively);
3793     _isChangingObscuredInsetsInteractively = NO;
3794     [self _updateVisibleContentRects];
3795 }
3796
3797 - (void)_hideContentUntilNextUpdate
3798 {
3799     if (auto* area = _page->drawingArea())
3800         area->hideContentUntilAnyUpdate();
3801 }
3802
3803 - (void)_beginAnimatedResizeWithUpdates:(void (^)(void))updateBlock
3804 {
3805     CGRect oldBounds = self.bounds;
3806     WebCore::FloatRect oldUnobscuredContentRect = _page->unobscuredContentRect();
3807
3808     if (_customContentView || !_hasCommittedLoadForMainFrame || CGRectIsEmpty(oldBounds) || oldUnobscuredContentRect.isEmpty()) {
3809         updateBlock();
3810         return;
3811     }
3812
3813     _dynamicViewportUpdateMode = DynamicViewportUpdateMode::ResizingWithAnimation;
3814
3815     WebCore::FloatSize oldMinimumLayoutSize = activeMinimumLayoutSize(self, oldBounds);
3816     WebCore::FloatSize oldMaximumUnobscuredSize = activeMaximumUnobscuredSize(self, oldBounds);
3817     int32_t oldOrientation = activeOrientation(self);
3818     UIEdgeInsets oldObscuredInsets = _obscuredInsets;
3819
3820     updateBlock();
3821
3822     CGRect newBounds = self.bounds;
3823     WebCore::FloatSize newMinimumLayoutSize = activeMinimumLayoutSize(self, newBounds);
3824     WebCore::FloatSize newMaximumUnobscuredSize = activeMaximumUnobscuredSize(self, newBounds);
3825     int32_t newOrientation = activeOrientation(self);
3826     UIEdgeInsets newObscuredInsets = _obscuredInsets;
3827     CGRect futureUnobscuredRectInSelfCoordinates = UIEdgeInsetsInsetRect(newBounds, _obscuredInsets);
3828     CGRect contentViewBounds = [_contentView bounds];
3829
3830     ASSERT_WITH_MESSAGE(!(_overridesMinimumLayoutSize && newMinimumLayoutSize.isEmpty()), "Clients controlling the layout size should maintain a valid layout size to minimize layouts.");
3831     if (CGRectIsEmpty(newBounds) || newMinimumLayoutSize.isEmpty() || CGRectIsEmpty(futureUnobscuredRectInSelfCoordinates) || CGRectIsEmpty(contentViewBounds)) {
3832         _dynamicViewportUpdateMode = DynamicViewportUpdateMode::NotResizing;
3833         [self _frameOrBoundsChanged];
3834         if (_overridesMinimumLayoutSize)
3835             _page->setViewportConfigurationMinimumLayoutSize(WebCore::FloatSize(newMinimumLayoutSize));
3836         if (_overridesMaximumUnobscuredSize)
3837             _page->setMaximumUnobscuredSize(WebCore::FloatSize(newMaximumUnobscuredSize));
3838         if (_overridesInterfaceOrientation)
3839             _page->setDeviceOrientation(newOrientation);
3840         [self _updateVisibleContentRects];
3841         return;
3842     }
3843
3844     if (CGRectEqualToRect(oldBounds, newBounds)
3845         && oldMinimumLayoutSize == newMinimumLayoutSize
3846         && oldMaximumUnobscuredSize == newMaximumUnobscuredSize
3847         && oldOrientation == newOrientation
3848         && UIEdgeInsetsEqualToEdgeInsets(oldObscuredInsets, newObscuredInsets)) {
3849         _dynamicViewportUpdateMode = DynamicViewportUpdateMode::NotResizing;
3850         [self _updateVisibleContentRects];
3851         return;
3852     }
3853
3854     _resizeAnimationTransformAdjustments = CATransform3DIdentity;
3855
3856     NSUInteger indexOfContentView = [[_scrollView subviews] indexOfObject:_contentView.get()];
3857     _resizeAnimationView = adoptNS([[UIView alloc] init]);
3858     [_scrollView insertSubview:_resizeAnimationView.get() atIndex:indexOfContentView];
3859     [_resizeAnimationView addSubview:_contentView.get()];
3860     [_resizeAnimationView addSubview:[_contentView unscaledView]];
3861
3862     CGSize contentSizeInContentViewCoordinates = contentViewBounds.size;
3863     [_scrollView setMinimumZoomScale:std::min(newMinimumLayoutSize.width() / contentSizeInContentViewCoordinates.width, [_scrollView minimumZoomScale])];
3864     [_scrollView setMaximumZoomScale:std::max(newMinimumLayoutSize.width() / contentSizeInContentViewCoordinates.width, [_scrollView maximumZoomScale])];
3865
3866     // Compute the new scale to keep the current content width in the scrollview.
3867     CGFloat oldWebViewWidthInContentViewCoordinates = oldUnobscuredContentRect.width();
3868     CGFloat visibleContentViewWidthInContentCoordinates = std::min(contentSizeInContentViewCoordinates.width, oldWebViewWidthInContentViewCoordinates);
3869     CGFloat targetScale = newMinimumLayoutSize.width() / visibleContentViewWidthInContentCoordinates;
3870     CGFloat resizeAnimationViewAnimationScale = targetScale / contentZoomScale(self);
3871     [_resizeAnimationView setTransform:CGAffineTransformMakeScale(resizeAnimationViewAnimationScale, resizeAnimationViewAnimationScale)];
3872
3873     // Compute a new position to keep the content centered.
3874     CGPoint originalContentCenter = oldUnobscuredContentRect.center();
3875     CGPoint originalContentCenterInSelfCoordinates = [self convertPoint:originalContentCenter fromView:_contentView.get()];
3876     CGPoint futureUnobscuredRectCenterInSelfCoordinates = CGPointMake(futureUnobscuredRectInSelfCoordinates.origin.x + futureUnobscuredRectInSelfCoordinates.size.width / 2, futureUnobscuredRectInSelfCoordinates.origin.y + futureUnobscuredRectInSelfCoordinates.size.height / 2);
3877
3878     CGPoint originalContentOffset = [_scrollView contentOffset];
3879     CGPoint contentOffset = originalContentOffset;
3880     contentOffset.x += (originalContentCenterInSelfCoordinates.x - futureUnobscuredRectCenterInSelfCoordinates.x);
3881     contentOffset.y += (originalContentCenterInSelfCoordinates.y - futureUnobscuredRectCenterInSelfCoordinates.y);
3882
3883     // Limit the new offset within the scrollview, we do not want to rubber band programmatically.
3884     CGSize futureContentSizeInSelfCoordinates = CGSizeMake(contentSizeInContentViewCoordinates.width * targetScale, contentSizeInContentViewCoordinates.height * targetScale);
3885     CGFloat maxHorizontalOffset = futureContentSizeInSelfCoordinates.width - newBounds.size.width + _obscuredInsets.right;
3886     contentOffset.x = std::min(contentOffset.x, maxHorizontalOffset);
3887     CGFloat maxVerticalOffset = futureContentSizeInSelfCoordinates.height - newBounds.size.height + _obscuredInsets.bottom;
3888     contentOffset.y = std::min(contentOffset.y, maxVerticalOffset);
3889
3890     contentOffset.x = std::max(contentOffset.x, -_obscuredInsets.left);
3891     contentOffset.y = std::max(contentOffset.y, -_obscuredInsets.top);
3892
3893     // Make the top/bottom edges "sticky" within 1 pixel.
3894     if (oldUnobscuredContentRect.maxY() > contentSizeInContentViewCoordinates.height - 1)
3895         contentOffset.y = maxVerticalOffset;
3896     if (oldUnobscuredContentRect.y() < 1)
3897         contentOffset.y = -_obscuredInsets.top;
3898
3899     // FIXME: if we have content centered after double tap to zoom, we should also try to keep that rect in view.
3900     [_scrollView setContentSize:roundScrollViewContentSize(*_page, futureContentSizeInSelfCoordinates)];
3901     [_scrollView setContentOffset:contentOffset];
3902
3903     CGRect visibleRectInContentCoordinates = [self convertRect:newBounds toView:_contentView.get()];
3904     CGRect unobscuredRectInContentCoordinates = [self convertRect:futureUnobscuredRectInSelfCoordinates toView:_contentView.get()];
3905
3906     _page->dynamicViewportSizeUpdate(newMinimumLayoutSize, newMaximumUnobscuredSize, visibleRectInContentCoordinates, unobscuredRectInContentCoordinates, futureUnobscuredRectInSelfCoordinates, targetScale, newOrientation);
3907     if (WebKit::DrawingAreaProxy* drawingArea = _page->drawingArea())
3908         drawingArea->setSize(WebCore::IntSize(newBounds.size), WebCore::IntSize(), WebCore::IntSize());
3909 }
3910
3911 - (void)_endAnimatedResize
3912 {
3913     if (_dynamicViewportUpdateMode == DynamicViewportUpdateMode::NotResizing)
3914         return;
3915
3916     _page->synchronizeDynamicViewportUpdate();
3917
3918     NSUInteger indexOfResizeAnimationView = [[_scrollView subviews] indexOfObject:_resizeAnimationView.get()];
3919     [_scrollView insertSubview:_contentView.get() atIndex:indexOfResizeAnimationView];
3920     [_scrollView insertSubview:[_contentView unscaledView] atIndex:indexOfResizeAnimationView + 1];
3921
3922     CALayer *contentViewLayer = [_contentView layer];
3923     CGFloat adjustmentScale = _resizeAnimationTransformAdjustments.m11;
3924     contentViewLayer.sublayerTransform = CATransform3DIdentity;
3925
3926     CGFloat animatingScaleTarget = [[_resizeAnimationView layer] transform].m11;
3927     CALayer *contentLayer = [_contentView layer];
3928     CATransform3D contentLayerTransform = contentLayer.transform;
3929     CGFloat currentScale = [[_resizeAnimationView layer] transform].m11 * contentLayerTransform.m11;
3930
3931     // We cannot use [UIScrollView setZoomScale:] directly because the UIScrollView delegate would get a callback with
3932     // an invalid contentOffset. The real content offset is only set below.
3933     // Since there is no public API for setting both the zoomScale and the contentOffset, we set the zoomScale manually
3934     // on the zoom layer and then only change the contentOffset.
3935     CGFloat adjustedScale = adjustmentScale * currentScale;
3936     contentLayerTransform.m11 = adjustedScale;
3937     contentLayerTransform.m22 = adjustedScale;
3938     contentLayer.transform = contentLayerTransform;
3939
3940     CGPoint currentScrollOffset = [_scrollView contentOffset];
3941     double horizontalScrollAdjustement = _resizeAnimationTransformAdjustments.m41 * animatingScaleTarget;
3942     double verticalScrollAdjustment = _resizeAnimationTransformAdjustments.m42 * animatingScaleTarget;
3943
3944     [_scrollView setContentSize:roundScrollViewContentSize(*_page, [_contentView frame].size)];
3945     [_scrollView setContentOffset:CGPointMake(currentScrollOffset.x - horizontalScrollAdjustement, currentScrollOffset.y - verticalScrollAdjustment)];
3946
3947     [_resizeAnimationView removeFromSuperview];
3948     _resizeAnimationView = nil;
3949     _resizeAnimationTransformAdjustments = CATransform3DIdentity;
3950
3951     _dynamicViewportUpdateMode = DynamicViewportUpdateMode::NotResizing;
3952     [_contentView setHidden:NO];
3953     [self _updateVisibleContentRects];
3954
3955     while (!_snapshotsDeferredDuringResize.isEmpty())
3956         _snapshotsDeferredDuringResize.takeLast()();
3957 }
3958
3959 - (void)_resizeWhileHidingContentWithUpdates:(void (^)(void))updateBlock
3960 {
3961     [self _beginAnimatedResizeWithUpdates:updateBlock];
3962     if (_dynamicViewportUpdateMode == DynamicViewportUpdateMode::ResizingWithAnimation) {
3963         [_contentView setHidden:YES];
3964         _dynamicViewportUpdateMode = DynamicViewportUpdateMode::ResizingWithDocumentHidden;
3965     }
3966 }
3967
3968 - (void)_setOverlaidAccessoryViewsInset:(CGSize)inset
3969 {
3970     [_customContentView web_setOverlaidAccessoryViewsInset:inset];
3971 }
3972
3973 - (void)_snapshotRect:(CGRect)rectInViewCoordinates intoImageOfWidth:(CGFloat)imageWidth completionHandler:(void(^)(CGImageRef))completionHandler
3974 {
3975     if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing) {
3976         // Defer snapshotting until after the current resize completes.
3977         void (^copiedCompletionHandler)(CGImageRef) = [completionHandler copy];
3978         RetainPtr<WKWebView> retainedSelf = self;
3979         _snapshotsDeferredDuringResize.append([retainedSelf, rectInViewCoordinates, imageWidth, copiedCompletionHandler] {
3980             [retainedSelf _snapshotRect:rectInViewCoordinates intoImageOfWidth:imageWidth completionHandler:copiedCompletionHandler];
3981             [copiedCompletionHandler release];
3982         });
3983         return;
3984     }
3985
3986     CGRect snapshotRectInContentCoordinates = [self convertRect:rectInViewCoordinates toView:self._currentContentView];
3987     CGFloat imageScale = imageWidth / snapshotRectInContentCoordinates.size.width;
3988     CGFloat imageHeight = imageScale * snapshotRectInContentCoordinates.size.height;
3989     CGSize imageSize = CGSizeMake(imageWidth, imageHeight);
3990
3991 #if USE(IOSURFACE)
3992     // If we are parented and thus won't incur a significant penalty from paging in tiles, snapshot the view hierarchy directly.
3993     if (CADisplay *display = self.window.screen._display) {
3994         auto surface = WebCore::IOSurface::create(WebCore::expandedIntSize(WebCore::FloatSize(imageSize)), WebCore::ColorSpaceSRGB);
3995         CGFloat imageScaleInViewCoordinates = imageWidth / rectInViewCoordinates.size.width;
3996         CATransform3D transform = CATransform3DMakeScale(imageScaleInViewCoordinates, imageScaleInViewCoordinates, 1);
3997         transform = CATransform3DTranslate(transform, -rectInViewCoordinates.origin.x, -rectInViewCoordinates.origin.y, 0);
3998         CARenderServerRenderDisplayLayerWithTransformAndTimeOffset(MACH_PORT_NULL, (CFStringRef)display.name, self.layer.context.contextId, reinterpret_cast<uint64_t>(self.layer), surface->surface(), 0, 0, &transform, 0);
3999         completionHandler(surface->createImage().get());
4000         return;
4001     }
4002 #endif
4003     
4004     if (_customContentView) {
4005         UIGraphicsBeginImageContextWithOptions(imageSize, YES, 1);
4006
4007         UIView *customContentView = _customContentView.get();
4008         [customContentView.backgroundColor set];
4009         UIRectFill(CGRectMake(0, 0, imageWidth, imageHeight));
4010
4011         CGContextRef context = UIGraphicsGetCurrentContext();
4012         CGContextTranslateCTM(context, -snapshotRectInContentCoordinates.origin.x * imageScale, -snapshotRectInContentCoordinates.origin.y * imageScale);
4013         CGContextScaleCTM(context, imageScale, imageScale);
4014         [customContentView.layer renderInContext:context];
4015
4016         completionHandler([UIGraphicsGetImageFromCurrentImageContext() CGImage]);
4017
4018         UIGraphicsEndImageContext();
4019         return;
4020     }
4021
4022     void(^copiedCompletionHandler)(CGImageRef) = [completionHandler copy];
4023     _page->takeSnapshot(WebCore::enclosingIntRect(snapshotRectInContentCoordinates), WebCore::expandedIntSize(WebCore::FloatSize(imageSize)), WebKit::SnapshotOptionsExcludeDeviceScaleFactor, [=](const WebKit::ShareableBitmap::Handle& imageHandle, WebKit::CallbackBase::Error) {
4024         if (imageHandle.isNull()) {
4025             copiedCompletionHandler(nullptr);
4026             [copiedCompletionHandler release];
4027             return;
4028         }
4029
4030         RefPtr<WebKit::ShareableBitmap> bitmap = WebKit::ShareableBitmap::create(imageHandle, WebKit::SharedMemory::Protection::ReadOnly);
4031
4032         if (!bitmap) {
4033             copiedCompletionHandler(nullptr);
4034             [copiedCompletionHandler release];
4035             return;
4036         }
4037
4038         RetainPtr<CGImageRef> cgImage;
4039         cgImage = bitmap->makeCGImage();
4040         copiedCompletionHandler(cgImage.get());
4041         [copiedCompletionHandler release];
4042     });
4043 }
4044
4045 - (void)_overrideLayoutParametersWithMinimumLayoutSize:(CGSize)minimumLayoutSize minimumLayoutSizeForMinimalUI:(CGSize)minimumLayoutSizeForMinimalUI maximumUnobscuredSizeOverride:(CGSize)maximumUnobscuredSizeOverride
4046 {
4047     UNUSED_PARAM(minimumLayoutSizeForMinimalUI);
4048     [self _overrideLayoutParametersWithMinimumLayoutSize:minimumLayoutSize maximumUnobscuredSizeOverride:maximumUnobscuredSizeOverride];
4049 }
4050
4051 - (void)_overrideLayoutParametersWithMinimumLayoutSize:(CGSize)minimumLayoutSize maximumUnobscuredSizeOverride:(CGSize)maximumUnobscuredSizeOverride
4052 {
4053     [self _setMinimumLayoutSizeOverride:minimumLayoutSize];
4054     [self _setMaximumUnobscuredSizeOverride:maximumUnobscuredSizeOverride];
4055 }
4056
4057 - (UIView *)_viewForFindUI
4058 {
4059     return [self viewForZoomingInScrollView:[self scrollView]];
4060 }
4061
4062 - (BOOL)_isDisplayingPDF
4063 {
4064     return [_customContentView isKindOfClass:[WKPDFView class]];
4065 }
4066
4067 - (NSData *)_dataForDisplayedPDF
4068 {
4069     if (![self _isDisplayingPDF])
4070         return nil;
4071     CGPDFDocumentRef pdfDocument = [(WKPDFView *)_customContentView pdfDocument];
4072     return [(NSData *)CGDataProviderCopyData(CGPDFDocumentGetDataProvider(pdfDocument)) autorelease];
4073 }
4074
4075 - (NSString *)_suggestedFilenameForDisplayedPDF
4076 {
4077     if (![self _isDisplayingPDF])
4078         return nil;
4079     return [(WKPDFView *)_customContentView.get() suggestedFilename];
4080 }
4081
4082 - (_WKWebViewPrintFormatter *)_webViewPrintFormatter
4083 {
4084     UIViewPrintFormatter *viewPrintFormatter = self.viewPrintFormatter;
4085     ASSERT([viewPrintFormatter isKindOfClass:[_WKWebViewPrintFormatter class]]);
4086     return (_WKWebViewPrintFormatter *)viewPrintFormatter;
4087 }
4088
4089 #else // #if PLATFORM(IOS)
4090
4091 #pragma mark - OS X-specific methods
4092
4093 - (BOOL)_drawsBackground
4094 {
4095     return _impl->drawsBackground();
4096 }
4097
4098 - (void)_setDrawsBackground:(BOOL)drawsBackground
4099 {
4100     _impl->setDrawsBackground(drawsBackground);
4101 }
4102
4103 #if WK_API_ENABLED
4104 - (NSView *)_inspectorAttachmentView
4105 {
4106     return _impl->inspectorAttachmentView();
4107 }
4108
4109 - (void)_setInspectorAttachmentView:(NSView *)newView
4110 {
4111     _impl->setInspectorAttachmentView(newView);
4112 }
4113 #endif
4114
4115 - (BOOL)_windowOcclusionDetectionEnabled
4116 {
4117     return _impl->windowOcclusionDetectionEnabled();
4118 }
4119
4120 - (void)_setWindowOcclusionDetectionEnabled:(BOOL)enabled
4121 {
4122     _impl->setWindowOcclusionDetectionEnabled(enabled);
4123 }
4124
4125 - (void)_setOverrideDeviceScaleFactor:(CGFloat)deviceScaleFactor
4126 {
4127     _impl->setOverrideDeviceScaleFactor(deviceScaleFactor);
4128 }
4129
4130 - (CGFloat)_overrideDeviceScaleFactor
4131 {
4132     return _impl->overrideDeviceScaleFactor();
4133 }
4134
4135 - (void)_setTopContentInset:(CGFloat)contentInset
4136 {
4137     return _impl->setTopContentInset(contentInset);
4138 }
4139
4140 - (CGFloat)_topContentInset
4141 {
4142     return _impl->topContentInset();
4143 }
4144
4145 - (NSColor *)_pageExtendedBackgroundColor
4146 {
4147     return _impl->pageExtendedBackgroundColor();
4148 }
4149
4150 - (id)_immediateActionAnimationControllerForHitTestResult:(_WKHitTestResult *)hitTestResult withType:(_WKImmediateActionType)type userData:(id<NSSecureCoding>)userData
4151 {
4152     return nil;
4153 }
4154
4155 - (void)_setAutomaticallyAdjustsContentInsets:(BOOL)automaticallyAdjustsContentInsets
4156 {
4157     _impl->setAutomaticallyAdjustsContentInsets(automaticallyAdjustsContentInsets);
4158 }
4159
4160 - (BOOL)_automaticallyAdjustsContentInsets