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