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