Ads in NYTimes app are shifted downwards by the scroll view's top content inset
[WebKit-https.git] / Source / WebKit / UIProcess / API / Cocoa / WKWebView.mm
1 /*
2  * Copyright (C) 2014-2018 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 "FrontBoardServicesSPI.h"
38 #import "FullscreenClient.h"
39 #import "IconLoadingDelegate.h"
40 #import "LegacySessionStateCoding.h"
41 #import "Logging.h"
42 #import "NavigationState.h"
43 #import "ObjCObjectGraph.h"
44 #import "RemoteLayerTreeScrollingPerformanceData.h"
45 #import "RemoteLayerTreeTransaction.h"
46 #import "RemoteObjectRegistry.h"
47 #import "RemoteObjectRegistryMessages.h"
48 #import "SandboxUtilities.h"
49 #import "UIDelegate.h"
50 #import "VersionChecks.h"
51 #import "ViewGestureController.h"
52 #import "ViewSnapshotStore.h"
53 #import "WKBackForwardListInternal.h"
54 #import "WKBackForwardListItemInternal.h"
55 #import "WKBrowsingContextHandleInternal.h"
56 #import "WKDragDestinationAction.h"
57 #import "WKErrorInternal.h"
58 #import "WKHistoryDelegatePrivate.h"
59 #import "WKLayoutMode.h"
60 #import "WKNSData.h"
61 #import "WKNSURLExtras.h"
62 #import "WKNavigationDelegate.h"
63 #import "WKNavigationInternal.h"
64 #import "WKPreferencesInternal.h"
65 #import "WKProcessPoolInternal.h"
66 #import "WKSharedAPICast.h"
67 #import "WKSnapshotConfiguration.h"
68 #import "WKUIDelegate.h"
69 #import "WKUIDelegatePrivate.h"
70 #import "WKUserContentControllerInternal.h"
71 #import "WKWebViewConfigurationInternal.h"
72 #import "WKWebViewContentProvider.h"
73 #import "WKWebsiteDataStoreInternal.h"
74 #import "WebBackForwardList.h"
75 #import "WebCertificateInfo.h"
76 #import "WebFullScreenManagerProxy.h"
77 #import "WebPageGroup.h"
78 #import "WebPageProxy.h"
79 #import "WebPreferencesKeys.h"
80 #import "WebProcessPool.h"
81 #import "WebProcessProxy.h"
82 #import "WebURLSchemeHandlerCocoa.h"
83 #import "WebViewImpl.h"
84 #import "_WKActivatedElementInfoInternal.h"
85 #import "_WKDiagnosticLoggingDelegate.h"
86 #import "_WKFindDelegate.h"
87 #import "_WKFrameHandleInternal.h"
88 #import "_WKFullscreenDelegate.h"
89 #import "_WKHitTestResultInternal.h"
90 #import "_WKInputDelegate.h"
91 #import "_WKRemoteObjectRegistryInternal.h"
92 #import "_WKSessionStateInternal.h"
93 #import "_WKVisitedLinkStoreInternal.h"
94 #import "_WKWebsitePoliciesInternal.h"
95 #import <WebCore/AttachmentTypes.h>
96 #import <WebCore/GraphicsContextCG.h>
97 #import <WebCore/IOSurface.h>
98 #import <WebCore/JSDOMBinding.h>
99 #import <WebCore/JSDOMExceptionHandling.h>
100 #import <WebCore/MIMETypeRegistry.h>
101 #import <WebCore/PlatformScreen.h>
102 #import <WebCore/RuntimeApplicationChecks.h>
103 #import <WebCore/SQLiteDatabaseTracker.h>
104 #import <WebCore/SchemeRegistry.h>
105 #import <WebCore/Settings.h>
106 #import <WebCore/SharedBuffer.h>
107 #import <WebCore/ValidationBubble.h>
108 #import <WebCore/ViewportArguments.h>
109 #import <WebCore/WritingMode.h>
110 #import <pal/spi/cocoa/NSKeyedArchiverSPI.h>
111 #import <pal/spi/mac/NSTextFinderSPI.h>
112 #import <wtf/BlockPtr.h>
113 #import <wtf/HashMap.h>
114 #import <wtf/MathExtras.h>
115 #import <wtf/NeverDestroyed.h>
116 #import <wtf/Optional.h>
117 #import <wtf/RetainPtr.h>
118 #import <wtf/SetForScope.h>
119 #import <wtf/UUID.h>
120 #import <wtf/spi/darwin/dyldSPI.h>
121 #import <wtf/text/TextStream.h>
122
123 #if ENABLE(APPLICATION_MANIFEST)
124 #import "_WKApplicationManifestInternal.h"
125 #endif
126
127 #if ENABLE(DATA_DETECTION)
128 #import "WKDataDetectorTypesInternal.h"
129 #endif
130
131 #if PLATFORM(IOS)
132 #import "InteractionInformationAtPosition.h"
133 #import "InteractionInformationRequest.h"
134 #import "ProcessThrottler.h"
135 #import "RemoteLayerTreeDrawingAreaProxy.h"
136 #import "RemoteScrollingCoordinatorProxy.h"
137 #import "UIKitSPI.h"
138 #import "VideoFullscreenManagerProxy.h"
139 #import "WKContentViewInteraction.h"
140 #import "WKPasswordView.h"
141 #import "WKScrollView.h"
142 #import "WKWebViewContentProviderRegistry.h"
143 #import "_WKWebViewPrintFormatter.h"
144 #import <UIKit/UIApplication.h>
145 #import <WebCore/FrameLoaderTypes.h>
146 #import <WebCore/InspectorOverlay.h>
147 #import <WebCore/ScrollableArea.h>
148 #import <WebCore/WebBackgroundTaskController.h>
149 #import <WebCore/WebSQLiteDatabaseTrackerClient.h>
150 #import <pal/spi/cg/CoreGraphicsSPI.h>
151 #import <pal/spi/cocoa/QuartzCoreSPI.h>
152
153 #define RELEASE_LOG_IF_ALLOWED(...) RELEASE_LOG_IF(_page && _page->isAlwaysOnLoggingAllowed(), ViewState, __VA_ARGS__)
154
155 @interface UIScrollView (UIScrollViewInternal)
156 - (void)_adjustForAutomaticKeyboardInfo:(NSDictionary*)info animated:(BOOL)animated lastAdjustment:(CGFloat*)lastAdjustment;
157 - (BOOL)_isScrollingToTop;
158 - (CGPoint)_animatedTargetOffset;
159 @end
160
161 @interface UIPeripheralHost(UIKitInternal)
162 - (CGFloat)getVerticalOverlapForView:(UIView *)view usingKeyboardInfo:(NSDictionary *)info;
163 @end
164
165 @interface UIView (UIViewInternal)
166 - (UIViewController *)_viewControllerForAncestor;
167 @end
168
169 @interface UIWindow (UIWindowInternal)
170 - (BOOL)_isHostedInAnotherProcess;
171 @end
172
173 @interface UIViewController (UIViewControllerInternal)
174 - (UIViewController *)_rootAncestorViewController;
175 - (UIViewController *)_viewControllerForSupportedInterfaceOrientations;
176 @end
177
178 enum class DynamicViewportUpdateMode {
179     NotResizing,
180     ResizingWithAnimation,
181     ResizingWithDocumentHidden,
182 };
183
184 #endif // PLATFORM(IOS)
185
186 #if PLATFORM(IOS)
187 static const uint32_t firstSDKVersionWithLinkPreviewEnabledByDefault = 0xA0000;
188 #endif
189
190 #if PLATFORM(MAC)
191 #import "WKTextFinderClient.h"
192 #import "WKViewInternal.h"
193 #import <WebCore/ColorMac.h>
194
195 @interface WKWebView () <WebViewImplDelegate, NSTextInputClient>
196 @end
197
198 #if HAVE(TOUCH_BAR)
199 @interface WKWebView () <NSTouchBarProvider>
200 @end
201 #endif // HAVE(TOUCH_BAR)
202 #endif // PLATFORM(MAC)
203
204 #if ENABLE(ACCESSIBILITY_EVENTS)
205 #include <wtf/darwin/WeakLinking.h>
206 #if __has_include(<AccessibilitySupport.h>)
207 #include <AccessibilitySupport.h>
208 #else
209 extern "C" {
210 CFStringRef kAXSWebAccessibilityEventsEnabledNotification;
211 Boolean _AXSWebAccessibilityEventsEnabled();
212 }
213 #endif
214 #endif
215
216 static HashMap<WebKit::WebPageProxy*, WKWebView *>& pageToViewMap()
217 {
218     static NeverDestroyed<HashMap<WebKit::WebPageProxy*, WKWebView *>> map;
219     return map;
220 }
221
222 WKWebView* fromWebPageProxy(WebKit::WebPageProxy& page)
223 {
224     return pageToViewMap().get(&page);
225 }
226
227 #if PLATFORM(MAC)
228 static _WKOverlayScrollbarStyle toAPIScrollbarStyle(std::optional<WebCore::ScrollbarOverlayStyle> coreScrollbarStyle)
229 {
230     if (!coreScrollbarStyle)
231         return _WKOverlayScrollbarStyleAutomatic;
232     
233     switch (coreScrollbarStyle.value()) {
234     case WebCore::ScrollbarOverlayStyleDark:
235         return _WKOverlayScrollbarStyleDark;
236     case WebCore::ScrollbarOverlayStyleLight:
237         return _WKOverlayScrollbarStyleLight;
238     case WebCore::ScrollbarOverlayStyleDefault:
239         return _WKOverlayScrollbarStyleDefault;
240     }
241     ASSERT_NOT_REACHED();
242     return _WKOverlayScrollbarStyleAutomatic;
243 }
244
245 static std::optional<WebCore::ScrollbarOverlayStyle> toCoreScrollbarStyle(_WKOverlayScrollbarStyle scrollbarStyle)
246 {
247     switch (scrollbarStyle) {
248     case _WKOverlayScrollbarStyleDark:
249         return WebCore::ScrollbarOverlayStyleDark;
250     case _WKOverlayScrollbarStyleLight:
251         return WebCore::ScrollbarOverlayStyleLight;
252     case _WKOverlayScrollbarStyleDefault:
253         return WebCore::ScrollbarOverlayStyleDefault;
254     case _WKOverlayScrollbarStyleAutomatic:
255         break;
256     }
257     return std::nullopt;
258 }
259 #endif
260
261 @implementation WKWebView {
262     std::unique_ptr<WebKit::NavigationState> _navigationState;
263     std::unique_ptr<WebKit::UIDelegate> _uiDelegate;
264     std::unique_ptr<WebKit::IconLoadingDelegate> _iconLoadingDelegate;
265
266     _WKRenderingProgressEvents _observedRenderingProgressEvents;
267
268     WebKit::WeakObjCPtr<id <_WKInputDelegate>> _inputDelegate;
269
270 #if PLATFORM(IOS)
271     RetainPtr<_WKRemoteObjectRegistry> _remoteObjectRegistry;
272
273     RetainPtr<WKScrollView> _scrollView;
274     RetainPtr<WKContentView> _contentView;
275
276 #if ENABLE(FULLSCREEN_API)
277     RetainPtr<WKFullScreenWindowController> _fullScreenWindowController;
278 #endif
279
280     BOOL _overridesViewLayoutSize;
281     CGSize _viewLayoutSizeOverride;
282     std::optional<WebCore::FloatSize> _lastSentViewLayoutSize;
283     BOOL _overridesMaximumUnobscuredSize;
284     CGSize _maximumUnobscuredSizeOverride;
285     std::optional<WebCore::FloatSize> _lastSentMaximumUnobscuredSize;
286     CGRect _inputViewBounds;
287     CGFloat _viewportMetaTagWidth;
288     BOOL _viewportMetaTagWidthWasExplicit;
289     BOOL _viewportMetaTagCameFromImageDocument;
290     CGFloat _initialScaleFactor;
291     BOOL _fastClickingIsDisabled;
292
293     BOOL _allowsLinkPreview;
294
295     UIEdgeInsets _obscuredInsets;
296     BOOL _haveSetObscuredInsets;
297     BOOL _isChangingObscuredInsetsInteractively;
298
299     UIEdgeInsets _unobscuredSafeAreaInsets;
300     BOOL _haveSetUnobscuredSafeAreaInsets;
301     BOOL _avoidsUnsafeArea;
302     UIRectEdge _obscuredInsetEdgesAffectedBySafeArea;
303
304     UIInterfaceOrientation _interfaceOrientationOverride;
305     BOOL _overridesInterfaceOrientation;
306     std::optional<int32_t> _lastSentDeviceOrientation;
307
308     BOOL _allowsViewportShrinkToFit;
309
310     BOOL _hasCommittedLoadForMainFrame;
311     BOOL _needsResetViewStateAfterCommitLoadForMainFrame;
312     uint64_t _firstPaintAfterCommitLoadTransactionID;
313     DynamicViewportUpdateMode _dynamicViewportUpdateMode;
314     CATransform3D _resizeAnimationTransformAdjustments;
315     std::optional<uint64_t> _resizeAnimationTransformTransactionID;
316     RetainPtr<UIView> _resizeAnimationView;
317     CGFloat _lastAdjustmentForScroller;
318     std::optional<CGRect> _frozenVisibleContentRect;
319     std::optional<CGRect> _frozenUnobscuredContentRect;
320
321     BOOL _commitDidRestoreScrollPosition;
322     std::optional<WebCore::FloatPoint> _scrollOffsetToRestore;
323     WebCore::FloatBoxExtent _obscuredInsetsWhenSaved;
324
325     std::optional<WebCore::FloatPoint> _unobscuredCenterToRestore;
326     std::optional<uint64_t> _firstTransactionIDAfterPageRestore;
327     double _scaleToRestore;
328
329     std::unique_ptr<WebKit::ViewGestureController> _gestureController;
330     BOOL _allowsBackForwardNavigationGestures;
331
332     RetainPtr<UIView <WKWebViewContentProvider>> _customContentView;
333     RetainPtr<UIView> _customContentFixedOverlayView;
334
335     RetainPtr<NSTimer> _enclosingScrollViewScrollTimer;
336     BOOL _didScrollSinceLastTimerFire;
337
338     WebCore::Color _scrollViewBackgroundColor;
339
340     // This value tracks the current adjustment added to the bottom inset due to the keyboard sliding out from the bottom
341     // when computing obscured content insets. This is used when updating the visible content rects where we should not
342     // include this adjustment.
343     CGFloat _totalScrollViewBottomInsetAdjustmentForKeyboard;
344     BOOL _currentlyAdjustingScrollViewInsetsForKeyboard;
345
346     BOOL _delayUpdateVisibleContentRects;
347     BOOL _hadDelayedUpdateVisibleContentRects;
348
349     int _activeAnimatedResizeCount;
350
351     Vector<WTF::Function<void ()>> _snapshotsDeferredDuringResize;
352     RetainPtr<NSMutableArray> _stableStatePresentationUpdateCallbacks;
353
354     RetainPtr<WKPasswordView> _passwordView;
355
356     BOOL _hasScheduledVisibleRectUpdate;
357     BOOL _visibleContentRectUpdateScheduledFromScrollViewInStableState;
358     Vector<BlockPtr<void ()>> _visibleContentRectUpdateCallbacks;
359 #endif
360 #if PLATFORM(MAC)
361     std::unique_ptr<WebKit::WebViewImpl> _impl;
362     RetainPtr<WKTextFinderClient> _textFinderClient;
363 #endif
364
365 #if PLATFORM(IOS)
366     _WKDragInteractionPolicy _dragInteractionPolicy;
367 #endif
368 }
369
370 - (instancetype)initWithFrame:(CGRect)frame
371 {
372     return [self initWithFrame:frame configuration:adoptNS([[WKWebViewConfiguration alloc] init]).get()];
373 }
374
375 - (BOOL)_isValid
376 {
377     return _page && _page->isValid();
378 }
379
380 #if PLATFORM(IOS)
381 static int32_t deviceOrientationForUIInterfaceOrientation(UIInterfaceOrientation orientation)
382 {
383     switch (orientation) {
384     case UIInterfaceOrientationUnknown:
385     case UIInterfaceOrientationPortrait:
386         return 0;
387     case UIInterfaceOrientationPortraitUpsideDown:
388         return 180;
389     case UIInterfaceOrientationLandscapeLeft:
390         return -90;
391     case UIInterfaceOrientationLandscapeRight:
392         return 90;
393     }
394 }
395
396 static int32_t deviceOrientation()
397 {
398     return deviceOrientationForUIInterfaceOrientation([[UIApplication sharedApplication] statusBarOrientation]);
399 }
400
401 - (BOOL)_isShowingVideoPictureInPicture
402 {
403 #if !HAVE(AVKIT)
404     return false;
405 #else
406     if (!_page || !_page->videoFullscreenManager())
407         return false;
408
409     return _page->videoFullscreenManager()->hasMode(WebCore::HTMLMediaElementEnums::VideoFullscreenModePictureInPicture);
410 #endif
411 }
412
413 - (BOOL)_mayAutomaticallyShowVideoPictureInPicture
414 {
415 #if !HAVE(AVKIT)
416     return false;
417 #else
418     if (!_page || !_page->videoFullscreenManager())
419         return false;
420
421     return _page->videoFullscreenManager()->mayAutomaticallyShowVideoPictureInPicture();
422 #endif
423 }
424
425 static bool shouldAllowPictureInPictureMediaPlayback()
426 {
427     static bool shouldAllowPictureInPictureMediaPlayback = dyld_get_program_sdk_version() >= DYLD_IOS_VERSION_9_0;
428     return shouldAllowPictureInPictureMediaPlayback;
429 }
430
431 static bool shouldAllowSettingAnyXHRHeaderFromFileURLs()
432 {
433     static bool shouldAllowSettingAnyXHRHeaderFromFileURLs = (WebCore::IOSApplication::isCardiogram() || WebCore::IOSApplication::isNike()) && !linkedOnOrAfter(WebKit::SDKVersion::FirstThatDisallowsSettingAnyXHRHeaderFromFileURLs);
434     return shouldAllowSettingAnyXHRHeaderFromFileURLs;
435 }
436
437 #endif
438
439 static bool shouldRequireUserGestureToLoadVideo()
440 {
441 #if PLATFORM(IOS)
442     static bool shouldRequireUserGestureToLoadVideo = dyld_get_program_sdk_version() >= DYLD_IOS_VERSION_10_0;
443     return shouldRequireUserGestureToLoadVideo;
444 #else
445     return false;
446 #endif
447 }
448
449 #if PLATFORM(MAC)
450 static uint32_t convertUserInterfaceDirectionPolicy(WKUserInterfaceDirectionPolicy policy)
451 {
452     switch (policy) {
453     case WKUserInterfaceDirectionPolicyContent:
454         return static_cast<uint32_t>(WebCore::UserInterfaceDirectionPolicy::Content);
455     case WKUserInterfaceDirectionPolicySystem:
456         return static_cast<uint32_t>(WebCore::UserInterfaceDirectionPolicy::System);
457     }
458     return static_cast<uint32_t>(WebCore::UserInterfaceDirectionPolicy::Content);
459 }
460
461 static uint32_t convertSystemLayoutDirection(NSUserInterfaceLayoutDirection direction)
462 {
463     switch (direction) {
464     case NSUserInterfaceLayoutDirectionLeftToRight:
465         return static_cast<uint32_t>(WebCore::UserInterfaceLayoutDirection::LTR);
466     case NSUserInterfaceLayoutDirectionRightToLeft:
467         return static_cast<uint32_t>(WebCore::UserInterfaceLayoutDirection::RTL);
468     }
469     return static_cast<uint32_t>(WebCore::UserInterfaceLayoutDirection::LTR);
470 }
471
472 #if USE(APPLE_INTERNAL_SDK)
473 #import <WebKitAdditions/WebViewAndWKWebViewAdditions.mm>
474 #else
475 - (bool)_defaultAppearance { return true; }
476 #endif
477
478 #endif // PLATFORM(MAC)
479
480 static void validate(WKWebViewConfiguration *configuration)
481 {
482     if (!configuration.processPool)
483         [NSException raise:NSInvalidArgumentException format:@"configuration.processPool is nil"];
484     
485     if (!configuration.preferences)
486         [NSException raise:NSInvalidArgumentException format:@"configuration.preferences is nil"];
487     
488     if (!configuration.userContentController)
489         [NSException raise:NSInvalidArgumentException format:@"configuration.userContentController is nil"];
490     
491     if (!configuration.websiteDataStore)
492         [NSException raise:NSInvalidArgumentException format:@"configuration.websiteDataStore is nil"];
493     
494     if (!configuration._visitedLinkStore)
495         [NSException raise:NSInvalidArgumentException format:@"configuration._visitedLinkStore is nil"];
496     
497 #if PLATFORM(IOS)
498     if (!configuration._contentProviderRegistry)
499         [NSException raise:NSInvalidArgumentException format:@"configuration._contentProviderRegistry is nil"];
500 #endif
501 }
502
503 - (void)_initializeWithConfiguration:(WKWebViewConfiguration *)configuration
504 {
505     if (!configuration)
506         [NSException raise:NSInvalidArgumentException format:@"Configuration cannot be nil"];
507
508     _configuration = adoptNS([configuration copy]);
509
510     if (WKWebView *relatedWebView = [_configuration _relatedWebView]) {
511         WKProcessPool *processPool = [_configuration processPool];
512         WKProcessPool *relatedWebViewProcessPool = [relatedWebView->_configuration processPool];
513         if (processPool && processPool != relatedWebViewProcessPool)
514             [NSException raise:NSInvalidArgumentException format:@"Related web view %@ has process pool %@ but configuration specifies a different process pool %@", relatedWebView, relatedWebViewProcessPool, configuration.processPool];
515
516         [_configuration setProcessPool:relatedWebViewProcessPool];
517     }
518
519     validate(_configuration.get());
520
521     WebKit::WebProcessPool& processPool = *[_configuration processPool]->_processPool;
522     processPool.setResourceLoadStatisticsEnabled(configuration.websiteDataStore._resourceLoadStatisticsEnabled);
523
524     auto pageConfiguration = API::PageConfiguration::create();
525
526     pageConfiguration->setProcessPool(&processPool);
527     pageConfiguration->setPreferences([_configuration preferences]->_preferences.get());
528     if (WKWebView *relatedWebView = [_configuration _relatedWebView])
529         pageConfiguration->setRelatedPage(relatedWebView->_page.get());
530
531     pageConfiguration->setUserContentController([_configuration userContentController]->_userContentControllerProxy.get());
532     pageConfiguration->setVisitedLinkStore([_configuration _visitedLinkStore]->_visitedLinkStore.get());
533     pageConfiguration->setWebsiteDataStore([_configuration websiteDataStore]->_websiteDataStore.get());
534     pageConfiguration->setTreatsSHA1SignedCertificatesAsInsecure([_configuration _treatsSHA1SignedCertificatesAsInsecure]);
535
536     if (NSString *overrideContentSecurityPolicy = configuration._overrideContentSecurityPolicy)
537         pageConfiguration->setOverrideContentSecurityPolicy(overrideContentSecurityPolicy);
538
539 #if PLATFORM(MAC)
540     if (auto pageGroup = WebKit::toImpl([configuration _pageGroup])) {
541         pageConfiguration->setPageGroup(pageGroup);
542         pageConfiguration->setUserContentController(&pageGroup->userContentController());
543     } else
544 #endif
545     {
546         NSString *groupIdentifier = configuration._groupIdentifier;
547         if (groupIdentifier.length)
548             pageConfiguration->setPageGroup(WebKit::WebPageGroup::create(configuration._groupIdentifier).ptr());
549     }
550
551     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::suppressesIncrementalRenderingKey(), WebKit::WebPreferencesStore::Value(!![_configuration suppressesIncrementalRendering]));
552
553     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::shouldRespectImageOrientationKey(), WebKit::WebPreferencesStore::Value(!![_configuration _respectsImageOrientation]));
554     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::shouldPrintBackgroundsKey(), WebKit::WebPreferencesStore::Value(!![_configuration _printsBackgrounds]));
555     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::incrementalRenderingSuppressionTimeoutKey(), WebKit::WebPreferencesStore::Value([_configuration _incrementalRenderingSuppressionTimeout]));
556     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::javaScriptMarkupEnabledKey(), WebKit::WebPreferencesStore::Value(!![_configuration _allowsJavaScriptMarkup]));
557     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::shouldConvertPositionStyleOnCopyKey(), WebKit::WebPreferencesStore::Value(!![_configuration _convertsPositionStyleOnCopy]));
558     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::httpEquivEnabledKey(), WebKit::WebPreferencesStore::Value(!![_configuration _allowsMetaRefresh]));
559     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::allowUniversalAccessFromFileURLsKey(), WebKit::WebPreferencesStore::Value(!![_configuration _allowUniversalAccessFromFileURLs]));
560     pageConfiguration->setInitialCapitalizationEnabled([_configuration _initialCapitalizationEnabled]);
561     pageConfiguration->setWaitsForPaintAfterViewDidMoveToWindow([_configuration _waitsForPaintAfterViewDidMoveToWindow]);
562     pageConfiguration->setControlledByAutomation([_configuration _isControlledByAutomation]);
563
564 #if ENABLE(APPLICATION_MANIFEST)
565     pageConfiguration->setApplicationManifest([_configuration _applicationManifest] ? [configuration _applicationManifest]->_applicationManifest.get() : nullptr);
566 #endif
567
568 #if PLATFORM(MAC)
569     if (auto cpuLimit = [_configuration _cpuLimit])
570         pageConfiguration->setCPULimit(cpuLimit);
571     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::showsURLsInToolTipsEnabledKey(), WebKit::WebPreferencesStore::Value(!![_configuration _showsURLsInToolTips]));
572     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::serviceControlsEnabledKey(), WebKit::WebPreferencesStore::Value(!![_configuration _serviceControlsEnabled]));
573     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::imageControlsEnabledKey(), WebKit::WebPreferencesStore::Value(!![_configuration _imageControlsEnabled]));
574
575     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::userInterfaceDirectionPolicyKey(), WebKit::WebPreferencesStore::Value(convertUserInterfaceDirectionPolicy([_configuration userInterfaceDirectionPolicy])));
576     // We are in the View's initialization routine, so our client hasn't had time to set our user interface direction.
577     // Therefore, according to the docs[1], "this property contains the value reported by the app's userInterfaceLayoutDirection property."
578     // [1] http://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSView_Class/index.html#//apple_ref/doc/uid/20000014-SW222
579     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::systemLayoutDirectionKey(), WebKit::WebPreferencesStore::Value(convertSystemLayoutDirection(self.userInterfaceLayoutDirection)));
580 #endif
581
582 #if PLATFORM(IOS)
583     pageConfiguration->setAlwaysRunsAtForegroundPriority([_configuration _alwaysRunsAtForegroundPriority]);
584
585     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::allowsInlineMediaPlaybackKey(), WebKit::WebPreferencesStore::Value(!![_configuration allowsInlineMediaPlayback]));
586     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::allowsInlineMediaPlaybackAfterFullscreenKey(), WebKit::WebPreferencesStore::Value(!![_configuration _allowsInlineMediaPlaybackAfterFullscreen]));
587     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::inlineMediaPlaybackRequiresPlaysInlineAttributeKey(), WebKit::WebPreferencesStore::Value(!![_configuration _inlineMediaPlaybackRequiresPlaysInlineAttribute]));
588     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::allowsPictureInPictureMediaPlaybackKey(), WebKit::WebPreferencesStore::Value(!![_configuration allowsPictureInPictureMediaPlayback] && shouldAllowPictureInPictureMediaPlayback()));
589     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::userInterfaceDirectionPolicyKey(), WebKit::WebPreferencesStore::Value(static_cast<uint32_t>(WebCore::UserInterfaceDirectionPolicy::Content)));
590     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::systemLayoutDirectionKey(), WebKit::WebPreferencesStore::Value(static_cast<uint32_t>(WebCore::LTR)));
591     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::allowSettingAnyXHRHeaderFromFileURLsKey(), WebKit::WebPreferencesStore::Value(shouldAllowSettingAnyXHRHeaderFromFileURLs()));
592 #endif
593
594     WKAudiovisualMediaTypes mediaTypesRequiringUserGesture = [_configuration mediaTypesRequiringUserActionForPlayback];
595     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::requiresUserGestureForVideoPlaybackKey(), WebKit::WebPreferencesStore::Value((mediaTypesRequiringUserGesture & WKAudiovisualMediaTypeVideo) == WKAudiovisualMediaTypeVideo));
596     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::requiresUserGestureForAudioPlaybackKey(), WebKit::WebPreferencesStore::Value(((mediaTypesRequiringUserGesture & WKAudiovisualMediaTypeAudio) == WKAudiovisualMediaTypeAudio)));
597     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::requiresUserGestureToLoadVideoKey(), WebKit::WebPreferencesStore::Value(shouldRequireUserGestureToLoadVideo()));
598     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::mainContentUserGestureOverrideEnabledKey(), WebKit::WebPreferencesStore::Value(!![_configuration _mainContentUserGestureOverrideEnabled]));
599     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::invisibleAutoplayNotPermittedKey(), WebKit::WebPreferencesStore::Value(!![_configuration _invisibleAutoplayNotPermitted]));
600     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::mediaDataLoadsAutomaticallyKey(), WebKit::WebPreferencesStore::Value(!![_configuration _mediaDataLoadsAutomatically]));
601     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::attachmentElementEnabledKey(), WebKit::WebPreferencesStore::Value(!![_configuration _attachmentElementEnabled]));
602
603 #if ENABLE(DATA_DETECTION) && PLATFORM(IOS)
604     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::dataDetectorTypesKey(), WebKit::WebPreferencesStore::Value(static_cast<uint32_t>(fromWKDataDetectorTypes([_configuration dataDetectorTypes]))));
605 #endif
606 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
607     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::allowsAirPlayForMediaPlaybackKey(), WebKit::WebPreferencesStore::Value(!![_configuration allowsAirPlayForMediaPlayback]));
608 #endif
609
610 #if ENABLE(APPLE_PAY)
611     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::applePayEnabledKey(), WebKit::WebPreferencesStore::Value(!![_configuration _applePayEnabled]));
612 #endif
613
614     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::needsStorageAccessFromFileURLsQuirkKey(), WebKit::WebPreferencesStore::Value(!![_configuration _needsStorageAccessFromFileURLsQuirk]));
615     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::mediaContentTypesRequiringHardwareSupportKey(), WebKit::WebPreferencesStore::Value(String([_configuration _mediaContentTypesRequiringHardwareSupport])));
616     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::allowMediaContentTypesRequiringHardwareSupportAsFallbackKey(), WebKit::WebPreferencesStore::Value(!![_configuration _allowMediaContentTypesRequiringHardwareSupportAsFallback]));
617
618 #if ENABLE(LEGACY_ENCRYPTED_MEDIA)
619     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::legacyEncryptedMediaAPIEnabledKey(), WebKit::WebPreferencesStore::Value(!![_configuration _legacyEncryptedMediaAPIEnabled]));
620 #endif
621
622 #if PLATFORM(IOS) && ENABLE(SERVICE_WORKER)
623     if (!WebKit::processHasEntitlement(@"com.apple.developer.WebKit.ServiceWorkers"))
624         pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::serviceWorkersEnabledKey(), WebKit::WebPreferencesStore::Value(false));
625 #endif
626
627 #if PLATFORM(IOS)
628     CGRect bounds = self.bounds;
629     _scrollView = adoptNS([[WKScrollView alloc] initWithFrame:bounds]);
630     [_scrollView setInternalDelegate:self];
631     [_scrollView setBouncesZoom:YES];
632
633     _avoidsUnsafeArea = YES;
634     [self _updateScrollViewInsetAdjustmentBehavior];
635
636     [self addSubview:_scrollView.get()];
637
638     static uint32_t programSDKVersion = dyld_get_program_sdk_version();
639     _allowsLinkPreview = programSDKVersion >= firstSDKVersionWithLinkPreviewEnabledByDefault;
640
641     _contentView = adoptNS([[WKContentView alloc] initWithFrame:bounds processPool:processPool configuration:WTFMove(pageConfiguration) webView:self]);
642
643     _page = [_contentView page];
644     [self _dispatchSetDeviceOrientation:deviceOrientation()];
645     _page->setDrawsBackground(self.opaque);
646
647     [_contentView layer].anchorPoint = CGPointZero;
648     [_contentView setFrame:bounds];
649     [_scrollView addSubview:_contentView.get()];
650     [_scrollView addSubview:[_contentView unscaledView]];
651     [self _updateScrollViewBackground];
652     _obscuredInsetEdgesAffectedBySafeArea = UIRectEdgeTop | UIRectEdgeLeft | UIRectEdgeRight;
653
654     _viewportMetaTagWidth = WebCore::ViewportArguments::ValueAuto;
655     _initialScaleFactor = 1;
656
657     if (NSNumber *enabledValue = [[NSUserDefaults standardUserDefaults] objectForKey:@"WebKitFastClickingDisabled"])
658         _fastClickingIsDisabled = enabledValue.boolValue;
659     else {
660 #if ENABLE(EXTRA_ZOOM_MODE)
661         _fastClickingIsDisabled = YES;
662 #else
663         _fastClickingIsDisabled = NO;
664 #endif
665     }
666
667     [self _frameOrBoundsChanged];
668
669     NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
670     [center addObserver:self selector:@selector(_keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];
671     [center addObserver:self selector:@selector(_keyboardDidChangeFrame:) name:UIKeyboardDidChangeFrameNotification object:nil];
672     [center addObserver:self selector:@selector(_keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
673     [center addObserver:self selector:@selector(_keyboardDidShow:) name:UIKeyboardDidShowNotification object:nil];
674     [center addObserver:self selector:@selector(_keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
675     [center addObserver:self selector:@selector(_windowDidRotate:) name:UIWindowDidRotateNotification object:nil];
676     [center addObserver:self selector:@selector(_contentSizeCategoryDidChange:) name:UIContentSizeCategoryDidChangeNotification object:nil];
677     _page->contentSizeCategoryDidChange([self _contentSizeCategory]);
678
679     [center addObserver:self selector:@selector(_accessibilitySettingsDidChange:) name:UIAccessibilityGrayscaleStatusDidChangeNotification object:nil];
680     [center addObserver:self selector:@selector(_accessibilitySettingsDidChange:) name:UIAccessibilityInvertColorsStatusDidChangeNotification object:nil];
681     [center addObserver:self selector:@selector(_accessibilitySettingsDidChange:) name:UIAccessibilityReduceMotionStatusDidChangeNotification object:nil];
682
683     [[_configuration _contentProviderRegistry] addPage:*_page];
684     _page->setForceAlwaysUserScalable([_configuration ignoresViewportScaleLimits]);
685 #endif
686
687 #if PLATFORM(MAC)
688     _impl = std::make_unique<WebKit::WebViewImpl>(self, self, processPool, WTFMove(pageConfiguration));
689     _page = &_impl->page();
690
691     _impl->setAutomaticallyAdjustsContentInsets(true);
692     _impl->setRequiresUserActionForEditingControlsManager([configuration _requiresUserActionForEditingControlsManager]);
693     _impl->setDefaultAppearance([self _defaultAppearance]);
694 #endif
695
696 #if ENABLE(ACCESSIBILITY_EVENTS)
697     // Check _AXSWebAccessibilityEventsEnabled here to avoid compiler optimizing
698     // out the null check of kAXSWebAccessibilityEventsEnabledNotification.
699     if (!isNullFunctionPointer(_AXSWebAccessibilityEventsEnabled))
700         CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), (__bridge const void *)(self), accessibilityEventsEnabledChangedCallback, kAXSWebAccessibilityEventsEnabledNotification, 0, CFNotificationSuspensionBehaviorDeliverImmediately);
701     [self _updateAccessibilityEventsEnabled];
702 #endif
703
704     _page->setBackgroundExtendsBeyondPage(true);
705
706     if (NSString *applicationNameForUserAgent = configuration.applicationNameForUserAgent)
707         _page->setApplicationNameForUserAgent(applicationNameForUserAgent);
708
709     _navigationState = std::make_unique<WebKit::NavigationState>(self);
710     _page->setNavigationClient(_navigationState->createNavigationClient());
711
712     _uiDelegate = std::make_unique<WebKit::UIDelegate>(self);
713     _page->setFindClient(std::make_unique<WebKit::FindClient>(self));
714     _page->setDiagnosticLoggingClient(std::make_unique<WebKit::DiagnosticLoggingClient>(self));
715
716     _iconLoadingDelegate = std::make_unique<WebKit::IconLoadingDelegate>(self);
717
718 #if PLATFORM(IOS)
719     [self _setUpSQLiteDatabaseTrackerClient];
720 #endif
721
722     auto *handlers = _configuration.get()._urlSchemeHandlers;
723     for (NSString *key in handlers)
724         _page->setURLSchemeHandlerForScheme(WebKit::WebURLSchemeHandlerCocoa::create(handlers[key]), key);
725
726     pageToViewMap().add(_page.get(), self);
727
728 #if PLATFORM(IOS)
729     _dragInteractionPolicy = _WKDragInteractionPolicyDefault;
730 #if ENABLE(EXTRA_ZOOM_MODE)
731     _allowsViewportShrinkToFit = YES;
732 #else
733     _allowsViewportShrinkToFit = NO;
734 #endif
735 #endif // PLATFORM(IOS)
736 }
737
738 - (void)_setUpSQLiteDatabaseTrackerClient
739 {
740 #if PLATFORM(IOS)
741     WebBackgroundTaskController *controller = [WebBackgroundTaskController sharedController];
742     if (controller.backgroundTaskStartBlock)
743         return;
744
745     controller.backgroundTaskStartBlock = ^NSUInteger (void (^expirationHandler)())
746     {
747         return [[UIApplication sharedApplication] beginBackgroundTaskWithName:@"com.apple.WebKit.DatabaseActivity" expirationHandler:expirationHandler];
748     };
749     controller.backgroundTaskEndBlock = ^(UIBackgroundTaskIdentifier taskIdentifier)
750     {
751         [[UIApplication sharedApplication] endBackgroundTask:taskIdentifier];
752     };
753     controller.invalidBackgroundTaskIdentifier = UIBackgroundTaskInvalid;
754
755     WebCore::SQLiteDatabaseTracker::setClient(&WebCore::WebSQLiteDatabaseTrackerClient::sharedWebSQLiteDatabaseTrackerClient());
756 #endif
757 }
758
759 - (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration
760 {
761     if (!(self = [super initWithFrame:frame]))
762         return nil;
763
764     [self _initializeWithConfiguration:configuration];
765
766     return self;
767 }
768
769 - (instancetype)initWithCoder:(NSCoder *)coder
770 {
771     if (!(self = [super initWithCoder:coder]))
772         return nil;
773
774     WKWebViewConfiguration *configuration = decodeObjectOfClassForKeyFromCoder([WKWebViewConfiguration class], @"configuration", coder);
775     [self _initializeWithConfiguration:configuration];
776
777     self.allowsBackForwardNavigationGestures = [coder decodeBoolForKey:@"allowsBackForwardNavigationGestures"];
778     self.customUserAgent = decodeObjectOfClassForKeyFromCoder([NSString class], @"customUserAgent", coder);
779     self.allowsLinkPreview = [coder decodeBoolForKey:@"allowsLinkPreview"];
780
781 #if PLATFORM(MAC)
782     self.allowsMagnification = [coder decodeBoolForKey:@"allowsMagnification"];
783     self.magnification = [coder decodeDoubleForKey:@"magnification"];
784 #endif
785
786     return self;
787 }
788
789 - (void)encodeWithCoder:(NSCoder *)coder
790 {
791     [super encodeWithCoder:coder];
792
793     [coder encodeObject:_configuration.get() forKey:@"configuration"];
794
795     [coder encodeBool:self.allowsBackForwardNavigationGestures forKey:@"allowsBackForwardNavigationGestures"];
796     [coder encodeObject:self.customUserAgent forKey:@"customUserAgent"];
797     [coder encodeBool:self.allowsLinkPreview forKey:@"allowsLinkPreview"];
798
799 #if PLATFORM(MAC)
800     [coder encodeBool:self.allowsMagnification forKey:@"allowsMagnification"];
801     [coder encodeDouble:self.magnification forKey:@"magnification"];
802 #endif
803 }
804
805 - (void)dealloc
806 {
807 #if PLATFORM(MAC)
808     [_textFinderClient willDestroyView:self];
809 #endif
810
811 #if PLATFORM(IOS)
812     [_contentView _webViewDestroyed];
813
814     if (_remoteObjectRegistry)
815         _page->process().processPool().removeMessageReceiver(Messages::RemoteObjectRegistry::messageReceiverName(), _page->pageID());
816 #endif
817
818     _page->close();
819
820 #if PLATFORM(IOS)
821     [_remoteObjectRegistry _invalidate];
822     [[_configuration _contentProviderRegistry] removePage:*_page];
823     [[NSNotificationCenter defaultCenter] removeObserver:self];
824     [_scrollView setInternalDelegate:nil];
825 #endif
826
827 #if ENABLE(ACCESSIBILITY_EVENTS)
828     CFNotificationCenterRemoveObserver(CFNotificationCenterGetDarwinNotifyCenter(), (__bridge const void *)(self), nullptr, nullptr);
829 #endif
830
831     pageToViewMap().remove(_page.get());
832
833     [super dealloc];
834 }
835
836 - (WKWebViewConfiguration *)configuration
837 {
838     return [[_configuration copy] autorelease];
839 }
840
841 - (WKBackForwardList *)backForwardList
842 {
843     return wrapper(_page->backForwardList());
844 }
845
846 - (id <WKNavigationDelegate>)navigationDelegate
847 {
848     return _navigationState->navigationDelegate().autorelease();
849 }
850
851 - (void)setNavigationDelegate:(id <WKNavigationDelegate>)navigationDelegate
852 {
853     _page->setNavigationClient(_navigationState->createNavigationClient());
854     _navigationState->setNavigationDelegate(navigationDelegate);
855 }
856
857 - (id <WKUIDelegate>)UIDelegate
858 {
859     return _uiDelegate->delegate().autorelease();
860 }
861
862 - (void)setUIDelegate:(id<WKUIDelegate>)UIDelegate
863 {
864     _uiDelegate->setDelegate(UIDelegate);
865 #if ENABLE(CONTEXT_MENUS)
866     _page->setContextMenuClient(_uiDelegate->createContextMenuClient());
867 #endif
868     _page->setUIClient(_uiDelegate->createUIClient());
869 }
870
871 - (id <_WKIconLoadingDelegate>)_iconLoadingDelegate
872 {
873     return _iconLoadingDelegate->delegate().autorelease();
874 }
875
876 - (void)_setIconLoadingDelegate:(id<_WKIconLoadingDelegate>)iconLoadingDelegate
877 {
878     _page->setIconLoadingClient(_iconLoadingDelegate->createIconLoadingClient());
879     _iconLoadingDelegate->setDelegate(iconLoadingDelegate);
880 }
881
882 - (WKNavigation *)loadRequest:(NSURLRequest *)request
883 {
884     auto navigation = _page->loadRequest(request);
885     if (!navigation)
886         return nil;
887
888     return [wrapper(*navigation.leakRef()) autorelease];
889 }
890
891 - (WKNavigation *)loadFileURL:(NSURL *)URL allowingReadAccessToURL:(NSURL *)readAccessURL
892 {
893     if (![URL isFileURL])
894         [NSException raise:NSInvalidArgumentException format:@"%@ is not a file URL", URL];
895
896     if (![readAccessURL isFileURL])
897         [NSException raise:NSInvalidArgumentException format:@"%@ is not a file URL", readAccessURL];
898
899     auto navigation = _page->loadFile([URL _web_originalDataAsWTFString], [readAccessURL _web_originalDataAsWTFString]);
900     if (!navigation)
901         return nil;
902
903     return [wrapper(*navigation.leakRef()) autorelease];
904 }
905
906 - (WKNavigation *)loadHTMLString:(NSString *)string baseURL:(NSURL *)baseURL
907 {
908     NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
909
910     return [self loadData:data MIMEType:@"text/html" characterEncodingName:@"UTF-8" baseURL:baseURL];
911 }
912
913 - (WKNavigation *)loadData:(NSData *)data MIMEType:(NSString *)MIMEType characterEncodingName:(NSString *)characterEncodingName baseURL:(NSURL *)baseURL
914 {
915     auto navigation = _page->loadData(API::Data::createWithoutCopying(data).ptr(), MIMEType, characterEncodingName, baseURL.absoluteString);
916     if (!navigation)
917         return nil;
918
919     return [wrapper(*navigation.leakRef()) autorelease];
920 }
921
922 - (WKNavigation *)goToBackForwardListItem:(WKBackForwardListItem *)item
923 {
924     auto navigation = _page->goToBackForwardItem(item._item);
925     if (!navigation)
926         return nil;
927
928     return [wrapper(*navigation.leakRef()) autorelease];
929 }
930
931 - (NSString *)title
932 {
933     return _page->pageLoadState().title();
934 }
935
936 - (NSURL *)URL
937 {
938     return [NSURL _web_URLWithWTFString:_page->pageLoadState().activeURL()];
939 }
940
941 - (BOOL)isLoading
942 {
943     return _page->pageLoadState().isLoading();
944 }
945
946 - (double)estimatedProgress
947 {
948     return _page->pageLoadState().estimatedProgress();
949 }
950
951 - (BOOL)hasOnlySecureContent
952 {
953     return _page->pageLoadState().hasOnlySecureContent();
954 }
955
956 - (SecTrustRef)serverTrust
957 {
958 #if HAVE(SEC_TRUST_SERIALIZATION)
959     auto certificateInfo = _page->pageLoadState().certificateInfo();
960     if (!certificateInfo)
961         return nil;
962
963     return certificateInfo->certificateInfo().trust();
964 #else
965     return nil;
966 #endif
967 }
968
969 - (BOOL)canGoBack
970 {
971     return _page->pageLoadState().canGoBack();
972 }
973
974 - (BOOL)canGoForward
975 {
976     return _page->pageLoadState().canGoForward();
977 }
978
979 - (WKNavigation *)goBack
980 {
981     auto navigation = _page->goBack();
982     if (!navigation)
983         return nil;
984
985     return [wrapper(*navigation.leakRef()) autorelease];
986 }
987
988 - (WKNavigation *)goForward
989 {
990     auto navigation = _page->goForward();
991     if (!navigation)
992         return nil;
993
994     return [wrapper(*navigation.leakRef()) autorelease];
995 }
996
997 - (WKNavigation *)reload
998 {
999     OptionSet<WebCore::ReloadOption> reloadOptions;
1000     if (linkedOnOrAfter(WebKit::SDKVersion::FirstWithExpiredOnlyReloadBehavior))
1001         reloadOptions |= WebCore::ReloadOption::ExpiredOnly;
1002
1003     auto navigation = _page->reload(reloadOptions);
1004     if (!navigation)
1005         return nil;
1006
1007     return [wrapper(*navigation.leakRef()) autorelease];
1008 }
1009
1010 - (WKNavigation *)reloadFromOrigin
1011 {
1012     auto navigation = _page->reload(WebCore::ReloadOption::FromOrigin);
1013     if (!navigation)
1014         return nil;
1015
1016     return [wrapper(*navigation.leakRef()) autorelease];
1017 }
1018
1019 - (void)stopLoading
1020 {
1021     _page->stopLoading();
1022 }
1023
1024 static WKErrorCode callbackErrorCode(WebKit::CallbackBase::Error error)
1025 {
1026     switch (error) {
1027     case WebKit::CallbackBase::Error::None:
1028         ASSERT_NOT_REACHED();
1029         return WKErrorUnknown;
1030
1031     case WebKit::CallbackBase::Error::Unknown:
1032         return WKErrorUnknown;
1033
1034     case WebKit::CallbackBase::Error::ProcessExited:
1035         return WKErrorWebContentProcessTerminated;
1036
1037     case WebKit::CallbackBase::Error::OwnerWasInvalidated:
1038         return WKErrorWebViewInvalidated;
1039     }
1040 }
1041
1042 - (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^)(id, NSError *))completionHandler
1043 {
1044     [self _evaluateJavaScript:javaScriptString forceUserGesture:YES completionHandler:completionHandler];
1045 }
1046
1047 - (void)_evaluateJavaScript:(NSString *)javaScriptString forceUserGesture:(BOOL)forceUserGesture completionHandler:(void (^)(id, NSError *))completionHandler
1048 {
1049     auto handler = adoptNS([completionHandler copy]);
1050
1051     _page->runJavaScriptInMainFrame(javaScriptString, forceUserGesture, [handler](API::SerializedScriptValue* serializedScriptValue, bool hadException, const WebCore::ExceptionDetails& details, WebKit::ScriptValueCallback::Error errorCode) {
1052         if (!handler)
1053             return;
1054
1055         if (errorCode != WebKit::ScriptValueCallback::Error::None) {
1056             auto error = createNSError(callbackErrorCode(errorCode));
1057             if (errorCode == WebKit::ScriptValueCallback::Error::OwnerWasInvalidated) {
1058                 // The OwnerWasInvalidated callback is synchronous. We don't want to call the block from within it
1059                 // because that can trigger re-entrancy bugs in WebKit.
1060                 // FIXME: It would be even better if GenericCallback did this for us.
1061                 dispatch_async(dispatch_get_main_queue(), [handler, error] {
1062                     auto rawHandler = (void (^)(id, NSError *))handler.get();
1063                     rawHandler(nil, error.get());
1064                 });
1065                 return;
1066             }
1067
1068             auto rawHandler = (void (^)(id, NSError *))handler.get();
1069             rawHandler(nil, error.get());
1070             return;
1071         }
1072
1073         auto rawHandler = (void (^)(id, NSError *))handler.get();
1074         if (hadException) {
1075             ASSERT(!serializedScriptValue);
1076
1077             RetainPtr<NSMutableDictionary> userInfo = adoptNS([[NSMutableDictionary alloc] init]);
1078
1079             [userInfo setObject:localizedDescriptionForErrorCode(WKErrorJavaScriptExceptionOccurred) forKey:NSLocalizedDescriptionKey];
1080             [userInfo setObject:static_cast<NSString *>(details.message) forKey:_WKJavaScriptExceptionMessageErrorKey];
1081             [userInfo setObject:@(details.lineNumber) forKey:_WKJavaScriptExceptionLineNumberErrorKey];
1082             [userInfo setObject:@(details.columnNumber) forKey:_WKJavaScriptExceptionColumnNumberErrorKey];
1083
1084             if (!details.sourceURL.isEmpty())
1085                 [userInfo setObject:[NSURL _web_URLWithWTFString:details.sourceURL] forKey:_WKJavaScriptExceptionSourceURLErrorKey];
1086
1087             rawHandler(nil, adoptNS([[NSError alloc] initWithDomain:WKErrorDomain code:WKErrorJavaScriptExceptionOccurred userInfo:userInfo.get()]).get());
1088             return;
1089         }
1090
1091         if (!serializedScriptValue) {
1092             rawHandler(nil, createNSError(WKErrorJavaScriptResultTypeIsUnsupported).get());
1093             return;
1094         }
1095
1096         id body = API::SerializedScriptValue::deserialize(serializedScriptValue->internalRepresentation(), 0);
1097         rawHandler(body, nil);
1098     });
1099 }
1100
1101 #if PLATFORM(MAC)
1102 - (void)takeSnapshotWithConfiguration:(WKSnapshotConfiguration *)snapshotConfiguration completionHandler:(void(^)(NSImage *, NSError *))completionHandler
1103 {
1104     CGRect rectInViewCoordinates = snapshotConfiguration && !CGRectIsNull(snapshotConfiguration.rect) ? snapshotConfiguration.rect : self.bounds;
1105     CGFloat snapshotWidth;
1106     if (snapshotConfiguration)
1107         snapshotWidth = snapshotConfiguration.snapshotWidth.doubleValue ?: rectInViewCoordinates.size.width;
1108     else
1109         snapshotWidth = self.bounds.size.width;
1110
1111     auto handler = makeBlockPtr(completionHandler);
1112     CGFloat imageScale = snapshotWidth / rectInViewCoordinates.size.width;
1113     CGFloat imageHeight = imageScale * rectInViewCoordinates.size.height;
1114
1115     // Need to scale by device scale factor or the image will be distorted.
1116     CGFloat deviceScale = _page->deviceScaleFactor();
1117     WebCore::IntSize bitmapSize(snapshotWidth, imageHeight);
1118     bitmapSize.scale(deviceScale, deviceScale);
1119
1120     // Software snapshot will not capture elements rendered with hardware acceleration (WebGL, video, etc).
1121     _page->takeSnapshot(WebCore::enclosingIntRect(rectInViewCoordinates), bitmapSize, WebKit::SnapshotOptionsInViewCoordinates, [handler, snapshotWidth, imageHeight](const WebKit::ShareableBitmap::Handle& imageHandle, WebKit::CallbackBase::Error errorCode) {
1122         if (errorCode != WebKit::ScriptValueCallback::Error::None) {
1123             auto error = createNSError(callbackErrorCode(errorCode));
1124             handler(nil, error.get());
1125             return;
1126         }
1127
1128         RefPtr<WebKit::ShareableBitmap> bitmap = WebKit::ShareableBitmap::create(imageHandle, WebKit::SharedMemory::Protection::ReadOnly);
1129         RetainPtr<CGImageRef> cgImage = bitmap ? bitmap->makeCGImage() : nullptr;
1130         RetainPtr<NSImage> nsImage = adoptNS([[NSImage alloc] initWithCGImage:cgImage.get() size:NSMakeSize(snapshotWidth, imageHeight)]);
1131         handler(nsImage.get(), nil);
1132     });
1133 }
1134
1135 #elif PLATFORM(IOS)
1136 - (void)takeSnapshotWithConfiguration:(WKSnapshotConfiguration *)snapshotConfiguration completionHandler:(void(^)(UIImage *, NSError *))completionHandler
1137 {
1138     CGRect rectInViewCoordinates = snapshotConfiguration && !CGRectIsNull(snapshotConfiguration.rect) ? snapshotConfiguration.rect : self.bounds;
1139     CGFloat snapshotWidth;
1140     if (snapshotConfiguration)
1141         snapshotWidth = snapshotConfiguration.snapshotWidth.doubleValue ?: rectInViewCoordinates.size.width;
1142     else
1143         snapshotWidth = self.bounds.size.width;
1144
1145     auto handler = makeBlockPtr(completionHandler);
1146     CGFloat deviceScale = _page->deviceScaleFactor();
1147
1148     [self _snapshotRect:rectInViewCoordinates intoImageOfWidth:(snapshotWidth * deviceScale) completionHandler:^(CGImageRef snapshotImage) {
1149         RetainPtr<NSError> error;
1150         RetainPtr<UIImage> uiImage;
1151
1152         if (!snapshotImage)
1153             error = createNSError(WKErrorUnknown);
1154         else
1155             uiImage = adoptNS([[UIImage alloc] initWithCGImage:snapshotImage scale:deviceScale orientation:UIImageOrientationUp]);
1156
1157         handler(uiImage.get(), error.get());
1158     }];
1159 }
1160 #endif
1161
1162 - (NSString *)customUserAgent
1163 {
1164     return _page->customUserAgent();
1165 }
1166
1167 - (void)setCustomUserAgent:(NSString *)customUserAgent
1168 {
1169     _page->setCustomUserAgent(customUserAgent);
1170 }
1171
1172 - (WKPageRef)_pageForTesting
1173 {
1174     return toAPI(_page.get());
1175 }
1176
1177 - (WebKit::WebPageProxy *)_page
1178 {
1179     return _page.get();
1180 }
1181
1182 - (BOOL)allowsLinkPreview
1183 {
1184 #if PLATFORM(MAC)
1185     return _impl->allowsLinkPreview();
1186 #elif PLATFORM(IOS)
1187     return _allowsLinkPreview;
1188 #endif
1189 }
1190
1191 - (void)setAllowsLinkPreview:(BOOL)allowsLinkPreview
1192 {
1193 #if PLATFORM(MAC)
1194     _impl->setAllowsLinkPreview(allowsLinkPreview);
1195     return;
1196 #elif PLATFORM(IOS)
1197     if (_allowsLinkPreview == allowsLinkPreview)
1198         return;
1199
1200     _allowsLinkPreview = allowsLinkPreview;
1201
1202 #if HAVE(LINK_PREVIEW)
1203     if (_allowsLinkPreview)
1204         [_contentView _registerPreview];
1205     else
1206         [_contentView _unregisterPreview];
1207 #endif // HAVE(LINK_PREVIEW)
1208 #endif // PLATFORM(IOS)
1209 }
1210
1211 - (CGSize)_viewportSizeForCSSViewportUnits
1212 {
1213     return _page->viewportSizeForCSSViewportUnits();
1214 }
1215
1216 - (void)_setViewportSizeForCSSViewportUnits:(CGSize)viewportSize
1217 {
1218     auto viewportSizeForViewportUnits = WebCore::IntSize(viewportSize);
1219     if (viewportSizeForViewportUnits.isEmpty())
1220         [NSException raise:NSInvalidArgumentException format:@"Viewport size should not be empty"];
1221
1222     _page->setViewportSizeForCSSViewportUnits(viewportSizeForViewportUnits);
1223 }
1224
1225 static NSTextAlignment nsTextAlignment(WebKit::TextAlignment alignment)
1226 {
1227     switch (alignment) {
1228     case WebKit::NoAlignment:
1229         return NSTextAlignmentNatural;
1230     case WebKit::LeftAlignment:
1231         return NSTextAlignmentLeft;
1232     case WebKit::RightAlignment:
1233         return NSTextAlignmentRight;
1234     case WebKit::CenterAlignment:
1235         return NSTextAlignmentCenter;
1236     case WebKit::JustifiedAlignment:
1237         return NSTextAlignmentJustified;
1238     }
1239     ASSERT_NOT_REACHED();
1240     return NSTextAlignmentNatural;
1241 }
1242
1243 static NSDictionary *dictionaryRepresentationForEditorState(const WebKit::EditorState& state)
1244 {
1245     if (state.isMissingPostLayoutData)
1246         return @{ @"post-layout-data" : @NO };
1247
1248     auto& postLayoutData = state.postLayoutData();
1249     return @{
1250         @"post-layout-data" : @YES,
1251         @"bold": postLayoutData.typingAttributes & WebKit::AttributeBold ? @YES : @NO,
1252         @"italic": postLayoutData.typingAttributes & WebKit::AttributeItalics ? @YES : @NO,
1253         @"underline": postLayoutData.typingAttributes & WebKit::AttributeUnderline ? @YES : @NO,
1254         @"text-alignment": @(nsTextAlignment(static_cast<WebKit::TextAlignment>(postLayoutData.textAlignment))),
1255         @"text-color": (NSString *)postLayoutData.textColor.cssText()
1256     };
1257 }
1258
1259 - (void)_didChangeEditorState
1260 {
1261     id <WKUIDelegatePrivate> uiDelegate = (id <WKUIDelegatePrivate>)self.UIDelegate;
1262     if ([uiDelegate respondsToSelector:@selector(_webView:editorStateDidChange:)])
1263         [uiDelegate _webView:self editorStateDidChange:dictionaryRepresentationForEditorState(_page->editorState())];
1264 }
1265
1266 #if ENABLE(ATTACHMENT_ELEMENT)
1267
1268 - (void)_didInsertAttachment:(NSString *)identifier withSource:(NSString *)source
1269 {
1270     id <WKUIDelegatePrivate> uiDelegate = (id <WKUIDelegatePrivate>)self.UIDelegate;
1271     if ([uiDelegate respondsToSelector:@selector(_webView:didInsertAttachment:withSource:)])
1272         [uiDelegate _webView:self didInsertAttachment:[wrapper(API::Attachment::create(identifier, *_page).leakRef()) autorelease] withSource:source];
1273     else if ([uiDelegate respondsToSelector:@selector(_webView:didInsertAttachment:)])
1274         [uiDelegate _webView:self didInsertAttachment:[wrapper(API::Attachment::create(identifier, *_page).leakRef()) autorelease]];
1275 }
1276
1277 - (void)_didRemoveAttachment:(NSString *)identifier
1278 {
1279     id <WKUIDelegatePrivate> uiDelegate = (id <WKUIDelegatePrivate>)self.UIDelegate;
1280     if ([uiDelegate respondsToSelector:@selector(_webView:didRemoveAttachment:)])
1281         [uiDelegate _webView:self didRemoveAttachment:[wrapper(API::Attachment::create(identifier, *_page).leakRef()) autorelease]];
1282 }
1283
1284 #endif // ENABLE(ATTACHMENT_ELEMENT)
1285
1286 #pragma mark iOS-specific methods
1287
1288 #if PLATFORM(IOS)
1289
1290 - (_WKDragInteractionPolicy)_dragInteractionPolicy
1291 {
1292     return _dragInteractionPolicy;
1293 }
1294
1295 - (void)_setDragInteractionPolicy:(_WKDragInteractionPolicy)policy
1296 {
1297     if (_dragInteractionPolicy == policy)
1298         return;
1299
1300     _dragInteractionPolicy = policy;
1301 #if ENABLE(DRAG_SUPPORT)
1302     [_contentView _didChangeDragInteractionPolicy];
1303 #endif
1304 }
1305
1306 - (void)_populateArchivedSubviews:(NSMutableSet *)encodedViews
1307 {
1308     [super _populateArchivedSubviews:encodedViews];
1309
1310     if (_scrollView)
1311         [encodedViews removeObject:_scrollView.get()];
1312     if (_customContentFixedOverlayView)
1313         [encodedViews removeObject:_customContentFixedOverlayView.get()];
1314 }
1315
1316 - (BOOL)_isBackground
1317 {
1318     if ([self _isDisplayingPDF])
1319         return [_customContentView web_isBackground];
1320     return [_contentView isBackground];
1321 }
1322
1323 - (void)setFrame:(CGRect)frame
1324 {
1325     CGRect oldFrame = self.frame;
1326     [super setFrame:frame];
1327
1328     if (!CGSizeEqualToSize(oldFrame.size, frame.size))
1329         [self _frameOrBoundsChanged];
1330 }
1331
1332 - (void)setBounds:(CGRect)bounds
1333 {
1334     CGRect oldBounds = self.bounds;
1335     [super setBounds:bounds];
1336     [_customContentFixedOverlayView setFrame:self.bounds];
1337
1338     if (!CGSizeEqualToSize(oldBounds.size, bounds.size))
1339         [self _frameOrBoundsChanged];
1340 }
1341
1342 - (void)layoutSubviews
1343 {
1344     [super layoutSubviews];
1345     [self _frameOrBoundsChanged];
1346 }
1347
1348 - (UIScrollView *)scrollView
1349 {
1350     return _scrollView.get();
1351 }
1352
1353 - (WKBrowsingContextController *)browsingContextController
1354 {
1355     return [_contentView browsingContextController];
1356 }
1357
1358 - (BOOL)becomeFirstResponder
1359 {
1360     UIView *currentContentView = self._currentContentView;
1361     if (currentContentView == _contentView && [_contentView superview])
1362         return [_contentView becomeFirstResponderForWebView] || [super becomeFirstResponder];
1363
1364     return [currentContentView becomeFirstResponder] || [super becomeFirstResponder];
1365 }
1366
1367 - (BOOL)canBecomeFirstResponder
1368 {
1369     if (self._currentContentView == _contentView)
1370         return [_contentView canBecomeFirstResponderForWebView];
1371
1372     return YES;
1373 }
1374
1375 - (BOOL)resignFirstResponder
1376 {
1377     if ([_contentView isFirstResponder])
1378         return [_contentView resignFirstResponderForWebView];
1379
1380     return [super resignFirstResponder];
1381 }
1382
1383 #define FORWARD_ACTION_TO_WKCONTENTVIEW(_action) \
1384     - (void)_action:(id)sender \
1385     { \
1386         if (self.usesStandardContentView) \
1387             [_contentView _action ## ForWebView:sender]; \
1388     }
1389
1390 FOR_EACH_WKCONTENTVIEW_ACTION(FORWARD_ACTION_TO_WKCONTENTVIEW)
1391
1392 #undef FORWARD_ACTION_TO_WKCONTENTVIEW
1393
1394 - (BOOL)canPerformAction:(SEL)action withSender:(id)sender
1395 {
1396     #define FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW(_action) \
1397         if (action == @selector(_action:)) \
1398             return self.usesStandardContentView && [_contentView canPerformActionForWebView:action withSender:sender];
1399
1400     FOR_EACH_WKCONTENTVIEW_ACTION(FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW)
1401
1402     #undef FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW
1403
1404     return [super canPerformAction:action withSender:sender];
1405 }
1406
1407 - (id)targetForAction:(SEL)action withSender:(id)sender
1408 {
1409     #define FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW(_action) \
1410         if (action == @selector(_action:) && self.usesStandardContentView) \
1411             return [_contentView targetForActionForWebView:action withSender:sender];
1412
1413     FOR_EACH_WKCONTENTVIEW_ACTION(FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW)
1414
1415     #undef FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW
1416
1417     return [super targetForAction:action withSender:sender];
1418 }
1419
1420 static inline CGFloat floorToDevicePixel(CGFloat input, float deviceScaleFactor)
1421 {
1422     return CGFloor(input * deviceScaleFactor) / deviceScaleFactor;
1423 }
1424
1425 static inline bool pointsEqualInDevicePixels(CGPoint a, CGPoint b, float deviceScaleFactor)
1426 {
1427     return fabs(a.x * deviceScaleFactor - b.x * deviceScaleFactor) < std::numeric_limits<float>::epsilon()
1428         && fabs(a.y * deviceScaleFactor - b.y * deviceScaleFactor) < std::numeric_limits<float>::epsilon();
1429 }
1430
1431 static CGSize roundScrollViewContentSize(const WebKit::WebPageProxy& page, CGSize contentSize)
1432 {
1433     float deviceScaleFactor = page.deviceScaleFactor();
1434     return CGSizeMake(floorToDevicePixel(contentSize.width, deviceScaleFactor), floorToDevicePixel(contentSize.height, deviceScaleFactor));
1435 }
1436
1437 - (UIView *)_currentContentView
1438 {
1439     return _customContentView ? [_customContentView web_contentView] : _contentView.get();
1440 }
1441
1442 - (WKWebViewContentProviderRegistry *)_contentProviderRegistry
1443 {
1444     return [_configuration _contentProviderRegistry];
1445 }
1446
1447 - (WKSelectionGranularity)_selectionGranularity
1448 {
1449     return [_configuration selectionGranularity];
1450 }
1451
1452 - (void)_setHasCustomContentView:(BOOL)pageHasCustomContentView loadedMIMEType:(const WTF::String&)mimeType
1453 {
1454     if (pageHasCustomContentView) {
1455         [_customContentView removeFromSuperview];
1456         [_customContentFixedOverlayView removeFromSuperview];
1457
1458         Class representationClass = [[_configuration _contentProviderRegistry] providerForMIMEType:mimeType];
1459         ASSERT(representationClass);
1460         _customContentView = adoptNS([[representationClass alloc] web_initWithFrame:self.bounds webView:self]);
1461         _customContentFixedOverlayView = adoptNS([[UIView alloc] initWithFrame:self.bounds]);
1462         [_customContentFixedOverlayView layer].name = @"CustomContentFixedOverlay";
1463         [_customContentFixedOverlayView setUserInteractionEnabled:NO];
1464
1465         [[_contentView unscaledView] removeFromSuperview];
1466         [_contentView removeFromSuperview];
1467         [_scrollView addSubview:_customContentView.get()];
1468         [self addSubview:_customContentFixedOverlayView.get()];
1469
1470         [_customContentView web_setMinimumSize:self.bounds.size];
1471         [_customContentView web_setFixedOverlayView:_customContentFixedOverlayView.get()];
1472
1473         _scrollViewBackgroundColor = WebCore::Color();
1474         [_scrollView setContentOffset:[self _initialContentOffsetForScrollView]];
1475
1476         [self _setAvoidsUnsafeArea:NO];
1477     } else if (_customContentView) {
1478         [_customContentView removeFromSuperview];
1479         _customContentView = nullptr;
1480
1481         [_customContentFixedOverlayView removeFromSuperview];
1482         _customContentFixedOverlayView = nullptr;
1483
1484         [_scrollView addSubview:_contentView.get()];
1485         [_scrollView addSubview:[_contentView unscaledView]];
1486         [_scrollView setContentSize:roundScrollViewContentSize(*_page, [_contentView frame].size)];
1487
1488         [_customContentFixedOverlayView setFrame:self.bounds];
1489         [self addSubview:_customContentFixedOverlayView.get()];
1490     }
1491
1492     if (self.isFirstResponder) {
1493         UIView *currentContentView = self._currentContentView;
1494         if (currentContentView == _contentView ? [_contentView canBecomeFirstResponderForWebView] : currentContentView.canBecomeFirstResponder)
1495             [currentContentView becomeFirstResponder];
1496     }
1497 }
1498
1499 - (void)_didFinishLoadingDataForCustomContentProviderWithSuggestedFilename:(const String&)suggestedFilename data:(NSData *)data
1500 {
1501     ASSERT(_customContentView);
1502     [_customContentView web_setContentProviderData:data suggestedFilename:suggestedFilename];
1503
1504     // FIXME: It may make more sense for custom content providers to invoke this when they're ready,
1505     // because there's no guarantee that all custom content providers will lay out synchronously.
1506     _page->didLayoutForCustomContentProvider();
1507 }
1508
1509 - (void)_willInvokeUIScrollViewDelegateCallback
1510 {
1511     _delayUpdateVisibleContentRects = YES;
1512 }
1513
1514 - (void)_didInvokeUIScrollViewDelegateCallback
1515 {
1516     _delayUpdateVisibleContentRects = NO;
1517     if (_hadDelayedUpdateVisibleContentRects) {
1518         _hadDelayedUpdateVisibleContentRects = NO;
1519         [self _scheduleVisibleContentRectUpdate];
1520     }
1521 }
1522
1523 static CGFloat contentZoomScale(WKWebView *webView)
1524 {
1525     CGFloat scale = webView._currentContentView.layer.affineTransform.a;
1526     ASSERT(scale == [webView->_scrollView zoomScale]);
1527     return scale;
1528 }
1529
1530 static WebCore::Color baseScrollViewBackgroundColor(WKWebView *webView)
1531 {
1532     if (webView->_customContentView)
1533         return [webView->_customContentView backgroundColor].CGColor;
1534
1535     if (webView->_gestureController) {
1536         WebCore::Color color = webView->_gestureController->backgroundColorForCurrentSnapshot();
1537         if (color.isValid())
1538             return color;
1539     }
1540
1541     return webView->_page->pageExtendedBackgroundColor();
1542 }
1543
1544 static WebCore::Color scrollViewBackgroundColor(WKWebView *webView)
1545 {
1546     if (!webView.opaque)
1547         return WebCore::Color::transparent;
1548
1549     WebCore::Color color = baseScrollViewBackgroundColor(webView);
1550
1551     if (!color.isValid())
1552         color = WebCore::Color::white;
1553
1554     CGFloat zoomScale = contentZoomScale(webView);
1555     CGFloat minimumZoomScale = [webView->_scrollView minimumZoomScale];
1556     if (zoomScale < minimumZoomScale) {
1557         CGFloat slope = 12;
1558         CGFloat opacity = std::max<CGFloat>(1 - slope * (minimumZoomScale - zoomScale), 0);
1559         color = WebCore::colorWithOverrideAlpha(color.rgb(), opacity);
1560     }
1561
1562     return color;
1563 }
1564
1565 - (void)_updateScrollViewBackground
1566 {
1567     WebCore::Color color = scrollViewBackgroundColor(self);
1568
1569     if (_scrollViewBackgroundColor == color)
1570         return;
1571
1572     _scrollViewBackgroundColor = color;
1573
1574     auto uiBackgroundColor = adoptNS([[UIColor alloc] initWithCGColor:cachedCGColor(color)]);
1575     [_scrollView setBackgroundColor:uiBackgroundColor.get()];
1576
1577     // Update the indicator style based on the lightness/darkness of the background color.
1578     double hue, saturation, lightness;
1579     color.getHSL(hue, saturation, lightness);
1580     if (lightness <= .5 && color.isVisible())
1581         [_scrollView setIndicatorStyle:UIScrollViewIndicatorStyleWhite];
1582     else
1583         [_scrollView setIndicatorStyle:UIScrollViewIndicatorStyleDefault];
1584 }
1585
1586 - (void)_videoControlsManagerDidChange
1587 {
1588 #if ENABLE(FULLSCREEN_API)
1589     if (_fullScreenWindowController)
1590         [_fullScreenWindowController videoControlsManagerDidChange];
1591 #endif
1592 }
1593
1594 - (CGPoint)_initialContentOffsetForScrollView
1595 {
1596     auto combinedUnobscuredAndScrollViewInset = [self _computedContentInset];
1597     return CGPointMake(-combinedUnobscuredAndScrollViewInset.left, -combinedUnobscuredAndScrollViewInset.top);
1598 }
1599
1600 - (CGPoint)_contentOffsetAdjustedForObscuredInset:(CGPoint)point
1601 {
1602     CGPoint result = point;
1603     UIEdgeInsets contentInset = [self _computedObscuredInset];
1604
1605     result.x -= contentInset.left;
1606     result.y -= contentInset.top;
1607
1608     return result;
1609 }
1610
1611 - (UIRectEdge)_effectiveObscuredInsetEdgesAffectedBySafeArea
1612 {
1613     if (![self usesStandardContentView])
1614         return UIRectEdgeAll;
1615     return _obscuredInsetEdgesAffectedBySafeArea;
1616 }
1617
1618 - (UIEdgeInsets)_computedObscuredInset
1619 {
1620     if (!linkedOnOrAfter(WebKit::SDKVersion::FirstWhereScrollViewContentInsetsAreNotObscuringInsets)) {
1621         // For binary compability with third party apps, treat scroll view content insets as obscuring insets when the app is compiled
1622         // against a WebKit version without the fix in r229641.
1623         return [self _computedContentInset];
1624     }
1625
1626     if (_haveSetObscuredInsets)
1627         return _obscuredInsets;
1628
1629 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000
1630     if (self._safeAreaShouldAffectObscuredInsets)
1631         return UIEdgeInsetsAdd(UIEdgeInsetsZero, self._scrollViewSystemContentInset, self._effectiveObscuredInsetEdgesAffectedBySafeArea);
1632 #endif
1633
1634     return UIEdgeInsetsZero;
1635 }
1636
1637 - (UIEdgeInsets)_computedContentInset
1638 {
1639     if (_haveSetObscuredInsets)
1640         return _obscuredInsets;
1641
1642     UIEdgeInsets insets = [_scrollView contentInset];
1643
1644 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000
1645     if (self._safeAreaShouldAffectObscuredInsets)
1646         insets = UIEdgeInsetsAdd(insets, self._scrollViewSystemContentInset, self._effectiveObscuredInsetEdgesAffectedBySafeArea);
1647 #endif
1648
1649     return insets;
1650 }
1651
1652 - (UIEdgeInsets)_computedUnobscuredSafeAreaInset
1653 {
1654     if (_haveSetUnobscuredSafeAreaInsets)
1655         return _unobscuredSafeAreaInsets;
1656
1657 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000
1658     if (!self._safeAreaShouldAffectObscuredInsets)
1659         return self.safeAreaInsets;
1660 #endif
1661
1662     return UIEdgeInsetsZero;
1663 }
1664
1665 - (void)_processDidExit
1666 {
1667     [self _hidePasswordView];
1668     if (!_customContentView && _dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing) {
1669         NSUInteger indexOfResizeAnimationView = [[_scrollView subviews] indexOfObject:_resizeAnimationView.get()];
1670         [_scrollView insertSubview:_contentView.get() atIndex:indexOfResizeAnimationView];
1671         [_scrollView insertSubview:[_contentView unscaledView] atIndex:indexOfResizeAnimationView + 1];
1672         [_resizeAnimationView removeFromSuperview];
1673         _resizeAnimationView = nil;
1674
1675         _resizeAnimationTransformAdjustments = CATransform3DIdentity;
1676     }
1677     [_contentView setFrame:self.bounds];
1678     [_scrollView setBackgroundColor:[UIColor whiteColor]];
1679     [_scrollView setContentOffset:[self _initialContentOffsetForScrollView]];
1680     [_scrollView setZoomScale:1];
1681
1682     _viewportMetaTagWidth = WebCore::ViewportArguments::ValueAuto;
1683     _initialScaleFactor = 1;
1684     _hasCommittedLoadForMainFrame = NO;
1685     _needsResetViewStateAfterCommitLoadForMainFrame = NO;
1686     _dynamicViewportUpdateMode = DynamicViewportUpdateMode::NotResizing;
1687     [_contentView setHidden:NO];
1688     _scrollOffsetToRestore = std::nullopt;
1689     _unobscuredCenterToRestore = std::nullopt;
1690     _scrollViewBackgroundColor = WebCore::Color();
1691     _delayUpdateVisibleContentRects = NO;
1692     _hadDelayedUpdateVisibleContentRects = NO;
1693     _lastSentViewLayoutSize = std::nullopt;
1694     _lastSentMaximumUnobscuredSize = std::nullopt;
1695     _lastSentDeviceOrientation = std::nullopt;
1696
1697     _frozenVisibleContentRect = std::nullopt;
1698     _frozenUnobscuredContentRect = std::nullopt;
1699
1700     _firstPaintAfterCommitLoadTransactionID = 0;
1701     _firstTransactionIDAfterPageRestore = std::nullopt;
1702     _resizeAnimationTransformTransactionID = std::nullopt;
1703
1704     _hasScheduledVisibleRectUpdate = NO;
1705     _commitDidRestoreScrollPosition = NO;
1706
1707     _avoidsUnsafeArea = YES;
1708 }
1709
1710 - (void)_didCommitLoadForMainFrame
1711 {
1712     _firstPaintAfterCommitLoadTransactionID = downcast<WebKit::RemoteLayerTreeDrawingAreaProxy>(*_page->drawingArea()).nextLayerTreeTransactionID();
1713
1714     _hasCommittedLoadForMainFrame = YES;
1715     _needsResetViewStateAfterCommitLoadForMainFrame = YES;
1716
1717     [_scrollView _stopScrollingAndZoomingAnimations];
1718 }
1719
1720 static CGPoint contentOffsetBoundedInValidRange(UIScrollView *scrollView, CGPoint contentOffset)
1721 {
1722 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000
1723     UIEdgeInsets contentInsets = scrollView.adjustedContentInset;
1724 #else
1725     UIEdgeInsets contentInsets = scrollView.contentInset;
1726 #endif
1727
1728     CGSize contentSize = scrollView.contentSize;
1729     CGSize scrollViewSize = scrollView.bounds.size;
1730
1731     CGPoint minimumContentOffset = CGPointMake(-contentInsets.left, -contentInsets.top);
1732     CGPoint maximumContentOffset = CGPointMake(std::max(minimumContentOffset.x, contentSize.width + contentInsets.right - scrollViewSize.width), std::max(minimumContentOffset.y, contentSize.height + contentInsets.bottom - scrollViewSize.height));
1733
1734     return CGPointMake(std::max(std::min(contentOffset.x, maximumContentOffset.x), minimumContentOffset.x), std::max(std::min(contentOffset.y, maximumContentOffset.y), minimumContentOffset.y));
1735 }
1736
1737 static void changeContentOffsetBoundedInValidRange(UIScrollView *scrollView, WebCore::FloatPoint contentOffset)
1738 {
1739     scrollView.contentOffset = contentOffsetBoundedInValidRange(scrollView, contentOffset);
1740 }
1741
1742 - (WebCore::FloatRect)visibleRectInViewCoordinates
1743 {
1744     WebCore::FloatRect bounds = self.bounds;
1745     bounds.moveBy([_scrollView contentOffset]);
1746     WebCore::FloatRect contentViewBounds = [_contentView bounds];
1747     bounds.intersect(contentViewBounds);
1748     return bounds;
1749 }
1750
1751 // WebCore stores the page scale factor as float instead of double. When we get a scale from WebCore,
1752 // we need to ignore differences that are within a small rounding error on floats.
1753 static inline bool areEssentiallyEqualAsFloat(float a, float b)
1754 {
1755     return WTF::areEssentiallyEqual(a, b);
1756 }
1757
1758 - (void)_didCommitLayerTree:(const WebKit::RemoteLayerTreeTransaction&)layerTreeTransaction
1759 {
1760     if (![self usesStandardContentView])
1761         return;
1762
1763     LOG_WITH_STREAM(VisibleRects, stream << "-[WKWebView " << _page->pageID() << " _didCommitLayerTree:] transactionID " <<  layerTreeTransaction.transactionID() << " _dynamicViewportUpdateMode " << (int)_dynamicViewportUpdateMode);
1764
1765     bool needUpdateVisibleContentRects = _page->updateLayoutViewportParameters(layerTreeTransaction);
1766
1767     if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing) {
1768         if (_resizeAnimationTransformTransactionID && layerTreeTransaction.transactionID() >= _resizeAnimationTransformTransactionID.value()) {
1769             _resizeAnimationTransformTransactionID = std::nullopt;
1770             [_resizeAnimationView layer].sublayerTransform = _resizeAnimationTransformAdjustments;
1771             if (_dynamicViewportUpdateMode == DynamicViewportUpdateMode::ResizingWithDocumentHidden) {
1772                 [_contentView setHidden:NO];
1773                 [self _endAnimatedResize];
1774             }
1775         }
1776         return;
1777     }
1778
1779     if (_activeAnimatedResizeCount)
1780         RELEASE_LOG_IF_ALLOWED("%p -[WKWebView _didCommitLayerTree:] - %d animated resizes in flight", self, _activeAnimatedResizeCount);
1781
1782     CGSize newContentSize = roundScrollViewContentSize(*_page, [_contentView frame].size);
1783     [_scrollView _setContentSizePreservingContentOffsetDuringRubberband:newContentSize];
1784
1785     [_scrollView setMinimumZoomScale:layerTreeTransaction.minimumScaleFactor()];
1786     [_scrollView setMaximumZoomScale:layerTreeTransaction.maximumScaleFactor()];
1787     [_scrollView setZoomEnabled:layerTreeTransaction.allowsUserScaling()];
1788     if (!layerTreeTransaction.scaleWasSetByUIProcess() && ![_scrollView isZooming] && ![_scrollView isZoomBouncing] && ![_scrollView _isAnimatingZoom] && [_scrollView zoomScale] != layerTreeTransaction.pageScaleFactor()) {
1789         LOG_WITH_STREAM(VisibleRects, stream << " updating scroll view with pageScaleFactor " << layerTreeTransaction.pageScaleFactor());
1790         [_scrollView setZoomScale:layerTreeTransaction.pageScaleFactor()];
1791     }
1792
1793     _viewportMetaTagWidth = layerTreeTransaction.viewportMetaTagWidth();
1794     _viewportMetaTagWidthWasExplicit = layerTreeTransaction.viewportMetaTagWidthWasExplicit();
1795     _viewportMetaTagCameFromImageDocument = layerTreeTransaction.viewportMetaTagCameFromImageDocument();
1796     _initialScaleFactor = layerTreeTransaction.initialScaleFactor();
1797     if (_page->inStableState() && layerTreeTransaction.isInStableState() && [_stableStatePresentationUpdateCallbacks count]) {
1798         for (dispatch_block_t action in _stableStatePresentationUpdateCallbacks.get())
1799             action();
1800
1801         [_stableStatePresentationUpdateCallbacks removeAllObjects];
1802         _stableStatePresentationUpdateCallbacks = nil;
1803     }
1804
1805     if (![_contentView _mayDisableDoubleTapGesturesDuringSingleTap])
1806         [_contentView _setDoubleTapGesturesEnabled:self._allowsDoubleTapGestures];
1807
1808     [self _updateScrollViewBackground];
1809     [self _setAvoidsUnsafeArea:layerTreeTransaction.avoidsUnsafeArea()];
1810
1811     if (_gestureController)
1812         _gestureController->setRenderTreeSize(layerTreeTransaction.renderTreeSize());
1813
1814     if (_needsResetViewStateAfterCommitLoadForMainFrame && layerTreeTransaction.transactionID() >= _firstPaintAfterCommitLoadTransactionID) {
1815         _needsResetViewStateAfterCommitLoadForMainFrame = NO;
1816         [_scrollView setContentOffset:[self _initialContentOffsetForScrollView]];
1817         if (_observedRenderingProgressEvents & _WKRenderingProgressEventFirstPaint)
1818             _navigationState->didFirstPaint();
1819
1820         needUpdateVisibleContentRects = true;
1821     }
1822
1823     if (_firstTransactionIDAfterPageRestore && layerTreeTransaction.transactionID() >= _firstTransactionIDAfterPageRestore.value()) {
1824         if (_scrollOffsetToRestore) {
1825             WebCore::FloatPoint scaledScrollOffset = _scrollOffsetToRestore.value();
1826             _scrollOffsetToRestore = std::nullopt;
1827
1828             if (areEssentiallyEqualAsFloat(contentZoomScale(self), _scaleToRestore)) {
1829                 scaledScrollOffset.scale(_scaleToRestore);
1830                 WebCore::FloatPoint contentOffsetInScrollViewCoordinates = scaledScrollOffset - WebCore::FloatSize(_obscuredInsetsWhenSaved.left(), _obscuredInsetsWhenSaved.top());
1831
1832                 changeContentOffsetBoundedInValidRange(_scrollView.get(), contentOffsetInScrollViewCoordinates);
1833                 _commitDidRestoreScrollPosition = YES;
1834             }
1835
1836             needUpdateVisibleContentRects = true;
1837         }
1838
1839         if (_unobscuredCenterToRestore) {
1840             WebCore::FloatPoint unobscuredCenterToRestore = _unobscuredCenterToRestore.value();
1841             _unobscuredCenterToRestore = std::nullopt;
1842
1843             if (areEssentiallyEqualAsFloat(contentZoomScale(self), _scaleToRestore)) {
1844                 CGRect unobscuredRect = UIEdgeInsetsInsetRect(self.bounds, _obscuredInsets);
1845                 WebCore::FloatSize unobscuredContentSizeAtNewScale = WebCore::FloatSize(unobscuredRect.size) / _scaleToRestore;
1846                 WebCore::FloatPoint topLeftInDocumentCoordinates = unobscuredCenterToRestore - unobscuredContentSizeAtNewScale / 2;
1847
1848                 topLeftInDocumentCoordinates.scale(_scaleToRestore);
1849                 topLeftInDocumentCoordinates.moveBy(WebCore::FloatPoint(-_obscuredInsets.left, -_obscuredInsets.top));
1850
1851                 changeContentOffsetBoundedInValidRange(_scrollView.get(), topLeftInDocumentCoordinates);
1852             }
1853
1854             needUpdateVisibleContentRects = true;
1855         }
1856
1857         if (_gestureController)
1858             _gestureController->didRestoreScrollPosition();
1859
1860         _firstTransactionIDAfterPageRestore = std::nullopt;
1861     }
1862
1863     if (needUpdateVisibleContentRects)
1864         [self _scheduleVisibleContentRectUpdate];
1865
1866     if (WebKit::RemoteLayerTreeScrollingPerformanceData* scrollPerfData = _page->scrollingPerformanceData())
1867         scrollPerfData->didCommitLayerTree([self visibleRectInViewCoordinates]);
1868 }
1869
1870 - (void)_layerTreeCommitComplete
1871 {
1872     _commitDidRestoreScrollPosition = NO;
1873 }
1874
1875 - (void)_dynamicViewportUpdateChangedTargetToScale:(double)newScale position:(CGPoint)newScrollPosition nextValidLayerTreeTransactionID:(uint64_t)nextValidLayerTreeTransactionID
1876 {
1877     LOG_WITH_STREAM(VisibleRects, stream << "-[WKWebView " << _page->pageID() << " _dynamicViewportUpdateChangedTargetToScale:] " <<  newScale << " _dynamicViewportUpdateMode " << (int)_dynamicViewportUpdateMode);
1878
1879     if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing) {
1880         CGFloat animatingScaleTarget = [[_resizeAnimationView layer] transform].m11;
1881         double currentTargetScale = animatingScaleTarget * [[_contentView layer] transform].m11;
1882         double scale = newScale / currentTargetScale;
1883         _resizeAnimationTransformAdjustments = CATransform3DMakeScale(scale, scale, 1);
1884
1885         CGPoint newContentOffset = [self _contentOffsetAdjustedForObscuredInset:CGPointMake(newScrollPosition.x * newScale, newScrollPosition.y * newScale)];
1886         CGPoint currentContentOffset = [_scrollView contentOffset];
1887
1888         _resizeAnimationTransformAdjustments.m41 = (currentContentOffset.x - newContentOffset.x) / animatingScaleTarget;
1889         _resizeAnimationTransformAdjustments.m42 = (currentContentOffset.y - newContentOffset.y) / animatingScaleTarget;
1890         _resizeAnimationTransformTransactionID = nextValidLayerTreeTransactionID;
1891     }
1892 }
1893
1894 - (void)_couldNotRestorePageState
1895 {
1896     // The gestureController may be waiting for the scroll position to be restored
1897     // in order to remove the swipe snapshot. Since the scroll position could not be
1898     // restored, tell the gestureController it was restored so that it no longer waits
1899     // for it.
1900     if (_gestureController)
1901         _gestureController->didRestoreScrollPosition();
1902 }
1903
1904 - (void)_restorePageScrollPosition:(std::optional<WebCore::FloatPoint>)scrollPosition scrollOrigin:(WebCore::FloatPoint)scrollOrigin previousObscuredInset:(WebCore::FloatBoxExtent)obscuredInsets scale:(double)scale
1905 {
1906     if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing)
1907         return;
1908
1909     if (![self usesStandardContentView])
1910         return;
1911
1912     _firstTransactionIDAfterPageRestore = downcast<WebKit::RemoteLayerTreeDrawingAreaProxy>(*_page->drawingArea()).nextLayerTreeTransactionID();
1913     if (scrollPosition)
1914         _scrollOffsetToRestore = WebCore::ScrollableArea::scrollOffsetFromPosition(WebCore::FloatPoint(scrollPosition.value()), WebCore::toFloatSize(scrollOrigin));
1915     else
1916         _scrollOffsetToRestore = std::nullopt;
1917
1918     _obscuredInsetsWhenSaved = obscuredInsets;
1919     _scaleToRestore = scale;
1920 }
1921
1922 - (void)_restorePageStateToUnobscuredCenter:(std::optional<WebCore::FloatPoint>)center scale:(double)scale
1923 {
1924     if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing)
1925         return;
1926
1927     if (![self usesStandardContentView])
1928         return;
1929
1930     _firstTransactionIDAfterPageRestore = downcast<WebKit::RemoteLayerTreeDrawingAreaProxy>(*_page->drawingArea()).nextLayerTreeTransactionID();
1931     _unobscuredCenterToRestore = center.value();
1932
1933     _scaleToRestore = scale;
1934 }
1935
1936 - (RefPtr<WebKit::ViewSnapshot>)_takeViewSnapshot
1937 {
1938 #if HAVE(CORE_ANIMATION_RENDER_SERVER)
1939     float deviceScale = WebCore::screenScaleFactor();
1940     WebCore::FloatSize snapshotSize(self.bounds.size);
1941     snapshotSize.scale(deviceScale);
1942
1943     CATransform3D transform = CATransform3DMakeScale(deviceScale, deviceScale, 1);
1944
1945 #if HAVE(IOSURFACE)
1946     WebCore::IOSurface::Format snapshotFormat = WebCore::screenSupportsExtendedColor() ? WebCore::IOSurface::Format::RGB10 : WebCore::IOSurface::Format::RGBA;
1947     auto surface = WebCore::IOSurface::create(WebCore::expandedIntSize(snapshotSize), WebCore::sRGBColorSpaceRef(), snapshotFormat);
1948     if (!surface)
1949         return nullptr;
1950     CARenderServerRenderLayerWithTransform(MACH_PORT_NULL, self.layer.context.contextId, reinterpret_cast<uint64_t>(self.layer), surface->surface(), 0, 0, &transform);
1951
1952 #if HAVE(IOSURFACE_ACCELERATOR)
1953     WebCore::IOSurface::Format compressedFormat = WebCore::IOSurface::Format::YUV422;
1954     if (WebCore::IOSurface::allowConversionFromFormatToFormat(snapshotFormat, compressedFormat)) {
1955         RefPtr<WebKit::ViewSnapshot> viewSnapshot = WebKit::ViewSnapshot::create(nullptr);
1956         WebCore::IOSurface::convertToFormat(WTFMove(surface), WebCore::IOSurface::Format::YUV422, [viewSnapshot](std::unique_ptr<WebCore::IOSurface> convertedSurface) {
1957             if (convertedSurface)
1958                 viewSnapshot->setSurface(WTFMove(convertedSurface));
1959         });
1960
1961         return viewSnapshot;
1962     }
1963 #endif // HAVE(IOSURFACE_ACCELERATOR)
1964
1965     return WebKit::ViewSnapshot::create(WTFMove(surface));
1966 #else // HAVE(IOSURFACE)
1967     uint32_t slotID = [WebKit::ViewSnapshotStore::snapshottingContext() createImageSlot:snapshotSize hasAlpha:YES];
1968
1969     if (!slotID)
1970         return nullptr;
1971
1972     CARenderServerCaptureLayerWithTransform(MACH_PORT_NULL, self.layer.context.contextId, (uint64_t)self.layer, slotID, 0, 0, &transform);
1973     WebCore::IntSize imageSize = WebCore::expandedIntSize(WebCore::FloatSize(snapshotSize));
1974     return WebKit::ViewSnapshot::create(slotID, imageSize, (imageSize.area() * 4).unsafeGet());
1975 #endif // HAVE(IOSURFACE)
1976 #else // HAVE(CORE_ANIMATION_RENDER_SERVER)
1977     return nullptr;
1978 #endif
1979 }
1980
1981 - (void)_zoomToPoint:(WebCore::FloatPoint)point atScale:(double)scale animated:(BOOL)animated
1982 {
1983     CFTimeInterval duration = 0;
1984     CGFloat zoomScale = contentZoomScale(self);
1985
1986     if (animated) {
1987         const double maximumZoomDuration = 0.4;
1988         const double minimumZoomDuration = 0.1;
1989         const double zoomDurationFactor = 0.3;
1990
1991         duration = std::min(fabs(log(zoomScale) - log(scale)) * zoomDurationFactor + minimumZoomDuration, maximumZoomDuration);
1992     }
1993
1994     if (scale != zoomScale)
1995         _page->willStartUserTriggeredZooming();
1996
1997     LOG_WITH_STREAM(VisibleRects, stream << "_zoomToPoint:" << point << " scale: " << scale << " duration:" << duration);
1998
1999     [_scrollView _zoomToCenter:point scale:scale duration:duration];
2000 }
2001
2002 - (void)_zoomToRect:(WebCore::FloatRect)targetRect atScale:(double)scale origin:(WebCore::FloatPoint)origin animated:(BOOL)animated
2003 {
2004     // FIXME: Some of this could be shared with _scrollToRect.
2005     const double visibleRectScaleChange = contentZoomScale(self) / scale;
2006     const WebCore::FloatRect visibleRect([self convertRect:self.bounds toView:self._currentContentView]);
2007     const WebCore::FloatRect unobscuredRect([self _contentRectForUserInteraction]);
2008
2009     const WebCore::FloatSize topLeftObscuredInsetAfterZoom((unobscuredRect.minXMinYCorner() - visibleRect.minXMinYCorner()) * visibleRectScaleChange);
2010     const WebCore::FloatSize bottomRightObscuredInsetAfterZoom((visibleRect.maxXMaxYCorner() - unobscuredRect.maxXMaxYCorner()) * visibleRectScaleChange);
2011
2012     const WebCore::FloatSize unobscuredRectSizeAfterZoom(unobscuredRect.size() * visibleRectScaleChange);
2013
2014     // Center to the target rect.
2015     WebCore::FloatPoint unobscuredRectLocationAfterZoom = targetRect.location() - (unobscuredRectSizeAfterZoom - targetRect.size()) * 0.5;
2016
2017     // Center to the tap point instead in case the target rect won't fit in a direction.
2018     if (targetRect.width() > unobscuredRectSizeAfterZoom.width())
2019         unobscuredRectLocationAfterZoom.setX(origin.x() - unobscuredRectSizeAfterZoom.width() / 2);
2020     if (targetRect.height() > unobscuredRectSizeAfterZoom.height())
2021         unobscuredRectLocationAfterZoom.setY(origin.y() - unobscuredRectSizeAfterZoom.height() / 2);
2022
2023     // We have computed where we want the unobscured rect to be. Now adjust for the obscuring insets.
2024     WebCore::FloatRect visibleRectAfterZoom(unobscuredRectLocationAfterZoom, unobscuredRectSizeAfterZoom);
2025     visibleRectAfterZoom.move(-topLeftObscuredInsetAfterZoom);
2026     visibleRectAfterZoom.expand(topLeftObscuredInsetAfterZoom + bottomRightObscuredInsetAfterZoom);
2027
2028     [self _zoomToPoint:visibleRectAfterZoom.center() atScale:scale animated:animated];
2029 }
2030
2031 static WebCore::FloatPoint constrainContentOffset(WebCore::FloatPoint contentOffset, WebCore::FloatSize contentSize, WebCore::FloatSize unobscuredContentSize)
2032 {
2033     WebCore::FloatSize maximumContentOffset = contentSize - unobscuredContentSize;
2034     return contentOffset.constrainedBetween(WebCore::FloatPoint(), WebCore::FloatPoint(maximumContentOffset));
2035 }
2036
2037 - (void)_scrollToContentScrollPosition:(WebCore::FloatPoint)scrollPosition scrollOrigin:(WebCore::IntPoint)scrollOrigin
2038 {
2039     if (_commitDidRestoreScrollPosition || _dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing)
2040         return;
2041
2042     WebCore::FloatPoint contentOffset = WebCore::ScrollableArea::scrollOffsetFromPosition(scrollPosition, toFloatSize(scrollOrigin));
2043
2044     WebCore::FloatPoint scaledOffset = contentOffset;
2045     CGFloat zoomScale = contentZoomScale(self);
2046     scaledOffset.scale(zoomScale);
2047
2048     CGPoint contentOffsetInScrollViewCoordinates = [self _contentOffsetAdjustedForObscuredInset:scaledOffset];
2049     contentOffsetInScrollViewCoordinates = contentOffsetBoundedInValidRange(_scrollView.get(), contentOffsetInScrollViewCoordinates);
2050
2051     [_scrollView _stopScrollingAndZoomingAnimations];
2052
2053     if (!CGPointEqualToPoint(contentOffsetInScrollViewCoordinates, [_scrollView contentOffset]))
2054         [_scrollView setContentOffset:contentOffsetInScrollViewCoordinates];
2055     else {
2056         // If we haven't changed anything, there would not be any VisibleContentRect update sent to the content.
2057         // The WebProcess would keep the invalid contentOffset as its scroll position.
2058         // To synchronize the WebProcess with what is on screen, we send the VisibleContentRect again.
2059         _page->resendLastVisibleContentRects();
2060     }
2061 }
2062
2063 - (BOOL)_scrollToRect:(WebCore::FloatRect)targetRect origin:(WebCore::FloatPoint)origin minimumScrollDistance:(float)minimumScrollDistance
2064 {
2065     WebCore::FloatRect unobscuredContentRect([self _contentRectForUserInteraction]);
2066     WebCore::FloatPoint unobscuredContentOffset = unobscuredContentRect.location();
2067     WebCore::FloatSize contentSize([self._currentContentView bounds].size);
2068
2069     // Center the target rect in the scroll view.
2070     // If the target doesn't fit in the scroll view, center on the gesture location instead.
2071     WebCore::FloatPoint newUnobscuredContentOffset;
2072     if (targetRect.width() <= unobscuredContentRect.width())
2073         newUnobscuredContentOffset.setX(targetRect.x() - (unobscuredContentRect.width() - targetRect.width()) / 2);
2074     else
2075         newUnobscuredContentOffset.setX(origin.x() - unobscuredContentRect.width() / 2);
2076     if (targetRect.height() <= unobscuredContentRect.height())
2077         newUnobscuredContentOffset.setY(targetRect.y() - (unobscuredContentRect.height() - targetRect.height()) / 2);
2078     else
2079         newUnobscuredContentOffset.setY(origin.y() - unobscuredContentRect.height() / 2);
2080     newUnobscuredContentOffset = constrainContentOffset(newUnobscuredContentOffset, contentSize, unobscuredContentRect.size());
2081
2082     if (unobscuredContentOffset == newUnobscuredContentOffset) {
2083         if (targetRect.width() > unobscuredContentRect.width())
2084             newUnobscuredContentOffset.setX(origin.x() - unobscuredContentRect.width() / 2);
2085         if (targetRect.height() > unobscuredContentRect.height())
2086             newUnobscuredContentOffset.setY(origin.y() - unobscuredContentRect.height() / 2);
2087         newUnobscuredContentOffset = constrainContentOffset(newUnobscuredContentOffset, contentSize, unobscuredContentRect.size());
2088     }
2089
2090     WebCore::FloatSize scrollViewOffsetDelta = newUnobscuredContentOffset - unobscuredContentOffset;
2091     scrollViewOffsetDelta.scale(contentZoomScale(self));
2092
2093     float scrollDistance = scrollViewOffsetDelta.diagonalLength();
2094     if (scrollDistance < minimumScrollDistance)
2095         return false;
2096
2097     [_contentView willStartZoomOrScroll];
2098
2099     LOG_WITH_STREAM(VisibleRects, stream << "_scrollToRect: scrolling to " << [_scrollView contentOffset] + scrollViewOffsetDelta);
2100
2101     [_scrollView setContentOffset:([_scrollView contentOffset] + scrollViewOffsetDelta) animated:YES];
2102     return true;
2103 }
2104
2105 - (void)_scrollByContentOffset:(WebCore::FloatPoint)contentOffsetDelta
2106 {
2107     WebCore::FloatPoint scaledOffsetDelta = contentOffsetDelta;
2108     CGFloat zoomScale = contentZoomScale(self);
2109     scaledOffsetDelta.scale(zoomScale);
2110
2111     CGPoint currentOffset = [_scrollView _isAnimatingScroll] ? [_scrollView _animatedTargetOffset] : [_scrollView contentOffset];
2112     CGPoint boundedOffset = contentOffsetBoundedInValidRange(_scrollView.get(), currentOffset + scaledOffsetDelta);
2113
2114     if (CGPointEqualToPoint(boundedOffset, currentOffset))
2115         return;
2116     [_contentView willStartZoomOrScroll];
2117
2118     LOG_WITH_STREAM(VisibleRects, stream << "_scrollByContentOffset: scrolling to " << WebCore::FloatPoint(boundedOffset));
2119
2120     [_scrollView setContentOffset:boundedOffset animated:YES];
2121 }
2122
2123 - (void)_zoomOutWithOrigin:(WebCore::FloatPoint)origin animated:(BOOL)animated
2124 {
2125     [self _zoomToPoint:origin atScale:[_scrollView minimumZoomScale] animated:animated];
2126 }
2127
2128 - (void)_zoomToInitialScaleWithOrigin:(WebCore::FloatPoint)origin animated:(BOOL)animated
2129 {
2130     ASSERT(_initialScaleFactor > 0);
2131     [self _zoomToPoint:origin atScale:_initialScaleFactor animated:animated];
2132 }
2133
2134 // focusedElementRect and selectionRect are both in document coordinates.
2135 - (void)_zoomToFocusRect:(WebCore::FloatRect)focusedElementRectInDocumentCoordinates selectionRect:(WebCore::FloatRect)selectionRectInDocumentCoordinates insideFixed:(BOOL)insideFixed
2136     fontSize:(float)fontSize minimumScale:(double)minimumScale maximumScale:(double)maximumScale allowScaling:(BOOL)allowScaling forceScroll:(BOOL)forceScroll
2137 {
2138     LOG_WITH_STREAM(VisibleRects, stream << "_zoomToFocusRect:" << focusedElementRectInDocumentCoordinates << " selectionRect:" << selectionRectInDocumentCoordinates);
2139     UNUSED_PARAM(insideFixed);
2140
2141     const double minimumHeightToShowContentAboveKeyboard = 106;
2142     const CFTimeInterval formControlZoomAnimationDuration = 0.25;
2143     const double caretOffsetFromWindowEdge = 20;
2144
2145     UIWindow *window = [_scrollView window];
2146
2147     // Find the portion of the view that is visible on the screen.
2148     UIViewController *topViewController = [[[_scrollView _viewControllerForAncestor] _rootAncestorViewController] _viewControllerForSupportedInterfaceOrientations];
2149     UIView *fullScreenView = topViewController.view;
2150     if (!fullScreenView)
2151         fullScreenView = window;
2152
2153     CGRect unobscuredScrollViewRectInWebViewCoordinates = UIEdgeInsetsInsetRect([self bounds], _obscuredInsets);
2154     CGRect visibleScrollViewBoundsInWebViewCoordinates = CGRectIntersection(unobscuredScrollViewRectInWebViewCoordinates, [fullScreenView convertRect:[fullScreenView bounds] toView:self]);
2155     CGRect formAssistantFrameInWebViewCoordinates = [window convertRect:_inputViewBounds toView:self];
2156     CGRect intersectionBetweenScrollViewAndFormAssistant = CGRectIntersection(visibleScrollViewBoundsInWebViewCoordinates, formAssistantFrameInWebViewCoordinates);
2157     CGSize visibleSize = visibleScrollViewBoundsInWebViewCoordinates.size;
2158
2159     CGFloat visibleOffsetFromTop = 0;
2160     if (!CGRectIsEmpty(intersectionBetweenScrollViewAndFormAssistant)) {
2161         CGFloat heightVisibleAboveFormAssistant = CGRectGetMinY(intersectionBetweenScrollViewAndFormAssistant) - CGRectGetMinY(visibleScrollViewBoundsInWebViewCoordinates);
2162         CGFloat heightVisibleBelowFormAssistant = CGRectGetMaxY(visibleScrollViewBoundsInWebViewCoordinates) - CGRectGetMaxY(intersectionBetweenScrollViewAndFormAssistant);
2163
2164         if (heightVisibleAboveFormAssistant >= minimumHeightToShowContentAboveKeyboard || heightVisibleBelowFormAssistant < heightVisibleAboveFormAssistant)
2165             visibleSize.height = heightVisibleAboveFormAssistant;
2166         else {
2167             visibleSize.height = heightVisibleBelowFormAssistant;
2168             visibleOffsetFromTop = CGRectGetMaxY(intersectionBetweenScrollViewAndFormAssistant) - CGRectGetMinY(visibleScrollViewBoundsInWebViewCoordinates);
2169         }
2170     }
2171
2172     // Zoom around the element's bounding frame. We use a "standard" size to determine the proper frame.
2173     double currentScale = contentZoomScale(self);
2174     double scale = currentScale;
2175     if (allowScaling) {
2176 #if ENABLE(EXTRA_ZOOM_MODE)
2177         const CGFloat minimumMarginForZoomingToEntireFocusRectInWebViewCoordinates = 10;
2178         const CGFloat maximumMarginForZoomingToEntireFocusRectInWebViewCoordinates = 35;
2179
2180         CGRect minimumTargetRectInDocumentCoordinates = UIRectInsetEdges(focusedElementRectInDocumentCoordinates, UIRectEdgeAll, -minimumMarginForZoomingToEntireFocusRectInWebViewCoordinates / currentScale);
2181         CGRect maximumTargetRectInDocumentCoordinates = UIRectInsetEdges(focusedElementRectInDocumentCoordinates, UIRectEdgeAll, -maximumMarginForZoomingToEntireFocusRectInWebViewCoordinates / currentScale);
2182
2183         double clampedMaximumTargetScale = clampTo<double>(std::min(visibleSize.width / CGRectGetWidth(minimumTargetRectInDocumentCoordinates), visibleSize.height / CGRectGetHeight(minimumTargetRectInDocumentCoordinates)), minimumScale, maximumScale);
2184         double clampedMinimumTargetScale = clampTo<double>(std::min(visibleSize.width / CGRectGetWidth(maximumTargetRectInDocumentCoordinates), visibleSize.height / CGRectGetHeight(maximumTargetRectInDocumentCoordinates)), minimumScale, maximumScale);
2185         scale = clampTo<double>(currentScale, clampedMinimumTargetScale, clampedMaximumTargetScale);
2186 #else
2187         const double webViewStandardFontSize = 16;
2188         scale = clampTo<double>(webViewStandardFontSize / fontSize, minimumScale, maximumScale);
2189 #endif
2190     }
2191
2192     CGFloat documentWidth = [_contentView bounds].size.width;
2193     scale = CGRound(documentWidth * scale) / documentWidth;
2194
2195     WebCore::FloatRect focusedElementRectInNewScale = focusedElementRectInDocumentCoordinates;
2196     focusedElementRectInNewScale.scale(scale);
2197     focusedElementRectInNewScale.moveBy([_contentView frame].origin);
2198
2199     BOOL selectionRectIsNotNull = !selectionRectInDocumentCoordinates.isZero();
2200     if (!forceScroll) {
2201         CGRect currentlyVisibleRegionInWebViewCoordinates;
2202         currentlyVisibleRegionInWebViewCoordinates.origin = unobscuredScrollViewRectInWebViewCoordinates.origin;
2203         currentlyVisibleRegionInWebViewCoordinates.origin.y += visibleOffsetFromTop;
2204         currentlyVisibleRegionInWebViewCoordinates.size = visibleSize;
2205
2206         // Don't bother scrolling if the entire node is already visible, whether or not we got a selectionRect.
2207         if (CGRectContainsRect(currentlyVisibleRegionInWebViewCoordinates, [self convertRect:focusedElementRectInDocumentCoordinates fromView:_contentView.get()]))
2208             return;
2209
2210         // Don't bother scrolling if we have a valid selectionRect and it is already visible.
2211         if (selectionRectIsNotNull && CGRectContainsRect(currentlyVisibleRegionInWebViewCoordinates, [self convertRect:selectionRectInDocumentCoordinates fromView:_contentView.get()]))
2212             return;
2213     }
2214
2215     // We want to zoom to the left/top corner of the DOM node, with as much spacing on all sides as we
2216     // can get based on the visible area after zooming (workingFrame).  The spacing in either dimension is half the
2217     // difference between the size of the DOM node and the size of the visible frame.
2218     CGFloat horizontalSpaceInWebViewCoordinates = std::max((visibleSize.width - focusedElementRectInNewScale.width()) / 2.0, 0.0);
2219     CGFloat verticalSpaceInWebViewCoordinates = std::max((visibleSize.height - focusedElementRectInNewScale.height()) / 2.0, 0.0);
2220
2221     CGPoint topLeft;
2222     topLeft.x = focusedElementRectInNewScale.x() - horizontalSpaceInWebViewCoordinates;
2223     topLeft.y = focusedElementRectInNewScale.y() - verticalSpaceInWebViewCoordinates - visibleOffsetFromTop;
2224
2225     CGFloat minimumAllowableHorizontalOffsetInWebViewCoordinates = -INFINITY;
2226     CGFloat minimumAllowableVerticalOffsetInWebViewCoordinates = -INFINITY;
2227     if (selectionRectIsNotNull) {
2228         WebCore::FloatRect selectionRectInNewScale = selectionRectInDocumentCoordinates;
2229         selectionRectInNewScale.scale(scale);
2230         selectionRectInNewScale.moveBy([_contentView frame].origin);
2231         minimumAllowableHorizontalOffsetInWebViewCoordinates = CGRectGetMaxX(selectionRectInNewScale) + caretOffsetFromWindowEdge - visibleSize.width;
2232         minimumAllowableVerticalOffsetInWebViewCoordinates = CGRectGetMaxY(selectionRectInNewScale) + caretOffsetFromWindowEdge - visibleSize.height - visibleOffsetFromTop;
2233     }
2234
2235     WebCore::FloatRect documentBoundsInNewScale = [_contentView bounds];
2236     documentBoundsInNewScale.scale(scale);
2237     documentBoundsInNewScale.moveBy([_contentView frame].origin);
2238
2239     // Constrain the left edge in document coordinates so that:
2240     //  - it isn't so small that the scrollVisibleRect isn't visible on the screen
2241     //  - it isn't so great that the document's right edge is less than the right edge of the screen
2242     if (selectionRectIsNotNull && topLeft.x < minimumAllowableHorizontalOffsetInWebViewCoordinates)
2243         topLeft.x = minimumAllowableHorizontalOffsetInWebViewCoordinates;
2244     else {
2245         CGFloat maximumAllowableHorizontalOffset = CGRectGetMaxX(documentBoundsInNewScale) - visibleSize.width;
2246         if (topLeft.x > maximumAllowableHorizontalOffset)
2247             topLeft.x = maximumAllowableHorizontalOffset;
2248     }
2249
2250     // Constrain the top edge in document coordinates so that:
2251     //  - it isn't so small that the scrollVisibleRect isn't visible on the screen
2252     //  - it isn't so great that the document's bottom edge is higher than the top of the form assistant
2253     if (selectionRectIsNotNull && topLeft.y < minimumAllowableVerticalOffsetInWebViewCoordinates)
2254         topLeft.y = minimumAllowableVerticalOffsetInWebViewCoordinates;
2255     else {
2256         CGFloat maximumAllowableVerticalOffset = CGRectGetMaxY(documentBoundsInNewScale) - visibleSize.height;
2257         if (topLeft.y > maximumAllowableVerticalOffset)
2258             topLeft.y = maximumAllowableVerticalOffset;
2259     }
2260
2261     WebCore::FloatPoint newCenter = CGPointMake(topLeft.x + unobscuredScrollViewRectInWebViewCoordinates.size.width / 2.0, topLeft.y + unobscuredScrollViewRectInWebViewCoordinates.size.height / 2.0);
2262
2263     if (scale != currentScale)
2264         _page->willStartUserTriggeredZooming();
2265
2266     LOG_WITH_STREAM(VisibleRects, stream << "_zoomToFocusRect: zooming to " << newCenter << " scale:" << scale);
2267
2268     // The newCenter has been computed in the new scale, but _zoomToCenter expected the center to be in the original scale.
2269     newCenter.scale(1 / scale);
2270     [_scrollView _zoomToCenter:newCenter scale:scale duration:formControlZoomAnimationDuration force:YES];
2271 }
2272
2273 - (CGFloat)_targetContentZoomScaleForRect:(const WebCore::FloatRect&)targetRect currentScale:(double)currentScale fitEntireRect:(BOOL)fitEntireRect minimumScale:(double)minimumScale maximumScale:(double)maximumScale
2274 {
2275     WebCore::FloatSize unobscuredContentSize([self _contentRectForUserInteraction].size);
2276     double horizontalScale = unobscuredContentSize.width() * currentScale / targetRect.width();
2277     double verticalScale = unobscuredContentSize.height() * currentScale / targetRect.height();
2278
2279     horizontalScale = std::min(std::max(horizontalScale, minimumScale), maximumScale);
2280     verticalScale = std::min(std::max(verticalScale, minimumScale), maximumScale);
2281
2282     return fitEntireRect ? std::min(horizontalScale, verticalScale) : horizontalScale;
2283 }
2284
2285 - (BOOL)_zoomToRect:(WebCore::FloatRect)targetRect withOrigin:(WebCore::FloatPoint)origin fitEntireRect:(BOOL)fitEntireRect minimumScale:(double)minimumScale maximumScale:(double)maximumScale minimumScrollDistance:(float)minimumScrollDistance
2286 {
2287     const float maximumScaleFactorDeltaForPanScroll = 0.02;
2288
2289     double currentScale = contentZoomScale(self);
2290     double targetScale = [self _targetContentZoomScaleForRect:targetRect currentScale:currentScale fitEntireRect:fitEntireRect minimumScale:minimumScale maximumScale:maximumScale];
2291
2292     if (fabs(targetScale - currentScale) < maximumScaleFactorDeltaForPanScroll) {
2293         if ([self _scrollToRect:targetRect origin:origin minimumScrollDistance:minimumScrollDistance])
2294             return true;
2295     } else if (targetScale != currentScale) {
2296         [self _zoomToRect:targetRect atScale:targetScale origin:origin animated:YES];
2297         return true;
2298     }
2299
2300     return false;
2301 }
2302
2303 - (void)didMoveToWindow
2304 {
2305     _page->activityStateDidChange(WebCore::ActivityState::AllFlags);
2306 }
2307
2308 - (void)setOpaque:(BOOL)opaque
2309 {
2310     BOOL oldOpaque = self.opaque;
2311
2312     [super setOpaque:opaque];
2313     [_contentView setOpaque:opaque];
2314
2315     if (oldOpaque == opaque)
2316         return;
2317
2318     if (!_page)
2319         return;
2320
2321     _page->setDrawsBackground(opaque);
2322     [self _updateScrollViewBackground];
2323 }
2324
2325 - (void)setBackgroundColor:(UIColor *)backgroundColor
2326 {
2327     [super setBackgroundColor:backgroundColor];
2328     [_contentView setBackgroundColor:backgroundColor];
2329 }
2330
2331 - (BOOL)_allowsDoubleTapGestures
2332 {
2333     if (_fastClickingIsDisabled)
2334         return YES;
2335
2336     // If the page is not user scalable, we don't allow double tap gestures.
2337     if (![_scrollView isZoomEnabled] || [_scrollView minimumZoomScale] >= [_scrollView maximumZoomScale])
2338         return NO;
2339
2340     // If the viewport width was not explicit, we allow double tap gestures.
2341     if (!_viewportMetaTagWidthWasExplicit || _viewportMetaTagCameFromImageDocument)
2342         return YES;
2343
2344     // If the page set a viewport width that wasn't the device width, then it was
2345     // scaled and thus will probably need to zoom.
2346     if (_viewportMetaTagWidth != WebCore::ViewportArguments::ValueDeviceWidth)
2347         return YES;
2348
2349     // At this point, we have a page that asked for width = device-width. However,
2350     // if the content's width and height were large, we might have had to shrink it.
2351     // Since we'll enable double tap zoom whenever we're not at the actual
2352     // initial scale, this simply becomes a test of the current scale against 1.
2353     return !areEssentiallyEqualAsFloat(contentZoomScale(self), 1);
2354 }
2355
2356 #pragma mark - UIScrollViewDelegate
2357
2358 - (BOOL)usesStandardContentView
2359 {
2360     return !_customContentView && !_passwordView;
2361 }
2362
2363 - (CGSize)scrollView:(UIScrollView*)scrollView contentSizeForZoomScale:(CGFloat)scale withProposedSize:(CGSize)proposedSize
2364 {
2365     return roundScrollViewContentSize(*_page, proposedSize);
2366 }
2367
2368 - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
2369 {
2370     ASSERT(_scrollView == scrollView);
2371     return self._currentContentView;
2372 }
2373
2374 - (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view
2375 {
2376     if (![self usesStandardContentView]) {
2377         if ([_customContentView respondsToSelector:@selector(web_scrollViewWillBeginZooming:withView:)])
2378             [_customContentView web_scrollViewWillBeginZooming:scrollView withView:view];
2379         return;
2380     }
2381
2382     if (scrollView.pinchGestureRecognizer.state == UIGestureRecognizerStateBegan) {
2383         _page->willStartUserTriggeredZooming();
2384         [_contentView scrollViewWillStartPanOrPinchGesture];
2385     }
2386     [_contentView willStartZoomOrScroll];
2387 }
2388
2389 - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
2390 {
2391     if (![self usesStandardContentView])
2392         return;
2393
2394     if (scrollView.panGestureRecognizer.state == UIGestureRecognizerStateBegan)
2395         [_contentView scrollViewWillStartPanOrPinchGesture];
2396
2397     [_contentView willStartZoomOrScroll];
2398 #if ENABLE(CSS_SCROLL_SNAP) && ENABLE(ASYNC_SCROLLING)
2399     // FIXME: We will want to detect whether snapping will occur before beginning to drag. See WebPageProxy::didCommitLayerTree.
2400     WebKit::RemoteScrollingCoordinatorProxy* coordinator = _page->scrollingCoordinatorProxy();
2401     ASSERT(scrollView == _scrollView.get());
2402     CGFloat scrollDecelerationFactor = (coordinator && coordinator->shouldSetScrollViewDecelerationRateFast()) ? UIScrollViewDecelerationRateFast : UIScrollViewDecelerationRateNormal;
2403     scrollView.horizontalScrollDecelerationFactor = scrollDecelerationFactor;
2404     scrollView.verticalScrollDecelerationFactor = scrollDecelerationFactor;
2405 #endif
2406 }
2407
2408 - (void)_didFinishScrolling
2409 {
2410     if (![self usesStandardContentView])
2411         return;
2412
2413     [self _scheduleVisibleContentRectUpdate];
2414     [_contentView didFinishScrolling];
2415 }
2416
2417 - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
2418 {
2419     // Work around <rdar://problem/16374753> by avoiding deceleration while
2420     // zooming. We'll animate to the right place once the zoom finishes.
2421     if ([scrollView isZooming])
2422         *targetContentOffset = [scrollView contentOffset];
2423 #if ENABLE(CSS_SCROLL_SNAP) && ENABLE(ASYNC_SCROLLING)
2424     if (WebKit::RemoteScrollingCoordinatorProxy* coordinator = _page->scrollingCoordinatorProxy()) {
2425         // FIXME: Here, I'm finding the maximum horizontal/vertical scroll offsets. There's probably a better way to do this.
2426         CGSize maxScrollOffsets = CGSizeMake(scrollView.contentSize.width - scrollView.bounds.size.width, scrollView.contentSize.height - scrollView.bounds.size.height);
2427
2428         UIEdgeInsets obscuredInset;
2429
2430         id<WKUIDelegatePrivate> uiDelegatePrivate = static_cast<id <WKUIDelegatePrivate>>([self UIDelegate]);
2431         if ([uiDelegatePrivate respondsToSelector:@selector(_webView:finalObscuredInsetsForScrollView:withVelocity:targetContentOffset:)])
2432             obscuredInset = [uiDelegatePrivate _webView:self finalObscuredInsetsForScrollView:scrollView withVelocity:velocity targetContentOffset:targetContentOffset];
2433         else
2434             obscuredInset = [self _computedObscuredInset];
2435
2436         CGRect unobscuredRect = UIEdgeInsetsInsetRect(self.bounds, obscuredInset);
2437
2438         coordinator->adjustTargetContentOffsetForSnapping(maxScrollOffsets, velocity, unobscuredRect.origin.y, targetContentOffset);
2439     }
2440 #endif
2441 }
2442
2443 - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
2444 {
2445     // If we're decelerating, scroll offset will be updated when scrollViewDidFinishDecelerating: is called.
2446     if (!decelerate)
2447         [self _didFinishScrolling];
2448 }
2449
2450 - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
2451 {
2452     [self _didFinishScrolling];
2453 }
2454
2455 - (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView
2456 {
2457     [self _didFinishScrolling];
2458 }
2459
2460 - (void)scrollViewDidScroll:(UIScrollView *)scrollView
2461 {
2462     if (![self usesStandardContentView] && [_customContentView respondsToSelector:@selector(web_scrollViewDidScroll:)])
2463         [_customContentView web_scrollViewDidScroll:(UIScrollView *)scrollView];
2464
2465     [self _scheduleVisibleContentRectUpdateAfterScrollInView:scrollView];
2466
2467     if (WebKit::RemoteLayerTreeScrollingPerformanceData* scrollPerfData = _page->scrollingPerformanceData())
2468         scrollPerfData->didScroll([self visibleRectInViewCoordinates]);
2469 }
2470
2471 - (void)scrollViewDidZoom:(UIScrollView *)scrollView
2472 {
2473     if (![self usesStandardContentView] && [_customContentView respondsToSelector:@selector(web_scrollViewDidZoom:)])
2474         [_customContentView web_scrollViewDidZoom:scrollView];
2475
2476     [self _updateScrollViewBackground];
2477     [self _scheduleVisibleContentRectUpdateAfterScrollInView:scrollView];
2478 }
2479
2480 - (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale
2481 {
2482     if (![self usesStandardContentView] && [_customContentView respondsToSelector:@selector(web_scrollViewDidEndZooming:withView:atScale:)])
2483         [_customContentView web_scrollViewDidEndZooming:scrollView withView:view atScale:scale];
2484
2485     ASSERT(scrollView == _scrollView);
2486     // FIXME: remove when rdar://problem/36065495 is fixed.
2487     // When rotating with two fingers down, UIScrollView can set a bogus content view position.
2488     // "Center" is top left because we set the anchorPoint to 0,0.
2489     [_contentView setCenter:self.bounds.origin];
2490
2491     [self _scheduleVisibleContentRectUpdateAfterScrollInView:scrollView];
2492     [_contentView didZoomToScale:scale];
2493 }
2494
2495 - (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
2496 {
2497     [self _didFinishScrolling];
2498 }
2499
2500 - (void)_scrollViewDidInterruptDecelerating:(UIScrollView *)scrollView
2501 {
2502     if (![self usesStandardContentView])
2503         return;
2504
2505     [_contentView didInterruptScrolling];
2506     [self _scheduleVisibleContentRectUpdateAfterScrollInView:scrollView];
2507 }
2508
2509 - (UIView *)_enclosingViewForExposedRectComputation
2510 {
2511     return [self _scroller];
2512 }
2513
2514 - (CGRect)_visibleRectInEnclosingView:(UIView *)enclosingView
2515 {
2516     if (!enclosingView)
2517         return self.bounds;
2518
2519     CGRect exposedRect = [enclosingView convertRect:enclosingView.bounds toView:self];
2520     return CGRectIntersectsRect(exposedRect, self.bounds) ? CGRectIntersection(exposedRect, self.bounds) : CGRectZero;
2521 }
2522
2523 - (CGRect)_visibleContentRect
2524 {
2525     if (_frozenVisibleContentRect)
2526         return _frozenVisibleContentRect.value();
2527
2528     CGRect visibleRectInContentCoordinates = [self convertRect:self.bounds toView:_contentView.get()];
2529
2530     if (UIView *enclosingView = [self _enclosingViewForExposedRectComputation]) {
2531         CGRect viewVisibleRect = [self _visibleRectInEnclosingView:enclosingView];
2532         CGRect viewVisibleContentRect = [self convertRect:viewVisibleRect toView:_contentView.get()];
2533         visibleRectInContentCoordinates = CGRectIntersection(visibleRectInContentCoordinates, viewVisibleContentRect);
2534     }
2535
2536     return visibleRectInContentCoordinates;
2537 }
2538
2539 // Called when some ancestor UIScrollView scrolls.
2540 - (void)_didScroll
2541 {
2542     [self _scheduleVisibleContentRectUpdateAfterScrollInView:[self _scroller]];
2543
2544     const NSTimeInterval ScrollingEndedTimerInterval = 0.032;
2545     if (!_enclosingScrollViewScrollTimer) {
2546         _enclosingScrollViewScrollTimer = adoptNS([[NSTimer alloc] initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:ScrollingEndedTimerInterval]
2547             interval:0 target:self selector:@selector(_enclosingScrollerScrollingEnded:) userInfo:nil repeats:YES]);
2548         [[NSRunLoop mainRunLoop] addTimer:_enclosingScrollViewScrollTimer.get() forMode:NSDefaultRunLoopMode];
2549     }
2550     _didScrollSinceLastTimerFire = YES;
2551 }
2552
2553 - (void)_enclosingScrollerScrollingEnded:(NSTimer *)timer
2554 {
2555     if (_didScrollSinceLastTimerFire) {
2556         _didScrollSinceLastTimerFire = NO;
2557         return;
2558     }
2559
2560     [self _scheduleVisibleContentRectUpdate];
2561     [_enclosingScrollViewScrollTimer invalidate];
2562     _enclosingScrollViewScrollTimer = nil;
2563 }
2564
2565 - (UIEdgeInsets)_scrollViewSystemContentInset
2566 {
2567     // It's not safe to access the scroll view's safeAreaInsets or _systemContentInset from
2568     // inside our layoutSubviews implementation, because they aren't updated until afterwards.
2569     // Instead, depend on the fact that the UIScrollView and WKWebView are in the same coordinate
2570     // space, and map the WKWebView's own insets into the scroll view manually.
2571     return UIEdgeInsetsAdd([_scrollView _contentScrollInset], self.safeAreaInsets, [_scrollView _edgesApplyingSafeAreaInsetsToContentInset]);
2572 }
2573
2574 - (WebCore::FloatSize)activeViewLayoutSize:(const CGRect&)bounds
2575 {
2576     if (_overridesViewLayoutSize)
2577         return WebCore::FloatSize(_viewLayoutSizeOverride);
2578
2579 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000
2580     return WebCore::FloatSize(UIEdgeInsetsInsetRect(CGRectMake(0, 0, bounds.size.width, bounds.size.height), self._scrollViewSystemContentInset).size);
2581 #else
2582     return WebCore::FloatSize { bounds.size };
2583 #endif
2584 }
2585
2586 - (void)_dispatchSetViewLayoutSize:(WebCore::FloatSize)viewLayoutSize
2587 {
2588     if (_lastSentViewLayoutSize && CGSizeEqualToSize(_lastSentViewLayoutSize.value(), viewLayoutSize))
2589         return;
2590
2591     LOG_WITH_STREAM(VisibleRects, stream << "-[WKWebView " << _page->pageID() << " _dispatchSetViewLayoutSize:] " << viewLayoutSize << " contentZoomScale " << contentZoomScale(self));
2592     _page->setViewportConfigurationViewLayoutSize(viewLayoutSize);
2593     _lastSentViewLayoutSize = viewLayoutSize;
2594 }
2595
2596 - (void)_dispatchSetMaximumUnobscuredSize:(WebCore::FloatSize)maximumUnobscuredSize
2597 {
2598     if (_lastSentMaximumUnobscuredSize && CGSizeEqualToSize(_lastSentMaximumUnobscuredSize.value(), maximumUnobscuredSize))
2599         return;
2600
2601     _page->setMaximumUnobscuredSize(maximumUnobscuredSize);
2602     _lastSentMaximumUnobscuredSize = maximumUnobscuredSize;
2603 }
2604
2605 - (void)_dispatchSetDeviceOrientation:(int32_t)deviceOrientation
2606 {
2607     if (_lastSentDeviceOrientation && _lastSentDeviceOrientation.value() == deviceOrientation)
2608         return;
2609
2610     _page->setDeviceOrientation(deviceOrientation);
2611     _lastSentDeviceOrientation = deviceOrientation;
2612 }
2613
2614 - (void)_frameOrBoundsChanged
2615 {
2616     CGRect bounds = self.bounds;
2617     [_scrollView setFrame:bounds];
2618
2619     if (_dynamicViewportUpdateMode == DynamicViewportUpdateMode::NotResizing) {
2620         if (!_overridesViewLayoutSize)
2621             [self _dispatchSetViewLayoutSize:[self activeViewLayoutSize:self.bounds]];
2622         if (!_overridesMaximumUnobscuredSize)
2623             [self _dispatchSetMaximumUnobscuredSize:WebCore::FloatSize(bounds.size)];
2624
2625         BOOL sizeChanged = NO;
2626         if (auto drawingArea = _page->drawingArea())
2627             sizeChanged = drawingArea->setSize(WebCore::IntSize(bounds.size));
2628
2629         if (sizeChanged & [self usesStandardContentView])
2630             [_contentView setSizeChangedSinceLastVisibleContentRectUpdate:YES];
2631     }
2632
2633     [_customContentView web_setMinimumSize:bounds.size];
2634     [self _scheduleVisibleContentRectUpdate];
2635 }
2636
2637 // 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.
2638 - (CGRect)_contentRectForUserInteraction
2639 {
2640     // FIXME: handle split keyboard.
2641     UIEdgeInsets obscuredInsets = _obscuredInsets;
2642     obscuredInsets.bottom = std::max(_obscuredInsets.bottom, _inputViewBounds.size.height);
2643     CGRect unobscuredRect = UIEdgeInsetsInsetRect(self.bounds, obscuredInsets);
2644     return [self convertRect:unobscuredRect toView:self._currentContentView];
2645 }
2646
2647 // Ideally UIScrollView would expose this for us: <rdar://problem/21394567>.
2648 - (BOOL)_scrollViewIsRubberBanding
2649 {
2650     float deviceScaleFactor = _page->deviceScaleFactor();
2651
2652     CGPoint contentOffset = [_scrollView contentOffset];
2653     CGPoint boundedOffset = contentOffsetBoundedInValidRange(_scrollView.get(), contentOffset);
2654     return !pointsEqualInDevicePixels(contentOffset, boundedOffset, deviceScaleFactor);
2655 }
2656
2657 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000
2658 - (void)safeAreaInsetsDidChange
2659 {
2660     [super safeAreaInsetsDidChange];
2661
2662     [self _scheduleVisibleContentRectUpdate];
2663 }
2664 #endif
2665
2666 - (void)_scheduleVisibleContentRectUpdate
2667 {
2668     // For visible rect updates not associated with a specific UIScrollView, just consider our own scroller.
2669     [self _scheduleVisibleContentRectUpdateAfterScrollInView:_scrollView.get()];
2670 }
2671
2672 - (BOOL)_scrollViewIsInStableState:(UIScrollView *)scrollView
2673 {
2674     BOOL isStableState = !([scrollView isDragging] || [scrollView isDecelerating] || [scrollView isZooming] || [scrollView _isAnimatingZoom] || [scrollView _isScrollingToTop]);
2675
2676     if (isStableState && scrollView == _scrollView.get())
2677         isStableState = !_isChangingObscuredInsetsInteractively;
2678
2679     if (isStableState && scrollView == _scrollView.get())
2680         isStableState = ![self _scrollViewIsRubberBanding];
2681
2682     if (isStableState)
2683         isStableState = !scrollView._isInterruptingDeceleration;
2684
2685     if (NSNumber *stableOverride = self._stableStateOverride)
2686         isStableState = stableOverride.boolValue;
2687
2688     return isStableState;
2689 }
2690
2691 - (void)_addUpdateVisibleContentRectPreCommitHandler
2692 {
2693     auto retainedSelf = retainPtr(self);
2694     [CATransaction addCommitHandler:[retainedSelf] {
2695         WKWebView *webView = retainedSelf.get();
2696         if (![webView _isValid])
2697             return;
2698         [webView _updateVisibleContentRects];
2699         webView->_hasScheduledVisibleRectUpdate = NO;
2700     } forPhase:kCATransactionPhasePreCommit];
2701 }
2702
2703 - (void)_scheduleVisibleContentRectUpdateAfterScrollInView:(UIScrollView *)scrollView
2704 {
2705     _visibleContentRectUpdateScheduledFromScrollViewInStableState = [self _scrollViewIsInStableState:scrollView];
2706
2707     if (_hasScheduledVisibleRectUpdate)
2708         return;
2709
2710     _hasScheduledVisibleRectUpdate = YES;
2711
2712 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000
2713     CATransactionPhase transactionPhase = [CATransaction currentPhase];
2714     if (transactionPhase == kCATransactionPhaseNull || transactionPhase == kCATransactionPhasePreLayout) {
2715         [self _addUpdateVisibleContentRectPreCommitHandler];
2716         return;
2717     }
2718 #endif
2719
2720     dispatch_async(dispatch_get_main_queue(), [retainedSelf = retainPtr(self)] {
2721         WKWebView *webView = retainedSelf.get();
2722         if (![webView _isValid])
2723             return;
2724         [webView _addUpdateVisibleContentRectPreCommitHandler];
2725     });
2726 }
2727
2728 static bool scrollViewCanScroll(UIScrollView *scrollView)
2729 {
2730     if (!scrollView)
2731         return NO;
2732
2733     UIEdgeInsets contentInset = scrollView.contentInset;
2734     CGSize contentSize = scrollView.contentSize;
2735     CGSize boundsSize = scrollView.bounds.size;
2736
2737     return (contentSize.width + contentInset.left + contentInset.right) > boundsSize.width
2738         || (contentSize.height + contentInset.top + contentInset.bottom) > boundsSize.height;
2739 }
2740
2741 - (CGRect)_contentBoundsExtendedForRubberbandingWithScale:(CGFloat)scaleFactor
2742 {
2743     CGPoint contentOffset = [_scrollView contentOffset];
2744     CGPoint boundedOffset = contentOffsetBoundedInValidRange(_scrollView.get(), contentOffset);
2745
2746     CGFloat horiontalRubberbandAmountInContentCoordinates = (contentOffset.x - boundedOffset.x) / scaleFactor;
2747     CGFloat verticalRubberbandAmountInContentCoordinates = (contentOffset.y - boundedOffset.y) / scaleFactor;
2748
2749     CGRect extendedBounds = [_contentView bounds];
2750
2751     if (horiontalRubberbandAmountInContentCoordinates < 0) {
2752         extendedBounds.origin.x += horiontalRubberbandAmountInContentCoordinates;
2753         extendedBounds.size.width -= horiontalRubberbandAmountInContentCoordinates;
2754     } else if (horiontalRubberbandAmountInContentCoordinates > 0)
2755         extendedBounds.size.width += horiontalRubberbandAmountInContentCoordinates;
2756
2757     if (verticalRubberbandAmountInContentCoordinates < 0) {
2758         extendedBounds.origin.y += verticalRubberbandAmountInContentCoordinates;
2759         extendedBounds.size.height -= verticalRubberbandAmountInContentCoordinates;
2760     } else if (verticalRubberbandAmountInContentCoordinates > 0)
2761         extendedBounds.size.height += verticalRubberbandAmountInContentCoordinates;
2762
2763     return extendedBounds;
2764 }
2765
2766 - (void)_updateVisibleContentRects
2767 {
2768     BOOL inStableState = _visibleContentRectUpdateScheduledFromScrollViewInStableState;
2769
2770     if (![self usesStandardContentView]) {
2771         [_passwordView setFrame:self.bounds];
2772         [_customContentView web_computedContentInsetDidChange];
2773         return;
2774     }
2775
2776     if (_delayUpdateVisibleContentRects) {
2777         _hadDelayedUpdateVisibleContentRects = YES;
2778         return;
2779     }
2780
2781     if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing
2782         || (_needsResetViewStateAfterCommitLoadForMainFrame && ![_contentView sizeChangedSinceLastVisibleContentRectUpdate])
2783         || [_scrollView isZoomBouncing]
2784         || _currentlyAdjustingScrollViewInsetsForKeyboard)
2785         return;
2786
2787     CGRect visibleRectInContentCoordinates = [self _visibleContentRect];
2788
2789     UIEdgeInsets computedContentInsetUnadjustedForKeyboard = [self _computedObscuredInset];
2790     if (!_haveSetObscuredInsets)
2791         computedContentInsetUnadjustedForKeyboard.bottom -= _totalScrollViewBottomInsetAdjustmentForKeyboard;
2792
2793     CGFloat scaleFactor = contentZoomScale(self);
2794
2795     CGRect unobscuredRect = UIEdgeInsetsInsetRect(self.bounds, computedContentInsetUnadjustedForKeyboard);
2796     CGRect unobscuredRectInContentCoordinates = _frozenUnobscuredContentRect ? _frozenUnobscuredContentRect.value() : [self convertRect:unobscuredRect toView:_contentView.get()];
2797     unobscuredRectInContentCoordinates = CGRectIntersection(unobscuredRectInContentCoordinates, [self _contentBoundsExtendedForRubberbandingWithScale:scaleFactor]);
2798
2799 #if ENABLE(CSS_SCROLL_SNAP) && ENABLE(ASYNC_SCROLLING)
2800     if (inStableState) {
2801         WebKit::RemoteScrollingCoordinatorProxy* coordinator = _page->scrollingCoordinatorProxy();
2802         if (coordinator && coordinator->hasActiveSnapPoint()) {
2803             CGPoint currentPoint = [_scrollView contentOffset];
2804             CGPoint activePoint = coordinator->nearestActiveContentInsetAdjustedSnapPoint(unobscuredRect.origin.y, currentPoint);
2805
2806             if (!CGPointEqualToPoint(activePoint, currentPoint)) {
2807                 RetainPtr<WKScrollView> strongScrollView = _scrollView;
2808                 dispatch_async(dispatch_get_main_queue(), [strongScrollView, activePoint] {
2809                     [strongScrollView setContentOffset:activePoint animated:NO];
2810                 });
2811             }
2812         }
2813     }
2814 #endif
2815
2816     [_contentView didUpdateVisibleRect:visibleRectInContentCoordinates
2817         unobscuredRect:unobscuredRectInContentCoordinates
2818         unobscuredRectInScrollViewCoordinates:unobscuredRect
2819         obscuredInsets:_obscuredInsets
2820         unobscuredSafeAreaInsets:[self _computedUnobscuredSafeAreaInset]
2821         inputViewBounds:_inputViewBounds
2822         scale:scaleFactor minimumScale:[_scrollView minimumZoomScale]
2823         inStableState:inStableState
2824         isChangingObscuredInsetsInteractively:_isChangingObscuredInsetsInteractively
2825         enclosedInScrollableAncestorView:scrollViewCanScroll([self _scroller])];
2826
2827     while (!_visibleContentRectUpdateCallbacks.isEmpty()) {
2828         auto callback = _visibleContentRectUpdateCallbacks.takeLast();
2829         callback();
2830     }
2831 }
2832
2833 - (void)_didFinishLoadForMainFrame
2834 {
2835     if (_gestureController)
2836         _gestureController->didFinishLoadForMainFrame();
2837 }
2838
2839 - (void)_didFailLoadForMainFrame
2840 {
2841     if (_gestureController)
2842         _gestureController->didFailLoadForMainFrame();
2843 }
2844
2845 - (void)_didSameDocumentNavigationForMainFrame:(WebKit::SameDocumentNavigationType)navigationType
2846 {
2847     [_customContentView web_didSameDocumentNavigation:toAPI(navigationType)];
2848
2849     if (_gestureController)
2850         _gestureController->didSameDocumentNavigationForMainFrame(navigationType);
2851 }
2852
2853 - (void)_keyboardChangedWithInfo:(NSDictionary *)keyboardInfo adjustScrollView:(BOOL)adjustScrollView
2854 {
2855     NSValue *endFrameValue = [keyboardInfo objectForKey:UIKeyboardFrameEndUserInfoKey];
2856     if (!endFrameValue)
2857         return;
2858
2859     // The keyboard rect is always in screen coordinates. In the view services case the window does not
2860     // have the interface orientation rotation transformation; its host does. So, it makes no sense to
2861     // clip the keyboard rect against its screen.
2862     if ([[self window] _isHostedInAnotherProcess])
2863         _inputViewBounds = [self.window convertRect:[endFrameValue CGRectValue] fromWindow:nil];
2864     else
2865         _inputViewBounds = [self.window convertRect:CGRectIntersection([endFrameValue CGRectValue], self.window.screen.bounds) fromWindow:nil];
2866
2867     if (adjustScrollView) {
2868         CGFloat bottomInsetBeforeAdjustment = [_scrollView contentInset].bottom;
2869         SetForScope<BOOL> insetAdjustmentGuard(_currentlyAdjustingScrollViewInsetsForKeyboard, YES);
2870         [_scrollView _adjustForAutomaticKeyboardInfo:keyboardInfo animated:YES lastAdjustment:&_lastAdjustmentForScroller];
2871         CGFloat bottomInsetAfterAdjustment = [_scrollView contentInset].bottom;
2872         if (bottomInsetBeforeAdjustment != bottomInsetAfterAdjustment)
2873             _totalScrollViewBottomInsetAdjustmentForKeyboard += bottomInsetAfterAdjustment - bottomInsetBeforeAdjustment;
2874     }
2875
2876     [self _scheduleVisibleContentRectUpdate];
2877 }
2878
2879 - (BOOL)_shouldUpdateKeyboardWithInfo:(NSDictionary *)keyboardInfo
2880 {
2881     if ([_contentView isAssistingNode])
2882         return YES;
2883
2884     NSNumber *isLocalKeyboard = [keyboardInfo valueForKey:UIKeyboardIsLocalUserInfoKey];
2885     return isLocalKeyboard && !isLocalKeyboard.boolValue;
2886 }
2887
2888 - (void)_keyboardWillChangeFrame:(NSNotification *)notification
2889 {
2890     if ([self _shouldUpdateKeyboardWithInfo:notification.userInfo])
2891         [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES];
2892 }
2893
2894 - (void)_keyboardDidChangeFrame:(NSNotification *)notification
2895 {
2896     [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:NO];
2897 }
2898
2899 - (void)_keyboardWillShow:(NSNotification *)notification
2900 {
2901     if ([self _shouldUpdateKeyboardWithInfo:notification.userInfo])
2902         [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES];
2903
2904     _page->setIsKeyboardAnimatingIn(true);
2905 }
2906
2907 - (void)_keyboardDidShow:(NSNotification *)notification
2908 {
2909     _page->setIsKeyboardAnimatingIn(false);
2910 }
2911
2912 - (void)_keyboardWillHide:(NSNotification *)notification
2913 {
2914     // Ignore keyboard will hide notifications sent during rotation. They're just there for
2915     // backwards compatibility reasons and processing the will hide notification would
2916     // temporarily screw up the unobscured view area.
2917     if ([[UIPeripheralHost sharedInstance] rotationState])
2918         return;
2919
2920     [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES];
2921 }
2922
2923 - (void)_windowDidRotate:(NSNotification *)notification
2924 {
2925     if (!_overridesInterfaceOrientation)
2926         [self _dispatchSetDeviceOrientation:deviceOrientation()];
2927 }
2928
2929 - (void)_contentSizeCategoryDidChange:(NSNotification *)notification
2930 {
2931     _page->contentSizeCategoryDidChange([self _contentSizeCategory]);
2932 }
2933
2934 - (NSString *)_contentSizeCategory
2935 {
2936     return [[UIApplication sharedApplication] preferredContentSizeCategory];
2937 }
2938
2939 - (void)_accessibilitySettingsDidChange:(NSNotification *)notification
2940 {
2941     _page->accessibilitySettingsDidChange();
2942 }
2943
2944 - (void)setAllowsBackForwardNavigationGestures:(BOOL)allowsBackForwardNavigationGestures
2945 {
2946     if (_allowsBackForwardNavigationGestures == allowsBackForwardNavigationGestures)
2947         return;
2948
2949     _allowsBackForwardNavigationGestures = allowsBackForwardNavigationGestures;
2950
2951     if (allowsBackForwardNavigationGestures) {
2952         if (!_gestureController) {
2953             _gestureController = std::make_unique<WebKit::ViewGestureController>(*_page);
2954             _gestureController->installSwipeHandler(self, [self scrollView]);
2955             if (WKWebView *alternateWebView = [_configuration _alternateWebViewForNavigationGestures])
2956                 _gestureController->setAlternateBackForwardListSourcePage(alternateWebView->_page.get());
2957         }
2958     } else
2959         _gestureController = nullptr;
2960
2961     _page->setShouldRecordNavigationSnapshots(allowsBackForwardNavigationGestures);
2962 }
2963
2964 - (BOOL)allowsBackForwardNavigationGestures
2965 {
2966     return _allowsBackForwardNavigationGestures;
2967 }
2968
2969 - (BOOL)_isNavigationSwipeGestureRecognizer:(UIGestureRecognizer *)recognizer
2970 {
2971     if (!_gestureController)
2972         return NO;
2973     return _gestureController->isNavigationSwipeGestureRecognizer(recognizer);
2974 }
2975
2976 - (void)_navigationGestureDidBegin
2977 {
2978     // During a back/forward swipe, there's a view interposed between this view and the content view that has
2979     // an offset and animation on it, which results in computing incorrect rectangles. Work around by using
2980     // frozen rects during swipes.
2981     CGRect fullViewRect = self.bounds;
2982     CGRect unobscuredRect = UIEdgeInsetsInsetRect(fullViewRect, [self _computedObscuredInset]);
2983
2984     _frozenVisibleContentRect = [self convertRect:fullViewRect toView:_contentView.get()];
2985     _frozenUnobscuredContentRect = [self convertRect:unobscuredRect toView:_contentView.get()];
2986
2987     LOG_WITH_STREAM(VisibleRects, stream << "_navigationGestureDidBegin: freezing visibleContentRect " << WebCore::FloatRect(_frozenVisibleContentRect.value()) << " UnobscuredContentRect " << WebCore::FloatRect(_frozenUnobscuredContentRect.value()));
2988 }
2989
2990 - (void)_navigationGestureDidEnd
2991 {
2992     _frozenVisibleContentRect = std::nullopt;
2993     _frozenUnobscuredContentRect = std::nullopt;
2994 }
2995
2996 - (void)_showPasswordViewWithDocumentName:(NSString *)documentName passwordHandler:(void (^)(NSString *))passwordHandler
2997 {
2998     ASSERT(!_passwordView);
2999     _passwordView = adoptNS([[WKPasswordView alloc] initWithFrame:self.bounds documentName:documentName]);
3000     [_passwordView setUserDidEnterPassword:passwordHandler];
3001     [_passwordView showInScrollView:_scrollView.get()];
3002     self._currentContentView.hidden = YES;
3003 }
3004
3005 - (void)_hidePasswordView
3006 {
3007     if (!_passwordView)
3008         return;
3009
3010     self._currentContentView.hidden = NO;
3011     [_passwordView hide];
3012     _passwordView = nil;
3013 }
3014
3015 - (WKPasswordView *)_passwordView
3016 {
3017     return _passwordView.get();
3018 }
3019
3020 - (void)_updateScrollViewInsetAdjustmentBehavior
3021 {
3022 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000
3023     if (![_scrollView _contentInsetAdjustmentBehaviorWasExternallyOverridden])
3024         [_scrollView _setContentInsetAdjustmentBehaviorInternal:self._safeAreaShouldAffectObscuredInsets ? UIScrollViewContentInsetAdjustmentAlways : UIScrollViewContentInsetAdjustmentNever];
3025 #endif
3026 }
3027
3028 - (void)_setAvoidsUnsafeArea:(BOOL)avoidsUnsafeArea
3029 {
3030     if (_avoidsUnsafeArea == avoidsUnsafeArea)
3031         return;
3032
3033     _avoidsUnsafeArea = avoidsUnsafeArea;
3034
3035     [self _updateScrollViewInsetAdjustmentBehavior];
3036     [self _scheduleVisibleContentRectUpdate];
3037
3038     id <WKUIDelegatePrivate> uiDelegate = (id <WKUIDelegatePrivate>)[self UIDelegate];
3039     if ([uiDelegate respondsToSelector:@selector(_webView:didChangeSafeAreaShouldAffectObscuredInsets:)])
3040         [uiDelegate _webView:self didChangeSafeAreaShouldAffectObscuredInsets:avoidsUnsafeArea];
3041 }
3042
3043 #endif // PLATFORM(IOS)
3044
3045 #if ENABLE(ACCESSIBILITY_EVENTS)
3046 static void accessibilityEventsEnabledChangedCallback(CFNotificationCenterRef, void* observer, CFStringRef, const void*, CFDictionaryRef)
3047 {
3048     ASSERT(observer);
3049     WKWebView* webview = static_cast<WKWebView*>(observer);
3050     [webview _updateAccessibilityEventsEnabled];
3051 }
3052
3053 - (void)_updateAccessibilityEventsEnabled
3054 {
3055     if (!isNullFunctionPointer(_AXSWebAccessibilityEventsEnabled))
3056         _page->updateAccessibilityEventsEnabled(_AXSWebAccessibilityEventsEnabled());
3057 }
3058 #endif
3059
3060 #pragma mark OS X-specific methods
3061
3062 #if PLATFORM(MAC)
3063
3064 - (BOOL)acceptsFirstResponder
3065 {
3066     return _impl->acceptsFirstResponder();
3067 }
3068
3069 - (BOOL)becomeFirstResponder
3070 {
3071     return _impl->becomeFirstResponder();
3072 }
3073
3074 - (BOOL)resignFirstResponder
3075 {
3076     return _impl->resignFirstResponder();
3077 }
3078
3079 - (void)viewWillStartLiveResize
3080 {
3081     _impl->viewWillStartLiveResize();
3082 }
3083
3084 - (void)viewDidEndLiveResize
3085 {
3086     _impl->viewDidEndLiveResize();
3087 }
3088
3089 - (BOOL)isFlipped
3090 {
3091     return YES;
3092 }
3093
3094 - (NSSize)intrinsicContentSize
3095 {
3096     return NSSizeFromCGSize(_impl->intrinsicContentSize());
3097 }
3098
3099 - (void)prepareContentInRect:(NSRect)rect
3100 {
3101     _impl->prepareContentInRect(NSRectToCGRect(rect));
3102 }
3103
3104 - (void)setFrameSize:(NSSize)size
3105 {
3106     [super setFrameSize:size];
3107     _impl->setFrameSize(NSSizeToCGSize(size));
3108 }
3109
3110 - (void)renewGState
3111 {
3112     _impl->renewGState();
3113     [super renewGState];
3114 }
3115
3116 #define WEBCORE_COMMAND(command) - (void)command:(id)sender { _impl->executeEditCommandForSelector(_cmd); }
3117
3118 WEBCORE_COMMAND(alignCenter)
3119 WEBCORE_COMMAND(alignJustified)
3120 WEBCORE_COMMAND(alignLeft)
3121 WEBCORE_COMMAND(alignRight)
3122 WEBCORE_COMMAND(copy)
3123 WEBCORE_COMMAND(cut)
3124 WEBCORE_COMMAND(delete)
3125 WEBCORE_COMMAND(deleteBackward)
3126 WEBCORE_COMMAND(deleteBackwardByDecomposingPreviousCharacter)
3127 WEBCORE_COMMAND(deleteForward)
3128 WEBCORE_COMMAND(deleteToBeginningOfLine)
3129 WEBCORE_COMMAND(deleteToBeginningOfParagraph)
3130 WEBCORE_COMMAND(deleteToEndOfLine)
3131 WEBCORE_COMMAND(deleteToEndOfParagraph)
3132 WEBCORE_COMMAND(deleteToMark)
3133 WEBCORE_COMMAND(deleteWordBackward)
3134 WEBCORE_COMMAND(deleteWordForward)
3135 WEBCORE_COMMAND(ignoreSpelling)
3136 WEBCORE_COMMAND(indent)
3137 WEBCORE_COMMAND(insertBacktab)
3138 WEBCORE_COMMAND(insertLineBreak)
3139 WEBCORE_COMMAND(insertNewline)
3140 WEBCORE_COMMAND(insertNewlineIgnoringFieldEditor)
3141 WEBCORE_COMMAND(insertParagraphSeparator)
3142 WEBCORE_COMMAND(insertTab)
3143 WEBCORE_COMMAND(insertTabIgnoringFieldEditor)
3144 WEBCORE_COMMAND(makeTextWritingDirectionLeftToRight)
3145 WEBCORE_COMMAND(makeTextWritingDirectionNatural)
3146 WEBCORE_COMMAND(makeTextWritingDirectionRightToLeft)
3147 WEBCORE_COMMAND(moveBackward)
3148 WEBCORE_COMMAND(moveBackwardAndModifySelection)
3149 WEBCORE_COMMAND(moveDown)
3150 WEBCORE_COMMAND(moveDownAndModifySelection)
3151 WEBCORE_COMMAND(moveForward)
3152 WEBCORE_COMMAND(moveForwardAndModifySelection)
3153 WEBCORE_COMMAND(moveLeft)
3154 WEBCORE_COMMAND(moveLeftAndModifySelection)
3155 WEBCORE_COMMAND(moveParagraphBackwardAndModifySelection)
3156 WEBCORE_COMMAND(moveParagraphForwardAndModifySelection)
3157 WEBCORE_COMMAND(moveRight)
3158 WEBCORE_COMMAND(moveRightAndModifySelection)
3159 WEBCORE_COMMAND(moveToBeginningOfDocument)
3160 WEBCORE_COMMAND(moveToBeginningOfDocumentAndModifySelection)
3161 WEBCORE_COMMAND(moveToBeginningOfLine)
3162 WEBCORE_COMMAND(moveToBeginningOfLineAndModifySelection)
3163 WEBCORE_COMMAND(moveToBeginningOfParagraph)
3164 WEBCORE_COMMAND(moveToBeginningOfParagraphAndModifySelection)
3165 WEBCORE_COMMAND(moveToBeginningOfSentence)
3166 WEBCORE_COMMAND(moveToBeginningOfSentenceAndModifySelection)
3167 WEBCORE_COMMAND(moveToEndOfDocument)
3168 WEBCORE_COMMAND(moveToEndOfDocumentAndModifySelection)
3169 WEBCORE_COMMAND(moveToEndOfLine)
3170 WEBCORE_COMMAND(moveToEndOfLineAndModifySelection)
3171 WEBCORE_COMMAND(moveToEndOfParagraph)
3172 WEBCORE_COMMAND(moveToEndOfParagraphAndModifySelection)
3173 WEBCORE_COMMAND(moveToEndOfSentence)
3174 WEBCORE_COMMAND(moveToEndOfSentenceAndModifySelection)
3175 WEBCORE_COMMAND(moveToLeftEndOfLine)
3176 WEBCORE_COMMAND(moveToLeftEndOfLineAndModifySelection)
3177 WEBCORE_COMMAND(moveToRightEndOfLine)
3178 WEBCORE_COMMAND(moveToRightEndOfLineAndModifySelection)
3179 WEBCORE_COMMAND(moveUp)
3180 WEBCORE_COMMAND(moveUpAndModifySelection)
3181 WEBCORE_COMMAND(moveWordBackward)
3182 WEBCORE_COMMAND(moveWordBackwardAndModifySelection)
3183 WEBCORE_COMMAND(moveWordForward)
3184 WEBCORE_COMMAND(moveWordForwardAndModifySelection)
3185 WEBCORE_COMMAND(moveWordLeft)
3186 WEBCORE_COMMAND(moveWordLeftAndModifySelection)
3187 WEBCORE_COMMAND(moveWordRight)
3188 WEBCORE_COMMAND(moveWordRightAndModifySelection)
3189 WEBCORE_COMMAND(outdent)
3190 WEBCORE_COMMAND(pageDown)
3191 WEBCORE_COMMAND(pageDownAndModifySelection)
3192 WEBCORE_COMMAND(pageUp)
3193 WEBCORE_COMMAND(pageUpAndModifySelection)
3194 WEBCORE_COMMAND(paste)
3195 WEBCORE_COMMAND(pasteAsPlainText)
3196 WEBCORE_COMMAND(scrollPageDown)
3197 WEBCORE_COMMAND(scrollPageUp)
3198 WEBCORE_COMMAND(scrollLineDown)
3199 WEBCORE_COMMAND(scrollLineUp)
3200 WEBCORE_COMMAND(scrollToBeginningOfDocument)
3201 WEBCORE_COMMAND(scrollToEndOfDocument)
3202 WEBCORE_COMMAND(selectAll)
3203 WEBCORE_COMMAND(selectLine)
3204 WEBCORE_COMMAND(selectParagraph)
3205 WEBCORE_COMMAND(selectSentence)
3206 WEBCORE_COMMAND(selectToMark)
3207 WEBCORE_COMMAND(selectWord)
3208 WEBCORE_COMMAND(setMark)
3209 WEBCORE_COMMAND(subscript)
3210 WEBCORE_COMMAND(superscript)
3211 WEBCORE_COMMAND(swapWithMark)
3212 WEBCORE_COMMAND(takeFindStringFromSelection)
3213 WEBCORE_COMMAND(transpose)
3214 WEBCORE_COMMAND(underline)
3215 WEBCORE_COMMAND(unscript)
3216 WEBCORE_COMMAND(yank)
3217 WEBCORE_COMMAND(yankAndSelect)
3218
3219 #undef WEBCORE_COMMAND
3220
3221 - (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pasteboard types:(NSArray *)types
3222 {
3223     return _impl->writeSelectionToPasteboard(pasteboard, types);
3224 }
3225
3226 - (void)centerSelectionInVisibleArea:(id)sender
3227 {
3228     _impl->centerSelectionInVisibleArea();
3229 }
3230
3231 - (id)validRequestorForSendType:(NSString *)sendType returnType:(NSString *)returnType
3232 {
3233     return _impl->validRequestorForSendAndReturnTypes(sendType, returnType);
3234 }
3235
3236 - (BOOL)readSelectionFromPasteboard:(NSPasteboard *)pasteboard
3237 {
3238     return _impl->readSelectionFromPasteboard(pasteboard);
3239 }
3240
3241 - (void)changeFont:(id)sender
3242 {
3243     _impl->changeFontFromFontPanel();
3244 }
3245
3246 - (IBAction)startSpeaking:(id)sender
3247 {
3248     _impl->startSpeaking();
3249 }
3250
3251 - (IBAction)stopSpeaking:(id)sender
3252 {
3253     _impl->stopSpeaking(sender);
3254 }
3255
3256 - (IBAction)showGuessPanel:(id)sender
3257 {
3258     _impl->showGuessPanel(sender);
3259 }
3260
3261 - (IBAction)checkSpelling:(id)sender
3262 {
3263     _impl->checkSpelling();
3264 }
3265
3266 - (void)changeSpelling:(id)sender
3267 {
3268     _impl->changeSpelling(sender);
3269 }
3270
3271 - (IBAction)toggleContinuousSpellChecking:(id)sender
3272 {
3273     _impl->toggleContinuousSpellChecking();
3274 }
3275
3276 - (BOOL)isGrammarCheckingEnabled
3277 {
3278     return _impl->isGrammarCheckingEnabled();
3279 }
3280
3281 - (void)setGrammarCheckingEnabled:(BOOL)flag
3282 {
3283     _impl->setGrammarCheckingEnabled(flag);
3284 }
3285
3286 - (IBAction)toggleGrammarChecking:(id)sender
3287 {
3288     _impl->toggleGrammarChecking();
3289 }
3290
3291 - (IBAction)toggleAutomaticSpellingCorrection:(id)sender
3292 {
3293     _impl->toggleAutomaticSpellingCorrection();
3294 }
3295
3296 - (void)orderFrontSubstitutionsPanel:(id)sender
3297 {
3298     _impl->orderFrontSubstitutionsPanel(sender);
3299 }
3300
3301 - (IBAction)toggleSmartInsertDelete:(id)sender
3302 {
3303     _impl->toggleSmartInsertDelete();
3304 }
3305
3306 - (BOOL)isAutomaticQuoteSubstitutionEnabled
3307 {
3308     return _impl->isAutomaticQuoteSubstitutionEnabled();
3309 }
3310
3311 - (void)setAutomaticQuoteSubstitutionEnabled:(BOOL)flag
3312 {
3313     _impl->setAutomaticQuoteSubstitutionEnabled(flag);
3314 }
3315
3316 - (void)toggleAutomaticQuoteSubstitution:(id)sender
3317 {
3318     _impl->toggleAutomaticQuoteSubstitution();
3319 }
3320
3321 - (BOOL)isAutomaticDashSubstitutionEnabled
3322 {
3323     return _impl->isAutomaticDashSubstitutionEnabled();
3324 }
3325
3326 - (void)setAutomaticDashSubstitutionEnabled:(BOOL)flag
3327 {
3328     _impl->setAutomaticDashSubstitutionEnabled(flag);
3329 }
3330
3331 - (void)toggleAutomaticDashSubstitution:(id)sender
3332 {
3333     _impl->toggleAutomaticDashSubstitution();
3334 }
3335
3336 - (BOOL)isAutomaticLinkDetectionEnabled
3337 {
3338     return _impl->isAutomaticLinkDetectionEnabled();
3339 }
3340
3341 - (void)setAutomaticLinkDetectionEnabled:(BOOL)flag
3342 {
3343     _impl->setAutomaticLinkDetectionEnabled(flag);
3344 }
3345
3346 - (void)toggleAutomaticLinkDetection:(id)sender
3347 {
3348     _impl->toggleAutomaticLinkDetection();
3349 }
3350
3351 - (BOOL)isAutomaticTextReplacementEnabled
3352 {
3353     return _impl->isAutomaticTextReplacementEnabled();
3354 }
3355
3356 - (void)setAutomaticTextReplacementEnabled:(BOOL)flag
3357 {
3358     _impl->setAutomaticTextReplacementEnabled(flag);
3359 }
3360
3361 - (void)toggleAutomaticTextReplacement:(id)sender
3362 {
3363     _impl->toggleAutomaticTextReplacement();
3364 }
3365
3366 - (void)uppercaseWord:(id)sender
3367 {
3368     _impl->uppercaseWord();
3369 }
3370
3371 - (void)lowercaseWord:(id)sender
3372 {
3373     _impl->lowercaseWord();
3374 }
3375
3376 - (void)capitalizeWord:(id)sender
3377 {
3378     _impl->capitalizeWord();
3379 }
3380
3381 - (BOOL)_wantsKeyDownForEvent:(NSEvent *)event
3382 {
3383     return _impl->wantsKeyDownForEvent(event);
3384 }
3385
3386 - (void)scrollWheel:(NSEvent *)event
3387 {
3388     _impl->scrollWheel(event);
3389 }
3390
3391 - (void)swipeWithEvent:(NSEvent *)event
3392 {
3393     _impl->swipeWithEvent(event);
3394 }
3395
3396 - (void)mouseMoved:(NSEvent *)event
3397 {
3398     _impl->mouseMoved(event);
3399 }
3400
3401 - (void)mouseDown:(NSEvent *)event
3402 {
3403     _impl->mouseDown(event);
3404 }
3405
3406 - (void)mouseUp:(NSEvent *)event
3407 {
3408     _impl->mouseUp(event);
3409 }
3410
3411 - (void)mouseDragged:(NSEvent *)event
3412 {
3413     _impl->mouseDragged(event);
3414 }
3415
3416 - (void)mouseEntered:(NSEvent *)event
3417 {
3418     _impl->mouseEntered(event);
3419 }
3420
3421 - (void)mouseExited:(NSEvent *)event
3422 {
3423     _impl->mouseExited(event);
3424 }
3425
3426 - (void)otherMouseDown:(NSEvent *)event
3427 {
3428     _impl->otherMouseDown(event);
3429 }
3430
3431 - (void)otherMouseDragged:(NSEvent *)event
3432 {
3433     _impl->otherMouseDragged(event);
3434 }
3435
3436 - (void)otherMouseUp:(NSEvent *)event
3437 {
3438     _impl->otherMouseUp(event);
3439 }
3440
3441 - (void)rightMouseDown:(NSEvent *)event
3442 {
3443     _impl->rightMouseDown(event);
3444 }
3445
3446 - (void)rightMouseDragged:(NSEvent *)event
3447 {
3448     _impl->rightMouseDragged(event);
3449 }
3450
3451 - (void)rightMouseUp:(NSEvent *)event
3452 {
3453     _impl->rightMouseUp(event);
3454 }
3455
3456 - (void)pressureChangeWithEvent:(NSEvent *)event
3457 {
3458     _impl->pressureChangeWithEvent(event);
3459 }
3460
3461 - (BOOL)acceptsFirstMouse:(NSEvent *)event
3462 {
3463     return _impl->acceptsFirstMouse(event);
3464 }
3465
3466 - (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent *)event
3467 {
3468     return _impl->shouldDelayWindowOrderingForEvent(event);
3469 }
3470
3471 - (void)doCommandBySelector:(SEL)selector
3472 {
3473     _impl->doCommandBySelector(selector);
3474 }
3475
3476 - (void)insertText:(id)string
3477 {
3478     _impl->insertText(string);
3479 }
3480
3481 - (void)insertText:(id)string replacementRange:(NSRange)replacementRange
3482 {
3483     _impl->insertText(string, replacementRange);
3484 }
3485
3486 - (NSTextInputContext *)inputContext
3487 {
3488     if (!_impl)
3489         return nil;
3490     return _impl->inputContext();