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