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