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