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