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