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