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