47ae4cb87a3cfb302c14b22eebece83c010cd762
[WebKit-https.git] / Source / WebKit / UIProcess / API / Cocoa / WKWebView.mm
1 /*
2  * Copyright (C) 2014-2017 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "WKWebViewInternal.h"
28
29 #if WK_API_ENABLED
30
31 #import "APIFormClient.h"
32 #import "APIPageConfiguration.h"
33 #import "APISerializedScriptValue.h"
34 #import "CompletionHandlerCallChecker.h"
35 #import "DiagnosticLoggingClient.h"
36 #import "FindClient.h"
37 #import "FullscreenClient.h"
38 #import "IconLoadingDelegate.h"
39 #import "LegacySessionStateCoding.h"
40 #import "Logging.h"
41 #import "NavigationState.h"
42 #import "ObjCObjectGraph.h"
43 #import "RemoteLayerTreeScrollingPerformanceData.h"
44 #import "RemoteLayerTreeTransaction.h"
45 #import "RemoteObjectRegistry.h"
46 #import "RemoteObjectRegistryMessages.h"
47 #import "UIDelegate.h"
48 #import "VersionChecks.h"
49 #import "ViewGestureController.h"
50 #import "ViewSnapshotStore.h"
51 #import "WKBackForwardListInternal.h"
52 #import "WKBackForwardListItemInternal.h"
53 #import "WKBrowsingContextHandleInternal.h"
54 #import "WKDragDestinationAction.h"
55 #import "WKErrorInternal.h"
56 #import "WKHistoryDelegatePrivate.h"
57 #import "WKLayoutMode.h"
58 #import "WKNSData.h"
59 #import "WKNSURLExtras.h"
60 #import "WKNavigationDelegate.h"
61 #import "WKNavigationInternal.h"
62 #import "WKPreferencesInternal.h"
63 #import "WKProcessPoolInternal.h"
64 #import "WKSharedAPICast.h"
65 #import "WKSnapshotConfiguration.h"
66 #import "WKUIDelegate.h"
67 #import "WKUIDelegatePrivate.h"
68 #import "WKUserContentControllerInternal.h"
69 #import "WKWebViewConfigurationInternal.h"
70 #import "WKWebViewContentProvider.h"
71 #import "WKWebsiteDataStoreInternal.h"
72 #import "WebBackForwardList.h"
73 #import "WebCertificateInfo.h"
74 #import "WebFullScreenManagerProxy.h"
75 #import "WebPageGroup.h"
76 #import "WebPageProxy.h"
77 #import "WebPaymentCoordinatorProxy.h"
78 #import "WebPreferencesKeys.h"
79 #import "WebProcessPool.h"
80 #import "WebProcessProxy.h"
81 #import "WebURLSchemeHandlerCocoa.h"
82 #import "WebViewImpl.h"
83 #import "_WKActivatedElementInfoInternal.h"
84 #import "_WKDiagnosticLoggingDelegate.h"
85 #import "_WKFindDelegate.h"
86 #import "_WKFrameHandleInternal.h"
87 #import "_WKFullscreenDelegate.h"
88 #import "_WKHitTestResultInternal.h"
89 #import "_WKInputDelegate.h"
90 #import "_WKRemoteObjectRegistryInternal.h"
91 #import "_WKSessionStateInternal.h"
92 #import "_WKVisitedLinkStoreInternal.h"
93 #import "_WKWebsitePoliciesInternal.h"
94 #import <WebCore/AttachmentTypes.h>
95 #import <WebCore/GraphicsContextCG.h>
96 #import <WebCore/IOSurface.h>
97 #import <WebCore/JSDOMBinding.h>
98 #import <WebCore/JSDOMExceptionHandling.h>
99 #import <WebCore/PlatformScreen.h>
100 #import <WebCore/RuntimeApplicationChecks.h>
101 #import <WebCore/SQLiteDatabaseTracker.h>
102 #import <WebCore/SchemeRegistry.h>
103 #import <WebCore/Settings.h>
104 #import <WebCore/SharedBuffer.h>
105 #import <WebCore/ValidationBubble.h>
106 #import <WebCore/ViewportArguments.h>
107 #import <WebCore/WritingMode.h>
108 #import <pal/spi/cocoa/NSKeyedArchiverSPI.h>
109 #import <pal/spi/mac/NSTextFinderSPI.h>
110 #import <wtf/BlockPtr.h>
111 #import <wtf/HashMap.h>
112 #import <wtf/MathExtras.h>
113 #import <wtf/NeverDestroyed.h>
114 #import <wtf/Optional.h>
115 #import <wtf/RetainPtr.h>
116 #import <wtf/SetForScope.h>
117 #import <wtf/UUID.h>
118 #import <wtf/spi/darwin/dyldSPI.h>
119 #import <wtf/text/TextStream.h>
120
121 #if ENABLE(APPLICATION_MANIFEST)
122 #import "_WKApplicationManifestInternal.h"
123 #endif
124
125 #if ENABLE(DATA_DETECTION)
126 #import "WKDataDetectorTypesInternal.h"
127 #endif
128
129 #if PLATFORM(IOS)
130 #import "InteractionInformationAtPosition.h"
131 #import "InteractionInformationRequest.h"
132 #import "ProcessThrottler.h"
133 #import "RemoteLayerTreeDrawingAreaProxy.h"
134 #import "RemoteScrollingCoordinatorProxy.h"
135 #import "UIKitSPI.h"
136 #import "VideoFullscreenManagerProxy.h"
137 #import "WKContentViewInteraction.h"
138 #import "WKPDFView.h"
139 #import "WKPasswordView.h"
140 #import "WKScrollView.h"
141 #import "WKWebViewContentProviderRegistry.h"
142 #import "_WKWebViewPrintFormatter.h"
143 #import <UIKit/UIApplication.h>
144 #import <WebCore/FrameLoaderTypes.h>
145 #import <WebCore/InspectorOverlay.h>
146 #import <WebCore/ScrollableArea.h>
147 #import <WebCore/WebBackgroundTaskController.h>
148 #import <WebCore/WebSQLiteDatabaseTrackerClient.h>
149 #import <pal/spi/cg/CoreGraphicsSPI.h>
150 #import <pal/spi/cocoa/QuartzCoreSPI.h>
151
152 #if __has_include(<AccessibilitySupport.h>)
153 #include <AccessibilitySupport.h>
154 #else
155 extern "C" {
156 CFStringRef kAXSAllowForceWebScalingEnabledNotification;
157 }
158 #endif
159
160 #define RELEASE_LOG_IF_ALLOWED(...) RELEASE_LOG_IF(_page && _page->isAlwaysOnLoggingAllowed(), ViewState, __VA_ARGS__)
161
162 @interface UIScrollView (UIScrollViewInternal)
163 - (void)_adjustForAutomaticKeyboardInfo:(NSDictionary*)info animated:(BOOL)animated lastAdjustment:(CGFloat*)lastAdjustment;
164 - (BOOL)_isScrollingToTop;
165 - (CGPoint)_animatedTargetOffset;
166 @end
167
168 @interface UIPeripheralHost(UIKitInternal)
169 - (CGFloat)getVerticalOverlapForView:(UIView *)view usingKeyboardInfo:(NSDictionary *)info;
170 @end
171
172 @interface UIView (UIViewInternal)
173 - (UIViewController *)_viewControllerForAncestor;
174 @end
175
176 @interface UIWindow (UIWindowInternal)
177 - (BOOL)_isHostedInAnotherProcess;
178 @end
179
180 @interface UIViewController (UIViewControllerInternal)
181 - (UIViewController *)_rootAncestorViewController;
182 - (UIViewController *)_viewControllerForSupportedInterfaceOrientations;
183 @end
184
185 enum class DynamicViewportUpdateMode {
186     NotResizing,
187     ResizingWithAnimation,
188     ResizingWithDocumentHidden,
189 };
190
191 #endif // PLATFORM(IOS)
192
193 #if PLATFORM(IOS)
194 static const uint32_t firstSDKVersionWithLinkPreviewEnabledByDefault = 0xA0000;
195 #endif
196
197 #if PLATFORM(MAC)
198 #import "WKTextFinderClient.h"
199 #import "WKViewInternal.h"
200 #import <WebCore/ColorMac.h>
201
202 @interface WKWebView () <WebViewImplDelegate, NSTextInputClient>
203 @end
204
205 #if HAVE(TOUCH_BAR)
206 @interface WKWebView () <NSTouchBarProvider>
207 @end
208 #endif // HAVE(TOUCH_BAR)
209 #endif // PLATFORM(MAC)
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     WebKit::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 _overridesMinimumLayoutSize;
276     CGSize _minimumLayoutSizeOverride;
277     std::optional<WebCore::FloatSize> _lastSentMinimumLayoutSize;
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     DynamicViewportUpdateMode _dynamicViewportUpdateMode;
309     CATransform3D _resizeAnimationTransformAdjustments;
310     std::optional<uint64_t> _resizeAnimationTransformTransactionID;
311     RetainPtr<UIView> _resizeAnimationView;
312     CGFloat _lastAdjustmentForScroller;
313     std::optional<CGRect> _frozenVisibleContentRect;
314     std::optional<CGRect> _frozenUnobscuredContentRect;
315
316     BOOL _commitDidRestoreScrollPosition;
317     std::optional<WebCore::FloatPoint> _scrollOffsetToRestore;
318     WebCore::FloatBoxExtent _obscuredInsetsWhenSaved;
319
320     std::optional<WebCore::FloatPoint> _unobscuredCenterToRestore;
321     std::optional<uint64_t> _firstTransactionIDAfterPageRestore;
322     double _scaleToRestore;
323
324     std::unique_ptr<WebKit::ViewGestureController> _gestureController;
325     BOOL _allowsBackForwardNavigationGestures;
326
327     RetainPtr<UIView <WKWebViewContentProvider>> _customContentView;
328     RetainPtr<UIView> _customContentFixedOverlayView;
329
330     RetainPtr<NSTimer> _enclosingScrollViewScrollTimer;
331     BOOL _didScrollSinceLastTimerFire;
332
333     WebCore::Color _scrollViewBackgroundColor;
334
335     // This value tracks the current adjustment added to the bottom inset due to the keyboard sliding out from the bottom
336     // when computing obscured content insets. This is used when updating the visible content rects where we should not
337     // include this adjustment.
338     CGFloat _totalScrollViewBottomInsetAdjustmentForKeyboard;
339     BOOL _currentlyAdjustingScrollViewInsetsForKeyboard;
340
341     BOOL _delayUpdateVisibleContentRects;
342     BOOL _hadDelayedUpdateVisibleContentRects;
343
344     int _activeAnimatedResizeCount;
345
346     Vector<WTF::Function<void ()>> _snapshotsDeferredDuringResize;
347     RetainPtr<NSMutableArray> _stableStatePresentationUpdateCallbacks;
348
349     RetainPtr<WKPasswordView> _passwordView;
350
351     BOOL _hasScheduledVisibleRectUpdate;
352     BOOL _visibleContentRectUpdateScheduledFromScrollViewInStableState;
353     Vector<BlockPtr<void ()>> _visibleContentRectUpdateCallbacks;
354 #endif
355 #if PLATFORM(MAC)
356     std::unique_ptr<WebKit::WebViewImpl> _impl;
357     RetainPtr<WKTextFinderClient> _textFinderClient;
358 #endif
359
360 #if PLATFORM(IOS)
361     _WKDragInteractionPolicy _dragInteractionPolicy;
362 #endif
363 }
364
365 - (instancetype)initWithFrame:(CGRect)frame
366 {
367     return [self initWithFrame:frame configuration:adoptNS([[WKWebViewConfiguration alloc] init]).get()];
368 }
369
370 - (BOOL)_isValid
371 {
372     return _page && _page->isValid();
373 }
374
375 #if PLATFORM(IOS)
376 static int32_t deviceOrientationForUIInterfaceOrientation(UIInterfaceOrientation orientation)
377 {
378     switch (orientation) {
379     case UIInterfaceOrientationUnknown:
380     case UIInterfaceOrientationPortrait:
381         return 0;
382     case UIInterfaceOrientationPortraitUpsideDown:
383         return 180;
384     case UIInterfaceOrientationLandscapeLeft:
385         return -90;
386     case UIInterfaceOrientationLandscapeRight:
387         return 90;
388     }
389 }
390
391 static int32_t deviceOrientation()
392 {
393     return deviceOrientationForUIInterfaceOrientation([[UIApplication sharedApplication] statusBarOrientation]);
394 }
395
396 - (BOOL)_isShowingVideoPictureInPicture
397 {
398 #if !HAVE(AVKIT)
399     return false;
400 #else
401     if (!_page || !_page->videoFullscreenManager())
402         return false;
403
404     return _page->videoFullscreenManager()->hasMode(WebCore::HTMLMediaElementEnums::VideoFullscreenModePictureInPicture);
405 #endif
406 }
407
408 - (BOOL)_mayAutomaticallyShowVideoPictureInPicture
409 {
410 #if !HAVE(AVKIT)
411     return false;
412 #else
413     if (!_page || !_page->videoFullscreenManager())
414         return false;
415
416     return _page->videoFullscreenManager()->mayAutomaticallyShowVideoPictureInPicture();
417 #endif
418 }
419
420 static bool shouldAllowPictureInPictureMediaPlayback()
421 {
422     static bool shouldAllowPictureInPictureMediaPlayback = dyld_get_program_sdk_version() >= DYLD_IOS_VERSION_9_0;
423     return shouldAllowPictureInPictureMediaPlayback;
424 }
425
426 #endif
427
428 static bool shouldRequireUserGestureToLoadVideo()
429 {
430 #if PLATFORM(IOS)
431     static bool shouldRequireUserGestureToLoadVideo = dyld_get_program_sdk_version() >= DYLD_IOS_VERSION_10_0;
432     return shouldRequireUserGestureToLoadVideo;
433 #else
434     return false;
435 #endif
436 }
437
438 #if PLATFORM(MAC)
439 static uint32_t convertUserInterfaceDirectionPolicy(WKUserInterfaceDirectionPolicy policy)
440 {
441     switch (policy) {
442     case WKUserInterfaceDirectionPolicyContent:
443         return static_cast<uint32_t>(WebCore::UserInterfaceDirectionPolicy::Content);
444     case WKUserInterfaceDirectionPolicySystem:
445         return static_cast<uint32_t>(WebCore::UserInterfaceDirectionPolicy::System);
446     }
447     return static_cast<uint32_t>(WebCore::UserInterfaceDirectionPolicy::Content);
448 }
449
450 static uint32_t convertSystemLayoutDirection(NSUserInterfaceLayoutDirection direction)
451 {
452     switch (direction) {
453     case NSUserInterfaceLayoutDirectionLeftToRight:
454         return static_cast<uint32_t>(WebCore::UserInterfaceLayoutDirection::LTR);
455     case NSUserInterfaceLayoutDirectionRightToLeft:
456         return static_cast<uint32_t>(WebCore::UserInterfaceLayoutDirection::RTL);
457     }
458     return static_cast<uint32_t>(WebCore::UserInterfaceLayoutDirection::LTR);
459 }
460 #endif
461
462 static void validate(WKWebViewConfiguration *configuration)
463 {
464     if (!configuration.processPool)
465         [NSException raise:NSInvalidArgumentException format:@"configuration.processPool is nil"];
466     
467     if (!configuration.preferences)
468         [NSException raise:NSInvalidArgumentException format:@"configuration.preferences is nil"];
469     
470     if (!configuration.userContentController)
471         [NSException raise:NSInvalidArgumentException format:@"configuration.userContentController is nil"];
472     
473     if (!configuration.websiteDataStore)
474         [NSException raise:NSInvalidArgumentException format:@"configuration.websiteDataStore is nil"];
475     
476     if (!configuration._visitedLinkStore)
477         [NSException raise:NSInvalidArgumentException format:@"configuration._visitedLinkStore is nil"];
478     
479 #if PLATFORM(IOS)
480     if (!configuration._contentProviderRegistry)
481         [NSException raise:NSInvalidArgumentException format:@"configuration._contentProviderRegistry is nil"];
482 #endif
483 }
484
485 - (void)_initializeWithConfiguration:(WKWebViewConfiguration *)configuration
486 {
487     if (!configuration)
488         [NSException raise:NSInvalidArgumentException format:@"Configuration cannot be nil"];
489
490     _configuration = adoptNS([configuration copy]);
491
492     if (WKWebView *relatedWebView = [_configuration _relatedWebView]) {
493         WKProcessPool *processPool = [_configuration processPool];
494         WKProcessPool *relatedWebViewProcessPool = [relatedWebView->_configuration processPool];
495         if (processPool && processPool != relatedWebViewProcessPool)
496             [NSException raise:NSInvalidArgumentException format:@"Related web view %@ has process pool %@ but configuration specifies a different process pool %@", relatedWebView, relatedWebViewProcessPool, configuration.processPool];
497
498         [_configuration setProcessPool:relatedWebViewProcessPool];
499     }
500
501     validate(_configuration.get());
502
503     WebKit::WebProcessPool& processPool = *[_configuration processPool]->_processPool;
504     processPool.setResourceLoadStatisticsEnabled(configuration.websiteDataStore._resourceLoadStatisticsEnabled);
505
506     auto pageConfiguration = API::PageConfiguration::create();
507
508     pageConfiguration->setProcessPool(&processPool);
509     pageConfiguration->setPreferences([_configuration preferences]->_preferences.get());
510     if (WKWebView *relatedWebView = [_configuration _relatedWebView])
511         pageConfiguration->setRelatedPage(relatedWebView->_page.get());
512
513     pageConfiguration->setUserContentController([_configuration userContentController]->_userContentControllerProxy.get());
514     pageConfiguration->setVisitedLinkStore([_configuration _visitedLinkStore]->_visitedLinkStore.get());
515     pageConfiguration->setWebsiteDataStore([_configuration websiteDataStore]->_websiteDataStore.get());
516     pageConfiguration->setTreatsSHA1SignedCertificatesAsInsecure([_configuration _treatsSHA1SignedCertificatesAsInsecure]);
517
518     if (NSString *overrideContentSecurityPolicy = configuration._overrideContentSecurityPolicy)
519         pageConfiguration->setOverrideContentSecurityPolicy(overrideContentSecurityPolicy);
520
521 #if PLATFORM(MAC)
522     if (auto pageGroup = WebKit::toImpl([configuration _pageGroup])) {
523         pageConfiguration->setPageGroup(pageGroup);
524         pageConfiguration->setUserContentController(&pageGroup->userContentController());
525     } else
526 #endif
527     {
528         NSString *groupIdentifier = configuration._groupIdentifier;
529         if (groupIdentifier.length)
530             pageConfiguration->setPageGroup(WebKit::WebPageGroup::create(configuration._groupIdentifier).ptr());
531     }
532
533     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::suppressesIncrementalRenderingKey(), WebKit::WebPreferencesStore::Value(!![_configuration suppressesIncrementalRendering]));
534
535     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::shouldRespectImageOrientationKey(), WebKit::WebPreferencesStore::Value(!![_configuration _respectsImageOrientation]));
536     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::shouldPrintBackgroundsKey(), WebKit::WebPreferencesStore::Value(!![_configuration _printsBackgrounds]));
537     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::incrementalRenderingSuppressionTimeoutKey(), WebKit::WebPreferencesStore::Value([_configuration _incrementalRenderingSuppressionTimeout]));
538     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::javaScriptMarkupEnabledKey(), WebKit::WebPreferencesStore::Value(!![_configuration _allowsJavaScriptMarkup]));
539     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::shouldConvertPositionStyleOnCopyKey(), WebKit::WebPreferencesStore::Value(!![_configuration _convertsPositionStyleOnCopy]));
540     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::httpEquivEnabledKey(), WebKit::WebPreferencesStore::Value(!![_configuration _allowsMetaRefresh]));
541     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::allowUniversalAccessFromFileURLsKey(), WebKit::WebPreferencesStore::Value(!![_configuration _allowUniversalAccessFromFileURLs]));
542     pageConfiguration->setInitialCapitalizationEnabled([_configuration _initialCapitalizationEnabled]);
543     pageConfiguration->setWaitsForPaintAfterViewDidMoveToWindow([_configuration _waitsForPaintAfterViewDidMoveToWindow]);
544     pageConfiguration->setControlledByAutomation([_configuration _isControlledByAutomation]);
545
546 #if ENABLE(APPLICATION_MANIFEST)
547     pageConfiguration->setApplicationManifest([_configuration _applicationManifest] ? [configuration _applicationManifest]->_applicationManifest.get() : nullptr);
548 #endif
549
550 #if PLATFORM(MAC)
551     if (auto cpuLimit = [_configuration _cpuLimit])
552         pageConfiguration->setCPULimit(cpuLimit);
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->setAlwaysRunsAtForegroundPriority([_configuration _alwaysRunsAtForegroundPriority]);
566
567     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::allowsInlineMediaPlaybackKey(), WebKit::WebPreferencesStore::Value(!![_configuration allowsInlineMediaPlayback]));
568     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::allowsInlineMediaPlaybackAfterFullscreenKey(), WebKit::WebPreferencesStore::Value(!![_configuration _allowsInlineMediaPlaybackAfterFullscreen]));
569     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::inlineMediaPlaybackRequiresPlaysInlineAttributeKey(), WebKit::WebPreferencesStore::Value(!![_configuration _inlineMediaPlaybackRequiresPlaysInlineAttribute]));
570     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::allowsPictureInPictureMediaPlaybackKey(), WebKit::WebPreferencesStore::Value(!![_configuration allowsPictureInPictureMediaPlayback] && shouldAllowPictureInPictureMediaPlayback()));
571     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::userInterfaceDirectionPolicyKey(), WebKit::WebPreferencesStore::Value(static_cast<uint32_t>(WebCore::UserInterfaceDirectionPolicy::Content)));
572     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::systemLayoutDirectionKey(), WebKit::WebPreferencesStore::Value(static_cast<uint32_t>(WebCore::LTR)));
573 #endif
574
575     WKAudiovisualMediaTypes mediaTypesRequiringUserGesture = [_configuration mediaTypesRequiringUserActionForPlayback];
576     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::requiresUserGestureForVideoPlaybackKey(), WebKit::WebPreferencesStore::Value((mediaTypesRequiringUserGesture & WKAudiovisualMediaTypeVideo) == WKAudiovisualMediaTypeVideo));
577     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::requiresUserGestureForAudioPlaybackKey(), WebKit::WebPreferencesStore::Value(((mediaTypesRequiringUserGesture & WKAudiovisualMediaTypeAudio) == WKAudiovisualMediaTypeAudio)));
578     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::requiresUserGestureToLoadVideoKey(), WebKit::WebPreferencesStore::Value(shouldRequireUserGestureToLoadVideo()));
579     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::mainContentUserGestureOverrideEnabledKey(), WebKit::WebPreferencesStore::Value(!![_configuration _mainContentUserGestureOverrideEnabled]));
580     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::invisibleAutoplayNotPermittedKey(), WebKit::WebPreferencesStore::Value(!![_configuration _invisibleAutoplayNotPermitted]));
581     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::mediaDataLoadsAutomaticallyKey(), WebKit::WebPreferencesStore::Value(!![_configuration _mediaDataLoadsAutomatically]));
582     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::attachmentElementEnabledKey(), WebKit::WebPreferencesStore::Value(!![_configuration _attachmentElementEnabled]));
583
584 #if ENABLE(DATA_DETECTION) && PLATFORM(IOS)
585     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::dataDetectorTypesKey(), WebKit::WebPreferencesStore::Value(static_cast<uint32_t>(fromWKDataDetectorTypes([_configuration dataDetectorTypes]))));
586 #endif
587 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
588     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::allowsAirPlayForMediaPlaybackKey(), WebKit::WebPreferencesStore::Value(!![_configuration allowsAirPlayForMediaPlayback]));
589 #endif
590
591 #if ENABLE(APPLE_PAY)
592     bool applePayEnabled = [_configuration _applePayEnabled] && WebKit::WebPaymentCoordinatorProxy::platformSupportsPayments();
593     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::applePayEnabledKey(), WebKit::WebPreferencesStore::Value(applePayEnabled));
594 #endif
595
596     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::needsStorageAccessFromFileURLsQuirkKey(), WebKit::WebPreferencesStore::Value(!![_configuration _needsStorageAccessFromFileURLsQuirk]));
597     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::mediaContentTypesRequiringHardwareSupportKey(), WebKit::WebPreferencesStore::Value(String([_configuration _mediaContentTypesRequiringHardwareSupport])));
598     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::allowMediaContentTypesRequiringHardwareSupportAsFallbackKey(), WebKit::WebPreferencesStore::Value(!![_configuration _allowMediaContentTypesRequiringHardwareSupportAsFallback]));
599
600 #if ENABLE(LEGACY_ENCRYPTED_MEDIA)
601     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::legacyEncryptedMediaAPIEnabledKey(), WebKit::WebPreferencesStore::Value(!![_configuration _legacyEncryptedMediaAPIEnabled]));
602 #endif
603
604 #if PLATFORM(IOS)
605     CGRect bounds = self.bounds;
606     _scrollView = adoptNS([[WKScrollView alloc] initWithFrame:bounds]);
607     [_scrollView setInternalDelegate:self];
608     [_scrollView setBouncesZoom:YES];
609
610     _avoidsUnsafeArea = YES;
611     [self _updateScrollViewInsetAdjustmentBehavior];
612
613     [self addSubview:_scrollView.get()];
614
615     static uint32_t programSDKVersion = dyld_get_program_sdk_version();
616     _allowsLinkPreview = programSDKVersion >= firstSDKVersionWithLinkPreviewEnabledByDefault;
617
618     _contentView = adoptNS([[WKContentView alloc] initWithFrame:bounds processPool:processPool configuration:WTFMove(pageConfiguration) webView:self]);
619
620     _page = [_contentView page];
621     [self _dispatchSetDeviceOrientation:deviceOrientation()];
622     _page->setDrawsBackground(self.opaque);
623
624     [_contentView layer].anchorPoint = CGPointZero;
625     [_contentView setFrame:bounds];
626     [_scrollView addSubview:_contentView.get()];
627     [_scrollView addSubview:[_contentView unscaledView]];
628     [self _updateScrollViewBackground];
629     _obscuredInsetEdgesAffectedBySafeArea = UIRectEdgeTop | UIRectEdgeLeft | UIRectEdgeRight;
630
631     _viewportMetaTagWidth = WebCore::ViewportArguments::ValueAuto;
632     _initialScaleFactor = 1;
633     _fastClickingIsDisabled = [[NSUserDefaults standardUserDefaults] boolForKey:@"WebKitFastClickingDisabled"];
634
635     [self _frameOrBoundsChanged];
636
637     NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
638     [center addObserver:self selector:@selector(_keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];
639     [center addObserver:self selector:@selector(_keyboardDidChangeFrame:) name:UIKeyboardDidChangeFrameNotification object:nil];
640     [center addObserver:self selector:@selector(_keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
641     [center addObserver:self selector:@selector(_keyboardDidShow:) name:UIKeyboardDidShowNotification object:nil];
642     [center addObserver:self selector:@selector(_keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
643     [center addObserver:self selector:@selector(_windowDidRotate:) name:UIWindowDidRotateNotification object:nil];
644     [center addObserver:self selector:@selector(_contentSizeCategoryDidChange:) name:UIContentSizeCategoryDidChangeNotification object:nil];
645     _page->contentSizeCategoryDidChange([self _contentSizeCategory]);
646
647     [center addObserver:self selector:@selector(_accessibilitySettingsDidChange:) name:UIAccessibilityGrayscaleStatusDidChangeNotification object:nil];
648     [center addObserver:self selector:@selector(_accessibilitySettingsDidChange:) name:UIAccessibilityInvertColorsStatusDidChangeNotification object:nil];
649     [center addObserver:self selector:@selector(_accessibilitySettingsDidChange:) name:UIAccessibilityReduceMotionStatusDidChangeNotification object:nil];
650
651     [[_configuration _contentProviderRegistry] addPage:*_page];
652     _page->setForceAlwaysUserScalable([_configuration ignoresViewportScaleLimits]);
653 #endif
654
655 #if PLATFORM(MAC)
656     _impl = std::make_unique<WebKit::WebViewImpl>(self, self, processPool, WTFMove(pageConfiguration));
657     _page = &_impl->page();
658
659     _impl->setAutomaticallyAdjustsContentInsets(true);
660     _impl->setRequiresUserActionForEditingControlsManager([configuration _requiresUserActionForEditingControlsManager]);
661 #endif
662
663     _page->setBackgroundExtendsBeyondPage(true);
664
665     if (NSString *applicationNameForUserAgent = configuration.applicationNameForUserAgent)
666         _page->setApplicationNameForUserAgent(applicationNameForUserAgent);
667
668     _navigationState = std::make_unique<WebKit::NavigationState>(self);
669     _page->setNavigationClient(_navigationState->createNavigationClient());
670
671     _uiDelegate = std::make_unique<WebKit::UIDelegate>(self);
672     _page->setFindClient(std::make_unique<WebKit::FindClient>(self));
673     _page->setDiagnosticLoggingClient(std::make_unique<WebKit::DiagnosticLoggingClient>(self));
674
675     _iconLoadingDelegate = std::make_unique<WebKit::IconLoadingDelegate>(self);
676
677 #if PLATFORM(IOS)
678     [self _setUpSQLiteDatabaseTrackerClient];
679 #endif
680
681     auto *handlers = _configuration.get()._urlSchemeHandlers;
682     for (NSString *key in handlers)
683         _page->setURLSchemeHandlerForScheme(WebKit::WebURLSchemeHandlerCocoa::create(handlers[key]), key);
684
685     pageToViewMap().add(_page.get(), self);
686
687 #if PLATFORM(IOS)
688     _dragInteractionPolicy = _WKDragInteractionPolicyDefault;
689 #endif
690 }
691
692 - (void)_setUpSQLiteDatabaseTrackerClient
693 {
694 #if PLATFORM(IOS)
695     WebBackgroundTaskController *controller = [WebBackgroundTaskController sharedController];
696     if (controller.backgroundTaskStartBlock)
697         return;
698
699     controller.backgroundTaskStartBlock = ^NSUInteger (void (^expirationHandler)())
700     {
701         return [[UIApplication sharedApplication] beginBackgroundTaskWithName:@"com.apple.WebKit.DatabaseActivity" expirationHandler:expirationHandler];
702     };
703     controller.backgroundTaskEndBlock = ^(UIBackgroundTaskIdentifier taskIdentifier)
704     {
705         [[UIApplication sharedApplication] endBackgroundTask:taskIdentifier];
706     };
707     controller.invalidBackgroundTaskIdentifier = UIBackgroundTaskInvalid;
708
709     WebCore::SQLiteDatabaseTracker::setClient(&WebCore::WebSQLiteDatabaseTrackerClient::sharedWebSQLiteDatabaseTrackerClient());
710 #endif
711 }
712
713 - (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration
714 {
715     if (!(self = [super initWithFrame:frame]))
716         return nil;
717
718     [self _initializeWithConfiguration:configuration];
719
720     return self;
721 }
722
723 - (instancetype)initWithCoder:(NSCoder *)coder
724 {
725     if (!(self = [super initWithCoder:coder]))
726         return nil;
727
728     WKWebViewConfiguration *configuration = decodeObjectOfClassForKeyFromCoder([WKWebViewConfiguration class], @"configuration", coder);
729     [self _initializeWithConfiguration:configuration];
730
731     self.allowsBackForwardNavigationGestures = [coder decodeBoolForKey:@"allowsBackForwardNavigationGestures"];
732     self.customUserAgent = decodeObjectOfClassForKeyFromCoder([NSString class], @"customUserAgent", coder);
733     self.allowsLinkPreview = [coder decodeBoolForKey:@"allowsLinkPreview"];
734
735 #if PLATFORM(MAC)
736     self.allowsMagnification = [coder decodeBoolForKey:@"allowsMagnification"];
737     self.magnification = [coder decodeDoubleForKey:@"magnification"];
738 #endif
739
740     return self;
741 }
742
743 - (void)encodeWithCoder:(NSCoder *)coder
744 {
745     [super encodeWithCoder:coder];
746
747     [coder encodeObject:_configuration.get() forKey:@"configuration"];
748
749     [coder encodeBool:self.allowsBackForwardNavigationGestures forKey:@"allowsBackForwardNavigationGestures"];
750     [coder encodeObject:self.customUserAgent forKey:@"customUserAgent"];
751     [coder encodeBool:self.allowsLinkPreview forKey:@"allowsLinkPreview"];
752
753 #if PLATFORM(MAC)
754     [coder encodeBool:self.allowsMagnification forKey:@"allowsMagnification"];
755     [coder encodeDouble:self.magnification forKey:@"magnification"];
756 #endif
757 }
758
759 - (void)dealloc
760 {
761 #if PLATFORM(MAC)
762     [_textFinderClient willDestroyView:self];
763 #endif
764
765 #if PLATFORM(IOS)
766     [_contentView _webViewDestroyed];
767
768     if (_remoteObjectRegistry)
769         _page->process().processPool().removeMessageReceiver(Messages::RemoteObjectRegistry::messageReceiverName(), _page->pageID());
770 #endif
771
772     _page->close();
773
774 #if PLATFORM(IOS)
775     [_remoteObjectRegistry _invalidate];
776     [[_configuration _contentProviderRegistry] removePage:*_page];
777     CFNotificationCenterRemoveObserver(CFNotificationCenterGetDarwinNotifyCenter(), (__bridge const void *)(self), nullptr, nullptr);
778     [[NSNotificationCenter defaultCenter] removeObserver:self];
779     [_scrollView setInternalDelegate:nil];
780 #endif
781
782     pageToViewMap().remove(_page.get());
783
784     [super dealloc];
785 }
786
787 - (WKWebViewConfiguration *)configuration
788 {
789     return [[_configuration copy] autorelease];
790 }
791
792 - (WKBackForwardList *)backForwardList
793 {
794     return wrapper(_page->backForwardList());
795 }
796
797 - (id <WKNavigationDelegate>)navigationDelegate
798 {
799     return _navigationState->navigationDelegate().autorelease();
800 }
801
802 - (void)setNavigationDelegate:(id <WKNavigationDelegate>)navigationDelegate
803 {
804     _page->setNavigationClient(_navigationState->createNavigationClient());
805     _navigationState->setNavigationDelegate(navigationDelegate);
806 }
807
808 - (id <WKUIDelegate>)UIDelegate
809 {
810     return _uiDelegate->delegate().autorelease();
811 }
812
813 - (void)setUIDelegate:(id<WKUIDelegate>)UIDelegate
814 {
815     _uiDelegate->setDelegate(UIDelegate);
816 #if ENABLE(CONTEXT_MENUS)
817     _page->setContextMenuClient(_uiDelegate->createContextMenuClient());
818 #endif
819     _page->setUIClient(_uiDelegate->createUIClient());
820 }
821
822 - (id <_WKIconLoadingDelegate>)_iconLoadingDelegate
823 {
824     return _iconLoadingDelegate->delegate().autorelease();
825 }
826
827 - (void)_setIconLoadingDelegate:(id<_WKIconLoadingDelegate>)iconLoadingDelegate
828 {
829     _page->setIconLoadingClient(_iconLoadingDelegate->createIconLoadingClient());
830     _iconLoadingDelegate->setDelegate(iconLoadingDelegate);
831 }
832
833 - (WKNavigation *)loadRequest:(NSURLRequest *)request
834 {
835     auto navigation = _page->loadRequest(request);
836     if (!navigation)
837         return nil;
838
839     return [wrapper(*navigation.leakRef()) autorelease];
840 }
841
842 - (WKNavigation *)loadFileURL:(NSURL *)URL allowingReadAccessToURL:(NSURL *)readAccessURL
843 {
844     if (![URL isFileURL])
845         [NSException raise:NSInvalidArgumentException format:@"%@ is not a file URL", URL];
846
847     if (![readAccessURL isFileURL])
848         [NSException raise:NSInvalidArgumentException format:@"%@ is not a file URL", readAccessURL];
849
850     auto navigation = _page->loadFile([URL _web_originalDataAsWTFString], [readAccessURL _web_originalDataAsWTFString]);
851     if (!navigation)
852         return nil;
853
854     return [wrapper(*navigation.leakRef()) autorelease];
855 }
856
857 - (WKNavigation *)loadHTMLString:(NSString *)string baseURL:(NSURL *)baseURL
858 {
859     NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
860
861     return [self loadData:data MIMEType:@"text/html" characterEncodingName:@"UTF-8" baseURL:baseURL];
862 }
863
864 - (WKNavigation *)loadData:(NSData *)data MIMEType:(NSString *)MIMEType characterEncodingName:(NSString *)characterEncodingName baseURL:(NSURL *)baseURL
865 {
866     auto navigation = _page->loadData(API::Data::createWithoutCopying(data).ptr(), MIMEType, characterEncodingName, baseURL.absoluteString);
867     if (!navigation)
868         return nil;
869
870     return [wrapper(*navigation.leakRef()) autorelease];
871 }
872
873 - (WKNavigation *)goToBackForwardListItem:(WKBackForwardListItem *)item
874 {
875     auto navigation = _page->goToBackForwardItem(&item._item);
876     if (!navigation)
877         return nil;
878
879     return [wrapper(*navigation.leakRef()) autorelease];
880 }
881
882 - (NSString *)title
883 {
884     return _page->pageLoadState().title();
885 }
886
887 - (NSURL *)URL
888 {
889     return [NSURL _web_URLWithWTFString:_page->pageLoadState().activeURL()];
890 }
891
892 - (BOOL)isLoading
893 {
894     return _page->pageLoadState().isLoading();
895 }
896
897 - (double)estimatedProgress
898 {
899     return _page->pageLoadState().estimatedProgress();
900 }
901
902 - (BOOL)hasOnlySecureContent
903 {
904     return _page->pageLoadState().hasOnlySecureContent();
905 }
906
907 - (SecTrustRef)serverTrust
908 {
909 #if HAVE(SEC_TRUST_SERIALIZATION)
910     auto certificateInfo = _page->pageLoadState().certificateInfo();
911     if (!certificateInfo)
912         return nil;
913
914     return certificateInfo->certificateInfo().trust();
915 #else
916     return nil;
917 #endif
918 }
919
920 - (BOOL)canGoBack
921 {
922     return _page->pageLoadState().canGoBack();
923 }
924
925 - (BOOL)canGoForward
926 {
927     return _page->pageLoadState().canGoForward();
928 }
929
930 - (WKNavigation *)goBack
931 {
932     auto navigation = _page->goBack();
933     if (!navigation)
934         return nil;
935
936     return [wrapper(*navigation.leakRef()) autorelease];
937 }
938
939 - (WKNavigation *)goForward
940 {
941     auto navigation = _page->goForward();
942     if (!navigation)
943         return nil;
944
945     return [wrapper(*navigation.leakRef()) autorelease];
946 }
947
948 - (WKNavigation *)reload
949 {
950     OptionSet<WebCore::ReloadOption> reloadOptions;
951     if (linkedOnOrAfter(WebKit::SDKVersion::FirstWithExpiredOnlyReloadBehavior))
952         reloadOptions |= WebCore::ReloadOption::ExpiredOnly;
953
954     auto navigation = _page->reload(reloadOptions);
955     if (!navigation)
956         return nil;
957
958     return [wrapper(*navigation.leakRef()) autorelease];
959 }
960
961 - (WKNavigation *)reloadFromOrigin
962 {
963     auto navigation = _page->reload(WebCore::ReloadOption::FromOrigin);
964     if (!navigation)
965         return nil;
966
967     return [wrapper(*navigation.leakRef()) autorelease];
968 }
969
970 - (void)stopLoading
971 {
972     _page->stopLoading();
973 }
974
975 static WKErrorCode callbackErrorCode(WebKit::CallbackBase::Error error)
976 {
977     switch (error) {
978     case WebKit::CallbackBase::Error::None:
979         ASSERT_NOT_REACHED();
980         return WKErrorUnknown;
981
982     case WebKit::CallbackBase::Error::Unknown:
983         return WKErrorUnknown;
984
985     case WebKit::CallbackBase::Error::ProcessExited:
986         return WKErrorWebContentProcessTerminated;
987
988     case WebKit::CallbackBase::Error::OwnerWasInvalidated:
989         return WKErrorWebViewInvalidated;
990     }
991 }
992
993 - (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^)(id, NSError *))completionHandler
994 {
995     [self _evaluateJavaScript:javaScriptString forceUserGesture:YES completionHandler:completionHandler];
996 }
997
998 - (void)_evaluateJavaScript:(NSString *)javaScriptString forceUserGesture:(BOOL)forceUserGesture completionHandler:(void (^)(id, NSError *))completionHandler
999 {
1000     auto handler = adoptNS([completionHandler copy]);
1001
1002     _page->runJavaScriptInMainFrame(javaScriptString, forceUserGesture, [handler](API::SerializedScriptValue* serializedScriptValue, bool hadException, const WebCore::ExceptionDetails& details, WebKit::ScriptValueCallback::Error errorCode) {
1003         if (!handler)
1004             return;
1005
1006         if (errorCode != WebKit::ScriptValueCallback::Error::None) {
1007             auto error = createNSError(callbackErrorCode(errorCode));
1008             if (errorCode == WebKit::ScriptValueCallback::Error::OwnerWasInvalidated) {
1009                 // The OwnerWasInvalidated callback is synchronous. We don't want to call the block from within it
1010                 // because that can trigger re-entrancy bugs in WebKit.
1011                 // FIXME: It would be even better if GenericCallback did this for us.
1012                 dispatch_async(dispatch_get_main_queue(), [handler, error] {
1013                     auto rawHandler = (void (^)(id, NSError *))handler.get();
1014                     rawHandler(nil, error.get());
1015                 });
1016                 return;
1017             }
1018
1019             auto rawHandler = (void (^)(id, NSError *))handler.get();
1020             rawHandler(nil, error.get());
1021             return;
1022         }
1023
1024         auto rawHandler = (void (^)(id, NSError *))handler.get();
1025         if (hadException) {
1026             ASSERT(!serializedScriptValue);
1027
1028             RetainPtr<NSMutableDictionary> userInfo = adoptNS([[NSMutableDictionary alloc] init]);
1029
1030             [userInfo setObject:localizedDescriptionForErrorCode(WKErrorJavaScriptExceptionOccurred) forKey:NSLocalizedDescriptionKey];
1031             [userInfo setObject:static_cast<NSString *>(details.message) forKey:_WKJavaScriptExceptionMessageErrorKey];
1032             [userInfo setObject:@(details.lineNumber) forKey:_WKJavaScriptExceptionLineNumberErrorKey];
1033             [userInfo setObject:@(details.columnNumber) forKey:_WKJavaScriptExceptionColumnNumberErrorKey];
1034
1035             if (!details.sourceURL.isEmpty())
1036                 [userInfo setObject:[NSURL _web_URLWithWTFString:details.sourceURL] forKey:_WKJavaScriptExceptionSourceURLErrorKey];
1037
1038             rawHandler(nil, adoptNS([[NSError alloc] initWithDomain:WKErrorDomain code:WKErrorJavaScriptExceptionOccurred userInfo:userInfo.get()]).get());
1039             return;
1040         }
1041
1042         if (!serializedScriptValue) {
1043             rawHandler(nil, createNSError(WKErrorJavaScriptResultTypeIsUnsupported).get());
1044             return;
1045         }
1046
1047         id body = API::SerializedScriptValue::deserialize(serializedScriptValue->internalRepresentation(), 0);
1048         rawHandler(body, nil);
1049     });
1050 }
1051
1052 #if PLATFORM(MAC)
1053 - (void)takeSnapshotWithConfiguration:(WKSnapshotConfiguration *)snapshotConfiguration completionHandler:(void(^)(NSImage *, NSError *))completionHandler
1054 {
1055     CGRect rectInViewCoordinates = snapshotConfiguration && !CGRectIsNull(snapshotConfiguration.rect) ? snapshotConfiguration.rect : self.bounds;
1056     CGFloat snapshotWidth;
1057     if (snapshotConfiguration)
1058         snapshotWidth = snapshotConfiguration.snapshotWidth.doubleValue ?: rectInViewCoordinates.size.width;
1059     else
1060         snapshotWidth = self.bounds.size.width;
1061
1062     auto handler = makeBlockPtr(completionHandler);
1063     CGFloat imageScale = snapshotWidth / rectInViewCoordinates.size.width;
1064     CGFloat imageHeight = imageScale * rectInViewCoordinates.size.height;
1065
1066     // Need to scale by device scale factor or the image will be distorted.
1067     CGFloat deviceScale = _page->deviceScaleFactor();
1068     WebCore::IntSize bitmapSize(snapshotWidth, imageHeight);
1069     bitmapSize.scale(deviceScale, deviceScale);
1070
1071     // Software snapshot will not capture elements rendered with hardware acceleration (WebGL, video, etc).
1072     _page->takeSnapshot(WebCore::enclosingIntRect(rectInViewCoordinates), bitmapSize, WebKit::SnapshotOptionsInViewCoordinates, [handler, snapshotWidth, imageHeight](const WebKit::ShareableBitmap::Handle& imageHandle, WebKit::CallbackBase::Error errorCode) {
1073         if (errorCode != WebKit::ScriptValueCallback::Error::None) {
1074             auto error = createNSError(callbackErrorCode(errorCode));
1075             handler(nil, error.get());
1076             return;
1077         }
1078
1079         RefPtr<WebKit::ShareableBitmap> bitmap = WebKit::ShareableBitmap::create(imageHandle, WebKit::SharedMemory::Protection::ReadOnly);
1080         RetainPtr<CGImageRef> cgImage = bitmap ? bitmap->makeCGImage() : nullptr;
1081         RetainPtr<NSImage> nsImage = adoptNS([[NSImage alloc] initWithCGImage:cgImage.get() size:NSMakeSize(snapshotWidth, imageHeight)]);
1082         handler(nsImage.get(), nil);
1083     });
1084 }
1085
1086 #elif PLATFORM(IOS)
1087 - (void)takeSnapshotWithConfiguration:(WKSnapshotConfiguration *)snapshotConfiguration completionHandler:(void(^)(UIImage *, NSError *))completionHandler
1088 {
1089     CGRect rectInViewCoordinates = snapshotConfiguration && !CGRectIsNull(snapshotConfiguration.rect) ? snapshotConfiguration.rect : self.bounds;
1090     CGFloat snapshotWidth;
1091     if (snapshotConfiguration)
1092         snapshotWidth = snapshotConfiguration.snapshotWidth.doubleValue ?: rectInViewCoordinates.size.width;
1093     else
1094         snapshotWidth = self.bounds.size.width;
1095
1096     auto handler = makeBlockPtr(completionHandler);
1097     CGFloat deviceScale = _page->deviceScaleFactor();
1098
1099     [self _snapshotRect:rectInViewCoordinates intoImageOfWidth:(snapshotWidth * deviceScale) completionHandler:^(CGImageRef snapshotImage) {
1100         RetainPtr<NSError> error;
1101         RetainPtr<UIImage> uiImage;
1102
1103         if (!snapshotImage)
1104             error = createNSError(WKErrorUnknown);
1105         else
1106             uiImage = adoptNS([[UIImage alloc] initWithCGImage:snapshotImage scale:deviceScale orientation:UIImageOrientationUp]);
1107
1108         handler(uiImage.get(), error.get());
1109     }];
1110 }
1111 #endif
1112
1113 - (NSString *)customUserAgent
1114 {
1115     return _page->customUserAgent();
1116 }
1117
1118 - (void)setCustomUserAgent:(NSString *)customUserAgent
1119 {
1120     _page->setCustomUserAgent(customUserAgent);
1121 }
1122
1123 - (WKPageRef)_pageForTesting
1124 {
1125     return toAPI(_page.get());
1126 }
1127
1128 - (WebKit::WebPageProxy *)_page
1129 {
1130     return _page.get();
1131 }
1132
1133 - (BOOL)allowsLinkPreview
1134 {
1135 #if PLATFORM(MAC)
1136     return _impl->allowsLinkPreview();
1137 #elif PLATFORM(IOS)
1138     return _allowsLinkPreview;
1139 #endif
1140 }
1141
1142 - (void)setAllowsLinkPreview:(BOOL)allowsLinkPreview
1143 {
1144 #if PLATFORM(MAC)
1145     _impl->setAllowsLinkPreview(allowsLinkPreview);
1146     return;
1147 #elif PLATFORM(IOS)
1148     if (_allowsLinkPreview == allowsLinkPreview)
1149         return;
1150
1151     _allowsLinkPreview = allowsLinkPreview;
1152
1153 #if HAVE(LINK_PREVIEW)
1154     if (_allowsLinkPreview)
1155         [_contentView _registerPreview];
1156     else
1157         [_contentView _unregisterPreview];
1158 #endif // HAVE(LINK_PREVIEW)
1159 #endif // PLATFORM(IOS)
1160 }
1161
1162 - (CGSize)_viewportSizeForCSSViewportUnits
1163 {
1164     return _page->viewportSizeForCSSViewportUnits();
1165 }
1166
1167 - (void)_setViewportSizeForCSSViewportUnits:(CGSize)viewportSize
1168 {
1169     auto viewportSizeForViewportUnits = WebCore::IntSize(viewportSize);
1170     if (viewportSizeForViewportUnits.isEmpty())
1171         [NSException raise:NSInvalidArgumentException format:@"Viewport size should not be empty"];
1172
1173     _page->setViewportSizeForCSSViewportUnits(viewportSizeForViewportUnits);
1174 }
1175
1176 static NSTextAlignment nsTextAlignment(WebKit::TextAlignment alignment)
1177 {
1178     switch (alignment) {
1179     case WebKit::NoAlignment:
1180         return NSTextAlignmentNatural;
1181     case WebKit::LeftAlignment:
1182         return NSTextAlignmentLeft;
1183     case WebKit::RightAlignment:
1184         return NSTextAlignmentRight;
1185     case WebKit::CenterAlignment:
1186         return NSTextAlignmentCenter;
1187     case WebKit::JustifiedAlignment:
1188         return NSTextAlignmentJustified;
1189     }
1190     ASSERT_NOT_REACHED();
1191     return NSTextAlignmentNatural;
1192 }
1193
1194 static NSDictionary *dictionaryRepresentationForEditorState(const WebKit::EditorState& state)
1195 {
1196     if (state.isMissingPostLayoutData)
1197         return @{ @"post-layout-data" : @NO };
1198
1199     auto& postLayoutData = state.postLayoutData();
1200     return @{
1201         @"post-layout-data" : @YES,
1202         @"bold": postLayoutData.typingAttributes & WebKit::AttributeBold ? @YES : @NO,
1203         @"italic": postLayoutData.typingAttributes & WebKit::AttributeItalics ? @YES : @NO,
1204         @"underline": postLayoutData.typingAttributes & WebKit::AttributeUnderline ? @YES : @NO,
1205         @"text-alignment": @(nsTextAlignment(static_cast<WebKit::TextAlignment>(postLayoutData.textAlignment))),
1206         @"text-color": (NSString *)postLayoutData.textColor.cssText()
1207     };
1208 }
1209
1210 - (void)_didChangeEditorState
1211 {
1212     id <WKUIDelegatePrivate> uiDelegate = (id <WKUIDelegatePrivate>)self.UIDelegate;
1213     if ([uiDelegate respondsToSelector:@selector(_webView:editorStateDidChange:)])
1214         [uiDelegate _webView:self editorStateDidChange:dictionaryRepresentationForEditorState(_page->editorState())];
1215 }
1216
1217 #if ENABLE(ATTACHMENT_ELEMENT)
1218
1219 - (void)_didInsertAttachment:(NSString *)identifier
1220 {
1221     id <WKUIDelegatePrivate> uiDelegate = (id <WKUIDelegatePrivate>)self.UIDelegate;
1222     if ([uiDelegate respondsToSelector:@selector(_webView:didInsertAttachment:)])
1223         [uiDelegate _webView:self didInsertAttachment:[wrapper(API::Attachment::create(identifier, *_page).leakRef()) autorelease]];
1224 }
1225
1226 - (void)_didRemoveAttachment:(NSString *)identifier
1227 {
1228     id <WKUIDelegatePrivate> uiDelegate = (id <WKUIDelegatePrivate>)self.UIDelegate;
1229     if ([uiDelegate respondsToSelector:@selector(_webView:didRemoveAttachment:)])
1230         [uiDelegate _webView:self didRemoveAttachment:[wrapper(API::Attachment::create(identifier, *_page).leakRef()) autorelease]];
1231 }
1232
1233 #endif // ENABLE(ATTACHMENT_ELEMENT)
1234
1235 #pragma mark iOS-specific methods
1236
1237 #if PLATFORM(IOS)
1238
1239 - (_WKDragInteractionPolicy)_dragInteractionPolicy
1240 {
1241     return _dragInteractionPolicy;
1242 }
1243
1244 - (void)_setDragInteractionPolicy:(_WKDragInteractionPolicy)policy
1245 {
1246     if (_dragInteractionPolicy == policy)
1247         return;
1248
1249     _dragInteractionPolicy = policy;
1250 #if ENABLE(DRAG_SUPPORT)
1251     [_contentView _didChangeDragInteractionPolicy];
1252 #endif
1253 }
1254
1255 - (void)_populateArchivedSubviews:(NSMutableSet *)encodedViews
1256 {
1257     [super _populateArchivedSubviews:encodedViews];
1258
1259     if (_scrollView)
1260         [encodedViews removeObject:_scrollView.get()];
1261     if (_customContentFixedOverlayView)
1262         [encodedViews removeObject:_customContentFixedOverlayView.get()];
1263 }
1264
1265 - (BOOL)_isBackground
1266 {
1267     if ([self _isDisplayingPDF])
1268         return [(WKPDFView *)_customContentView isBackground];
1269
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.get() : _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]);
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 _adjustedContentOffset:CGPointZero]];
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 - (CGPoint)_adjustedContentOffset:(CGPoint)point
1537 {
1538     CGPoint result = point;
1539     UIEdgeInsets contentInset = [self _computedContentInset];
1540
1541     result.x -= contentInset.left;
1542     result.y -= contentInset.top;
1543
1544     return result;
1545 }
1546
1547 - (UIRectEdge)_effectiveObscuredInsetEdgesAffectedBySafeArea
1548 {
1549     if (![self usesStandardContentView])
1550         return UIRectEdgeAll;
1551     return _obscuredInsetEdgesAffectedBySafeArea;
1552 }
1553
1554 - (UIEdgeInsets)_computedContentInset
1555 {
1556     if (_haveSetObscuredInsets)
1557         return _obscuredInsets;
1558
1559     UIEdgeInsets insets = [_scrollView contentInset];
1560
1561 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000
1562     if (self._safeAreaShouldAffectObscuredInsets)
1563         insets = UIEdgeInsetsAdd(insets, [_scrollView _systemContentInset], self._effectiveObscuredInsetEdgesAffectedBySafeArea);
1564 #endif
1565
1566     return insets;
1567 }
1568
1569 - (UIEdgeInsets)_computedUnobscuredSafeAreaInset
1570 {
1571     if (_haveSetUnobscuredSafeAreaInsets)
1572         return _unobscuredSafeAreaInsets;
1573
1574 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000
1575     if (!self._safeAreaShouldAffectObscuredInsets)
1576         return self.safeAreaInsets;
1577 #endif
1578
1579     return UIEdgeInsetsZero;
1580 }
1581
1582 - (void)_processDidExit
1583 {
1584     [self _hidePasswordView];
1585     if (!_customContentView && _dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing) {
1586         NSUInteger indexOfResizeAnimationView = [[_scrollView subviews] indexOfObject:_resizeAnimationView.get()];
1587         [_scrollView insertSubview:_contentView.get() atIndex:indexOfResizeAnimationView];
1588         [_scrollView insertSubview:[_contentView unscaledView] atIndex:indexOfResizeAnimationView + 1];
1589         [_resizeAnimationView removeFromSuperview];
1590         _resizeAnimationView = nil;
1591
1592         _resizeAnimationTransformAdjustments = CATransform3DIdentity;
1593     }
1594     [_contentView setFrame:self.bounds];
1595     [_scrollView setBackgroundColor:[UIColor whiteColor]];
1596     [_scrollView setContentOffset:[self _adjustedContentOffset:CGPointZero]];
1597     [_scrollView setZoomScale:1];
1598
1599     _viewportMetaTagWidth = WebCore::ViewportArguments::ValueAuto;
1600     _initialScaleFactor = 1;
1601     _hasCommittedLoadForMainFrame = NO;
1602     _needsResetViewStateAfterCommitLoadForMainFrame = NO;
1603     _dynamicViewportUpdateMode = DynamicViewportUpdateMode::NotResizing;
1604     [_contentView setHidden:NO];
1605     _scrollOffsetToRestore = std::nullopt;
1606     _unobscuredCenterToRestore = std::nullopt;
1607     _scrollViewBackgroundColor = WebCore::Color();
1608     _delayUpdateVisibleContentRects = NO;
1609     _hadDelayedUpdateVisibleContentRects = NO;
1610     _lastSentMinimumLayoutSize = std::nullopt;
1611     _lastSentMaximumUnobscuredSize = std::nullopt;
1612     _lastSentDeviceOrientation = std::nullopt;
1613
1614     _frozenVisibleContentRect = std::nullopt;
1615     _frozenUnobscuredContentRect = std::nullopt;
1616
1617     _firstPaintAfterCommitLoadTransactionID = 0;
1618     _firstTransactionIDAfterPageRestore = std::nullopt;
1619     _resizeAnimationTransformTransactionID = std::nullopt;
1620
1621     _hasScheduledVisibleRectUpdate = NO;
1622     _commitDidRestoreScrollPosition = NO;
1623
1624     _avoidsUnsafeArea = YES;
1625 }
1626
1627 - (void)_didCommitLoadForMainFrame
1628 {
1629     _firstPaintAfterCommitLoadTransactionID = downcast<WebKit::RemoteLayerTreeDrawingAreaProxy>(*_page->drawingArea()).nextLayerTreeTransactionID();
1630
1631     _hasCommittedLoadForMainFrame = YES;
1632     _needsResetViewStateAfterCommitLoadForMainFrame = YES;
1633
1634     [_scrollView _stopScrollingAndZoomingAnimations];
1635 }
1636
1637 static CGPoint contentOffsetBoundedInValidRange(UIScrollView *scrollView, CGPoint contentOffset)
1638 {
1639 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000
1640     UIEdgeInsets contentInsets = scrollView.adjustedContentInset;
1641 #else
1642     UIEdgeInsets contentInsets = scrollView.contentInset;
1643 #endif
1644
1645     CGSize contentSize = scrollView.contentSize;
1646     CGSize scrollViewSize = scrollView.bounds.size;
1647
1648     CGPoint minimumContentOffset = CGPointMake(-contentInsets.left, -contentInsets.top);
1649     CGPoint maximumContentOffset = CGPointMake(std::max(minimumContentOffset.x, contentSize.width + contentInsets.right - scrollViewSize.width), std::max(minimumContentOffset.y, contentSize.height + contentInsets.bottom - scrollViewSize.height));
1650
1651     return CGPointMake(std::max(std::min(contentOffset.x, maximumContentOffset.x), minimumContentOffset.x), std::max(std::min(contentOffset.y, maximumContentOffset.y), minimumContentOffset.y));
1652 }
1653
1654 static void changeContentOffsetBoundedInValidRange(UIScrollView *scrollView, WebCore::FloatPoint contentOffset)
1655 {
1656     scrollView.contentOffset = contentOffsetBoundedInValidRange(scrollView, contentOffset);
1657 }
1658
1659 - (WebCore::FloatRect)visibleRectInViewCoordinates
1660 {
1661     WebCore::FloatRect bounds = self.bounds;
1662     bounds.moveBy([_scrollView contentOffset]);
1663     WebCore::FloatRect contentViewBounds = [_contentView bounds];
1664     bounds.intersect(contentViewBounds);
1665     return bounds;
1666 }
1667
1668 // WebCore stores the page scale factor as float instead of double. When we get a scale from WebCore,
1669 // we need to ignore differences that are within a small rounding error on floats.
1670 static inline bool areEssentiallyEqualAsFloat(float a, float b)
1671 {
1672     return WTF::areEssentiallyEqual(a, b);
1673 }
1674
1675 - (void)_didCommitLayerTree:(const WebKit::RemoteLayerTreeTransaction&)layerTreeTransaction
1676 {
1677     if (![self usesStandardContentView])
1678         return;
1679
1680     LOG_WITH_STREAM(VisibleRects, stream << "-[WKWebView " << _page->pageID() << " _didCommitLayerTree:] transactionID " <<  layerTreeTransaction.transactionID() << " _dynamicViewportUpdateMode " << (int)_dynamicViewportUpdateMode);
1681
1682     bool needUpdateVisibleContentRects = _page->updateLayoutViewportParameters(layerTreeTransaction);
1683
1684     if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing) {
1685         if (_resizeAnimationTransformTransactionID && layerTreeTransaction.transactionID() >= _resizeAnimationTransformTransactionID.value()) {
1686             _resizeAnimationTransformTransactionID = std::nullopt;
1687             [_resizeAnimationView layer].sublayerTransform = _resizeAnimationTransformAdjustments;
1688             if (_dynamicViewportUpdateMode == DynamicViewportUpdateMode::ResizingWithDocumentHidden) {
1689                 [_contentView setHidden:NO];
1690                 [self _endAnimatedResize];
1691             }
1692         }
1693         return;
1694     }
1695
1696     if (_activeAnimatedResizeCount)
1697         RELEASE_LOG_IF_ALLOWED("%p -[WKWebView _didCommitLayerTree:] - %d animated resizes in flight", self, _activeAnimatedResizeCount);
1698
1699     CGSize newContentSize = roundScrollViewContentSize(*_page, [_contentView frame].size);
1700     [_scrollView _setContentSizePreservingContentOffsetDuringRubberband:newContentSize];
1701
1702     [_scrollView setMinimumZoomScale:layerTreeTransaction.minimumScaleFactor()];
1703     [_scrollView setMaximumZoomScale:layerTreeTransaction.maximumScaleFactor()];
1704     [_scrollView setZoomEnabled:layerTreeTransaction.allowsUserScaling()];
1705     if (!layerTreeTransaction.scaleWasSetByUIProcess() && ![_scrollView isZooming] && ![_scrollView isZoomBouncing] && ![_scrollView _isAnimatingZoom] && [_scrollView zoomScale] != layerTreeTransaction.pageScaleFactor()) {
1706         LOG_WITH_STREAM(VisibleRects, stream << " updating scroll view with pageScaleFactor " << layerTreeTransaction.pageScaleFactor());
1707         [_scrollView setZoomScale:layerTreeTransaction.pageScaleFactor()];
1708     }
1709
1710     _viewportMetaTagWidth = layerTreeTransaction.viewportMetaTagWidth();
1711     _viewportMetaTagWidthWasExplicit = layerTreeTransaction.viewportMetaTagWidthWasExplicit();
1712     _viewportMetaTagCameFromImageDocument = layerTreeTransaction.viewportMetaTagCameFromImageDocument();
1713     _initialScaleFactor = layerTreeTransaction.initialScaleFactor();
1714     if (_page->inStableState() && layerTreeTransaction.isInStableState() && [_stableStatePresentationUpdateCallbacks count]) {
1715         for (dispatch_block_t action in _stableStatePresentationUpdateCallbacks.get())
1716             action();
1717
1718         [_stableStatePresentationUpdateCallbacks removeAllObjects];
1719         _stableStatePresentationUpdateCallbacks = nil;
1720     }
1721
1722     if (![_contentView _mayDisableDoubleTapGesturesDuringSingleTap])
1723         [_contentView _setDoubleTapGesturesEnabled:self._allowsDoubleTapGestures];
1724
1725     [self _updateScrollViewBackground];
1726     [self _setAvoidsUnsafeArea:layerTreeTransaction.avoidsUnsafeArea()];
1727
1728     if (_gestureController)
1729         _gestureController->setRenderTreeSize(layerTreeTransaction.renderTreeSize());
1730
1731     if (_needsResetViewStateAfterCommitLoadForMainFrame && layerTreeTransaction.transactionID() >= _firstPaintAfterCommitLoadTransactionID) {
1732         _needsResetViewStateAfterCommitLoadForMainFrame = NO;
1733         [_scrollView setContentOffset:[self _adjustedContentOffset:CGPointZero]];
1734         if (_observedRenderingProgressEvents & _WKRenderingProgressEventFirstPaint)
1735             _navigationState->didFirstPaint();
1736
1737         needUpdateVisibleContentRects = true;
1738     }
1739
1740     if (_firstTransactionIDAfterPageRestore && layerTreeTransaction.transactionID() >= _firstTransactionIDAfterPageRestore.value()) {
1741         if (_scrollOffsetToRestore) {
1742             WebCore::FloatPoint scaledScrollOffset = _scrollOffsetToRestore.value();
1743             _scrollOffsetToRestore = std::nullopt;
1744
1745             if (areEssentiallyEqualAsFloat(contentZoomScale(self), _scaleToRestore)) {
1746                 scaledScrollOffset.scale(_scaleToRestore);
1747                 WebCore::FloatPoint contentOffsetInScrollViewCoordinates = scaledScrollOffset - WebCore::FloatSize(_obscuredInsetsWhenSaved.left(), _obscuredInsetsWhenSaved.top());
1748
1749                 changeContentOffsetBoundedInValidRange(_scrollView.get(), contentOffsetInScrollViewCoordinates);
1750                 _commitDidRestoreScrollPosition = YES;
1751             }
1752
1753             needUpdateVisibleContentRects = true;
1754         }
1755
1756         if (_unobscuredCenterToRestore) {
1757             WebCore::FloatPoint unobscuredCenterToRestore = _unobscuredCenterToRestore.value();
1758             _unobscuredCenterToRestore = std::nullopt;
1759
1760             if (areEssentiallyEqualAsFloat(contentZoomScale(self), _scaleToRestore)) {
1761                 CGRect unobscuredRect = UIEdgeInsetsInsetRect(self.bounds, _obscuredInsets);
1762                 WebCore::FloatSize unobscuredContentSizeAtNewScale = WebCore::FloatSize(unobscuredRect.size) / _scaleToRestore;
1763                 WebCore::FloatPoint topLeftInDocumentCoordinates = unobscuredCenterToRestore - unobscuredContentSizeAtNewScale / 2;
1764
1765                 topLeftInDocumentCoordinates.scale(_scaleToRestore);
1766                 topLeftInDocumentCoordinates.moveBy(WebCore::FloatPoint(-_obscuredInsets.left, -_obscuredInsets.top));
1767
1768                 changeContentOffsetBoundedInValidRange(_scrollView.get(), topLeftInDocumentCoordinates);
1769             }
1770
1771             needUpdateVisibleContentRects = true;
1772         }
1773
1774         if (_gestureController)
1775             _gestureController->didRestoreScrollPosition();
1776
1777         _firstTransactionIDAfterPageRestore = std::nullopt;
1778     }
1779
1780     if (needUpdateVisibleContentRects)
1781         [self _scheduleVisibleContentRectUpdate];
1782
1783     if (WebKit::RemoteLayerTreeScrollingPerformanceData* scrollPerfData = _page->scrollingPerformanceData())
1784         scrollPerfData->didCommitLayerTree([self visibleRectInViewCoordinates]);
1785 }
1786
1787 - (void)_layerTreeCommitComplete
1788 {
1789     _commitDidRestoreScrollPosition = NO;
1790 }
1791
1792 - (void)_dynamicViewportUpdateChangedTargetToScale:(double)newScale position:(CGPoint)newScrollPosition nextValidLayerTreeTransactionID:(uint64_t)nextValidLayerTreeTransactionID
1793 {
1794     LOG_WITH_STREAM(VisibleRects, stream << "-[WKWebView " << _page->pageID() << " _dynamicViewportUpdateChangedTargetToScale:] " <<  newScale << " _dynamicViewportUpdateMode " << (int)_dynamicViewportUpdateMode);
1795
1796     if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing) {
1797         CGFloat animatingScaleTarget = [[_resizeAnimationView layer] transform].m11;
1798         double currentTargetScale = animatingScaleTarget * [[_contentView layer] transform].m11;
1799         double scale = newScale / currentTargetScale;
1800         _resizeAnimationTransformAdjustments = CATransform3DMakeScale(scale, scale, 1);
1801
1802         CGPoint newContentOffset = [self _adjustedContentOffset:CGPointMake(newScrollPosition.x * newScale, newScrollPosition.y * newScale)];
1803         CGPoint currentContentOffset = [_scrollView contentOffset];
1804
1805         _resizeAnimationTransformAdjustments.m41 = (currentContentOffset.x - newContentOffset.x) / animatingScaleTarget;
1806         _resizeAnimationTransformAdjustments.m42 = (currentContentOffset.y - newContentOffset.y) / animatingScaleTarget;
1807         _resizeAnimationTransformTransactionID = nextValidLayerTreeTransactionID;
1808     }
1809 }
1810
1811 - (void)_couldNotRestorePageState
1812 {
1813     // The gestureController may be waiting for the scroll position to be restored
1814     // in order to remove the swipe snapshot. Since the scroll position could not be
1815     // restored, tell the gestureController it was restored so that it no longer waits
1816     // for it.
1817     if (_gestureController)
1818         _gestureController->didRestoreScrollPosition();
1819 }
1820
1821 - (void)_restorePageScrollPosition:(std::optional<WebCore::FloatPoint>)scrollPosition scrollOrigin:(WebCore::FloatPoint)scrollOrigin previousObscuredInset:(WebCore::FloatBoxExtent)obscuredInsets scale:(double)scale
1822 {
1823     if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing)
1824         return;
1825
1826     if (![self usesStandardContentView])
1827         return;
1828
1829     _firstTransactionIDAfterPageRestore = downcast<WebKit::RemoteLayerTreeDrawingAreaProxy>(*_page->drawingArea()).nextLayerTreeTransactionID();
1830     if (scrollPosition)
1831         _scrollOffsetToRestore = WebCore::ScrollableArea::scrollOffsetFromPosition(WebCore::FloatPoint(scrollPosition.value()), WebCore::toFloatSize(scrollOrigin));
1832     else
1833         _scrollOffsetToRestore = std::nullopt;
1834
1835     _obscuredInsetsWhenSaved = obscuredInsets;
1836     _scaleToRestore = scale;
1837 }
1838
1839 - (void)_restorePageStateToUnobscuredCenter:(std::optional<WebCore::FloatPoint>)center scale:(double)scale
1840 {
1841     if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing)
1842         return;
1843
1844     if (![self usesStandardContentView])
1845         return;
1846
1847     _firstTransactionIDAfterPageRestore = downcast<WebKit::RemoteLayerTreeDrawingAreaProxy>(*_page->drawingArea()).nextLayerTreeTransactionID();
1848     _unobscuredCenterToRestore = center.value();
1849
1850     _scaleToRestore = scale;
1851 }
1852
1853 - (RefPtr<WebKit::ViewSnapshot>)_takeViewSnapshot
1854 {
1855     float deviceScale = WebCore::screenScaleFactor();
1856     WebCore::FloatSize snapshotSize(self.bounds.size);
1857     snapshotSize.scale(deviceScale);
1858
1859     CATransform3D transform = CATransform3DMakeScale(deviceScale, deviceScale, 1);
1860
1861 #if USE(IOSURFACE)
1862     WebCore::IOSurface::Format snapshotFormat = WebCore::screenSupportsExtendedColor() ? WebCore::IOSurface::Format::RGB10 : WebCore::IOSurface::Format::RGBA;
1863     auto surface = WebCore::IOSurface::create(WebCore::expandedIntSize(snapshotSize), WebCore::sRGBColorSpaceRef(), snapshotFormat);
1864     if (!surface)
1865         return nullptr;
1866     CARenderServerRenderLayerWithTransform(MACH_PORT_NULL, self.layer.context.contextId, reinterpret_cast<uint64_t>(self.layer), surface->surface(), 0, 0, &transform);
1867
1868     WebCore::IOSurface::Format compressedFormat = WebCore::IOSurface::Format::YUV422;
1869     if (WebCore::IOSurface::allowConversionFromFormatToFormat(snapshotFormat, compressedFormat)) {
1870         RefPtr<WebKit::ViewSnapshot> viewSnapshot = WebKit::ViewSnapshot::create(nullptr);
1871         WebCore::IOSurface::convertToFormat(WTFMove(surface), WebCore::IOSurface::Format::YUV422, [viewSnapshot](std::unique_ptr<WebCore::IOSurface> convertedSurface) {
1872             if (convertedSurface)
1873                 viewSnapshot->setSurface(WTFMove(convertedSurface));
1874         });
1875
1876         return viewSnapshot;
1877     }
1878
1879     return WebKit::ViewSnapshot::create(WTFMove(surface));
1880 #else
1881     uint32_t slotID = [WebKit::ViewSnapshotStore::snapshottingContext() createImageSlot:snapshotSize hasAlpha:YES];
1882
1883     if (!slotID)
1884         return nullptr;
1885
1886     CARenderServerCaptureLayerWithTransform(MACH_PORT_NULL, self.layer.context.contextId, (uint64_t)self.layer, slotID, 0, 0, &transform);
1887     WebCore::IntSize imageSize = WebCore::expandedIntSize(WebCore::FloatSize(snapshotSize));
1888     return WebKit::ViewSnapshot::create(slotID, imageSize, (imageSize.area() * 4).unsafeGet());
1889 #endif
1890 }
1891
1892 - (void)_zoomToPoint:(WebCore::FloatPoint)point atScale:(double)scale animated:(BOOL)animated
1893 {
1894     CFTimeInterval duration = 0;
1895     CGFloat zoomScale = contentZoomScale(self);
1896
1897     if (animated) {
1898         const double maximumZoomDuration = 0.4;
1899         const double minimumZoomDuration = 0.1;
1900         const double zoomDurationFactor = 0.3;
1901
1902         duration = std::min(fabs(log(zoomScale) - log(scale)) * zoomDurationFactor + minimumZoomDuration, maximumZoomDuration);
1903     }
1904
1905     if (scale != zoomScale)
1906         _page->willStartUserTriggeredZooming();
1907
1908     LOG_WITH_STREAM(VisibleRects, stream << "_zoomToPoint:" << point << " scale: " << scale << " duration:" << duration);
1909
1910     [_scrollView _zoomToCenter:point scale:scale duration:duration];
1911 }
1912
1913 - (void)_zoomToRect:(WebCore::FloatRect)targetRect atScale:(double)scale origin:(WebCore::FloatPoint)origin animated:(BOOL)animated
1914 {
1915     // FIXME: Some of this could be shared with _scrollToRect.
1916     const double visibleRectScaleChange = contentZoomScale(self) / scale;
1917     const WebCore::FloatRect visibleRect([self convertRect:self.bounds toView:self._currentContentView]);
1918     const WebCore::FloatRect unobscuredRect([self _contentRectForUserInteraction]);
1919
1920     const WebCore::FloatSize topLeftObscuredInsetAfterZoom((unobscuredRect.minXMinYCorner() - visibleRect.minXMinYCorner()) * visibleRectScaleChange);
1921     const WebCore::FloatSize bottomRightObscuredInsetAfterZoom((visibleRect.maxXMaxYCorner() - unobscuredRect.maxXMaxYCorner()) * visibleRectScaleChange);
1922
1923     const WebCore::FloatSize unobscuredRectSizeAfterZoom(unobscuredRect.size() * visibleRectScaleChange);
1924
1925     // Center to the target rect.
1926     WebCore::FloatPoint unobscuredRectLocationAfterZoom = targetRect.location() - (unobscuredRectSizeAfterZoom - targetRect.size()) * 0.5;
1927
1928     // Center to the tap point instead in case the target rect won't fit in a direction.
1929     if (targetRect.width() > unobscuredRectSizeAfterZoom.width())
1930         unobscuredRectLocationAfterZoom.setX(origin.x() - unobscuredRectSizeAfterZoom.width() / 2);
1931     if (targetRect.height() > unobscuredRectSizeAfterZoom.height())
1932         unobscuredRectLocationAfterZoom.setY(origin.y() - unobscuredRectSizeAfterZoom.height() / 2);
1933
1934     // We have computed where we want the unobscured rect to be. Now adjust for the obscuring insets.
1935     WebCore::FloatRect visibleRectAfterZoom(unobscuredRectLocationAfterZoom, unobscuredRectSizeAfterZoom);
1936     visibleRectAfterZoom.move(-topLeftObscuredInsetAfterZoom);
1937     visibleRectAfterZoom.expand(topLeftObscuredInsetAfterZoom + bottomRightObscuredInsetAfterZoom);
1938
1939     [self _zoomToPoint:visibleRectAfterZoom.center() atScale:scale animated:animated];
1940 }
1941
1942 static WebCore::FloatPoint constrainContentOffset(WebCore::FloatPoint contentOffset, WebCore::FloatSize contentSize, WebCore::FloatSize unobscuredContentSize)
1943 {
1944     WebCore::FloatSize maximumContentOffset = contentSize - unobscuredContentSize;
1945     return contentOffset.constrainedBetween(WebCore::FloatPoint(), WebCore::FloatPoint(maximumContentOffset));
1946 }
1947
1948 - (void)_scrollToContentScrollPosition:(WebCore::FloatPoint)scrollPosition scrollOrigin:(WebCore::IntPoint)scrollOrigin
1949 {
1950     if (_commitDidRestoreScrollPosition || _dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing)
1951         return;
1952
1953     WebCore::FloatPoint contentOffset = WebCore::ScrollableArea::scrollOffsetFromPosition(scrollPosition, toFloatSize(scrollOrigin));
1954
1955     WebCore::FloatPoint scaledOffset = contentOffset;
1956     CGFloat zoomScale = contentZoomScale(self);
1957     scaledOffset.scale(zoomScale);
1958
1959     CGPoint contentOffsetInScrollViewCoordinates = [self _adjustedContentOffset:scaledOffset];
1960     contentOffsetInScrollViewCoordinates = contentOffsetBoundedInValidRange(_scrollView.get(), contentOffsetInScrollViewCoordinates);
1961
1962     [_scrollView _stopScrollingAndZoomingAnimations];
1963
1964     if (!CGPointEqualToPoint(contentOffsetInScrollViewCoordinates, [_scrollView contentOffset]))
1965         [_scrollView setContentOffset:contentOffsetInScrollViewCoordinates];
1966     else {
1967         // If we haven't changed anything, there would not be any VisibleContentRect update sent to the content.
1968         // The WebProcess would keep the invalid contentOffset as its scroll position.
1969         // To synchronize the WebProcess with what is on screen, we send the VisibleContentRect again.
1970         _page->resendLastVisibleContentRects();
1971     }
1972 }
1973
1974 - (BOOL)_scrollToRect:(WebCore::FloatRect)targetRect origin:(WebCore::FloatPoint)origin minimumScrollDistance:(float)minimumScrollDistance
1975 {
1976     WebCore::FloatRect unobscuredContentRect([self _contentRectForUserInteraction]);
1977     WebCore::FloatPoint unobscuredContentOffset = unobscuredContentRect.location();
1978     WebCore::FloatSize contentSize([self._currentContentView bounds].size);
1979
1980     // Center the target rect in the scroll view.
1981     // If the target doesn't fit in the scroll view, center on the gesture location instead.
1982     WebCore::FloatPoint newUnobscuredContentOffset;
1983     if (targetRect.width() <= unobscuredContentRect.width())
1984         newUnobscuredContentOffset.setX(targetRect.x() - (unobscuredContentRect.width() - targetRect.width()) / 2);
1985     else
1986         newUnobscuredContentOffset.setX(origin.x() - unobscuredContentRect.width() / 2);
1987     if (targetRect.height() <= unobscuredContentRect.height())
1988         newUnobscuredContentOffset.setY(targetRect.y() - (unobscuredContentRect.height() - targetRect.height()) / 2);
1989     else
1990         newUnobscuredContentOffset.setY(origin.y() - unobscuredContentRect.height() / 2);
1991     newUnobscuredContentOffset = constrainContentOffset(newUnobscuredContentOffset, contentSize, unobscuredContentRect.size());
1992
1993     if (unobscuredContentOffset == newUnobscuredContentOffset) {
1994         if (targetRect.width() > unobscuredContentRect.width())
1995             newUnobscuredContentOffset.setX(origin.x() - unobscuredContentRect.width() / 2);
1996         if (targetRect.height() > unobscuredContentRect.height())
1997             newUnobscuredContentOffset.setY(origin.y() - unobscuredContentRect.height() / 2);
1998         newUnobscuredContentOffset = constrainContentOffset(newUnobscuredContentOffset, contentSize, unobscuredContentRect.size());
1999     }
2000
2001     WebCore::FloatSize scrollViewOffsetDelta = newUnobscuredContentOffset - unobscuredContentOffset;
2002     scrollViewOffsetDelta.scale(contentZoomScale(self));
2003
2004     float scrollDistance = scrollViewOffsetDelta.diagonalLength();
2005     if (scrollDistance < minimumScrollDistance)
2006         return false;
2007
2008     [_contentView willStartZoomOrScroll];
2009
2010     LOG_WITH_STREAM(VisibleRects, stream << "_scrollToRect: scrolling to " << [_scrollView contentOffset] + scrollViewOffsetDelta);
2011
2012     [_scrollView setContentOffset:([_scrollView contentOffset] + scrollViewOffsetDelta) animated:YES];
2013     return true;
2014 }
2015
2016 - (void)_scrollByContentOffset:(WebCore::FloatPoint)contentOffsetDelta
2017 {
2018     WebCore::FloatPoint scaledOffsetDelta = contentOffsetDelta;
2019     CGFloat zoomScale = contentZoomScale(self);
2020     scaledOffsetDelta.scale(zoomScale);
2021
2022     CGPoint currentOffset = [_scrollView _isAnimatingScroll] ? [_scrollView _animatedTargetOffset] : [_scrollView contentOffset];
2023     CGPoint boundedOffset = contentOffsetBoundedInValidRange(_scrollView.get(), currentOffset + scaledOffsetDelta);
2024
2025     if (CGPointEqualToPoint(boundedOffset, currentOffset))
2026         return;
2027     [_contentView willStartZoomOrScroll];
2028
2029     LOG_WITH_STREAM(VisibleRects, stream << "_scrollByContentOffset: scrolling to " << WebCore::FloatPoint(boundedOffset));
2030
2031     [_scrollView setContentOffset:boundedOffset animated:YES];
2032 }
2033
2034 - (void)_zoomOutWithOrigin:(WebCore::FloatPoint)origin animated:(BOOL)animated
2035 {
2036     [self _zoomToPoint:origin atScale:[_scrollView minimumZoomScale] animated:animated];
2037 }
2038
2039 - (void)_zoomToInitialScaleWithOrigin:(WebCore::FloatPoint)origin animated:(BOOL)animated
2040 {
2041     ASSERT(_initialScaleFactor > 0);
2042     [self _zoomToPoint:origin atScale:_initialScaleFactor animated:animated];
2043 }
2044
2045 // focusedElementRect and selectionRect are both in document coordinates.
2046 - (void)_zoomToFocusRect:(WebCore::FloatRect)focusedElementRectInDocumentCoordinates selectionRect:(WebCore::FloatRect)selectionRectInDocumentCoordinates insideFixed:(BOOL)insideFixed
2047     fontSize:(float)fontSize minimumScale:(double)minimumScale maximumScale:(double)maximumScale allowScaling:(BOOL)allowScaling forceScroll:(BOOL)forceScroll
2048 {
2049     LOG_WITH_STREAM(VisibleRects, stream << "_zoomToFocusRect:" << focusedElementRectInDocumentCoordinates << " selectionRect:" << selectionRectInDocumentCoordinates);
2050     UNUSED_PARAM(insideFixed);
2051
2052     const double WKWebViewStandardFontSize = 16;
2053     const double kMinimumHeightToShowContentAboveKeyboard = 106;
2054     const CFTimeInterval UIWebFormAnimationDuration = 0.25;
2055     const double CaretOffsetFromWindowEdge = 20;
2056
2057     // Zoom around the element's bounding frame. We use a "standard" size to determine the proper frame.
2058     double scale = allowScaling ? std::min(std::max(WKWebViewStandardFontSize / fontSize, minimumScale), maximumScale) : contentZoomScale(self);
2059     CGFloat documentWidth = [_contentView bounds].size.width;
2060     scale = CGRound(documentWidth * scale) / documentWidth;
2061
2062     UIWindow *window = [_scrollView window];
2063
2064     WebCore::FloatRect focusedElementRectInNewScale = focusedElementRectInDocumentCoordinates;
2065     focusedElementRectInNewScale.scale(scale);
2066     focusedElementRectInNewScale.moveBy([_contentView frame].origin);
2067
2068     // Find the portion of the view that is visible on the screen.
2069     UIViewController *topViewController = [[[_scrollView _viewControllerForAncestor] _rootAncestorViewController] _viewControllerForSupportedInterfaceOrientations];
2070     UIView *fullScreenView = topViewController.view;
2071     if (!fullScreenView)
2072         fullScreenView = window;
2073
2074     CGRect unobscuredScrollViewRectInWebViewCoordinates = UIEdgeInsetsInsetRect([self bounds], _obscuredInsets);
2075     CGRect visibleScrollViewBoundsInWebViewCoordinates = CGRectIntersection(unobscuredScrollViewRectInWebViewCoordinates, [fullScreenView convertRect:[fullScreenView bounds] toView:self]);
2076     CGRect formAssistantFrameInWebViewCoordinates = [window convertRect:_inputViewBounds toView:self];
2077     CGRect intersectionBetweenScrollViewAndFormAssistant = CGRectIntersection(visibleScrollViewBoundsInWebViewCoordinates, formAssistantFrameInWebViewCoordinates);
2078     CGSize visibleSize = visibleScrollViewBoundsInWebViewCoordinates.size;
2079
2080     CGFloat visibleOffsetFromTop = 0;
2081     if (!CGRectIsEmpty(intersectionBetweenScrollViewAndFormAssistant)) {
2082         CGFloat heightVisibleAboveFormAssistant = CGRectGetMinY(intersectionBetweenScrollViewAndFormAssistant) - CGRectGetMinY(visibleScrollViewBoundsInWebViewCoordinates);
2083         CGFloat heightVisibleBelowFormAssistant = CGRectGetMaxY(visibleScrollViewBoundsInWebViewCoordinates) - CGRectGetMaxY(intersectionBetweenScrollViewAndFormAssistant);
2084
2085         if (heightVisibleAboveFormAssistant >= kMinimumHeightToShowContentAboveKeyboard || heightVisibleBelowFormAssistant < heightVisibleAboveFormAssistant)
2086             visibleSize.height = heightVisibleAboveFormAssistant;
2087         else {
2088             visibleSize.height = heightVisibleBelowFormAssistant;
2089             visibleOffsetFromTop = CGRectGetMaxY(intersectionBetweenScrollViewAndFormAssistant) - CGRectGetMinY(visibleScrollViewBoundsInWebViewCoordinates);
2090         }
2091     }
2092
2093     BOOL selectionRectIsNotNull = !selectionRectInDocumentCoordinates.isZero();
2094     if (!forceScroll) {
2095         CGRect currentlyVisibleRegionInWebViewCoordinates;
2096         currentlyVisibleRegionInWebViewCoordinates.origin = unobscuredScrollViewRectInWebViewCoordinates.origin;
2097         currentlyVisibleRegionInWebViewCoordinates.origin.y += visibleOffsetFromTop;
2098         currentlyVisibleRegionInWebViewCoordinates.size = visibleSize;
2099
2100         // Don't bother scrolling if the entire node is already visible, whether or not we got a selectionRect.
2101         if (CGRectContainsRect(currentlyVisibleRegionInWebViewCoordinates, [self convertRect:focusedElementRectInDocumentCoordinates fromView:_contentView.get()]))
2102             return;
2103
2104         // Don't bother scrolling if we have a valid selectionRect and it is already visible.
2105         if (selectionRectIsNotNull && CGRectContainsRect(currentlyVisibleRegionInWebViewCoordinates, [self convertRect:selectionRectInDocumentCoordinates fromView:_contentView.get()]))
2106             return;
2107     }
2108
2109     // We want to zoom to the left/top corner of the DOM node, with as much spacing on all sides as we
2110     // can get based on the visible area after zooming (workingFrame).  The spacing in either dimension is half the
2111     // difference between the size of the DOM node and the size of the visible frame.
2112     CGFloat horizontalSpaceInWebViewCoordinates = std::max((visibleSize.width - focusedElementRectInNewScale.width()) / 2.0, 0.0);
2113     CGFloat verticalSpaceInWebViewCoordinates = std::max((visibleSize.height - focusedElementRectInNewScale.height()) / 2.0, 0.0);
2114
2115     CGPoint topLeft;
2116     topLeft.x = focusedElementRectInNewScale.x() - horizontalSpaceInWebViewCoordinates;
2117     topLeft.y = focusedElementRectInNewScale.y() - verticalSpaceInWebViewCoordinates - visibleOffsetFromTop;
2118
2119     CGFloat minimumAllowableHorizontalOffsetInWebViewCoordinates = -INFINITY;
2120     CGFloat minimumAllowableVerticalOffsetInWebViewCoordinates = -INFINITY;
2121     if (selectionRectIsNotNull) {
2122         WebCore::FloatRect selectionRectInNewScale = selectionRectInDocumentCoordinates;
2123         selectionRectInNewScale.scale(scale);
2124         selectionRectInNewScale.moveBy([_contentView frame].origin);
2125         minimumAllowableHorizontalOffsetInWebViewCoordinates = CGRectGetMaxX(selectionRectInNewScale) + CaretOffsetFromWindowEdge - visibleSize.width;
2126         minimumAllowableVerticalOffsetInWebViewCoordinates = CGRectGetMaxY(selectionRectInNewScale) + CaretOffsetFromWindowEdge - visibleSize.height - visibleOffsetFromTop;
2127     }
2128
2129     WebCore::FloatRect documentBoundsInNewScale = [_contentView bounds];
2130     documentBoundsInNewScale.scale(scale);
2131     documentBoundsInNewScale.moveBy([_contentView frame].origin);
2132
2133     // Constrain the left edge in document coordinates so that:
2134     //  - it isn't so small that the scrollVisibleRect isn't visible on the screen
2135     //  - it isn't so great that the document's right edge is less than the right edge of the screen
2136     if (selectionRectIsNotNull && topLeft.x < minimumAllowableHorizontalOffsetInWebViewCoordinates)
2137         topLeft.x = minimumAllowableHorizontalOffsetInWebViewCoordinates;
2138     else {
2139         CGFloat maximumAllowableHorizontalOffset = CGRectGetMaxX(documentBoundsInNewScale) - visibleSize.width;
2140         if (topLeft.x > maximumAllowableHorizontalOffset)
2141             topLeft.x = maximumAllowableHorizontalOffset;
2142     }
2143
2144     // Constrain the top edge in document coordinates so that:
2145     //  - it isn't so small that the scrollVisibleRect isn't visible on the screen
2146     //  - it isn't so great that the document's bottom edge is higher than the top of the form assistant
2147     if (selectionRectIsNotNull && topLeft.y < minimumAllowableVerticalOffsetInWebViewCoordinates)
2148         topLeft.y = minimumAllowableVerticalOffsetInWebViewCoordinates;
2149     else {
2150         CGFloat maximumAllowableVerticalOffset = CGRectGetMaxY(documentBoundsInNewScale) - visibleSize.height;
2151         if (topLeft.y > maximumAllowableVerticalOffset)
2152             topLeft.y = maximumAllowableVerticalOffset;
2153     }
2154
2155     WebCore::FloatPoint newCenter = CGPointMake(topLeft.x + unobscuredScrollViewRectInWebViewCoordinates.size.width / 2.0, topLeft.y + unobscuredScrollViewRectInWebViewCoordinates.size.height / 2.0);
2156
2157     if (scale != contentZoomScale(self))
2158         _page->willStartUserTriggeredZooming();
2159
2160     LOG_WITH_STREAM(VisibleRects, stream << "_zoomToFocusRect: zooming to " << newCenter << " scale:" << scale);
2161
2162     // The newCenter has been computed in the new scale, but _zoomToCenter expected the center to be in the original scale.
2163     newCenter.scale(1 / scale);
2164     [_scrollView _zoomToCenter:newCenter
2165                         scale:scale
2166                      duration:UIWebFormAnimationDuration
2167                         force:YES];
2168 }
2169
2170 - (CGFloat)_targetContentZoomScaleForRect:(const WebCore::FloatRect&)targetRect currentScale:(double)currentScale fitEntireRect:(BOOL)fitEntireRect minimumScale:(double)minimumScale maximumScale:(double)maximumScale
2171 {
2172     WebCore::FloatSize unobscuredContentSize([self _contentRectForUserInteraction].size);
2173     double horizontalScale = unobscuredContentSize.width() * currentScale / targetRect.width();
2174     double verticalScale = unobscuredContentSize.height() * currentScale / targetRect.height();
2175
2176     horizontalScale = std::min(std::max(horizontalScale, minimumScale), maximumScale);
2177     verticalScale = std::min(std::max(verticalScale, minimumScale), maximumScale);
2178
2179     return fitEntireRect ? std::min(horizontalScale, verticalScale) : horizontalScale;
2180 }
2181
2182 - (BOOL)_zoomToRect:(WebCore::FloatRect)targetRect withOrigin:(WebCore::FloatPoint)origin fitEntireRect:(BOOL)fitEntireRect minimumScale:(double)minimumScale maximumScale:(double)maximumScale minimumScrollDistance:(float)minimumScrollDistance
2183 {
2184     const float maximumScaleFactorDeltaForPanScroll = 0.02;
2185
2186     double currentScale = contentZoomScale(self);
2187     double targetScale = [self _targetContentZoomScaleForRect:targetRect currentScale:currentScale fitEntireRect:fitEntireRect minimumScale:minimumScale maximumScale:maximumScale];
2188
2189     if (fabs(targetScale - currentScale) < maximumScaleFactorDeltaForPanScroll) {
2190         if ([self _scrollToRect:targetRect origin:origin minimumScrollDistance:minimumScrollDistance])
2191             return true;
2192     } else if (targetScale != currentScale) {
2193         [self _zoomToRect:targetRect atScale:targetScale origin:origin animated:YES];
2194         return true;
2195     }
2196
2197     return false;
2198 }
2199
2200 - (void)didMoveToWindow
2201 {
2202     _page->activityStateDidChange(WebCore::ActivityState::AllFlags);
2203 }
2204
2205 - (void)setOpaque:(BOOL)opaque
2206 {
2207     BOOL oldOpaque = self.opaque;
2208
2209     [super setOpaque:opaque];
2210     [_contentView setOpaque:opaque];
2211
2212     if (oldOpaque == opaque)
2213         return;
2214
2215     if (!_page)
2216         return;
2217
2218     _page->setDrawsBackground(opaque);
2219     [self _updateScrollViewBackground];
2220 }
2221
2222 - (void)setBackgroundColor:(UIColor *)backgroundColor
2223 {
2224     [super setBackgroundColor:backgroundColor];
2225     [_contentView setBackgroundColor:backgroundColor];
2226 }
2227
2228 - (BOOL)_allowsDoubleTapGestures
2229 {
2230     if (_fastClickingIsDisabled)
2231         return YES;
2232
2233     // If the page is not user scalable, we don't allow double tap gestures.
2234     if (![_scrollView isZoomEnabled] || [_scrollView minimumZoomScale] >= [_scrollView maximumZoomScale])
2235         return NO;
2236
2237     // If the viewport width was not explicit, we allow double tap gestures.
2238     if (!_viewportMetaTagWidthWasExplicit || _viewportMetaTagCameFromImageDocument)
2239         return YES;
2240
2241     // If the page set a viewport width that wasn't the device width, then it was
2242     // scaled and thus will probably need to zoom.
2243     if (_viewportMetaTagWidth != WebCore::ViewportArguments::ValueDeviceWidth)
2244         return YES;
2245
2246     // At this point, we have a page that asked for width = device-width. However,
2247     // if the content's width and height were large, we might have had to shrink it.
2248     // Since we'll enable double tap zoom whenever we're not at the actual
2249     // initial scale, this simply becomes a test of the current scale against 1.
2250     return !areEssentiallyEqualAsFloat(contentZoomScale(self), 1);
2251 }
2252
2253 #pragma mark - UIScrollViewDelegate
2254
2255 - (BOOL)usesStandardContentView
2256 {
2257     return !_customContentView && !_passwordView;
2258 }
2259
2260 - (CGSize)scrollView:(UIScrollView*)scrollView contentSizeForZoomScale:(CGFloat)scale withProposedSize:(CGSize)proposedSize
2261 {
2262     return roundScrollViewContentSize(*_page, proposedSize);
2263 }
2264
2265 - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
2266 {
2267     ASSERT(_scrollView == scrollView);
2268
2269     if (_customContentView)
2270         return _customContentView.get();
2271
2272     return _contentView.get();
2273 }
2274
2275 - (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view
2276 {
2277     if (![self usesStandardContentView])
2278         return;
2279
2280     if (scrollView.pinchGestureRecognizer.state == UIGestureRecognizerStateBegan) {
2281         _page->willStartUserTriggeredZooming();
2282         [_contentView scrollViewWillStartPanOrPinchGesture];
2283     }
2284     [_contentView willStartZoomOrScroll];
2285 }
2286
2287 - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
2288 {
2289     if (![self usesStandardContentView])
2290         return;
2291
2292     if (scrollView.panGestureRecognizer.state == UIGestureRecognizerStateBegan)
2293         [_contentView scrollViewWillStartPanOrPinchGesture];
2294
2295     [_contentView willStartZoomOrScroll];
2296 #if ENABLE(CSS_SCROLL_SNAP) && ENABLE(ASYNC_SCROLLING)
2297     // FIXME: We will want to detect whether snapping will occur before beginning to drag. See WebPageProxy::didCommitLayerTree.
2298     WebKit::RemoteScrollingCoordinatorProxy* coordinator = _page->scrollingCoordinatorProxy();
2299     ASSERT(scrollView == _scrollView.get());
2300     CGFloat scrollDecelerationFactor = (coordinator && coordinator->shouldSetScrollViewDecelerationRateFast()) ? UIScrollViewDecelerationRateFast : UIScrollViewDecelerationRateNormal;
2301     scrollView.horizontalScrollDecelerationFactor = scrollDecelerationFactor;
2302     scrollView.verticalScrollDecelerationFactor = scrollDecelerationFactor;
2303 #endif
2304 }
2305
2306 - (void)_didFinishScrolling
2307 {
2308     if (![self usesStandardContentView])
2309         return;
2310
2311     [self _scheduleVisibleContentRectUpdate];
2312     [_contentView didFinishScrolling];
2313 }
2314
2315 - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
2316 {
2317     // Work around <rdar://problem/16374753> by avoiding deceleration while
2318     // zooming. We'll animate to the right place once the zoom finishes.
2319     if ([scrollView isZooming])
2320         *targetContentOffset = [scrollView contentOffset];
2321 #if ENABLE(CSS_SCROLL_SNAP) && ENABLE(ASYNC_SCROLLING)
2322     if (WebKit::RemoteScrollingCoordinatorProxy* coordinator = _page->scrollingCoordinatorProxy()) {
2323         // FIXME: Here, I'm finding the maximum horizontal/vertical scroll offsets. There's probably a better way to do this.
2324         CGSize maxScrollOffsets = CGSizeMake(scrollView.contentSize.width - scrollView.bounds.size.width, scrollView.contentSize.height - scrollView.bounds.size.height);
2325
2326         CGRect fullViewRect = self.bounds;
2327
2328         UIEdgeInsets contentInset;
2329
2330         id<WKUIDelegatePrivate> uiDelegatePrivate = static_cast<id <WKUIDelegatePrivate>>([self UIDelegate]);
2331         if ([uiDelegatePrivate respondsToSelector:@selector(_webView:finalObscuredInsetsForScrollView:withVelocity:targetContentOffset:)])
2332             contentInset = [uiDelegatePrivate _webView:self finalObscuredInsetsForScrollView:scrollView withVelocity:velocity targetContentOffset:targetContentOffset];
2333         else
2334             contentInset = [self _computedContentInset];
2335
2336         CGRect unobscuredRect = UIEdgeInsetsInsetRect(fullViewRect, contentInset);
2337
2338         coordinator->adjustTargetContentOffsetForSnapping(maxScrollOffsets, velocity, unobscuredRect.origin.y, targetContentOffset);
2339     }
2340 #endif
2341 }
2342
2343 - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
2344 {
2345     // If we're decelerating, scroll offset will be updated when scrollViewDidFinishDecelerating: is called.
2346     if (!decelerate)
2347         [self _didFinishScrolling];
2348 }
2349
2350 - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
2351 {
2352     [self _didFinishScrolling];
2353 }
2354
2355 - (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView
2356 {
2357     [self _didFinishScrolling];
2358 }
2359
2360 - (void)scrollViewDidScroll:(UIScrollView *)scrollView
2361 {
2362     if (![self usesStandardContentView])
2363         [_customContentView scrollViewDidScroll:(UIScrollView *)scrollView];
2364
2365     [self _scheduleVisibleContentRectUpdateAfterScrollInView:scrollView];
2366
2367     if (WebKit::RemoteLayerTreeScrollingPerformanceData* scrollPerfData = _page->scrollingPerformanceData())
2368         scrollPerfData->didScroll([self visibleRectInViewCoordinates]);
2369 }
2370
2371 - (void)scrollViewDidZoom:(UIScrollView *)scrollView
2372 {
2373     [self _updateScrollViewBackground];
2374     [self _scheduleVisibleContentRectUpdateAfterScrollInView:scrollView];
2375 }
2376
2377 - (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale
2378 {
2379     ASSERT(scrollView == _scrollView);
2380     // FIXME: remove when rdar://problem/36065495 is fixed.
2381     // When rotating with two fingers down, UIScrollView can set a bogus content view position.
2382     // "Center" is top left because we set the anchorPoint to 0,0.
2383     [_contentView setCenter:self.bounds.origin];
2384
2385     [self _scheduleVisibleContentRectUpdateAfterScrollInView:scrollView];
2386     [_contentView didZoomToScale:scale];
2387 }
2388
2389 - (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
2390 {
2391     [self _didFinishScrolling];
2392 }
2393
2394 - (void)_scrollViewDidInterruptDecelerating:(UIScrollView *)scrollView
2395 {
2396     if (![self usesStandardContentView])
2397         return;
2398
2399     [_contentView didInterruptScrolling];
2400     [self _scheduleVisibleContentRectUpdateAfterScrollInView:scrollView];
2401 }
2402
2403 - (UIView *)_enclosingViewForExposedRectComputation
2404 {
2405     return [self _scroller];
2406 }
2407
2408 - (CGRect)_visibleRectInEnclosingView:(UIView *)enclosingView
2409 {
2410     if (!enclosingView)
2411         return self.bounds;
2412
2413     CGRect exposedRect = [enclosingView convertRect:enclosingView.bounds toView:self];
2414     return CGRectIntersectsRect(exposedRect, self.bounds) ? CGRectIntersection(exposedRect, self.bounds) : CGRectZero;
2415 }
2416
2417 - (CGRect)_visibleContentRect
2418 {
2419     if (_frozenVisibleContentRect)
2420         return _frozenVisibleContentRect.value();
2421
2422     CGRect visibleRectInContentCoordinates = [self convertRect:self.bounds toView:_contentView.get()];
2423
2424     if (UIView *enclosingView = [self _enclosingViewForExposedRectComputation]) {
2425         CGRect viewVisibleRect = [self _visibleRectInEnclosingView:enclosingView];
2426         CGRect viewVisibleContentRect = [self convertRect:viewVisibleRect toView:_contentView.get()];
2427         visibleRectInContentCoordinates = CGRectIntersection(visibleRectInContentCoordinates, viewVisibleContentRect);
2428     }
2429
2430     return visibleRectInContentCoordinates;
2431 }
2432
2433 // Called when some ancestor UIScrollView scrolls.
2434 - (void)_didScroll
2435 {
2436     [self _scheduleVisibleContentRectUpdateAfterScrollInView:[self _scroller]];
2437
2438     const NSTimeInterval ScrollingEndedTimerInterval = 0.032;
2439     if (!_enclosingScrollViewScrollTimer) {
2440         _enclosingScrollViewScrollTimer = adoptNS([[NSTimer alloc] initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:ScrollingEndedTimerInterval]
2441             interval:0 target:self selector:@selector(_enclosingScrollerScrollingEnded:) userInfo:nil repeats:YES]);
2442         [[NSRunLoop mainRunLoop] addTimer:_enclosingScrollViewScrollTimer.get() forMode:NSDefaultRunLoopMode];
2443     }
2444     _didScrollSinceLastTimerFire = YES;
2445 }
2446
2447 - (void)_enclosingScrollerScrollingEnded:(NSTimer *)timer
2448 {
2449     if (_didScrollSinceLastTimerFire) {
2450         _didScrollSinceLastTimerFire = NO;
2451         return;
2452     }
2453
2454     [self _scheduleVisibleContentRectUpdate];
2455     [_enclosingScrollViewScrollTimer invalidate];
2456     _enclosingScrollViewScrollTimer = nil;
2457 }
2458
2459 static WebCore::FloatSize activeMinimumLayoutSize(WKWebView *webView, const CGRect& bounds)
2460 {
2461     if (webView->_overridesMinimumLayoutSize)
2462         return WebCore::FloatSize(webView->_minimumLayoutSizeOverride);
2463
2464 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000
2465     UIEdgeInsets systemContentInset = [webView->_scrollView _systemContentInset];
2466     return WebCore::FloatSize(UIEdgeInsetsInsetRect(CGRectMake(0, 0, bounds.size.width, bounds.size.height), systemContentInset).size);
2467 #else
2468     return WebCore::FloatSize(bounds.size);
2469 #endif
2470 }
2471
2472 - (void)_dispatchSetMinimumLayoutSize:(WebCore::FloatSize)minimumLayoutSize
2473 {
2474     if (_lastSentMinimumLayoutSize && CGSizeEqualToSize(_lastSentMinimumLayoutSize.value(), minimumLayoutSize))
2475         return;
2476
2477     LOG_WITH_STREAM(VisibleRects, stream << "-[WKWebView " << _page->pageID() << " _dispatchSetMinimumLayoutSize:] " << minimumLayoutSize << " contentZoomScale " << contentZoomScale(self));
2478     _page->setViewportConfigurationMinimumLayoutSize(minimumLayoutSize);
2479     _lastSentMinimumLayoutSize = minimumLayoutSize;
2480 }
2481
2482 - (void)_dispatchSetMaximumUnobscuredSize:(WebCore::FloatSize)maximumUnobscuredSize
2483 {
2484     if (_lastSentMaximumUnobscuredSize && CGSizeEqualToSize(_lastSentMaximumUnobscuredSize.value(), maximumUnobscuredSize))
2485         return;
2486
2487     _page->setMaximumUnobscuredSize(maximumUnobscuredSize);
2488     _lastSentMaximumUnobscuredSize = maximumUnobscuredSize;
2489 }
2490
2491 - (void)_dispatchSetDeviceOrientation:(int32_t)deviceOrientation
2492 {
2493     if (_lastSentDeviceOrientation && _lastSentDeviceOrientation.value() == deviceOrientation)
2494         return;
2495
2496     _page->setDeviceOrientation(deviceOrientation);
2497     _lastSentDeviceOrientation = deviceOrientation;
2498 }
2499
2500 - (void)_frameOrBoundsChanged
2501 {
2502     CGRect bounds = self.bounds;
2503     [_scrollView setFrame:bounds];
2504
2505     if (_dynamicViewportUpdateMode == DynamicViewportUpdateMode::NotResizing) {
2506         if (!_overridesMinimumLayoutSize)
2507             [self _dispatchSetMinimumLayoutSize:activeMinimumLayoutSize(self, self.bounds)];
2508         if (!_overridesMaximumUnobscuredSize)
2509             [self _dispatchSetMaximumUnobscuredSize:WebCore::FloatSize(bounds.size)];
2510
2511         BOOL sizeChanged = NO;
2512         if (auto drawingArea = _page->drawingArea())
2513             sizeChanged = drawingArea->setSize(WebCore::IntSize(bounds.size), WebCore::IntSize(), WebCore::IntSize());
2514
2515         if (sizeChanged & [self usesStandardContentView])
2516             [_contentView setSizeChangedSinceLastVisibleContentRectUpdate:YES];
2517     }
2518
2519     [_customContentView web_setMinimumSize:bounds.size];
2520     [self _scheduleVisibleContentRectUpdate];
2521 }
2522
2523 // 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.
2524 - (CGRect)_contentRectForUserInteraction
2525 {
2526     // FIXME: handle split keyboard.
2527     UIEdgeInsets obscuredInsets = _obscuredInsets;
2528     obscuredInsets.bottom = std::max(_obscuredInsets.bottom, _inputViewBounds.size.height);
2529     CGRect unobscuredRect = UIEdgeInsetsInsetRect(self.bounds, obscuredInsets);
2530     return [self convertRect:unobscuredRect toView:self._currentContentView];
2531 }
2532
2533 // Ideally UIScrollView would expose this for us: <rdar://problem/21394567>.
2534 - (BOOL)_scrollViewIsRubberBanding
2535 {
2536     float deviceScaleFactor = _page->deviceScaleFactor();
2537
2538     CGPoint contentOffset = [_scrollView contentOffset];
2539     CGPoint boundedOffset = contentOffsetBoundedInValidRange(_scrollView.get(), contentOffset);
2540     return !pointsEqualInDevicePixels(contentOffset, boundedOffset, deviceScaleFactor);
2541 }
2542
2543 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000
2544 - (void)safeAreaInsetsDidChange
2545 {
2546     [super safeAreaInsetsDidChange];
2547
2548     [self _scheduleVisibleContentRectUpdate];
2549 }
2550 #endif
2551
2552 - (void)_scheduleVisibleContentRectUpdate
2553 {
2554     // For visible rect updates not associated with a specific UIScrollView, just consider our own scroller.
2555     [self _scheduleVisibleContentRectUpdateAfterScrollInView:_scrollView.get()];
2556 }
2557
2558 - (BOOL)_scrollViewIsInStableState:(UIScrollView *)scrollView
2559 {
2560     BOOL isStableState = !([scrollView isDragging] || [scrollView isDecelerating] || [scrollView isZooming] || [scrollView _isAnimatingZoom] || [scrollView _isScrollingToTop]);
2561
2562     if (isStableState && scrollView == _scrollView.get())
2563         isStableState = !_isChangingObscuredInsetsInteractively;
2564
2565     if (isStableState && scrollView == _scrollView.get())
2566         isStableState = ![self _scrollViewIsRubberBanding];
2567
2568     if (isStableState)
2569         isStableState = !scrollView._isInterruptingDeceleration;
2570
2571     if (NSNumber *stableOverride = self._stableStateOverride)
2572         isStableState = stableOverride.boolValue;
2573
2574     return isStableState;
2575 }
2576
2577 - (void)_addUpdateVisibleContentRectPreCommitHandler
2578 {
2579     auto retainedSelf = retainPtr(self);
2580     [CATransaction addCommitHandler:[retainedSelf] {
2581         WKWebView *webView = retainedSelf.get();
2582         if (![webView _isValid])
2583             return;
2584         [webView _updateVisibleContentRects];
2585         webView->_hasScheduledVisibleRectUpdate = NO;
2586     } forPhase:kCATransactionPhasePreCommit];
2587 }
2588
2589 - (void)_scheduleVisibleContentRectUpdateAfterScrollInView:(UIScrollView *)scrollView
2590 {
2591     _visibleContentRectUpdateScheduledFromScrollViewInStableState = [self _scrollViewIsInStableState:scrollView];
2592
2593     if (_hasScheduledVisibleRectUpdate)
2594         return;
2595
2596     _hasScheduledVisibleRectUpdate = YES;
2597
2598 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000
2599     CATransactionPhase transactionPhase = [CATransaction currentPhase];
2600     if (transactionPhase == kCATransactionPhaseNull || transactionPhase == kCATransactionPhasePreLayout) {
2601         [self _addUpdateVisibleContentRectPreCommitHandler];
2602         return;
2603     }
2604 #endif
2605
2606     dispatch_async(dispatch_get_main_queue(), [retainedSelf = retainPtr(self)] {
2607         WKWebView *webView = retainedSelf.get();
2608         if (![webView _isValid])
2609             return;
2610         [webView _addUpdateVisibleContentRectPreCommitHandler];
2611     });
2612 }
2613
2614 static bool scrollViewCanScroll(UIScrollView *scrollView)
2615 {
2616     if (!scrollView)
2617         return NO;
2618
2619     UIEdgeInsets contentInset = scrollView.contentInset;
2620     CGSize contentSize = scrollView.contentSize;
2621     CGSize boundsSize = scrollView.bounds.size;
2622
2623     return (contentSize.width + contentInset.left + contentInset.right) > boundsSize.width
2624         || (contentSize.height + contentInset.top + contentInset.bottom) > boundsSize.height;
2625 }
2626
2627 - (CGRect)_contentBoundsExtendedForRubberbandingWithScale:(CGFloat)scaleFactor
2628 {
2629     CGPoint contentOffset = [_scrollView contentOffset];
2630     CGPoint boundedOffset = contentOffsetBoundedInValidRange(_scrollView.get(), contentOffset);
2631
2632     CGFloat horiontalRubberbandAmountInContentCoordinates = (contentOffset.x - boundedOffset.x) / scaleFactor;
2633     CGFloat verticalRubberbandAmountInContentCoordinates = (contentOffset.y - boundedOffset.y) / scaleFactor;
2634
2635     CGRect extendedBounds = [_contentView bounds];
2636
2637     if (horiontalRubberbandAmountInContentCoordinates < 0) {
2638         extendedBounds.origin.x += horiontalRubberbandAmountInContentCoordinates;
2639         extendedBounds.size.width -= horiontalRubberbandAmountInContentCoordinates;
2640     } else if (horiontalRubberbandAmountInContentCoordinates > 0)
2641         extendedBounds.size.width += horiontalRubberbandAmountInContentCoordinates;
2642
2643     if (verticalRubberbandAmountInContentCoordinates < 0) {
2644         extendedBounds.origin.y += verticalRubberbandAmountInContentCoordinates;
2645         extendedBounds.size.height -= verticalRubberbandAmountInContentCoordinates;
2646     } else if (verticalRubberbandAmountInContentCoordinates > 0)
2647         extendedBounds.size.height += verticalRubberbandAmountInContentCoordinates;
2648
2649     return extendedBounds;
2650 }
2651
2652 - (void)_updateVisibleContentRects
2653 {
2654     BOOL inStableState = _visibleContentRectUpdateScheduledFromScrollViewInStableState;
2655
2656     if (![self usesStandardContentView]) {
2657         [_passwordView setFrame:self.bounds];
2658         [_customContentView web_computedContentInsetDidChange];
2659         return;
2660     }
2661
2662     if (_delayUpdateVisibleContentRects) {
2663         _hadDelayedUpdateVisibleContentRects = YES;
2664         return;
2665     }
2666
2667     if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing
2668         || (_needsResetViewStateAfterCommitLoadForMainFrame && ![_contentView sizeChangedSinceLastVisibleContentRectUpdate])
2669         || [_scrollView isZoomBouncing]
2670         || _currentlyAdjustingScrollViewInsetsForKeyboard)
2671         return;
2672
2673     CGRect fullViewRect = self.bounds;
2674     CGRect visibleRectInContentCoordinates = [self _visibleContentRect];
2675
2676     UIEdgeInsets computedContentInsetUnadjustedForKeyboard = [self _computedContentInset];
2677     if (!_haveSetObscuredInsets)
2678         computedContentInsetUnadjustedForKeyboard.bottom -= _totalScrollViewBottomInsetAdjustmentForKeyboard;
2679
2680     CGFloat scaleFactor = contentZoomScale(self);
2681
2682     CGRect unobscuredRect = UIEdgeInsetsInsetRect(fullViewRect, computedContentInsetUnadjustedForKeyboard);
2683     CGRect unobscuredRectInContentCoordinates = _frozenUnobscuredContentRect ? _frozenUnobscuredContentRect.value() : [self convertRect:unobscuredRect toView:_contentView.get()];
2684     unobscuredRectInContentCoordinates = CGRectIntersection(unobscuredRectInContentCoordinates, [self _contentBoundsExtendedForRubberbandingWithScale:scaleFactor]);
2685
2686 #if ENABLE(CSS_SCROLL_SNAP) && ENABLE(ASYNC_SCROLLING)
2687     if (inStableState) {
2688         WebKit::RemoteScrollingCoordinatorProxy* coordinator = _page->scrollingCoordinatorProxy();
2689         if (coordinator && coordinator->hasActiveSnapPoint()) {
2690             CGRect unobscuredRect = UIEdgeInsetsInsetRect(fullViewRect, computedContentInsetUnadjustedForKeyboard);
2691
2692             CGPoint currentPoint = [_scrollView contentOffset];
2693             CGPoint activePoint = coordinator->nearestActiveContentInsetAdjustedSnapPoint(unobscuredRect.origin.y, currentPoint);
2694
2695             if (!CGPointEqualToPoint(activePoint, currentPoint)) {
2696                 RetainPtr<WKScrollView> strongScrollView = _scrollView;
2697                 dispatch_async(dispatch_get_main_queue(), [strongScrollView, activePoint] {
2698                     [strongScrollView setContentOffset:activePoint animated:NO];
2699                 });
2700             }
2701         }
2702     }
2703 #endif
2704
2705     [_contentView didUpdateVisibleRect:visibleRectInContentCoordinates
2706         unobscuredRect:unobscuredRectInContentCoordinates
2707         unobscuredRectInScrollViewCoordinates:unobscuredRect
2708         obscuredInsets:_obscuredInsets
2709         unobscuredSafeAreaInsets:[self _computedUnobscuredSafeAreaInset]
2710         inputViewBounds:_inputViewBounds
2711         scale:scaleFactor minimumScale:[_scrollView minimumZoomScale]
2712         inStableState:inStableState
2713         isChangingObscuredInsetsInteractively:_isChangingObscuredInsetsInteractively
2714         enclosedInScrollableAncestorView:scrollViewCanScroll([self _scroller])];
2715
2716     while (!_visibleContentRectUpdateCallbacks.isEmpty()) {
2717         auto callback = _visibleContentRectUpdateCallbacks.takeLast();
2718         callback();
2719     }
2720 }
2721
2722 - (void)_didFinishLoadForMainFrame
2723 {
2724     if (_gestureController)
2725         _gestureController->didFinishLoadForMainFrame();
2726 }
2727
2728 - (void)_didFailLoadForMainFrame
2729 {
2730     if (_gestureController)
2731         _gestureController->didFailLoadForMainFrame();
2732 }
2733
2734 - (void)_didSameDocumentNavigationForMainFrame:(WebKit::SameDocumentNavigationType)navigationType
2735 {
2736     [_customContentView web_didSameDocumentNavigation:toAPI(navigationType)];
2737
2738     if (_gestureController)
2739         _gestureController->didSameDocumentNavigationForMainFrame(navigationType);
2740 }
2741
2742 - (void)_keyboardChangedWithInfo:(NSDictionary *)keyboardInfo adjustScrollView:(BOOL)adjustScrollView
2743 {
2744     NSValue *endFrameValue = [keyboardInfo objectForKey:UIKeyboardFrameEndUserInfoKey];
2745     if (!endFrameValue)
2746         return;
2747
2748     // The keyboard rect is always in screen coordinates. In the view services case the window does not
2749     // have the interface orientation rotation transformation; its host does. So, it makes no sense to
2750     // clip the keyboard rect against its screen.
2751     if ([[self window] _isHostedInAnotherProcess])
2752         _inputViewBounds = [self.window convertRect:[endFrameValue CGRectValue] fromWindow:nil];
2753     else
2754         _inputViewBounds = [self.window convertRect:CGRectIntersection([endFrameValue CGRectValue], self.window.screen.bounds) fromWindow:nil];
2755
2756     if (adjustScrollView) {
2757         CGFloat bottomInsetBeforeAdjustment = [_scrollView contentInset].bottom;
2758         SetForScope<BOOL> insetAdjustmentGuard(_currentlyAdjustingScrollViewInsetsForKeyboard, YES);
2759         [_scrollView _adjustForAutomaticKeyboardInfo:keyboardInfo animated:YES lastAdjustment:&_lastAdjustmentForScroller];
2760         CGFloat bottomInsetAfterAdjustment = [_scrollView contentInset].bottom;
2761         if (bottomInsetBeforeAdjustment != bottomInsetAfterAdjustment)
2762             _totalScrollViewBottomInsetAdjustmentForKeyboard += bottomInsetAfterAdjustment - bottomInsetBeforeAdjustment;
2763     }
2764
2765     [self _scheduleVisibleContentRectUpdate];
2766 }
2767
2768 - (BOOL)_shouldUpdateKeyboardWithInfo:(NSDictionary *)keyboardInfo
2769 {
2770     if ([_contentView isAssistingNode])
2771         return YES;
2772
2773     NSNumber *isLocalKeyboard = [keyboardInfo valueForKey:UIKeyboardIsLocalUserInfoKey];
2774     return isLocalKeyboard && !isLocalKeyboard.boolValue;
2775 }
2776
2777 - (void)_keyboardWillChangeFrame:(NSNotification *)notification
2778 {
2779     if ([self _shouldUpdateKeyboardWithInfo:notification.userInfo])
2780         [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES];
2781 }
2782
2783 - (void)_keyboardDidChangeFrame:(NSNotification *)notification
2784 {
2785     [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:NO];
2786 }
2787
2788 - (void)_keyboardWillShow:(NSNotification *)notification
2789 {
2790     if ([self _shouldUpdateKeyboardWithInfo:notification.userInfo])
2791         [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES];
2792
2793     _page->setIsKeyboardAnimatingIn(true);
2794 }
2795
2796 - (void)_keyboardDidShow:(NSNotification *)notification
2797 {
2798     _page->setIsKeyboardAnimatingIn(false);
2799 }
2800
2801 - (void)_keyboardWillHide:(NSNotification *)notification
2802 {
2803     // Ignore keyboard will hide notifications sent during rotation. They're just there for
2804     // backwards compatibility reasons and processing the will hide notification would
2805     // temporarily screw up the unobscured view area.
2806     if ([[UIPeripheralHost sharedInstance] rotationState])
2807         return;
2808
2809     [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES];
2810 }
2811
2812 - (void)_windowDidRotate:(NSNotification *)notification
2813 {
2814     if (!_overridesInterfaceOrientation)
2815         [self _dispatchSetDeviceOrientation:deviceOrientation()];
2816 }
2817
2818 - (void)_contentSizeCategoryDidChange:(NSNotification *)notification
2819 {
2820     _page->contentSizeCategoryDidChange([self _contentSizeCategory]);
2821 }
2822
2823 - (NSString *)_contentSizeCategory
2824 {
2825     return [[UIApplication sharedApplication] preferredContentSizeCategory];
2826 }
2827
2828 - (void)_accessibilitySettingsDidChange:(NSNotification *)notification
2829 {
2830     _page->accessibilitySettingsDidChange();
2831 }
2832
2833 - (void)setAllowsBackForwardNavigationGestures:(BOOL)allowsBackForwardNavigationGestures
2834 {
2835     if (_allowsBackForwardNavigationGestures == allowsBackForwardNavigationGestures)
2836         return;
2837
2838     _allowsBackForwardNavigationGestures = allowsBackForwardNavigationGestures;
2839
2840     if (allowsBackForwardNavigationGestures) {
2841         if (!_gestureController) {
2842             _gestureController = std::make_unique<WebKit::ViewGestureController>(*_page);
2843             _gestureController->installSwipeHandler(self, [self scrollView]);
2844             if (WKWebView *alternateWebView = [_configuration _alternateWebViewForNavigationGestures])
2845                 _gestureController->setAlternateBackForwardListSourcePage(alternateWebView->_page.get());
2846         }
2847     } else
2848         _gestureController = nullptr;
2849
2850     _page->setShouldRecordNavigationSnapshots(allowsBackForwardNavigationGestures);
2851 }
2852
2853 - (BOOL)allowsBackForwardNavigationGestures
2854 {
2855     return _allowsBackForwardNavigationGestures;
2856 }
2857
2858 - (BOOL)_isNavigationSwipeGestureRecognizer:(UIGestureRecognizer *)recognizer
2859 {
2860     if (!_gestureController)
2861         return NO;
2862     return _gestureController->isNavigationSwipeGestureRecognizer(recognizer);
2863 }
2864
2865 - (void)_navigationGestureDidBegin
2866 {
2867     // During a back/forward swipe, there's a view interposed between this view and the content view that has
2868     // an offset and animation on it, which results in computing incorrect rectangles. Work around by using
2869     // frozen rects during swipes.
2870     CGRect fullViewRect = self.bounds;
2871     CGRect unobscuredRect = UIEdgeInsetsInsetRect(fullViewRect, [self _computedContentInset]);
2872
2873     _frozenVisibleContentRect = [self convertRect:fullViewRect toView:_contentView.get()];
2874     _frozenUnobscuredContentRect = [self convertRect:unobscuredRect toView:_contentView.get()];
2875
2876     LOG_WITH_STREAM(VisibleRects, stream << "_navigationGestureDidBegin: freezing visibleContentRect " << WebCore::FloatRect(_frozenVisibleContentRect.value()) << " UnobscuredContentRect " << WebCore::FloatRect(_frozenUnobscuredContentRect.value()));
2877 }
2878
2879 - (void)_navigationGestureDidEnd
2880 {
2881     _frozenVisibleContentRect = std::nullopt;
2882     _frozenUnobscuredContentRect = std::nullopt;
2883 }
2884
2885 - (void)_showPasswordViewWithDocumentName:(NSString *)documentName passwordHandler:(void (^)(NSString *))passwordHandler
2886 {
2887     ASSERT(!_passwordView);
2888     _passwordView = adoptNS([[WKPasswordView alloc] initWithFrame:self.bounds documentName:documentName]);
2889     [_passwordView setUserDidEnterPassword:passwordHandler];
2890     [_passwordView showInScrollView:_scrollView.get()];
2891     self._currentContentView.hidden = YES;
2892 }
2893
2894 - (void)_hidePasswordView
2895 {
2896     if (!_passwordView)
2897         return;
2898
2899     self._currentContentView.hidden = NO;
2900     [_passwordView hide];
2901     _passwordView = nil;
2902 }
2903
2904 - (WKPasswordView *)_passwordView
2905 {
2906     return _passwordView.get();
2907 }
2908
2909 - (void)_updateScrollViewInsetAdjustmentBehavior
2910 {
2911 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000
2912     if (![_scrollView _contentInsetAdjustmentBehaviorWasExternallyOverridden])
2913         [_scrollView _setContentInsetAdjustmentBehaviorInternal:self._safeAreaShouldAffectObscuredInsets ? UIScrollViewContentInsetAdjustmentAlways : UIScrollViewContentInsetAdjustmentNever];
2914 #endif
2915 }
2916
2917 - (void)_setAvoidsUnsafeArea:(BOOL)avoidsUnsafeArea
2918 {
2919     if (_avoidsUnsafeArea == avoidsUnsafeArea)
2920         return;
2921
2922     _avoidsUnsafeArea = avoidsUnsafeArea;
2923
2924     [self _updateScrollViewInsetAdjustmentBehavior];
2925     [self _scheduleVisibleContentRectUpdate];
2926
2927     id <WKUIDelegatePrivate> uiDelegate = (id <WKUIDelegatePrivate>)[self UIDelegate];
2928     if ([uiDelegate respondsToSelector:@selector(_webView:didChangeSafeAreaShouldAffectObscuredInsets:)])
2929         [uiDelegate _webView:self didChangeSafeAreaShouldAffectObscuredInsets:avoidsUnsafeArea];
2930 }
2931
2932 #endif // PLATFORM(IOS)
2933
2934 #pragma mark OS X-specific methods
2935
2936 #if PLATFORM(MAC)
2937
2938 - (BOOL)acceptsFirstResponder
2939 {
2940     return _impl->acceptsFirstResponder();
2941 }
2942
2943 - (BOOL)becomeFirstResponder
2944 {
2945     return _impl->becomeFirstResponder();
2946 }
2947
2948 - (BOOL)resignFirstResponder
2949 {
2950     return _impl->resignFirstResponder();
2951 }
2952
2953 - (void)viewWillStartLiveResize
2954 {
2955     _impl->viewWillStartLiveResize();
2956 }
2957
2958 - (void)viewDidEndLiveResize
2959 {
2960     _impl->viewDidEndLiveResize();
2961 }
2962
2963 - (BOOL)isFlipped
2964 {
2965     return YES;
2966 }
2967
2968 - (NSSize)intrinsicContentSize
2969 {
2970     return NSSizeFromCGSize(_impl->intrinsicContentSize());
2971 }
2972
2973 - (void)prepareContentInRect:(NSRect)rect
2974 {
2975     _impl->prepareContentInRect(NSRectToCGRect(rect));
2976 }
2977
2978 - (void)setFrameSize:(NSSize)size
2979 {
2980     [super setFrameSize:size];
2981     _impl->setFrameSize(NSSizeToCGSize(size));
2982 }
2983
2984 - (void)renewGState
2985 {
2986     _impl->renewGState();
2987     [super renewGState];
2988 }
2989
2990 #define WEBCORE_COMMAND(command) - (void)command:(id)sender { _impl->executeEditCommandForSelector(_cmd); }
2991
2992 WEBCORE_COMMAND(alignCenter)
2993 WEBCORE_COMMAND(alignJustified)
2994 WEBCORE_COMMAND(alignLeft)
2995 WEBCORE_COMMAND(alignRight)
2996 WEBCORE_COMMAND(copy)
2997 WEBCORE_COMMAND(cut)
2998 WEBCORE_COMMAND(delete)
2999 WEBCORE_COMMAND(deleteBackward)
3000 WEBCORE_COMMAND(deleteBackwardByDecomposingPreviousCharacter)
3001 WEBCORE_COMMAND(deleteForward)
3002 WEBCORE_COMMAND(deleteToBeginningOfLine)
3003 WEBCORE_COMMAND(deleteToBeginningOfParagraph)
3004 WEBCORE_COMMAND(deleteToEndOfLine)
3005 WEBCORE_COMMAND(deleteToEndOfParagraph)
3006 WEBCORE_COMMAND(deleteToMark)
3007 WEBCORE_COMMAND(deleteWordBackward)
3008 WEBCORE_COMMAND(deleteWordForward)
3009 WEBCORE_COMMAND(ignoreSpelling)
3010 WEBCORE_COMMAND(indent)
3011 WEBCORE_COMMAND(insertBacktab)
3012 WEBCORE_COMMAND(insertLineBreak)
3013 WEBCORE_COMMAND(insertNewline)
3014 WEBCORE_COMMAND(insertNewlineIgnoringFieldEditor)
3015 WEBCORE_COMMAND(insertParagraphSeparator)
3016 WEBCORE_COMMAND(insertTab)
3017 WEBCORE_COMMAND(insertTabIgnoringFieldEditor)
3018 WEBCORE_COMMAND(makeTextWritingDirectionLeftToRight)
3019 WEBCORE_COMMAND(makeTextWritingDirectionNatural)
3020 WEBCORE_COMMAND(makeTextWritingDirectionRightToLeft)
3021 WEBCORE_COMMAND(moveBackward)
3022 WEBCORE_COMMAND(moveBackwardAndModifySelection)
3023 WEBCORE_COMMAND(moveDown)
3024 WEBCORE_COMMAND(moveDownAndModifySelection)
3025 WEBCORE_COMMAND(moveForward)
3026 WEBCORE_COMMAND(moveForwardAndModifySelection)
3027 WEBCORE_COMMAND(moveLeft)
3028 WEBCORE_COMMAND(moveLeftAndModifySelection)
3029 WEBCORE_COMMAND(moveParagraphBackwardAndModifySelection)
3030 WEBCORE_COMMAND(moveParagraphForwardAndModifySelection)
3031 WEBCORE_COMMAND(moveRight)
3032 WEBCORE_COMMAND(moveRightAndModifySelection)
3033 WEBCORE_COMMAND(moveToBeginningOfDocument)
3034 WEBCORE_COMMAND(moveToBeginningOfDocumentAndModifySelection)
3035 WEBCORE_COMMAND(moveToBeginningOfLine)
3036 WEBCORE_COMMAND(moveToBeginningOfLineAndModifySelection)
3037 WEBCORE_COMMAND(moveToBeginningOfParagraph)
3038 WEBCORE_COMMAND(moveToBeginningOfParagraphAndModifySelection)
3039 WEBCORE_COMMAND(moveToBeginningOfSentence)
3040 WEBCORE_COMMAND(moveToBeginningOfSentenceAndModifySelection)
3041 WEBCORE_COMMAND(moveToEndOfDocument)
3042 WEBCORE_COMMAND(moveToEndOfDocumentAndModifySelection)
3043 WEBCORE_COMMAND(moveToEndOfLine)
3044 WEBCORE_COMMAND(moveToEndOfLineAndModifySelection)
3045 WEBCORE_COMMAND(moveToEndOfParagraph)
3046 WEBCORE_COMMAND(moveToEndOfParagraphAndModifySelection)
3047 WEBCORE_COMMAND(moveToEndOfSentence)
3048 WEBCORE_COMMAND(moveToEndOfSentenceAndModifySelection)
3049 WEBCORE_COMMAND(moveToLeftEndOfLine)
3050 WEBCORE_COMMAND(moveToLeftEndOfLineAndModifySelection)
3051 WEBCORE_COMMAND(moveToRightEndOfLine)
3052 WEBCORE_COMMAND(moveToRightEndOfLineAndModifySelection)
3053 WEBCORE_COMMAND(moveUp)
3054 WEBCORE_COMMAND(moveUpAndModifySelection)
3055 WEBCORE_COMMAND(moveWordBackward)
3056 WEBCORE_COMMAND(moveWordBackwardAndModifySelection)
3057 WEBCORE_COMMAND(moveWordForward)
3058 WEBCORE_COMMAND(moveWordForwardAndModifySelection)
3059 WEBCORE_COMMAND(moveWordLeft)
3060 WEBCORE_COMMAND(moveWordLeftAndModifySelection)
3061 WEBCORE_COMMAND(moveWordRight)
3062 WEBCORE_COMMAND(moveWordRightAndModifySelection)
3063 WEBCORE_COMMAND(outdent)
3064 WEBCORE_COMMAND(pageDown)
3065 WEBCORE_COMMAND(pageDownAndModifySelection)
3066 WEBCORE_COMMAND(pageUp)
3067 WEBCORE_COMMAND(pageUpAndModifySelection)
3068 WEBCORE_COMMAND(paste)
3069 WEBCORE_COMMAND(pasteAsPlainText)
3070 WEBCORE_COMMAND(scrollPageDown)
3071 WEBCORE_COMMAND(scrollPageUp)
3072 WEBCORE_COMMAND(scrollLineDown)
3073 WEBCORE_COMMAND(scrollLineUp)
3074 WEBCORE_COMMAND(scrollToBeginningOfDocument)
3075 WEBCORE_COMMAND(scrollToEndOfDocument)
3076 WEBCORE_COMMAND(selectAll)
3077 WEBCORE_COMMAND(selectLine)
3078 WEBCORE_COMMAND(selectParagraph)
3079 WEBCORE_COMMAND(selectSentence)
3080 WEBCORE_COMMAND(selectToMark)
3081 WEBCORE_COMMAND(selectWord)
3082 WEBCORE_COMMAND(setMark)
3083 WEBCORE_COMMAND(subscript)
3084 WEBCORE_COMMAND(superscript)
3085 WEBCORE_COMMAND(swapWithMark)
3086 WEBCORE_COMMAND(takeFindStringFromSelection)
3087 WEBCORE_COMMAND(transpose)
3088 WEBCORE_COMMAND(underline)
3089 WEBCORE_COMMAND(unscript)
3090 WEBCORE_COMMAND(yank)
3091 WEBCORE_COMMAND(yankAndSelect)
3092
3093 #undef WEBCORE_COMMAND
3094
3095 - (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pasteboard types:(NSArray *)types
3096 {
3097     return _impl->writeSelectionToPasteboard(pasteboard, types);
3098 }
3099
3100 - (void)centerSelectionInVisibleArea:(id)sender
3101 {
3102     _impl->centerSelectionInVisibleArea();
3103 }
3104
3105 - (id)validRequestorForSendType:(NSString *)sendType returnType:(NSString *)returnType
3106 {
3107     return _impl->validRequestorForSendAndReturnTypes(sendType, returnType);
3108 }
3109
3110 - (BOOL)readSelectionFromPasteboard:(NSPasteboard *)pasteboard
3111 {
3112     return _impl->readSelectionFromPasteboard(pasteboard);
3113 }
3114
3115 - (void)changeFont:(id)sender
3116 {
3117     _impl->changeFontFromFontPanel();
3118 }
3119
3120 - (IBAction)startSpeaking:(id)sender
3121 {
3122     _impl->startSpeaking();
3123 }
3124
3125 - (IBAction)stopSpeaking:(id)sender
3126 {
3127     _impl->stopSpeaking(sender);
3128 }
3129
3130 - (IBAction)showGuessPanel:(id)sender
3131 {
3132     _impl->showGuessPanel(sender);
3133 }
3134
3135 - (IBAction)checkSpelling:(id)sender
3136 {
3137     _impl->checkSpelling();
3138 }
3139
3140 - (void)changeSpelling:(id)sender
3141 {
3142     _impl->changeSpelling(sender);
3143 }
3144
3145 - (IBAction)toggleContinuousSpellChecking:(id)sender
3146 {
3147     _impl->toggleContinuousSpellChecking();
3148 }
3149
3150 - (BOOL)isGrammarCheckingEnabled
3151 {
3152     return _impl->isGrammarCheckingEnabled();
3153 }
3154
3155 - (void)setGrammarCheckingEnabled:(BOOL)flag
3156 {
3157     _impl->setGrammarCheckingEnabled(flag);
3158 }
3159
3160 - (IBAction)toggleGrammarChecking:(id)sender
3161 {
3162     _impl->toggleGrammarChecking();
3163 }
3164
3165 - (IBAction)toggleAutomaticSpellingCorrection:(id)sender
3166 {
3167     _impl->toggleAutomaticSpellingCorrection();
3168 }
3169
3170 - (void)orderFrontSubstitutionsPanel:(id)sender
3171 {
3172     _impl->orderFrontSubstitutionsPanel(sender);
3173 }
3174
3175 - (IBAction)toggleSmartInsertDelete:(id)sender
3176 {
3177     _impl->toggleSmartInsertDelete();
3178 }
3179
3180 - (BOOL)isAutomaticQuoteSubstitutionEnabled
3181 {
3182     return _impl->isAutomaticQuoteSubstitutionEnabled();
3183 }
3184
3185 - (void)setAutomaticQuoteSubstitutionEnabled:(BOOL)flag
3186 {
3187     _impl->setAutomaticQuoteSubstitutionEnabled(flag);
3188 }
3189
3190 - (void)toggleAutomaticQuoteSubstitution:(id)sender
3191 {
3192     _impl->toggleAutomaticQuoteSubstitution();
3193 }
3194
3195 - (BOOL)isAutomaticDashSubstitutionEnabled
3196 {
3197     return _impl->isAutomaticDashSubstitutionEnabled();
3198 }
3199
3200 - (void)setAutomaticDashSubstitutionEnabled:(BOOL)flag
3201 {
3202     _impl->setAutomaticDashSubstitutionEnabled(flag);
3203 }
3204
3205 - (void)toggleAutomaticDashSubstitution:(id)sender
3206 {
3207     _impl->toggleAutomaticDashSubstitution();
3208 }
3209
3210 - (BOOL)isAutomaticLinkDetectionEnabled
3211 {
3212     return _impl->isAutomaticLinkDetectionEnabled();
3213 }
3214
3215 - (void)setAutomaticLinkDetectionEnabled:(BOOL)flag
3216 {
3217     _impl->setAutomaticLinkDetectionEnabled(flag);
3218 }
3219
3220 - (void)toggleAutomaticLinkDetection:(id)sender
3221 {
3222     _impl->toggleAutomaticLinkDetection();
3223 }
3224
3225 - (BOOL)isAutomaticTextReplacementEnabled
3226 {
3227     return _impl->isAutomaticTextReplacementEnabled();
3228 }
3229
3230 - (void)setAutomaticTextReplacementEnabled:(BOOL)flag
3231 {
3232     _impl->setAutomaticTextReplacementEnabled(flag);
3233 }
3234
3235 - (void)toggleAutomaticTextReplacement:(id)sender
3236 {
3237     _impl->toggleAutomaticTextReplacement();
3238 }
3239
3240 - (void)uppercaseWord:(id)sender
3241 {
3242     _impl->uppercaseWord();
3243 }
3244
3245 - (void)lowercaseWord:(id)sender
3246 {
3247     _impl->lowercaseWord();
3248 }
3249
3250 - (void)capitalizeWord:(id)sender
3251 {
3252     _impl->capitalizeWord();
3253 }
3254
3255 - (BOOL)_wantsKeyDownForEvent:(NSEvent *)event
3256 {
3257     return _impl->wantsKeyDownForEvent(event);
3258 }
3259
3260 - (void)scrollWheel:(NSEvent *)event
3261 {
3262     _impl->scrollWheel(event);
3263 }
3264
3265 - (void)swipeWithEvent:(NSEvent *)event
3266 {
3267     _impl->swipeWithEvent(event);
3268 }
3269
3270 - (void)mouseMoved:(NSEvent *)event
3271 {
3272     _impl->mouseMoved(event);
3273 }
3274
3275 - (void)mouseDown:(NSEvent *)event
3276 {
3277     _impl->mouseDown(event);
3278 }
3279
3280 - (void)mouseUp:(NSEvent *)event
3281 {
3282     _impl->mouseUp(event);
3283 }
3284
3285 - (void)mouseDragged:(NSEvent *)event
3286 {
3287     _impl->mouseDragged(event);
3288 }
3289
3290 - (void)mouseEntered:(NSEvent *)event
3291 {
3292     _impl->mouseEntered(event);
3293 }
3294
3295 - (void)mouseExited:(NSEvent *)event
3296 {
3297     _impl->mouseExited(event);
3298 }
3299
3300 - (void)otherMouseDown:(NSEvent *)event
3301 {
3302     _impl->otherMouseDown(event);
3303 }
3304
3305 - (void)otherMouseDragged:(NSEvent *)event
3306 {
3307     _impl->otherMouseDragged(event);
3308 }
3309
3310 - (void)otherMouseUp:(NSEvent *)event
3311 {
3312     _impl->otherMouseUp(event);
3313 }
3314
3315 - (void)rightMouseDown:(NSEvent *)event
3316 {
3317     _impl->rightMouseDown(event);
3318 }
3319
3320 - (void)rightMouseDragged:(NSEvent *)event
3321 {
3322     _impl->rightMouseDragged(event);
3323 }
3324
3325 - (void)rightMouseUp:(NSEvent *)event
3326 {
3327     _impl->rightMouseUp(event);
3328 }
3329
3330 - (void)pressureChangeWithEvent:(NSEvent *)event
3331 {
3332     _impl->pressureChangeWithEvent(event);
3333 }
3334
3335 - (BOOL)acceptsFirstMouse:(NSEvent *)event
3336 {
3337     return _impl->acceptsFirstMouse(event);
3338 }
3339
3340 - (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent *)event
3341 {
3342     return _impl->shouldDelayWindowOrderingForEvent(event);
3343 }
3344
3345 - (void)doCommandBySelector:(SEL)selector
3346 {
3347     _impl->doCommandBySelector(selector);
3348 }
3349
3350 - (void)insertText:(id)string
3351 {
3352     _impl->insertText(string);
3353 }
3354
3355 - (void)insertText:(id)string replacementRange:(NSRange)replacementRange
3356 {
3357     _impl->insertText(string, replacementRange);
3358 }
3359
3360 - (NSTextInputContext *)inputContext
3361 {
3362     if (!_impl)
3363         return nil;
3364     return _impl->inputContext();
3365 }
3366
3367 - (BOOL)performKeyEquivalent:(NSEvent *)event
3368 {
3369     return _impl->performKeyEquivalent(event);
3370 }
3371
3372 - (void)keyUp:(NSEvent *)theEvent
3373 {
3374     _impl->keyUp(theEvent);
3375 }
3376
3377 - (void)keyDown:(NSEvent *)theEvent
3378 {
3379     _impl->keyDown(theEvent);
3380 }
3381
3382 - (void)flagsChanged:(NSEvent *)theEvent
3383 {
3384     _impl->flagsChanged(theEvent);
3385 }
3386
3387 - (void)setMarkedText:(id)string selectedRange:(NSRange)newSelectedRange replacementRange:(NSRange)replacementRange
3388 {
3389     _impl->setMarkedText(string, newSelectedRange, replacementRange);
3390 }
3391
3392 - (void)unmarkText
3393 {
3394     _impl->unmarkText();
3395 }
3396
3397 - (NSRange)selectedRange
3398 {
3399     return _impl->selectedRange();
3400 }
3401
3402 - (BOOL)hasMarkedText
3403 {
3404     return _impl->hasMarkedText();
3405 }
3406
3407 - (NSRange)markedRange
3408 {
3409     return _impl->markedRange();
3410 }
3411
3412 - (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)nsRange actualRange:(NSRangePointer)actualRange
3413 {
3414     return _impl->attributedSubstringForProposedRange(nsRange, actualRange);
3415 }
3416
3417 - (NSUInteger)characterIndexForPoint:(NSPoint)thePoint
3418 {
3419     return _impl->characterIndexForPoint(thePoint);
3420 }
3421
3422 - (NSRect)firstRectForCharacterRange:(NSRange)theRange actualRange:(NSRangePointer)actualRange
3423 {
3424     return _impl->firstRectForCharacterRange(theRange, actualRange);
3425 }
3426
3427 - (void)selectedRangeWithCompletionHandler:(void(^)(NSRange selectedRange))completionHandlerPtr
3428 {
3429     _impl->selectedRangeWithCompletionHandler(completionHandlerPtr);
3430 }
3431
3432 - (void)markedRangeWithCompletionHandler:(void(^)(NSRange markedRange))completionHandlerPtr
3433 {
3434     _impl->markedRangeWithCompletionHandler(completionHandlerPtr);
3435 }
3436
3437 - (void)hasMarkedTextWithCompletionHandler:(void(^)(BOOL hasMarkedText))completionHandlerPtr
3438 {
3439     _impl->hasMarkedTextWithCompletionHandler(completionHandlerPtr);
3440 }
3441
3442 - (void)attributedSubstringForProposedRange:(NSRange)nsRange completionHandler:(void(^)(NSAttributedString *attrString, NSRange actualRange))completionHandlerPtr
3443 {
3444     _impl->attributedSubstringForProposedRange(nsRange, completionHandlerPtr);
3445 }
3446
3447 - (void)firstRectForCharacterRange:(NSRange)theRange completionHandler:(void(^)(NSRect firstRect, NSRange actualRange))completionHandlerPtr
3448 {
3449     _impl->firstRectForCharacterRange(theRange, completionHandlerPtr);
3450 }
3451
3452 - (void)characterIndexForPoint:(NSPoint)thePoint completionHandler:(void(^)(NSUInteger))completionHandlerPtr
3453 {
3454     _impl->characterIndexForPoint(thePoint, completionHandlerPtr);
3455 }
3456
3457 - (NSArray *)validAttributesForMarkedText
3458 {
3459     return _impl->validAttributesForMarkedText();
3460 }
3461
3462 #if ENABLE(DRAG_SUPPORT)
3463 - (void)draggedImage:(NSImage *)image endedAt:(NSPoint)endPoint operation:(NSDragOperation)operation
3464 {
3465     _impl->draggedImage(image, NSPointToCGPoint(endPoint), operation);
3466 }
3467
3468 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)draggingInfo
3469 {
3470     return _impl->draggingEntered(draggingInfo);
3471 }
3472
3473 - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)draggingInfo
3474 {
3475     return _impl->draggingUpdated(draggingInfo);
3476 }
3477
3478 - (void)draggingExited:(id <NSDraggingInfo>)draggingInfo
3479 {
3480     _impl->draggingExited(draggingInfo);
3481 }
3482
3483 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)draggingInfo
3484 {
3485     return _impl->prepareForDragOperation(draggingInfo);
3486 }
3487
3488 - (BOOL)performDragOperation:(id <NSDraggingInfo>)draggingInfo
3489 {
3490     return _impl->performDragOperation(draggingInfo);
3491 }
3492
3493 - (NSView *)_hitTest:(NSPoint *)point dragTypes:(NSSet *)types
3494 {
3495     return _impl->hitTestForDragTypes(NSPointToCGPoint(*point), types);
3496 }
3497 #endif // ENABLE(DRAG_SUPPORT)
3498
3499 - (BOOL)_windowResizeMouseLocationIsInVisibleScrollerThumb:(NSPoint)point
3500 {
3501     return _impl->windowResizeMouseLocationIsInVisibleScrollerThumb(NSPointToCGPoint(point));
3502 }
3503
3504 - (void)viewWillMoveToWindow:(NSWindow *)window
3505 {
3506     _impl->viewWillMoveToWindow(window);
3507 }
3508
3509 - (void)viewDidMoveToWindow
3510 {
3511     _impl->viewDidMoveToWindow();
3512 }
3513
3514 - (void)drawRect:(NSRect)rect
3515 {
3516     _impl->drawRect(NSRectToCGRect(rect));
3517 }
3518
3519 - (BOOL)isOpaque
3520 {
3521     return _impl->isOpaque();
3522 }
3523
3524 - (BOOL)mouseDownCanMoveWindow
3525 {
3526     return WebKit::WebViewImpl::mouseDownCanMoveWindow();
3527 }
3528
3529 - (void)viewDidHide
3530 {
3531     _impl->viewDidHide();
3532 }
3533
3534 - (void)viewDidUnhide
3535 {
3536     _impl->viewDidUnhide();
3537 }
3538
3539 - (void)viewDidChangeBackingProperties
3540 {
3541     _impl->viewDidChangeBackingProperties();
3542 }
3543
3544 - (void)_activeSpaceDidChange:(NSNotification *)notification
3545 {
3546     _impl->activeSpaceDidChange();
3547 }
3548
3549 - (id)accessibilityFocusedUIElement
3550 {
3551     return _impl->accessibilityFocusedUIElement();
3552 }
3553
3554 - (BOOL)accessibilityIsIgnored
3555 {
3556     return _impl->accessibilityIsIgnored();
3557 }
3558
3559 - (id)accessibilityHitTest:(NSPoint)point
3560 {
3561     return _impl->accessibilityHitTest(NSPointToCGPoint(point));
3562 }
3563
3564 - (id)accessibilityAttributeValue:(NSString *)attribute
3565 {
3566     return _impl->accessibilityAttributeValue(attribute);
3567 }
3568
3569 - (NSView *)hitTest:(NSPoint)point
3570 {
3571     if (!_impl)
3572         return [super hitTest:point];
3573     return _impl->hitTest(NSPointToCGPoint(point));
3574 }
3575
3576 - (NSInteger)conversationIdentifier
3577 {
3578     return (NSInteger)self;
3579 }
3580
3581 - (void)quickLookWithEvent:(NSEvent *)event
3582 {
3583     _impl->quickLookWithEvent(event);
3584 }
3585
3586 - (NSTrackingRectTag)addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside
3587 {
3588     return _impl->addTrackingRect(NSRectToCGRect(rect), owner, data, assumeInside);
3589 }
3590
3591 - (NSTrackingRectTag)_addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside useTrackingNum:(int)tag
3592 {
3593     return _impl->addTrackingRectWithTrackingNum(NSRectToCGRect(rect), owner, data, assumeInside, tag);
3594 }
3595
3596 - (void)_addTrackingRects:(NSRect *)rects owner:(id)owner userDataList:(void **)userDataList assumeInsideList:(BOOL *)assumeInsideList trackingNums:(NSTrackingRectTag *)trackingNums count:(int)count
3597 {
3598     CGRect *cgRects = (CGRect *)calloc(1, sizeof(CGRect));
3599     for (int i = 0; i < count; i++)
3600         cgRects[i] = NSRectToCGRect(rects[i]);
3601     _impl->addTrackingRectsWithTrackingNums(cgRects, owner, userDataList, assumeInsideList, trackingNums, count);
3602     free(cgRects);
3603 }
3604
3605 - (void)removeTrackingRect:(NSTrackingRectTag)tag
3606 {
3607     if (!_impl)
3608         return;
3609     _impl->removeTrackingRect(tag);
3610 }
3611
3612 - (void)_removeTrackingRects:(NSTrackingRectTag *)tags count:(int)count
3613 {
3614     if (!_impl)
3615         return;
3616     _impl->removeTrackingRects(tags, count);
3617 }
3618
3619 - (NSString *)view:(NSView *)view stringForToolTip:(NSToolTipTag)tag point:(NSPoint)point userData:(void *)data
3620 {
3621     return _impl->stringForToolTip(tag);
3622 }
3623
3624 - (void)pasteboardChangedOwner:(NSPasteboard *)pasteboard
3625 {
3626     _impl->pasteboardChangedOwner(pasteboard);
3627 }
3628
3629 - (void)pasteboard:(NSPasteboard *)pasteboard provideDataForType:(NSString *)type
3630 {
3631     _impl->provideDataForPasteboard(pasteboard, type);
3632 }
3633
3634 - (NSArray *)namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination
3635 {
3636     return _impl->namesOfPromisedFilesDroppedAtDestination(dropDestination);
3637 }
3638
3639 - (BOOL)wantsUpdateLayer
3640 {
3641     return WebKit::WebViewImpl::wantsUpdateLayer();
3642 }
3643
3644 - (void)updateLayer
3645 {
3646     _impl->updateLayer();
3647 }
3648
3649 - (void)setAllowsBackForwardNavigationGestures:(BOOL)allowsBackForwardNavigationGestures
3650 {
3651     _impl->setAllowsBackForwardNavigationGestures(allowsBackForwardNavigationGestures);
3652 }
3653
3654 - (BOOL)allowsBackForwardNavigationGestures
3655 {
3656     return _impl->allowsBackForwardNavigationGestures();
3657 }
3658
3659 - (void)smartMagnifyWithEvent:(NSEvent *)event
3660 {
3661     _impl->smartMagnifyWithEvent(event);
3662 }
3663
3664 - (void)setMagnification:(double)magnification centeredAtPoint:(NSPoint)point
3665 {
3666     _impl->setMagnification(magnification, NSPointToCGPoint(point));
3667 }
3668
3669 - (void)setMagnification:(double)magnification
3670 {
3671     _impl->setMagnification(magnification);
3672 }
3673
3674 - (double)magnification
3675 {
3676     return _impl->magnification();
3677 }
3678
3679 - (void)setAllowsMagnification:(BOOL)allowsMagnification
3680 {
3681     _impl->setAllowsMagnification(allowsMagnification);
3682 }
3683
3684 - (BOOL)allowsMagnification
3685 {
3686     return _impl->allowsMagnification();
3687 }
3688
3689 - (void)magnifyWithEvent:(NSEvent *)event
3690 {
3691     _impl->magnifyWithEvent(event);
3692 }
3693
3694 #if ENABLE(MAC_GESTURE_EVENTS)
3695 - (void)rotateWithEvent:(NSEvent *)event
3696 {
3697     _impl->rotateWithEvent(event);
3698 }
3699 #endif
3700
3701 - (WKTextFinderClient *)_ensureTextFinderClient
3702 {
3703     if (!_textFinderClient)
3704         _textFinderClient = adoptNS([[WKTextFinderClient alloc] initWithPage:*_page view:self]);
3705     return _textFinderClient.get();
3706 }
3707
3708 - (void)findMatchesForString:(NSString *)targetString relativeToMatch:(id <NSTextFinderAsynchronousDocumentFindMatch>)relativeMatch findOptions:(NSTextFinderAsynchronousDocumentFindOptions)findOptions maxResults:(NSUInteger)maxResults resultCollector:(void (^)(NSArray *matches, BOOL didWrap))resultCollector
3709 {
3710     [[self _ensureTextFinderClient] findMatchesForString:targetString relativeToMatch:relativeMatch findOptions:findOptions maxResults:maxResults resultCollector:resultCollector];
3711 }
3712
3713 - (NSView *)documentContainerView
3714 {
3715     return self;
3716 }
3717
3718 - (void)getSelectedText:(void (^)(NSString *selectedTextString))completionHandler
3719 {
3720     [[self _ensureTextFinderClient] getSelectedText:completionHandler];
3721 }
3722
3723 - (void)selectFindMatch:(id <NSTextFinderAsynchronousDocumentFindMatch>)findMatch completionHandler:(void (^)(void))completionHandler
3724 {
3725     [[self _ensureTextFinderClient] selectFindMatch:findMatch completionHandler:completionHandler];
3726 }
3727
3728 - (NSTextInputContext *)_web_superInputContext
3729 {
3730     return [super inputContext];
3731 }
3732
3733 - (void)_web_superQuickLookWithEvent:(NSEvent *)event
3734 {
3735     [super quickLookWithEvent:event];
3736 }
3737
3738 - (void)_web_superSwipeWithEvent:(NSEvent *)event
3739 {
3740     [super swipeWithEvent:event];
3741 }
3742
3743 - (void)_web_superMagnifyWithEvent:(NSEvent *)event
3744 {
3745     [super magnifyWithEvent:event];
3746 }
3747
3748 - (void)_web_superSmartMagnifyWithEvent:(NSEvent *)event
3749 {
3750     [super smartMagnifyWithEvent:event];
3751 }
3752
3753 - (void)_web_superRemoveTrackingRect:(NSTrackingRectTag)tag
3754 {
3755     [super removeTrackingRect:tag];
3756 }
3757
3758 - (id)_web_superAccessibilityAttributeValue:(NSString *)attribute
3759 {
3760 #pragma clang diagnostic push
3761 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
3762     return [super accessibilityAttributeValue:attribute];
3763 #pragma clang diagnostic pop
3764 }
3765
3766 - (void)_web_superDoCommandBySelector:(SEL)selector
3767 {
3768     [super doCommandBySelector:selector];
3769 }
3770
3771 - (BOOL)_web_superPerformKeyEquivalent:(NSEvent *)event
3772 {
3773     return [super performKeyEquivalent:event];
3774 }
3775
3776 - (void)_web_superKeyDown:(NSEvent *)event
3777 {
3778     [super keyDown:event];
3779 }
3780
3781 - (NSView *)_web_superHitTest:(NSPoint)point
3782 {
3783     return [super hitTest:point];
3784 }
3785
3786 - (id)_web_immediateActionAnimationControllerForHitTestResultInternal:(API::HitTestResult*)hitTestResult withType:(uint32_t)type userData:(API::Object*)userData
3787 {
3788     id<NSSecureCoding> data = userData ? static_cast<id<NSSecureCoding>>(userData->wrapper()) : nil;
3789     return [self _immediateActionAnimationControllerForHitTestResult:wrapper(*hitTestResult) withType:(_WKImmediateActionType)type userData:data];
3790 }
3791
3792 - (void)_web_prepareForImmediateActionAnimation
3793 {
3794     id <WKUIDelegatePrivate> uiDelegate = (id <WKUIDelegatePrivate>)[self UIDelegate];
3795     if ([uiDelegate respondsToSelector:@selector(_prepareForImmediateActionAnimationForWebView:)])
3796         [uiDelegate _prepareForImmediateActionAnimationForWebView:self];
3797     else
3798         [self _prepareForImmediateActionAnimation];
3799 }
3800
3801 - (void)_web_cancelImmediateActionAnimation
3802 {
3803     id <WKUIDelegatePrivate> uiDelegate = (id <WKUIDelegatePrivate>)[self UIDelegate];
3804     if ([uiDelegate respondsToSelector:@selector(_cancelImmediateActionAnimationForWebView:)])
3805         [uiDelegate _cancelImmediateActionAnimationForWebView:self];
3806     else
3807         [self _cancelImmediateActionAnimation];
3808 }
3809
3810 - (void)_web_completeImmediateActionAnimation
3811 {
3812     id <WKUIDelegatePrivate> uiDelegate = (id <WKUIDelegatePrivate>)[self UIDelegate];
3813     if ([uiDelegate respondsToSelector:@selector(_completeImmediateActionAnimationForWebView:)])
3814         [uiDelegate _completeImmediateActionAnimationForWebView:self];
3815     else
3816         [self _completeImmediateActionAnimation];
3817 }
3818
3819 - (void)_web_didChangeContentSize:(NSSize)newSize
3820 {
3821 }
3822
3823 #if ENABLE(DRAG_SUPPORT)
3824
3825 - (WKDragDestinationAction)_web_dragDestinationActionForDraggingInfo:(id <NSDraggingInfo>)draggingInfo
3826 {
3827     id <WKUIDelegatePrivate> uiDelegate = (id <WKUIDelegatePrivate>)[self UIDelegate];
3828     if ([uiDelegate respondsToSelector:@selector(_webView:dragDestinationActionMaskForDraggingInfo:)])
3829         return [uiDelegate _webView:self dragDestinationActionMaskForDraggingInfo:draggingInfo];
3830
3831     if (!linkedOnOrAfter(WebKit::SDKVersion::FirstWithDropToNavigateDisallowedByDefault))
3832         return WKDragDestinationActionAny;
3833
3834     return WKDragDestinationActionAny & ~WKDragDestinationActionLoad;
3835 }
3836
3837 #endif
3838
3839 - (void)_web_dismissContentRelativeChildWindows
3840 {
3841     _impl->dismissContentRelativeChildWindowsFromViewOnly();
3842 }
3843
3844 - (void)_web_dismissContentRelativeChildWindowsWithAnimation:(BOOL)withAnimation
3845 {
3846     _impl->dismissContentRelativeChildWindowsWithAnimationFromViewOnly(withAnimation);
3847 }
3848
3849 - (void)_web_editorStateDidChange
3850 {
3851     [self _didChangeEditorState];
3852 }
3853
3854 - (void)_web_gestureEventWasNotHandledByWebCore:(NSEvent *)event
3855 {
3856     [self _gestureEventWasNotHandledByWebCore:event];
3857 }
3858
3859 #endif // PLATFORM(MAC)
3860
3861 #if HAVE(TOUCH_BAR)
3862
3863 @dynamic touchBar;
3864
3865 - (NSTouchBar *)makeTouchBar
3866 {
3867     return _impl->makeTouchBar();
3868 }
3869
3870 - (NSCandidateListTouchBarItem *)candidateListTouchBarItem
3871 {
3872     return _impl->candidateListTouchBarItem();
3873 }
3874
3875 - (void)_web_didAddMediaControlsManager:(id)controlsManager
3876 {
3877     [self _addMediaPlaybackControlsView:controlsManager];
3878 }
3879
3880 - (void)_web_didRemoveMediaControlsManager
3881 {
3882     [self _removeMediaPlaybackControlsView];
3883 }
3884
3885 - (void)_interactWithMediaControlsForTesting
3886 {
3887     [self _setWantsMediaPlaybackControlsView:YES];
3888     [self makeTouchBar];
3889 }
3890
3891 #endif // HAVE(TOUCH_BAR)
3892
3893 - (id <WKURLSchemeHandler>)urlSchemeHandlerForURLScheme:(NSString *)urlScheme
3894 {
3895     auto* handler = static_cast<WebKit::WebURLSchemeHandlerCocoa*>(_page->urlSchemeHandlerForScheme(urlScheme));
3896     return handler ? handler->apiHandler() : nil;
3897 }
3898
3899 + (BOOL)handlesURLScheme:(NSString *)urlScheme
3900 {
3901     return WebCore::SchemeRegistry::isBuiltinScheme(urlScheme);
3902 }
3903
3904 @end
3905
3906 @implementation WKWebView (WKPrivate)
3907
3908 - (BOOL)_isEditable
3909 {
3910     return _page->isEditable();
3911 }
3912
3913 - (void)_setEditable:(BOOL)editable
3914 {
3915     _page->setEditable(editable);
3916 #if PLATFORM(MAC)
3917     if (editable)
3918         _impl->didBecomeEditable();
3919 #endif
3920 }
3921
3922 - (_WKRemoteObjectRegistry *)_remoteObjectRegistry
3923 {
3924 #if PLATFORM(MAC)
3925     return _impl->remoteObjectRegistry();
3926 #else
3927     if (!_remoteObjectRegistry) {
3928         _remoteObjectRegistry = adoptNS([[_WKRemoteObjectRegistry alloc] _initWithMessageSender:*_page]);
3929         _page->process().processPool().addMessageReceiver(Messages::RemoteObjectRegistry::messageReceiverName(), _page->pageID(), [_remoteObjectRegistry remoteObjectRegistry]);
3930     }
3931
3932     return _remoteObjectRegistry.get();
3933 #endif
3934 }
3935
3936 - (WKBrowsingContextHandle *)_handle
3937 {
3938     return [[[WKBrowsingContextHandle alloc] _initWithPageID:_page->pageID()] autorelease];
3939 }
3940
3941 - (_WKRenderingProgressEvents)_observedRenderingProgressEvents
3942 {
3943     return _observedRenderingProgressEvents;
3944 }
3945
3946 - (id <WKHistoryDelegatePrivate>)_historyDelegate
3947 {
3948     return _navigationState->historyDelegate().autorelease();
3949 }
3950
3951 - (void)_setHistoryDelegate:(id <WKHistoryDelegatePrivate>)historyDelegate
3952 {
3953     _page->setHistoryClient(_navigationState->createHistoryClient());
3954     _navigationState->setHistoryDelegate(historyDelegate);
3955 }
3956
3957 - (NSURL *)_unreachableURL
3958 {
3959     return [NSURL _web_URLWithWTFString:_page->pageLoadState().unreachableURL()];
3960 }
3961
3962 - (void)_loadAlternateHTMLString:(NSString *)string baseURL:(NSURL *)baseURL forUnreachableURL:(NSURL *)unreachableURL
3963 {
3964     _page->loadAlternateHTMLString(string, baseURL, unreachableURL);
3965 }
3966
3967 - (WKNavigation *)_loadData:(NSData *)data MIMEType:(NSString *)MIMEType characterEncodingName:(NSString *)characterEncodingName baseURL:(NSURL *)baseURL userData:(id)userData
3968 {
3969     auto navigation = _page->loadData(API::Data::createWithoutCopying(data).ptr(), MIMEType, characterEncodingName, baseURL.absoluteString, WebKit::ObjCObjectGraph::create(userData).ptr());
3970     if (!navigation)
3971         return nil;
3972
3973     return [wrapper(*navigation.leakRef()) autorelease];
3974 }
3975
3976 - (WKNavigation *)_loadRequest:(NSURLRequest *)request shouldOpenExternalURLs:(BOOL)shouldOpenExternalURLs
3977 {
3978     auto navigation = _page->loadRequest(request, shouldOpenExternalURLs ? WebCore::ShouldOpenExternalURLsPolicy::ShouldAllow : WebCore::ShouldOpenExternalURLsPolicy::ShouldNotAllow);
3979     if (!navigation)
3980         return nil;
3981
3982     return [wrapper(*navigation.leakRef()) autorelease];
3983 }
3984
3985 - (NSArray *)_certificateChain
3986 {
3987     if (WebKit::WebFrameProxy* mainFrame = _page->mainFrame())
3988         return mainFrame->certificateInfo() ? (NSArray *)mainFrame->certificateInfo()->certificateInfo().certificateChain() : nil;
3989
3990     return nil;
3991 }
3992
3993 - (NSURL *)_committedURL
3994 {
3995     return [NSURL _web_URLWithWTFString:_page->pageLoadState().url()];
3996 }
3997
3998 - (NSString *)_MIMEType
3999 {
4000     if (_page->mainFrame())
4001         return _page->mainFrame()->mimeType();
4002
4003     return nil;
4004 }
4005
4006 - (NSString *)_userAgent
4007 {
4008     return _page->userAgent();
4009 }
4010
4011 - (NSString *)_applicationNameForUserAgent
4012 {
4013     return _page->applicationNameForUserAgent();
4014 }
4015
4016 - (void)_setApplicationNameForUserAgent:(NSString *)applicationNameForUserAgent
4017 {
4018     _page->setApplicationNameForUserAgent(applicationNameForUserAgent);
4019 }
4020
4021 - (NSString *)_customUserAgent
4022 {
4023     return self.customUserAgent;
4024 }
4025
4026 - (void)_setCustomUserAgent:(NSString *)customUserAgent
4027 {
4028     self.customUserAgent = customUserAgent;
4029 }
4030
4031 - (void)_setUserContentExtensionsEnabled:(BOOL)userContentExtensionsEnabled
4032 {
4033     // This is kept for binary compatibility with iOS 9.
4034 }
4035
4036 - (BOOL)_userContentExtensionsEnabled
4037 {
4038     // This is kept for binary compatibility with iOS 9.
4039     return true;
4040 }
4041
4042 - (pid_t)_webProcessIdentifier
4043 {
4044     if (![self _isValid])
4045         return 0;
4046
4047     return _page->processIdentifier();
4048 }
4049
4050 - (void)_killWebContentProcess
4051 {
4052     if (![self _isValid])
4053         return;
4054
4055     _page->process().terminate();
4056 }
4057
4058 - (WKNavigation *)_reloadWithoutContentBlockers
4059 {
4060     auto navigation = _page->reload(WebCore::ReloadOption::DisableContentBlockers);
4061     if (!navigation)
4062         return nil;
4063
4064     return [wrapper(*navigation.leakRef()) autorelease];
4065 }
4066
4067 - (WKNavigation *)_reloadExpiredOnly
4068 {
4069     auto navigation = _page->reload(WebCore::ReloadOption::ExpiredOnly);
4070     if (!navigation)
4071         return nil;
4072
4073     return [wrapper(*navigation.leakRef()) autorelease];
4074 }
4075
4076 - (void)_killWebContentProcessAndResetState
4077 {
4078     _page->process().requestTermination(WebKit::ProcessTerminationReason::RequestedByClient);
4079 }
4080
4081 #if PLATFORM(MAC)
4082 - (void)_setShouldSuppressFirstResponderChanges:(BOOL)shouldSuppress
4083 {
4084     _impl->setShouldSuppressFirstResponderChanges(shouldSuppress);
4085 }
4086 #endif
4087
4088 #if PLATFORM(IOS)
4089 static WebCore::FloatSize activeMaximumUnobscuredSize(WKWebView *webView, const CGRect& bounds)
4090 {
4091     return WebCore::FloatSize(webView->_overridesMaximumUnobscuredSize ? webView->_maximumUnobscuredSizeOverride : bounds.size);
4092 }
4093
4094 static int32_t activeOrientation(WKWebView *webView)
4095 {
4096     return webView->_overridesInterfaceOrientation ? deviceOrientationForUIInterfaceOrientation(webView->_interfaceOrientationOverride) : webView->_page->deviceOrientation();
4097 }
4098
4099 - (void (^)(void))_retainActiveFocusedState
4100 {
4101     ++_activeFocusedStateRetainCount;
4102
4103     // FIXME: Use something like CompletionHandlerCallChecker to ensure that the returned block is called before it's released.
4104     return [[[self] {
4105         --_activeFocusedStateRetainCount;
4106     } copy] autorelease];
4107 }
4108
4109 - (void)_becomeFirstResponderWithSelectionMovingForward:(BOOL)selectingForward completionHandler:(void (^)(BOOL didBecomeFirstResponder))completionHandler
4110 {
4111     typeof(completionHandler) completionHandlerCopy = nil;
4112     if (completionHandler)
4113         completionHandlerCopy = Block_copy(completionHandler);
4114
4115     [_contentView _becomeFirstResponderWithSelectionMovingForward:selectingForward completionHandler:[completionHandlerCopy](BOOL didBecomeFirstResponder) {
4116         if (!completionHandlerCopy)
4117             return;
4118
4119         completionHandlerCopy(didBecomeFirstResponder);
4120         Block_release(completionHandlerCopy);
4121     }];
4122 }
4123
4124 - (id)_snapshotLayerContentsForBackForwardListItem:(WKBackForwardListItem *)item
4125 {
4126     if (_page->backForwardList().currentItem() == &item._item)
4127         _page->recordNavigationSnapshot(*_page->backForwardList().currentItem());
4128
4129     if (auto* viewSnapshot = item._item.snapshot())
4130         return viewSnapshot->asLayerContents();
4131
4132     return nil;
4133 }
4134
4135 - (NSArray *)_dataDetectionResults
4136 {
4137 #if ENABLE(DATA_DETECTION)
4138     return [_contentView _dataDetectionResults];
4139 #else
4140     return nil;
4141 #endif
4142 }
4143
4144 - (void)_accessibilityRetrieveSpeakSelectionContent
4145 {
4146     [_contentView accessibilityRetrieveSpeakSelectionContent];
4147 }
4148
4149 // This method is for subclasses to override.
4150 // Currently it's only in TestRunnerWKWebView.
4151 - (void)_accessibilityDidGetSpeakSelectionContent:(NSString *)content
4152 {
4153 }
4154
4155 #endif
4156
4157 - (NSData *)_sessionStateData
4158 {
4159     WebKit::SessionState sessionState = _page->sessionState();
4160
4161     // FIXME: This should not use the legacy session state encoder.
4162     return [wrapper(*WebKit::encodeLegacySessionState(sessionState).leakRef()) autorelease];
4163 }
4164
4165 - (_WKSessionState *)_sessionState
4166 {
4167     return adoptNS([[_WKSessionState alloc] _initWithSessionState:_page->sessionState()]).autorelease();
4168 }
4169
4170 - (_WKSessionState *)_sessionStateWithFilter:(BOOL (^)(WKBackForwardListItem *item))filter
4171 {
4172     WebKit::SessionState sessionState = _page->sessionState([filter](WebKit::WebBackForwardListItem& item) {
4173         if (!filter)
4174             return true;
4175
4176         return (bool)filter(wrapper(item));
4177     });