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