WKWebView.goBack should reload if there is a safe browsing warning
[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     if (self._safeBrowsingWarning)
1012         return [self reload];
1013     return wrapper(_page->goBack());
1014 }
1015
1016 - (WKNavigation *)goForward
1017 {
1018     return wrapper(_page->goForward());
1019 }
1020
1021 - (WKNavigation *)reload
1022 {
1023     OptionSet<WebCore::ReloadOption> reloadOptions;
1024     if (linkedOnOrAfter(WebKit::SDKVersion::FirstWithExpiredOnlyReloadBehavior))
1025         reloadOptions.add(WebCore::ReloadOption::ExpiredOnly);
1026
1027     return wrapper(_page->reload(reloadOptions));
1028 }
1029
1030 - (WKNavigation *)reloadFromOrigin
1031 {
1032     return wrapper(_page->reload(WebCore::ReloadOption::FromOrigin));
1033 }
1034
1035 - (void)stopLoading
1036 {
1037     _page->stopLoading();
1038 }
1039
1040 static WKErrorCode callbackErrorCode(WebKit::CallbackBase::Error error)
1041 {
1042     switch (error) {
1043     case WebKit::CallbackBase::Error::None:
1044         ASSERT_NOT_REACHED();
1045         return WKErrorUnknown;
1046
1047     case WebKit::CallbackBase::Error::Unknown:
1048         return WKErrorUnknown;
1049
1050     case WebKit::CallbackBase::Error::ProcessExited:
1051         return WKErrorWebContentProcessTerminated;
1052
1053     case WebKit::CallbackBase::Error::OwnerWasInvalidated:
1054         return WKErrorWebViewInvalidated;
1055     }
1056 }
1057
1058 - (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^)(id, NSError *))completionHandler
1059 {
1060     [self _evaluateJavaScript:javaScriptString forceUserGesture:YES completionHandler:completionHandler];
1061 }
1062
1063 - (void)_evaluateJavaScript:(NSString *)javaScriptString forceUserGesture:(BOOL)forceUserGesture completionHandler:(void (^)(id, NSError *))completionHandler
1064 {
1065     auto handler = adoptNS([completionHandler copy]);
1066
1067     _page->runJavaScriptInMainFrame(javaScriptString, forceUserGesture, [handler](API::SerializedScriptValue* serializedScriptValue, bool hadException, const WebCore::ExceptionDetails& details, WebKit::ScriptValueCallback::Error errorCode) {
1068         if (!handler)
1069             return;
1070
1071         if (errorCode != WebKit::ScriptValueCallback::Error::None) {
1072             auto error = createNSError(callbackErrorCode(errorCode));
1073             if (errorCode == WebKit::ScriptValueCallback::Error::OwnerWasInvalidated) {
1074                 // The OwnerWasInvalidated callback is synchronous. We don't want to call the block from within it
1075                 // because that can trigger re-entrancy bugs in WebKit.
1076                 // FIXME: It would be even better if GenericCallback did this for us.
1077                 dispatch_async(dispatch_get_main_queue(), [handler, error] {
1078                     auto rawHandler = (void (^)(id, NSError *))handler.get();
1079                     rawHandler(nil, error.get());
1080                 });
1081                 return;
1082             }
1083
1084             auto rawHandler = (void (^)(id, NSError *))handler.get();
1085             rawHandler(nil, error.get());
1086             return;
1087         }
1088
1089         auto rawHandler = (void (^)(id, NSError *))handler.get();
1090         if (hadException) {
1091             ASSERT(!serializedScriptValue);
1092
1093             RetainPtr<NSMutableDictionary> userInfo = adoptNS([[NSMutableDictionary alloc] init]);
1094
1095             [userInfo setObject:localizedDescriptionForErrorCode(WKErrorJavaScriptExceptionOccurred) forKey:NSLocalizedDescriptionKey];
1096             [userInfo setObject:static_cast<NSString *>(details.message) forKey:_WKJavaScriptExceptionMessageErrorKey];
1097             [userInfo setObject:@(details.lineNumber) forKey:_WKJavaScriptExceptionLineNumberErrorKey];
1098             [userInfo setObject:@(details.columnNumber) forKey:_WKJavaScriptExceptionColumnNumberErrorKey];
1099
1100             if (!details.sourceURL.isEmpty())
1101                 [userInfo setObject:[NSURL _web_URLWithWTFString:details.sourceURL] forKey:_WKJavaScriptExceptionSourceURLErrorKey];
1102
1103             rawHandler(nil, adoptNS([[NSError alloc] initWithDomain:WKErrorDomain code:WKErrorJavaScriptExceptionOccurred userInfo:userInfo.get()]).get());
1104             return;
1105         }
1106
1107         if (!serializedScriptValue) {
1108             rawHandler(nil, createNSError(WKErrorJavaScriptResultTypeIsUnsupported).get());
1109             return;
1110         }
1111
1112         id body = API::SerializedScriptValue::deserialize(serializedScriptValue->internalRepresentation(), 0);
1113         rawHandler(body, nil);
1114     });
1115 }
1116
1117 #if PLATFORM(MAC)
1118 - (void)takeSnapshotWithConfiguration:(WKSnapshotConfiguration *)snapshotConfiguration completionHandler:(void(^)(NSImage *, NSError *))completionHandler
1119 {
1120     CGRect rectInViewCoordinates = snapshotConfiguration && !CGRectIsNull(snapshotConfiguration.rect) ? snapshotConfiguration.rect : self.bounds;
1121     CGFloat snapshotWidth;
1122     if (snapshotConfiguration)
1123         snapshotWidth = snapshotConfiguration.snapshotWidth.doubleValue ?: rectInViewCoordinates.size.width;
1124     else
1125         snapshotWidth = self.bounds.size.width;
1126
1127     auto handler = makeBlockPtr(completionHandler);
1128     CGFloat imageScale = snapshotWidth / rectInViewCoordinates.size.width;
1129     CGFloat imageHeight = imageScale * rectInViewCoordinates.size.height;
1130
1131     // Need to scale by device scale factor or the image will be distorted.
1132     CGFloat deviceScale = _page->deviceScaleFactor();
1133     WebCore::IntSize bitmapSize(snapshotWidth, imageHeight);
1134     bitmapSize.scale(deviceScale, deviceScale);
1135
1136     // Software snapshot will not capture elements rendered with hardware acceleration (WebGL, video, etc).
1137     _page->takeSnapshot(WebCore::enclosingIntRect(rectInViewCoordinates), bitmapSize, WebKit::SnapshotOptionsInViewCoordinates, [handler, snapshotWidth, imageHeight](const WebKit::ShareableBitmap::Handle& imageHandle, WebKit::CallbackBase::Error errorCode) {
1138         if (errorCode != WebKit::ScriptValueCallback::Error::None) {
1139             auto error = createNSError(callbackErrorCode(errorCode));
1140             handler(nil, error.get());
1141             return;
1142         }
1143
1144         auto bitmap = WebKit::ShareableBitmap::create(imageHandle, WebKit::SharedMemory::Protection::ReadOnly);
1145         RetainPtr<CGImageRef> cgImage = bitmap ? bitmap->makeCGImage() : nullptr;
1146         RetainPtr<NSImage> nsImage = adoptNS([[NSImage alloc] initWithCGImage:cgImage.get() size:NSMakeSize(snapshotWidth, imageHeight)]);
1147         handler(nsImage.get(), nil);
1148     });
1149 }
1150
1151 #elif PLATFORM(IOS_FAMILY)
1152 - (void)takeSnapshotWithConfiguration:(WKSnapshotConfiguration *)snapshotConfiguration completionHandler:(void(^)(UIImage *, NSError *))completionHandler
1153 {
1154     CGRect rectInViewCoordinates = snapshotConfiguration && !CGRectIsNull(snapshotConfiguration.rect) ? snapshotConfiguration.rect : self.bounds;
1155     CGFloat snapshotWidth;
1156     if (snapshotConfiguration)
1157         snapshotWidth = snapshotConfiguration.snapshotWidth.doubleValue ?: rectInViewCoordinates.size.width;
1158     else
1159         snapshotWidth = self.bounds.size.width;
1160
1161     auto handler = makeBlockPtr(completionHandler);
1162     CGFloat deviceScale = _page->deviceScaleFactor();
1163
1164     [self _snapshotRect:rectInViewCoordinates intoImageOfWidth:(snapshotWidth * deviceScale) completionHandler:^(CGImageRef snapshotImage) {
1165         RetainPtr<NSError> error;
1166         RetainPtr<UIImage> uiImage;
1167
1168         if (!snapshotImage)
1169             error = createNSError(WKErrorUnknown);
1170         else
1171             uiImage = adoptNS([[UIImage alloc] initWithCGImage:snapshotImage scale:deviceScale orientation:UIImageOrientationUp]);
1172
1173         handler(uiImage.get(), error.get());
1174     }];
1175 }
1176 #endif
1177
1178 - (NSString *)customUserAgent
1179 {
1180     return _page->customUserAgent();
1181 }
1182
1183 - (void)setCustomUserAgent:(NSString *)customUserAgent
1184 {
1185     _page->setCustomUserAgent(customUserAgent);
1186 }
1187
1188 - (WKPageRef)_pageForTesting
1189 {
1190     return toAPI(_page.get());
1191 }
1192
1193 - (WebKit::WebPageProxy *)_page
1194 {
1195     return _page.get();
1196 }
1197
1198 - (BOOL)allowsLinkPreview
1199 {
1200 #if PLATFORM(MAC)
1201     return _impl->allowsLinkPreview();
1202 #elif PLATFORM(IOS_FAMILY)
1203     return _allowsLinkPreview;
1204 #endif
1205 }
1206
1207 - (void)setAllowsLinkPreview:(BOOL)allowsLinkPreview
1208 {
1209 #if PLATFORM(MAC)
1210     _impl->setAllowsLinkPreview(allowsLinkPreview);
1211     return;
1212 #elif PLATFORM(IOS_FAMILY)
1213     if (_allowsLinkPreview == allowsLinkPreview)
1214         return;
1215
1216     _allowsLinkPreview = allowsLinkPreview;
1217
1218 #if HAVE(LINK_PREVIEW)
1219     if (_allowsLinkPreview)
1220         [_contentView _registerPreview];
1221     else
1222         [_contentView _unregisterPreview];
1223 #endif // HAVE(LINK_PREVIEW)
1224 #endif // PLATFORM(IOS_FAMILY)
1225 }
1226
1227 - (CGSize)_viewportSizeForCSSViewportUnits
1228 {
1229     return _page->viewportSizeForCSSViewportUnits();
1230 }
1231
1232 - (void)_setViewportSizeForCSSViewportUnits:(CGSize)viewportSize
1233 {
1234     auto viewportSizeForViewportUnits = WebCore::IntSize(viewportSize);
1235     if (viewportSizeForViewportUnits.isEmpty())
1236         [NSException raise:NSInvalidArgumentException format:@"Viewport size should not be empty"];
1237
1238     _page->setViewportSizeForCSSViewportUnits(viewportSizeForViewportUnits);
1239 }
1240
1241 static NSTextAlignment nsTextAlignment(WebKit::TextAlignment alignment)
1242 {
1243     switch (alignment) {
1244     case WebKit::NoAlignment:
1245         return NSTextAlignmentNatural;
1246     case WebKit::LeftAlignment:
1247         return NSTextAlignmentLeft;
1248     case WebKit::RightAlignment:
1249         return NSTextAlignmentRight;
1250     case WebKit::CenterAlignment:
1251         return NSTextAlignmentCenter;
1252     case WebKit::JustifiedAlignment:
1253         return NSTextAlignmentJustified;
1254     }
1255     ASSERT_NOT_REACHED();
1256     return NSTextAlignmentNatural;
1257 }
1258
1259 static NSDictionary *dictionaryRepresentationForEditorState(const WebKit::EditorState& state)
1260 {
1261     if (state.isMissingPostLayoutData)
1262         return @{ @"post-layout-data" : @NO };
1263
1264     auto& postLayoutData = state.postLayoutData();
1265     return @{
1266         @"post-layout-data" : @YES,
1267         @"bold": postLayoutData.typingAttributes & WebKit::AttributeBold ? @YES : @NO,
1268         @"italic": postLayoutData.typingAttributes & WebKit::AttributeItalics ? @YES : @NO,
1269         @"underline": postLayoutData.typingAttributes & WebKit::AttributeUnderline ? @YES : @NO,
1270         @"text-alignment": @(nsTextAlignment(static_cast<WebKit::TextAlignment>(postLayoutData.textAlignment))),
1271         @"text-color": (NSString *)postLayoutData.textColor.cssText()
1272     };
1273 }
1274
1275 static _WKSelectionAttributes selectionAttributes(const WebKit::EditorState& editorState, _WKSelectionAttributes previousAttributes)
1276 {
1277     _WKSelectionAttributes attributes = _WKSelectionAttributeNoSelection;
1278     if (editorState.selectionIsNone)
1279         return attributes;
1280
1281     if (editorState.selectionIsRange)
1282         attributes |= _WKSelectionAttributeIsRange;
1283     else
1284         attributes |= _WKSelectionAttributeIsCaret;
1285
1286     if (!editorState.isMissingPostLayoutData) {
1287 #if PLATFORM(IOS_FAMILY)
1288         if (editorState.postLayoutData().atStartOfSentence)
1289             attributes |= _WKSelectionAttributeAtStartOfSentence;
1290 #endif
1291     } else if (previousAttributes & _WKSelectionAttributeAtStartOfSentence)
1292         attributes |= _WKSelectionAttributeAtStartOfSentence;
1293
1294     return attributes;
1295 }
1296
1297 - (void)_didChangeEditorState
1298 {
1299     auto newSelectionAttributes = selectionAttributes(_page->editorState(), _selectionAttributes);
1300     if (_selectionAttributes != newSelectionAttributes) {
1301         NSString *selectionAttributesKey = NSStringFromSelector(@selector(_selectionAttributes));
1302         [self willChangeValueForKey:selectionAttributesKey];
1303         _selectionAttributes = newSelectionAttributes;
1304         [self didChangeValueForKey:selectionAttributesKey];
1305     }
1306
1307     // FIXME: We should either rename -_webView:editorStateDidChange: to clarify that it's only intended for use when testing,
1308     // or remove it entirely and use -_webView:didChangeFontAttributes: instead once text alignment is supported in the set of
1309     // font attributes.
1310     id <WKUIDelegatePrivate> uiDelegate = (id <WKUIDelegatePrivate>)self.UIDelegate;
1311     if ([uiDelegate respondsToSelector:@selector(_webView:editorStateDidChange:)])
1312         [uiDelegate _webView:self editorStateDidChange:dictionaryRepresentationForEditorState(_page->editorState())];
1313 }
1314
1315 - (_WKSelectionAttributes)_selectionAttributes
1316 {
1317     return _selectionAttributes;
1318 }
1319
1320 - (void)_showSafeBrowsingWarning:(const WebKit::SafeBrowsingWarning&)warning completionHandler:(CompletionHandler<void(Variant<WebKit::ContinueUnsafeLoad, URL>&&)>&&)completionHandler
1321 {
1322     _safeBrowsingWarning = adoptNS([[WKSafeBrowsingWarning alloc] initWithFrame:self.bounds safeBrowsingWarning:warning completionHandler:[weakSelf = WeakObjCPtr<WKWebView>(self), completionHandler = WTFMove(completionHandler)] (auto&& result) mutable {
1323         completionHandler(WTFMove(result));
1324         auto strongSelf = weakSelf.get();
1325         if (!strongSelf)
1326             return;
1327         bool navigatesMainFrame = WTF::switchOn(result,
1328             [] (WebKit::ContinueUnsafeLoad continueUnsafeLoad) { return continueUnsafeLoad == WebKit::ContinueUnsafeLoad::Yes; },
1329             [] (const URL&) { return true; }
1330         );
1331         if (navigatesMainFrame && [strongSelf->_safeBrowsingWarning forMainFrameNavigation])
1332             return;
1333         [std::exchange(strongSelf->_safeBrowsingWarning, nullptr) removeFromSuperview];
1334     }]);
1335     [self addSubview:_safeBrowsingWarning.get()];
1336 }
1337
1338 - (void)_clearSafeBrowsingWarning
1339 {
1340     [std::exchange(_safeBrowsingWarning, nullptr) removeFromSuperview];
1341 }
1342
1343 - (void)_clearSafeBrowsingWarningIfForMainFrameNavigation
1344 {
1345     if ([_safeBrowsingWarning forMainFrameNavigation])
1346         [self _clearSafeBrowsingWarning];
1347 }
1348
1349 #if ENABLE(ATTACHMENT_ELEMENT)
1350
1351 - (void)_didInsertAttachment:(API::Attachment&)attachment withSource:(NSString *)source
1352 {
1353     id <WKUIDelegatePrivate> uiDelegate = (id <WKUIDelegatePrivate>)self.UIDelegate;
1354     if ([uiDelegate respondsToSelector:@selector(_webView:didInsertAttachment:withSource:)])
1355         [uiDelegate _webView:self didInsertAttachment:wrapper(attachment) withSource:source];
1356 }
1357
1358 - (void)_didRemoveAttachment:(API::Attachment&)attachment
1359 {
1360     id <WKUIDelegatePrivate> uiDelegate = (id <WKUIDelegatePrivate>)self.UIDelegate;
1361     if ([uiDelegate respondsToSelector:@selector(_webView:didRemoveAttachment:)])
1362         [uiDelegate _webView:self didRemoveAttachment:wrapper(attachment)];
1363 }
1364
1365 - (void)_didInvalidateDataForAttachment:(API::Attachment&)attachment
1366 {
1367     id <WKUIDelegatePrivate> uiDelegate = (id <WKUIDelegatePrivate>)self.UIDelegate;
1368     if ([uiDelegate respondsToSelector:@selector(_webView:didInvalidateDataForAttachment:)])
1369         [uiDelegate _webView:self didInvalidateDataForAttachment:wrapper(attachment)];
1370 }
1371
1372 #endif // ENABLE(ATTACHMENT_ELEMENT)
1373
1374 #pragma mark iOS-specific methods
1375
1376 #if PLATFORM(IOS_FAMILY)
1377
1378 - (BOOL)_contentViewIsFirstResponder
1379 {
1380     return self._currentContentView.isFirstResponder;
1381 }
1382
1383 - (_WKDragInteractionPolicy)_dragInteractionPolicy
1384 {
1385     return _dragInteractionPolicy;
1386 }
1387
1388 - (void)_setDragInteractionPolicy:(_WKDragInteractionPolicy)policy
1389 {
1390     if (_dragInteractionPolicy == policy)
1391         return;
1392
1393     _dragInteractionPolicy = policy;
1394 #if ENABLE(DRAG_SUPPORT)
1395     [_contentView _didChangeDragInteractionPolicy];
1396 #endif
1397 }
1398
1399 - (void)_populateArchivedSubviews:(NSMutableSet *)encodedViews
1400 {
1401     [super _populateArchivedSubviews:encodedViews];
1402
1403     if (_scrollView)
1404         [encodedViews removeObject:_scrollView.get()];
1405     if (_customContentFixedOverlayView)
1406         [encodedViews removeObject:_customContentFixedOverlayView.get()];
1407 }
1408
1409 - (BOOL)_isBackground
1410 {
1411     if ([self _isDisplayingPDF])
1412         return [_customContentView web_isBackground];
1413     return [_contentView isBackground];
1414 }
1415
1416 - (void)setFrame:(CGRect)frame
1417 {
1418     CGRect oldFrame = self.frame;
1419     [super setFrame:frame];
1420
1421     if (!CGSizeEqualToSize(oldFrame.size, frame.size))
1422         [self _frameOrBoundsChanged];
1423 }
1424
1425 - (void)setBounds:(CGRect)bounds
1426 {
1427     CGRect oldBounds = self.bounds;
1428     [super setBounds:bounds];
1429     [_customContentFixedOverlayView setFrame:self.bounds];
1430
1431     if (!CGSizeEqualToSize(oldBounds.size, bounds.size))
1432         [self _frameOrBoundsChanged];
1433 }
1434
1435 - (void)layoutSubviews
1436 {
1437     [_safeBrowsingWarning setFrame:self.bounds];
1438     [super layoutSubviews];
1439     [self _frameOrBoundsChanged];
1440 }
1441
1442 - (UIScrollView *)scrollView
1443 {
1444     return _scrollView.get();
1445 }
1446
1447 ALLOW_DEPRECATED_DECLARATIONS_BEGIN
1448 - (WKBrowsingContextController *)browsingContextController
1449 {
1450     return [_contentView browsingContextController];
1451 }
1452 ALLOW_DEPRECATED_DECLARATIONS_END
1453
1454 - (BOOL)becomeFirstResponder
1455 {
1456     UIView *currentContentView = self._currentContentView;
1457     if (currentContentView == _contentView && [_contentView superview])
1458         return [_contentView becomeFirstResponderForWebView] || [super becomeFirstResponder];
1459
1460     return [currentContentView becomeFirstResponder] || [super becomeFirstResponder];
1461 }
1462
1463 - (BOOL)canBecomeFirstResponder
1464 {
1465     if (self._currentContentView == _contentView)
1466         return [_contentView canBecomeFirstResponderForWebView];
1467
1468     return YES;
1469 }
1470
1471 - (BOOL)resignFirstResponder
1472 {
1473     if ([_contentView isFirstResponder])
1474         return [_contentView resignFirstResponderForWebView];
1475
1476     return [super resignFirstResponder];
1477 }
1478
1479 FOR_EACH_WKCONTENTVIEW_ACTION(FORWARD_ACTION_TO_WKCONTENTVIEW)
1480
1481 - (BOOL)canPerformAction:(SEL)action withSender:(id)sender
1482 {
1483     #define FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW(_action) \
1484         if (action == @selector(_action:)) \
1485             return self.usesStandardContentView && [_contentView canPerformActionForWebView:action withSender:sender];
1486
1487     FOR_EACH_WKCONTENTVIEW_ACTION(FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW)
1488     FOR_EACH_PRIVATE_WKCONTENTVIEW_ACTION(FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW)
1489     FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW(_setTextColor:sender)
1490     FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW(_setFontSize:sender)
1491     FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW(_setFont:sender)
1492
1493     #undef FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW
1494
1495     return [super canPerformAction:action withSender:sender];
1496 }
1497
1498 - (id)targetForAction:(SEL)action withSender:(id)sender
1499 {
1500     #define FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW(_action) \
1501         if (action == @selector(_action:) && self.usesStandardContentView) \
1502             return [_contentView targetForActionForWebView:action withSender:sender];
1503
1504     FOR_EACH_WKCONTENTVIEW_ACTION(FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW)
1505     FOR_EACH_PRIVATE_WKCONTENTVIEW_ACTION(FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW)
1506     FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW(_setTextColor:sender)
1507     FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW(_setFontSize:sender)
1508     FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW(_setFont:sender)
1509
1510     #undef FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW
1511
1512     return [super targetForAction:action withSender:sender];
1513 }
1514
1515 static inline CGFloat floorToDevicePixel(CGFloat input, float deviceScaleFactor)
1516 {
1517     return CGFloor(input * deviceScaleFactor) / deviceScaleFactor;
1518 }
1519
1520 static inline bool pointsEqualInDevicePixels(CGPoint a, CGPoint b, float deviceScaleFactor)
1521 {
1522     return fabs(a.x * deviceScaleFactor - b.x * deviceScaleFactor) < std::numeric_limits<float>::epsilon()
1523         && fabs(a.y * deviceScaleFactor - b.y * deviceScaleFactor) < std::numeric_limits<float>::epsilon();
1524 }
1525
1526 static CGSize roundScrollViewContentSize(const WebKit::WebPageProxy& page, CGSize contentSize)
1527 {
1528     float deviceScaleFactor = page.deviceScaleFactor();
1529     return CGSizeMake(floorToDevicePixel(contentSize.width, deviceScaleFactor), floorToDevicePixel(contentSize.height, deviceScaleFactor));
1530 }
1531
1532 - (UIView *)_currentContentView
1533 {
1534     return _customContentView ? [_customContentView web_contentView] : _contentView.get();
1535 }
1536
1537 - (WKWebViewContentProviderRegistry *)_contentProviderRegistry
1538 {
1539     return [_configuration _contentProviderRegistry];
1540 }
1541
1542 - (WKSelectionGranularity)_selectionGranularity
1543 {
1544     return [_configuration selectionGranularity];
1545 }
1546
1547 - (void)_setHasCustomContentView:(BOOL)pageHasCustomContentView loadedMIMEType:(const WTF::String&)mimeType
1548 {
1549     if (pageHasCustomContentView) {
1550         [_customContentView removeFromSuperview];
1551         [_customContentFixedOverlayView removeFromSuperview];
1552
1553         Class representationClass = [[_configuration _contentProviderRegistry] providerForMIMEType:mimeType];
1554         ASSERT(representationClass);
1555         _customContentView = adoptNS([[representationClass alloc] web_initWithFrame:self.bounds webView:self mimeType:mimeType]);
1556         _customContentFixedOverlayView = adoptNS([[UIView alloc] initWithFrame:self.bounds]);
1557         [_customContentFixedOverlayView layer].name = @"CustomContentFixedOverlay";
1558         [_customContentFixedOverlayView setUserInteractionEnabled:NO];
1559
1560         [[_contentView unscaledView] removeFromSuperview];
1561         [_contentView removeFromSuperview];
1562         [_scrollView addSubview:_customContentView.get()];
1563         [self addSubview:_customContentFixedOverlayView.get()];
1564
1565         [_customContentView web_setMinimumSize:self.bounds.size];
1566         [_customContentView web_setFixedOverlayView:_customContentFixedOverlayView.get()];
1567
1568         _scrollViewBackgroundColor = WebCore::Color();
1569         [_scrollView setContentOffset:[self _initialContentOffsetForScrollView]];
1570
1571         [self _setAvoidsUnsafeArea:NO];
1572     } else if (_customContentView) {
1573         [_customContentView removeFromSuperview];
1574         _customContentView = nullptr;
1575
1576         [_customContentFixedOverlayView removeFromSuperview];
1577         _customContentFixedOverlayView = nullptr;
1578
1579         [_scrollView addSubview:_contentView.get()];
1580         [_scrollView addSubview:[_contentView unscaledView]];
1581         [_scrollView setContentSize:roundScrollViewContentSize(*_page, [_contentView frame].size)];
1582
1583         [_customContentFixedOverlayView setFrame:self.bounds];
1584         [self addSubview:_customContentFixedOverlayView.get()];
1585     }
1586
1587     if (self.isFirstResponder) {
1588         UIView *currentContentView = self._currentContentView;
1589         if (currentContentView == _contentView ? [_contentView canBecomeFirstResponderForWebView] : currentContentView.canBecomeFirstResponder)
1590             [currentContentView becomeFirstResponder];
1591     }
1592 }
1593
1594 - (void)_didFinishLoadingDataForCustomContentProviderWithSuggestedFilename:(const String&)suggestedFilename data:(NSData *)data
1595 {
1596     ASSERT(_customContentView);
1597     [_customContentView web_setContentProviderData:data suggestedFilename:suggestedFilename];
1598
1599     // FIXME: It may make more sense for custom content providers to invoke this when they're ready,
1600     // because there's no guarantee that all custom content providers will lay out synchronously.
1601     _page->didLayoutForCustomContentProvider();
1602 }
1603
1604 - (void)_handleKeyUIEvent:(::UIEvent *)event
1605 {
1606     // We only want to handle key events from the hardware keyboard when we are
1607     // first responder and a custom content view is installed; otherwise,
1608     // WKContentView will be the first responder and expects to get key events directly.
1609     if ([self isFirstResponder] && event._hidEvent) {
1610         if ([_customContentView respondsToSelector:@selector(web_handleKeyEvent:)]) {
1611             if ([_customContentView web_handleKeyEvent:event])
1612                 return;
1613         }
1614     }
1615
1616     [super _handleKeyUIEvent:event];
1617 }
1618
1619 - (void)_willInvokeUIScrollViewDelegateCallback
1620 {
1621     _delayUpdateVisibleContentRects = YES;
1622 }
1623
1624 - (void)_didInvokeUIScrollViewDelegateCallback
1625 {
1626     _delayUpdateVisibleContentRects = NO;
1627     if (_hadDelayedUpdateVisibleContentRects) {
1628         _hadDelayedUpdateVisibleContentRects = NO;
1629         [self _scheduleVisibleContentRectUpdate];
1630     }
1631 }
1632
1633 static CGFloat contentZoomScale(WKWebView *webView)
1634 {
1635     CGFloat scale = webView._currentContentView.layer.affineTransform.a;
1636     ASSERT(scale == [webView->_scrollView zoomScale]);
1637     return scale;
1638 }
1639
1640 static WebCore::Color baseScrollViewBackgroundColor(WKWebView *webView)
1641 {
1642     if (webView->_customContentView)
1643         return [webView->_customContentView backgroundColor].CGColor;
1644
1645     if (webView->_gestureController) {
1646         WebCore::Color color = webView->_gestureController->backgroundColorForCurrentSnapshot();
1647         if (color.isValid())
1648             return color;
1649     }
1650
1651     return webView->_page->pageExtendedBackgroundColor();
1652 }
1653
1654 static WebCore::Color scrollViewBackgroundColor(WKWebView *webView)
1655 {
1656     if (!webView.opaque)
1657         return WebCore::Color::transparent;
1658
1659     WebCore::Color color = baseScrollViewBackgroundColor(webView);
1660
1661     if (!color.isValid())
1662         color = WebCore::Color::white;
1663
1664     CGFloat zoomScale = contentZoomScale(webView);
1665     CGFloat minimumZoomScale = [webView->_scrollView minimumZoomScale];
1666     if (zoomScale < minimumZoomScale) {
1667         CGFloat slope = 12;
1668         CGFloat opacity = std::max<CGFloat>(1 - slope * (minimumZoomScale - zoomScale), 0);
1669         color = WebCore::colorWithOverrideAlpha(color.rgb(), opacity);
1670     }
1671
1672     return color;
1673 }
1674
1675 - (void)_updateScrollViewBackground
1676 {
1677     WebCore::Color color = scrollViewBackgroundColor(self);
1678
1679     if (_scrollViewBackgroundColor == color)
1680         return;
1681
1682     _scrollViewBackgroundColor = color;
1683
1684     auto uiBackgroundColor = adoptNS([[UIColor alloc] initWithCGColor:cachedCGColor(color)]);
1685     [_scrollView setBackgroundColor:uiBackgroundColor.get()];
1686
1687     // Update the indicator style based on the lightness/darkness of the background color.
1688     double hue, saturation, lightness;
1689     color.getHSL(hue, saturation, lightness);
1690     if (lightness <= .5 && color.isVisible())
1691         [_scrollView setIndicatorStyle:UIScrollViewIndicatorStyleWhite];
1692     else
1693         [_scrollView setIndicatorStyle:UIScrollViewIndicatorStyleDefault];
1694 }
1695
1696 - (void)_videoControlsManagerDidChange
1697 {
1698 #if ENABLE(FULLSCREEN_API)
1699     if (_fullScreenWindowController)
1700         [_fullScreenWindowController videoControlsManagerDidChange];
1701 #endif
1702 }
1703
1704 - (CGPoint)_initialContentOffsetForScrollView
1705 {
1706     auto combinedUnobscuredAndScrollViewInset = [self _computedContentInset];
1707     return CGPointMake(-combinedUnobscuredAndScrollViewInset.left, -combinedUnobscuredAndScrollViewInset.top);
1708 }
1709
1710 - (CGPoint)_contentOffsetAdjustedForObscuredInset:(CGPoint)point
1711 {
1712     CGPoint result = point;
1713     UIEdgeInsets contentInset = [self _computedObscuredInset];
1714
1715     result.x -= contentInset.left;
1716     result.y -= contentInset.top;
1717
1718     return result;
1719 }
1720
1721 - (UIRectEdge)_effectiveObscuredInsetEdgesAffectedBySafeArea
1722 {
1723     if (![self usesStandardContentView])
1724         return UIRectEdgeAll;
1725     return _obscuredInsetEdgesAffectedBySafeArea;
1726 }
1727
1728 - (UIEdgeInsets)_computedObscuredInset
1729 {
1730     if (!linkedOnOrAfter(WebKit::SDKVersion::FirstWhereScrollViewContentInsetsAreNotObscuringInsets)) {
1731         // For binary compability with third party apps, treat scroll view content insets as obscuring insets when the app is compiled
1732         // against a WebKit version without the fix in r229641.
1733         return [self _computedContentInset];
1734     }
1735
1736     if (_haveSetObscuredInsets)
1737         return _obscuredInsets;
1738
1739 #if PLATFORM(IOS)
1740     if (self._safeAreaShouldAffectObscuredInsets)
1741         return UIEdgeInsetsAdd(UIEdgeInsetsZero, self._scrollViewSystemContentInset, self._effectiveObscuredInsetEdgesAffectedBySafeArea);
1742 #endif
1743
1744     return UIEdgeInsetsZero;
1745 }
1746
1747 - (UIEdgeInsets)_computedContentInset
1748 {
1749     if (_haveSetObscuredInsets)
1750         return _obscuredInsets;
1751
1752     UIEdgeInsets insets = [_scrollView contentInset];
1753
1754 #if PLATFORM(IOS)
1755     if (self._safeAreaShouldAffectObscuredInsets)
1756         insets = UIEdgeInsetsAdd(insets, self._scrollViewSystemContentInset, self._effectiveObscuredInsetEdgesAffectedBySafeArea);
1757 #endif
1758
1759     return insets;
1760 }
1761
1762 - (UIEdgeInsets)_computedUnobscuredSafeAreaInset
1763 {
1764     if (_haveSetUnobscuredSafeAreaInsets)
1765         return _unobscuredSafeAreaInsets;
1766
1767 #if PLATFORM(IOS)
1768     if (!self._safeAreaShouldAffectObscuredInsets)
1769         return self.safeAreaInsets;
1770 #endif
1771
1772     return UIEdgeInsetsZero;
1773 }
1774
1775 - (void)_processWillSwapOrDidExit
1776 {
1777     // FIXME: Which ones of these need to be done in the process swap case and which ones in the exit case?
1778     [self _hidePasswordView];
1779     [self _cancelAnimatedResize];
1780
1781     _viewportMetaTagWidth = WebCore::ViewportArguments::ValueAuto;
1782     _initialScaleFactor = 1;
1783     _hasCommittedLoadForMainFrame = NO;
1784     _needsResetViewStateAfterCommitLoadForMainFrame = NO;
1785     _dynamicViewportUpdateMode = WebKit::DynamicViewportUpdateMode::NotResizing;
1786     _waitingForEndAnimatedResize = NO;
1787     _waitingForCommitAfterAnimatedResize = NO;
1788     _animatedResizeOriginalContentWidth = 0;
1789     [_contentView setHidden:NO];
1790     _scrollOffsetToRestore = WTF::nullopt;
1791     _unobscuredCenterToRestore = WTF::nullopt;
1792     _scrollViewBackgroundColor = WebCore::Color();
1793     _delayUpdateVisibleContentRects = NO;
1794     _hadDelayedUpdateVisibleContentRects = NO;
1795     _currentlyAdjustingScrollViewInsetsForKeyboard = NO;
1796     _lastSentViewLayoutSize = WTF::nullopt;
1797     _lastSentMaximumUnobscuredSize = WTF::nullopt;
1798     _lastSentDeviceOrientation = WTF::nullopt;
1799
1800     _frozenVisibleContentRect = WTF::nullopt;
1801     _frozenUnobscuredContentRect = WTF::nullopt;
1802
1803     _firstPaintAfterCommitLoadTransactionID = 0;
1804     _firstTransactionIDAfterPageRestore = WTF::nullopt;
1805
1806     _hasScheduledVisibleRectUpdate = NO;
1807     _commitDidRestoreScrollPosition = NO;
1808
1809     _avoidsUnsafeArea = YES;
1810 }
1811
1812 - (void)_processWillSwap
1813 {
1814     RELEASE_LOG_IF_ALLOWED("%p -[WKWebView _processWillSwap]", self);
1815     [self _processWillSwapOrDidExit];
1816 }
1817
1818 - (void)_processDidExit
1819 {
1820     RELEASE_LOG_IF_ALLOWED("%p -[WKWebView _processDidExit]", self);
1821
1822     [self _processWillSwapOrDidExit];
1823
1824     [_contentView setFrame:self.bounds];
1825     [_scrollView setBackgroundColor:[UIColor whiteColor]];
1826     [_scrollView setContentOffset:[self _initialContentOffsetForScrollView]];
1827     [_scrollView setZoomScale:1];
1828     
1829 }
1830
1831 - (void)_didRelaunchProcess
1832 {
1833     RELEASE_LOG_IF_ALLOWED("%p -[WKWebView _didRelaunchProcess]", self);
1834     _hasScheduledVisibleRectUpdate = NO;
1835     _visibleContentRectUpdateScheduledFromScrollViewInStableState = YES;
1836 }
1837
1838 - (void)_didCommitLoadForMainFrame
1839 {
1840     _firstPaintAfterCommitLoadTransactionID = downcast<WebKit::RemoteLayerTreeDrawingAreaProxy>(*_page->drawingArea()).nextLayerTreeTransactionID();
1841
1842     _hasCommittedLoadForMainFrame = YES;
1843     _needsResetViewStateAfterCommitLoadForMainFrame = YES;
1844
1845     [_scrollView _stopScrollingAndZoomingAnimations];
1846 }
1847
1848 static CGPoint contentOffsetBoundedInValidRange(UIScrollView *scrollView, CGPoint contentOffset)
1849 {
1850 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000
1851     UIEdgeInsets contentInsets = scrollView.adjustedContentInset;
1852 #else
1853     UIEdgeInsets contentInsets = scrollView.contentInset;
1854 #endif
1855
1856     CGSize contentSize = scrollView.contentSize;
1857     CGSize scrollViewSize = scrollView.bounds.size;
1858
1859     CGPoint minimumContentOffset = CGPointMake(-contentInsets.left, -contentInsets.top);
1860     CGPoint maximumContentOffset = CGPointMake(std::max(minimumContentOffset.x, contentSize.width + contentInsets.right - scrollViewSize.width), std::max(minimumContentOffset.y, contentSize.height + contentInsets.bottom - scrollViewSize.height));
1861
1862     return CGPointMake(std::max(std::min(contentOffset.x, maximumContentOffset.x), minimumContentOffset.x), std::max(std::min(contentOffset.y, maximumContentOffset.y), minimumContentOffset.y));
1863 }
1864
1865 static void changeContentOffsetBoundedInValidRange(UIScrollView *scrollView, WebCore::FloatPoint contentOffset)
1866 {
1867     scrollView.contentOffset = contentOffsetBoundedInValidRange(scrollView, contentOffset);
1868 }
1869
1870 - (WebCore::FloatRect)visibleRectInViewCoordinates
1871 {
1872     WebCore::FloatRect bounds = self.bounds;
1873     bounds.moveBy([_scrollView contentOffset]);
1874     WebCore::FloatRect contentViewBounds = [_contentView bounds];
1875     bounds.intersect(contentViewBounds);
1876     return bounds;
1877 }
1878
1879 // WebCore stores the page scale factor as float instead of double. When we get a scale from WebCore,
1880 // we need to ignore differences that are within a small rounding error on floats.
1881 static inline bool areEssentiallyEqualAsFloat(float a, float b)
1882 {
1883     return WTF::areEssentiallyEqual(a, b);
1884 }
1885
1886 - (void)_didCommitLayerTreeDuringAnimatedResize:(const WebKit::RemoteLayerTreeTransaction&)layerTreeTransaction
1887 {
1888     auto updateID = layerTreeTransaction.dynamicViewportSizeUpdateID();
1889     if (updateID && *updateID == _currentDynamicViewportSizeUpdateID) {
1890         double pageScale = layerTreeTransaction.pageScaleFactor();
1891         WebCore::IntPoint scrollPosition = layerTreeTransaction.scrollPosition();
1892
1893         CGFloat animatingScaleTarget = [[_resizeAnimationView layer] transform].m11;
1894         double currentTargetScale = animatingScaleTarget * [[_contentView layer] transform].m11;
1895         double scale = pageScale / currentTargetScale;
1896         _resizeAnimationTransformAdjustments = CATransform3DMakeScale(scale, scale, 1);
1897
1898         CGPoint newContentOffset = [self _contentOffsetAdjustedForObscuredInset:CGPointMake(scrollPosition.x() * pageScale, scrollPosition.y() * pageScale)];
1899         CGPoint currentContentOffset = [_scrollView contentOffset];
1900
1901         _resizeAnimationTransformAdjustments.m41 = (currentContentOffset.x - newContentOffset.x) / animatingScaleTarget;
1902         _resizeAnimationTransformAdjustments.m42 = (currentContentOffset.y - newContentOffset.y) / animatingScaleTarget;
1903
1904         [_resizeAnimationView layer].sublayerTransform = _resizeAnimationTransformAdjustments;
1905
1906         // If we've already passed endAnimatedResize, immediately complete
1907         // the resize when we have an up-to-date layer tree. Otherwise,
1908         // we will defer completion until endAnimatedResize.
1909         _waitingForCommitAfterAnimatedResize = NO;
1910         if (!_waitingForEndAnimatedResize)
1911             [self _didCompleteAnimatedResize];
1912
1913         return;
1914     }
1915
1916     // If a commit arrives during the live part of a resize but before the
1917     // layer tree takes the current resize into account, it could change the
1918     // WKContentView's size. Update the resizeAnimationView's scale to ensure
1919     // we continue to fill the width of the resize target.
1920
1921     if (_waitingForEndAnimatedResize)
1922         return;
1923
1924     auto newViewLayoutSize = [self activeViewLayoutSize:self.bounds];
1925     CGFloat resizeAnimationViewScale = _animatedResizeOriginalContentWidth / newViewLayoutSize.width();
1926     [[_resizeAnimationView layer] setSublayerTransform:CATransform3DMakeScale(resizeAnimationViewScale, resizeAnimationViewScale, 1)];
1927 }
1928
1929 - (void)_didCommitLayerTree:(const WebKit::RemoteLayerTreeTransaction&)layerTreeTransaction
1930 {
1931     if (![self usesStandardContentView])
1932         return;
1933
1934     LOG_WITH_STREAM(VisibleRects, stream << "-[WKWebView " << _page->pageID() << " _didCommitLayerTree:] transactionID " <<  layerTreeTransaction.transactionID() << " _dynamicViewportUpdateMode " << (int)_dynamicViewportUpdateMode);
1935
1936     bool needUpdateVisibleContentRects = _page->updateLayoutViewportParameters(layerTreeTransaction);
1937
1938     if (_dynamicViewportUpdateMode != WebKit::DynamicViewportUpdateMode::NotResizing) {
1939         [self _didCommitLayerTreeDuringAnimatedResize:layerTreeTransaction];
1940         return;
1941     }
1942
1943     if (_resizeAnimationView)
1944         RELEASE_LOG_IF_ALLOWED("%p -[WKWebView _didCommitLayerTree:] - dynamicViewportUpdateMode is NotResizing, but still have a live resizeAnimationView (unpaired begin/endAnimatedResize?)", self);
1945
1946     CGSize newContentSize = roundScrollViewContentSize(*_page, [_contentView frame].size);
1947     [_scrollView _setContentSizePreservingContentOffsetDuringRubberband:newContentSize];
1948
1949     [_scrollView setMinimumZoomScale:layerTreeTransaction.minimumScaleFactor()];
1950     [_scrollView setMaximumZoomScale:layerTreeTransaction.maximumScaleFactor()];
1951     [_scrollView setZoomEnabled:layerTreeTransaction.allowsUserScaling()];
1952     if (!layerTreeTransaction.scaleWasSetByUIProcess() && ![_scrollView isZooming] && ![_scrollView isZoomBouncing] && ![_scrollView _isAnimatingZoom] && [_scrollView zoomScale] != layerTreeTransaction.pageScaleFactor()) {
1953         LOG_WITH_STREAM(VisibleRects, stream << " updating scroll view with pageScaleFactor " << layerTreeTransaction.pageScaleFactor());
1954         [_scrollView setZoomScale:layerTreeTransaction.pageScaleFactor()];
1955     }
1956
1957     _viewportMetaTagWidth = layerTreeTransaction.viewportMetaTagWidth();
1958     _viewportMetaTagWidthWasExplicit = layerTreeTransaction.viewportMetaTagWidthWasExplicit();
1959     _viewportMetaTagCameFromImageDocument = layerTreeTransaction.viewportMetaTagCameFromImageDocument();
1960     _initialScaleFactor = layerTreeTransaction.initialScaleFactor();
1961     if (_page->inStableState() && layerTreeTransaction.isInStableState() && [_stableStatePresentationUpdateCallbacks count]) {
1962         for (dispatch_block_t action in _stableStatePresentationUpdateCallbacks.get())
1963             action();
1964
1965         [_stableStatePresentationUpdateCallbacks removeAllObjects];
1966         _stableStatePresentationUpdateCallbacks = nil;
1967     }
1968
1969     if (![_contentView _mayDisableDoubleTapGesturesDuringSingleTap])
1970         [_contentView _setDoubleTapGesturesEnabled:self._allowsDoubleTapGestures];
1971
1972     [self _updateScrollViewBackground];
1973     [self _setAvoidsUnsafeArea:layerTreeTransaction.avoidsUnsafeArea()];
1974
1975     if (_gestureController)
1976         _gestureController->setRenderTreeSize(layerTreeTransaction.renderTreeSize());
1977
1978     if (_needsResetViewStateAfterCommitLoadForMainFrame && layerTreeTransaction.transactionID() >= _firstPaintAfterCommitLoadTransactionID) {
1979         _needsResetViewStateAfterCommitLoadForMainFrame = NO;
1980         [_scrollView setContentOffset:[self _initialContentOffsetForScrollView]];
1981         if (_observedRenderingProgressEvents & _WKRenderingProgressEventFirstPaint)
1982             _navigationState->didFirstPaint();
1983
1984         needUpdateVisibleContentRects = true;
1985     }
1986
1987     if (_firstTransactionIDAfterPageRestore && layerTreeTransaction.transactionID() >= _firstTransactionIDAfterPageRestore.value()) {
1988         if (_scrollOffsetToRestore) {
1989             WebCore::FloatPoint scaledScrollOffset = _scrollOffsetToRestore.value();
1990             _scrollOffsetToRestore = WTF::nullopt;
1991
1992             if (areEssentiallyEqualAsFloat(contentZoomScale(self), _scaleToRestore)) {
1993                 scaledScrollOffset.scale(_scaleToRestore);
1994                 WebCore::FloatPoint contentOffsetInScrollViewCoordinates = scaledScrollOffset - WebCore::FloatSize(_obscuredInsetsWhenSaved.left(), _obscuredInsetsWhenSaved.top());
1995
1996                 changeContentOffsetBoundedInValidRange(_scrollView.get(), contentOffsetInScrollViewCoordinates);
1997                 _commitDidRestoreScrollPosition = YES;
1998             }
1999
2000             needUpdateVisibleContentRects = true;
2001         }
2002
2003         if (_unobscuredCenterToRestore) {
2004             WebCore::FloatPoint unobscuredCenterToRestore = _unobscuredCenterToRestore.value();
2005             _unobscuredCenterToRestore = WTF::nullopt;
2006
2007             if (areEssentiallyEqualAsFloat(contentZoomScale(self), _scaleToRestore)) {
2008                 CGRect unobscuredRect = UIEdgeInsetsInsetRect(self.bounds, _obscuredInsets);
2009                 WebCore::FloatSize unobscuredContentSizeAtNewScale = WebCore::FloatSize(unobscuredRect.size) / _scaleToRestore;
2010                 WebCore::FloatPoint topLeftInDocumentCoordinates = unobscuredCenterToRestore - unobscuredContentSizeAtNewScale / 2;
2011
2012                 topLeftInDocumentCoordinates.scale(_scaleToRestore);
2013                 topLeftInDocumentCoordinates.moveBy(WebCore::FloatPoint(-_obscuredInsets.left, -_obscuredInsets.top));
2014
2015                 changeContentOffsetBoundedInValidRange(_scrollView.get(), topLeftInDocumentCoordinates);
2016             }
2017
2018             needUpdateVisibleContentRects = true;
2019         }
2020
2021         if (_gestureController)
2022             _gestureController->didRestoreScrollPosition();
2023
2024         _firstTransactionIDAfterPageRestore = WTF::nullopt;
2025     }
2026
2027     if (needUpdateVisibleContentRects)
2028         [self _scheduleVisibleContentRectUpdate];
2029
2030     if (WebKit::RemoteLayerTreeScrollingPerformanceData* scrollPerfData = _page->scrollingPerformanceData())
2031         scrollPerfData->didCommitLayerTree([self visibleRectInViewCoordinates]);
2032 }
2033
2034 - (void)_layerTreeCommitComplete
2035 {
2036     _commitDidRestoreScrollPosition = NO;
2037 }
2038
2039 - (void)_couldNotRestorePageState
2040 {
2041     // The gestureController may be waiting for the scroll position to be restored
2042     // in order to remove the swipe snapshot. Since the scroll position could not be
2043     // restored, tell the gestureController it was restored so that it no longer waits
2044     // for it.
2045     if (_gestureController)
2046         _gestureController->didRestoreScrollPosition();
2047 }
2048
2049 - (void)_restorePageScrollPosition:(Optional<WebCore::FloatPoint>)scrollPosition scrollOrigin:(WebCore::FloatPoint)scrollOrigin previousObscuredInset:(WebCore::FloatBoxExtent)obscuredInsets scale:(double)scale
2050 {
2051     if (_dynamicViewportUpdateMode != WebKit::DynamicViewportUpdateMode::NotResizing)
2052         return;
2053
2054     if (![self usesStandardContentView])
2055         return;
2056
2057     _firstTransactionIDAfterPageRestore = downcast<WebKit::RemoteLayerTreeDrawingAreaProxy>(*_page->drawingArea()).nextLayerTreeTransactionID();
2058     if (scrollPosition)
2059         _scrollOffsetToRestore = WebCore::ScrollableArea::scrollOffsetFromPosition(WebCore::FloatPoint(scrollPosition.value()), WebCore::toFloatSize(scrollOrigin));
2060     else
2061         _scrollOffsetToRestore = WTF::nullopt;
2062
2063     _obscuredInsetsWhenSaved = obscuredInsets;
2064     _scaleToRestore = scale;
2065 }
2066
2067 - (void)_restorePageStateToUnobscuredCenter:(Optional<WebCore::FloatPoint>)center scale:(double)scale
2068 {
2069     if (_dynamicViewportUpdateMode != WebKit::DynamicViewportUpdateMode::NotResizing)
2070         return;
2071
2072     if (![self usesStandardContentView])
2073         return;
2074
2075     _firstTransactionIDAfterPageRestore = downcast<WebKit::RemoteLayerTreeDrawingAreaProxy>(*_page->drawingArea()).nextLayerTreeTransactionID();
2076     _unobscuredCenterToRestore = center;
2077
2078     _scaleToRestore = scale;
2079 }
2080
2081 - (RefPtr<WebKit::ViewSnapshot>)_takeViewSnapshot
2082 {
2083 #if HAVE(CORE_ANIMATION_RENDER_SERVER)
2084     float deviceScale = WebCore::screenScaleFactor();
2085     WebCore::FloatSize snapshotSize(self.bounds.size);
2086     snapshotSize.scale(deviceScale);
2087
2088     CATransform3D transform = CATransform3DMakeScale(deviceScale, deviceScale, 1);
2089
2090 #if HAVE(IOSURFACE)
2091     WebCore::IOSurface::Format snapshotFormat = WebCore::screenSupportsExtendedColor() ? WebCore::IOSurface::Format::RGB10 : WebCore::IOSurface::Format::RGBA;
2092     auto surface = WebCore::IOSurface::create(WebCore::expandedIntSize(snapshotSize), WebCore::sRGBColorSpaceRef(), snapshotFormat);
2093     if (!surface)
2094         return nullptr;
2095     CARenderServerRenderLayerWithTransform(MACH_PORT_NULL, self.layer.context.contextId, reinterpret_cast<uint64_t>(self.layer), surface->surface(), 0, 0, &transform);
2096
2097 #if HAVE(IOSURFACE_ACCELERATOR)
2098     WebCore::IOSurface::Format compressedFormat = WebCore::IOSurface::Format::YUV422;
2099     if (WebCore::IOSurface::allowConversionFromFormatToFormat(snapshotFormat, compressedFormat)) {
2100         auto viewSnapshot = WebKit::ViewSnapshot::create(nullptr);
2101         WebCore::IOSurface::convertToFormat(WTFMove(surface), WebCore::IOSurface::Format::YUV422, [viewSnapshot = viewSnapshot.copyRef()](std::unique_ptr<WebCore::IOSurface> convertedSurface) {
2102             if (convertedSurface)
2103                 viewSnapshot->setSurface(WTFMove(convertedSurface));
2104         });
2105
2106         return viewSnapshot;
2107     }
2108 #endif // HAVE(IOSURFACE_ACCELERATOR)
2109
2110     return WebKit::ViewSnapshot::create(WTFMove(surface));
2111 #else // HAVE(IOSURFACE)
2112     uint32_t slotID = [WebKit::ViewSnapshotStore::snapshottingContext() createImageSlot:snapshotSize hasAlpha:YES];
2113
2114     if (!slotID)
2115         return nullptr;
2116
2117     CARenderServerCaptureLayerWithTransform(MACH_PORT_NULL, self.layer.context.contextId, (uint64_t)self.layer, slotID, 0, 0, &transform);
2118     WebCore::IntSize imageSize = WebCore::expandedIntSize(WebCore::FloatSize(snapshotSize));
2119     return WebKit::ViewSnapshot::create(slotID, imageSize, (imageSize.area() * 4).unsafeGet());
2120 #endif // HAVE(IOSURFACE)
2121 #else // HAVE(CORE_ANIMATION_RENDER_SERVER)
2122     return nullptr;
2123 #endif
2124 }
2125
2126 - (void)_zoomToPoint:(WebCore::FloatPoint)point atScale:(double)scale animated:(BOOL)animated
2127 {
2128     CFTimeInterval duration = 0;
2129     CGFloat zoomScale = contentZoomScale(self);
2130
2131     if (animated) {
2132         const double maximumZoomDuration = 0.4;
2133         const double minimumZoomDuration = 0.1;
2134         const double zoomDurationFactor = 0.3;
2135
2136         duration = std::min(fabs(log(zoomScale) - log(scale)) * zoomDurationFactor + minimumZoomDuration, maximumZoomDuration);
2137     }
2138
2139     if (scale != zoomScale)
2140         _page->willStartUserTriggeredZooming();
2141
2142     LOG_WITH_STREAM(VisibleRects, stream << "_zoomToPoint:" << point << " scale: " << scale << " duration:" << duration);
2143
2144     [_scrollView _zoomToCenter:point scale:scale duration:duration];
2145 }
2146
2147 - (void)_zoomToRect:(WebCore::FloatRect)targetRect atScale:(double)scale origin:(WebCore::FloatPoint)origin animated:(BOOL)animated
2148 {
2149     // FIXME: Some of this could be shared with _scrollToRect.
2150     const double visibleRectScaleChange = contentZoomScale(self) / scale;
2151     const WebCore::FloatRect visibleRect([self convertRect:self.bounds toView:self._currentContentView]);
2152     const WebCore::FloatRect unobscuredRect([self _contentRectForUserInteraction]);
2153
2154     const WebCore::FloatSize topLeftObscuredInsetAfterZoom((unobscuredRect.minXMinYCorner() - visibleRect.minXMinYCorner()) * visibleRectScaleChange);
2155     const WebCore::FloatSize bottomRightObscuredInsetAfterZoom((visibleRect.maxXMaxYCorner() - unobscuredRect.maxXMaxYCorner()) * visibleRectScaleChange);
2156
2157     const WebCore::FloatSize unobscuredRectSizeAfterZoom(unobscuredRect.size() * visibleRectScaleChange);
2158
2159     // Center to the target rect.
2160     WebCore::FloatPoint unobscuredRectLocationAfterZoom = targetRect.location() - (unobscuredRectSizeAfterZoom - targetRect.size()) * 0.5;
2161
2162     // Center to the tap point instead in case the target rect won't fit in a direction.
2163     if (targetRect.width() > unobscuredRectSizeAfterZoom.width())
2164         unobscuredRectLocationAfterZoom.setX(origin.x() - unobscuredRectSizeAfterZoom.width() / 2);
2165     if (targetRect.height() > unobscuredRectSizeAfterZoom.height())
2166         unobscuredRectLocationAfterZoom.setY(origin.y() - unobscuredRectSizeAfterZoom.height() / 2);
2167
2168     // We have computed where we want the unobscured rect to be. Now adjust for the obscuring insets.
2169     WebCore::FloatRect visibleRectAfterZoom(unobscuredRectLocationAfterZoom, unobscuredRectSizeAfterZoom);
2170     visibleRectAfterZoom.move(-topLeftObscuredInsetAfterZoom);
2171     visibleRectAfterZoom.expand(topLeftObscuredInsetAfterZoom + bottomRightObscuredInsetAfterZoom);
2172
2173     [self _zoomToPoint:visibleRectAfterZoom.center() atScale:scale animated:animated];
2174 }
2175
2176 static WebCore::FloatPoint constrainContentOffset(WebCore::FloatPoint contentOffset, WebCore::FloatSize contentSize, WebCore::FloatSize unobscuredContentSize)
2177 {
2178     WebCore::FloatSize maximumContentOffset = contentSize - unobscuredContentSize;
2179     return contentOffset.constrainedBetween(WebCore::FloatPoint(), WebCore::FloatPoint(maximumContentOffset));
2180 }
2181
2182 - (void)_scrollToContentScrollPosition:(WebCore::FloatPoint)scrollPosition scrollOrigin:(WebCore::IntPoint)scrollOrigin
2183 {
2184     if (_commitDidRestoreScrollPosition || _dynamicViewportUpdateMode != WebKit::DynamicViewportUpdateMode::NotResizing)
2185         return;
2186
2187     WebCore::FloatPoint contentOffset = WebCore::ScrollableArea::scrollOffsetFromPosition(scrollPosition, toFloatSize(scrollOrigin));
2188
2189     WebCore::FloatPoint scaledOffset = contentOffset;
2190     CGFloat zoomScale = contentZoomScale(self);
2191     scaledOffset.scale(zoomScale);
2192
2193     CGPoint contentOffsetInScrollViewCoordinates = [self _contentOffsetAdjustedForObscuredInset:scaledOffset];
2194     contentOffsetInScrollViewCoordinates = contentOffsetBoundedInValidRange(_scrollView.get(), contentOffsetInScrollViewCoordinates);
2195
2196     [_scrollView _stopScrollingAndZoomingAnimations];
2197
2198     if (!CGPointEqualToPoint(contentOffsetInScrollViewCoordinates, [_scrollView contentOffset]))
2199         [_scrollView setContentOffset:contentOffsetInScrollViewCoordinates];
2200     else {
2201         // If we haven't changed anything, there would not be any VisibleContentRect update sent to the content.
2202         // The WebProcess would keep the invalid contentOffset as its scroll position.
2203         // To synchronize the WebProcess with what is on screen, we send the VisibleContentRect again.
2204         _page->resendLastVisibleContentRects();
2205     }
2206 }
2207
2208 - (BOOL)_scrollToRect:(WebCore::FloatRect)targetRect origin:(WebCore::FloatPoint)origin minimumScrollDistance:(float)minimumScrollDistance
2209 {
2210     WebCore::FloatRect unobscuredContentRect([self _contentRectForUserInteraction]);
2211     WebCore::FloatPoint unobscuredContentOffset = unobscuredContentRect.location();
2212     WebCore::FloatSize contentSize([self._currentContentView bounds].size);
2213
2214     // Center the target rect in the scroll view.
2215     // If the target doesn't fit in the scroll view, center on the gesture location instead.
2216     WebCore::FloatPoint newUnobscuredContentOffset;
2217     if (targetRect.width() <= unobscuredContentRect.width())
2218         newUnobscuredContentOffset.setX(targetRect.x() - (unobscuredContentRect.width() - targetRect.width()) / 2);
2219     else
2220         newUnobscuredContentOffset.setX(origin.x() - unobscuredContentRect.width() / 2);
2221     if (targetRect.height() <= unobscuredContentRect.height())
2222         newUnobscuredContentOffset.setY(targetRect.y() - (unobscuredContentRect.height() - targetRect.height()) / 2);
2223     else
2224         newUnobscuredContentOffset.setY(origin.y() - unobscuredContentRect.height() / 2);
2225     newUnobscuredContentOffset = constrainContentOffset(newUnobscuredContentOffset, contentSize, unobscuredContentRect.size());
2226
2227     if (unobscuredContentOffset == newUnobscuredContentOffset) {
2228         if (targetRect.width() > unobscuredContentRect.width())
2229             newUnobscuredContentOffset.setX(origin.x() - unobscuredContentRect.width() / 2);
2230         if (targetRect.height() > unobscuredContentRect.height())
2231             newUnobscuredContentOffset.setY(origin.y() - unobscuredContentRect.height() / 2);
2232         newUnobscuredContentOffset = constrainContentOffset(newUnobscuredContentOffset, contentSize, unobscuredContentRect.size());
2233     }
2234
2235     WebCore::FloatSize scrollViewOffsetDelta = newUnobscuredContentOffset - unobscuredContentOffset;
2236     scrollViewOffsetDelta.scale(contentZoomScale(self));
2237
2238     float scrollDistance = scrollViewOffsetDelta.diagonalLength();
2239     if (scrollDistance < minimumScrollDistance)
2240         return false;
2241
2242     [_contentView willStartZoomOrScroll];
2243
2244     LOG_WITH_STREAM(VisibleRects, stream << "_scrollToRect: scrolling to " << [_scrollView contentOffset] + scrollViewOffsetDelta);
2245
2246     [_scrollView setContentOffset:([_scrollView contentOffset] + scrollViewOffsetDelta) animated:YES];
2247     return true;
2248 }
2249
2250 - (void)_zoomOutWithOrigin:(WebCore::FloatPoint)origin animated:(BOOL)animated
2251 {
2252     [self _zoomToPoint:origin atScale:[_scrollView minimumZoomScale] animated:animated];
2253 }
2254
2255 - (void)_zoomToInitialScaleWithOrigin:(WebCore::FloatPoint)origin animated:(BOOL)animated
2256 {
2257     ASSERT(_initialScaleFactor > 0);
2258     [self _zoomToPoint:origin atScale:_initialScaleFactor animated:animated];
2259 }
2260
2261 // focusedElementRect and selectionRect are both in document coordinates.
2262 - (void)_zoomToFocusRect:(const WebCore::FloatRect&)focusedElementRectInDocumentCoordinates selectionRect:(const WebCore::FloatRect&)selectionRectInDocumentCoordinates insideFixed:(BOOL)insideFixed
2263     fontSize:(float)fontSize minimumScale:(double)minimumScale maximumScale:(double)maximumScale allowScaling:(BOOL)allowScaling forceScroll:(BOOL)forceScroll
2264 {
2265     LOG_WITH_STREAM(VisibleRects, stream << "_zoomToFocusRect:" << focusedElementRectInDocumentCoordinates << " selectionRect:" << selectionRectInDocumentCoordinates);
2266     UNUSED_PARAM(insideFixed);
2267
2268     const double minimumHeightToShowContentAboveKeyboard = 106;
2269     const CFTimeInterval formControlZoomAnimationDuration = 0.25;
2270     const double caretOffsetFromWindowEdge = 8;
2271
2272     UIWindow *window = [_scrollView window];
2273
2274     // Find the portion of the view that is visible on the screen.
2275     UIViewController *topViewController = [[[_scrollView _viewControllerForAncestor] _rootAncestorViewController] _viewControllerForSupportedInterfaceOrientations];
2276     UIView *fullScreenView = topViewController.view;
2277     if (!fullScreenView)
2278         fullScreenView = window;
2279
2280     CGRect unobscuredScrollViewRectInWebViewCoordinates = UIEdgeInsetsInsetRect([self bounds], _obscuredInsets);
2281     CGRect visibleScrollViewBoundsInWebViewCoordinates = CGRectIntersection(unobscuredScrollViewRectInWebViewCoordinates, [fullScreenView convertRect:[fullScreenView bounds] toView:self]);
2282     CGRect formAssistantFrameInWebViewCoordinates = [window convertRect:_inputViewBounds toView:self];
2283     CGRect intersectionBetweenScrollViewAndFormAssistant = CGRectIntersection(visibleScrollViewBoundsInWebViewCoordinates, formAssistantFrameInWebViewCoordinates);
2284     CGSize visibleSize = visibleScrollViewBoundsInWebViewCoordinates.size;
2285
2286     CGFloat visibleOffsetFromTop = 0;
2287     if (!CGRectIsEmpty(intersectionBetweenScrollViewAndFormAssistant)) {
2288         CGFloat heightVisibleAboveFormAssistant = CGRectGetMinY(intersectionBetweenScrollViewAndFormAssistant) - CGRectGetMinY(visibleScrollViewBoundsInWebViewCoordinates);
2289         CGFloat heightVisibleBelowFormAssistant = CGRectGetMaxY(visibleScrollViewBoundsInWebViewCoordinates) - CGRectGetMaxY(intersectionBetweenScrollViewAndFormAssistant);
2290
2291         if (heightVisibleAboveFormAssistant >= minimumHeightToShowContentAboveKeyboard || heightVisibleBelowFormAssistant < heightVisibleAboveFormAssistant)
2292             visibleSize.height = heightVisibleAboveFormAssistant;
2293         else {
2294             visibleSize.height = heightVisibleBelowFormAssistant;
2295             visibleOffsetFromTop = CGRectGetMaxY(intersectionBetweenScrollViewAndFormAssistant) - CGRectGetMinY(visibleScrollViewBoundsInWebViewCoordinates);
2296         }
2297     }
2298
2299     // Zoom around the element's bounding frame. We use a "standard" size to determine the proper frame.
2300     double currentScale = contentZoomScale(self);
2301     double scale = currentScale;
2302     if (allowScaling) {
2303 #if PLATFORM(WATCHOS)
2304         const CGFloat minimumMarginForZoomingToEntireFocusRectInWebViewCoordinates = 10;
2305         const CGFloat maximumMarginForZoomingToEntireFocusRectInWebViewCoordinates = 35;
2306
2307         CGRect minimumTargetRectInDocumentCoordinates = UIRectInsetEdges(focusedElementRectInDocumentCoordinates, UIRectEdgeAll, -minimumMarginForZoomingToEntireFocusRectInWebViewCoordinates / currentScale);
2308         CGRect maximumTargetRectInDocumentCoordinates = UIRectInsetEdges(focusedElementRectInDocumentCoordinates, UIRectEdgeAll, -maximumMarginForZoomingToEntireFocusRectInWebViewCoordinates / currentScale);
2309
2310         double clampedMaximumTargetScale = clampTo<double>(std::min(visibleSize.width / CGRectGetWidth(minimumTargetRectInDocumentCoordinates), visibleSize.height / CGRectGetHeight(minimumTargetRectInDocumentCoordinates)), minimumScale, maximumScale);
2311         double clampedMinimumTargetScale = clampTo<double>(std::min(visibleSize.width / CGRectGetWidth(maximumTargetRectInDocumentCoordinates), visibleSize.height / CGRectGetHeight(maximumTargetRectInDocumentCoordinates)), minimumScale, maximumScale);
2312         scale = clampTo<double>(currentScale, clampedMinimumTargetScale, clampedMaximumTargetScale);
2313 #else
2314         const double webViewStandardFontSize = 16;
2315         scale = clampTo<double>(webViewStandardFontSize / fontSize, minimumScale, maximumScale);
2316 #endif
2317     }
2318
2319     CGFloat documentWidth = [_contentView bounds].size.width;
2320     scale = CGRound(documentWidth * scale) / documentWidth;
2321
2322     WebCore::FloatRect focusedElementRectInNewScale = focusedElementRectInDocumentCoordinates;
2323     focusedElementRectInNewScale.scale(scale);
2324     focusedElementRectInNewScale.moveBy([_contentView frame].origin);
2325
2326     BOOL selectionRectIsNotNull = !selectionRectInDocumentCoordinates.isZero();
2327     if (!forceScroll) {
2328         CGRect currentlyVisibleRegionInWebViewCoordinates;
2329         currentlyVisibleRegionInWebViewCoordinates.origin = unobscuredScrollViewRectInWebViewCoordinates.origin;
2330         currentlyVisibleRegionInWebViewCoordinates.origin.y += visibleOffsetFromTop;
2331         currentlyVisibleRegionInWebViewCoordinates.size = visibleSize;
2332
2333         // Don't bother scrolling if the entire node is already visible, whether or not we got a selectionRect.
2334         if (CGRectContainsRect(currentlyVisibleRegionInWebViewCoordinates, [self convertRect:focusedElementRectInDocumentCoordinates fromView:_contentView.get()]))
2335             return;
2336
2337         // Don't bother scrolling if we have a valid selectionRect and it is already visible.
2338         if (selectionRectIsNotNull && CGRectContainsRect(currentlyVisibleRegionInWebViewCoordinates, [self convertRect:selectionRectInDocumentCoordinates fromView:_contentView.get()]))
2339             return;
2340     }
2341
2342     // We want to center the focused element within the viewport, with as much spacing on all sides as
2343     // we can get based on the visible area after zooming. The spacing in either dimension is half the
2344     // difference between the size of the DOM node and the size of the visible frame.
2345     // If the element is too wide to be horizontally centered or too tall to be vertically centered, we
2346     // instead scroll such that the left edge or top edge of the element is within the left half or top
2347     // half of the viewport, respectively.
2348     CGFloat horizontalSpaceInWebViewCoordinates = (visibleSize.width - focusedElementRectInNewScale.width()) / 2.0;
2349     CGFloat verticalSpaceInWebViewCoordinates = (visibleSize.height - focusedElementRectInNewScale.height()) / 2.0;
2350
2351     auto topLeft = CGPointZero;
2352     auto scrollViewInsets = [_scrollView _effectiveContentInset];
2353     auto currentTopLeft = [_scrollView contentOffset];
2354
2355     if (_haveSetObscuredInsets) {
2356         currentTopLeft.x += _obscuredInsets.left;
2357         currentTopLeft.y += _obscuredInsets.top;
2358     }
2359
2360     if (horizontalSpaceInWebViewCoordinates > 0)
2361         topLeft.x = focusedElementRectInNewScale.x() - horizontalSpaceInWebViewCoordinates;
2362     else {
2363         auto minimumOffsetToRevealLeftEdge = std::max(-scrollViewInsets.left, focusedElementRectInNewScale.x() - visibleSize.width / 2);
2364         auto maximumOffsetToRevealLeftEdge = focusedElementRectInNewScale.x();
2365         topLeft.x = clampTo<double>(currentTopLeft.x, minimumOffsetToRevealLeftEdge, maximumOffsetToRevealLeftEdge);
2366     }
2367
2368     if (verticalSpaceInWebViewCoordinates > 0)
2369         topLeft.y = focusedElementRectInNewScale.y() - verticalSpaceInWebViewCoordinates;
2370     else {
2371         auto minimumOffsetToRevealTopEdge = std::max(-scrollViewInsets.top, focusedElementRectInNewScale.y() - visibleSize.height / 2);
2372         auto maximumOffsetToRevealTopEdge = focusedElementRectInNewScale.y();
2373         topLeft.y = clampTo<double>(currentTopLeft.y, minimumOffsetToRevealTopEdge, maximumOffsetToRevealTopEdge);
2374     }
2375
2376     topLeft.y -= visibleOffsetFromTop;
2377
2378     WebCore::FloatRect documentBoundsInNewScale = [_contentView bounds];
2379     documentBoundsInNewScale.scale(scale);
2380     documentBoundsInNewScale.moveBy([_contentView frame].origin);
2381
2382     CGFloat minimumAllowableHorizontalOffsetInWebViewCoordinates = -INFINITY;
2383     CGFloat minimumAllowableVerticalOffsetInWebViewCoordinates = -INFINITY;
2384     CGFloat maximumAllowableHorizontalOffsetInWebViewCoordinates = CGRectGetMaxX(documentBoundsInNewScale) - visibleSize.width;
2385     CGFloat maximumAllowableVerticalOffsetInWebViewCoordinates = CGRectGetMaxY(documentBoundsInNewScale) - visibleSize.height;
2386
2387     if (selectionRectIsNotNull) {
2388         WebCore::FloatRect selectionRectInNewScale = selectionRectInDocumentCoordinates;
2389         selectionRectInNewScale.scale(scale);
2390         selectionRectInNewScale.moveBy([_contentView frame].origin);
2391         // Adjust the min and max allowable scroll offsets, such that the selection rect remains visible.
2392         minimumAllowableHorizontalOffsetInWebViewCoordinates = CGRectGetMaxX(selectionRectInNewScale) + caretOffsetFromWindowEdge - visibleSize.width;
2393         minimumAllowableVerticalOffsetInWebViewCoordinates = CGRectGetMaxY(selectionRectInNewScale) + caretOffsetFromWindowEdge - visibleSize.height - visibleOffsetFromTop;
2394         maximumAllowableHorizontalOffsetInWebViewCoordinates = std::min<CGFloat>(maximumAllowableHorizontalOffsetInWebViewCoordinates, CGRectGetMinX(selectionRectInNewScale) - caretOffsetFromWindowEdge);
2395         maximumAllowableVerticalOffsetInWebViewCoordinates = std::min<CGFloat>(maximumAllowableVerticalOffsetInWebViewCoordinates, CGRectGetMinY(selectionRectInNewScale) - caretOffsetFromWindowEdge - visibleOffsetFromTop);
2396     }
2397
2398     // Constrain the left edge in document coordinates so that:
2399     //  - it isn't so small that the scrollVisibleRect isn't visible on the screen
2400     //  - it isn't so great that the document's right edge is less than the right edge of the screen
2401     topLeft.x = clampTo<CGFloat>(topLeft.x, minimumAllowableHorizontalOffsetInWebViewCoordinates, maximumAllowableHorizontalOffsetInWebViewCoordinates);
2402
2403     // Constrain the top edge in document coordinates so that:
2404     //  - it isn't so small that the scrollVisibleRect isn't visible on the screen
2405     //  - it isn't so great that the document's bottom edge is higher than the top of the form assistant
2406     topLeft.y = clampTo<CGFloat>(topLeft.y, minimumAllowableVerticalOffsetInWebViewCoordinates, maximumAllowableVerticalOffsetInWebViewCoordinates);
2407
2408     if (_haveSetObscuredInsets) {
2409         // This looks unintuitive, but is necessary in order to precisely center the focused element in the visible area.
2410         // The top left position already accounts for top and left obscured insets - i.e., a topLeft of (0, 0) corresponds
2411         // to the top- and left-most point below (and to the right of) the top inset area and left inset areas, respectively.
2412         // However, when telling WKScrollView to scroll to a given center position, this center position is computed relative
2413         // to the coordinate space of the scroll view. Thus, to compute our center position from the top left position, we
2414         // need to first move the top left position up and to the left, and then add half the width and height of the content
2415         // area (including obscured insets).
2416         topLeft.x -= _obscuredInsets.left;
2417         topLeft.y -= _obscuredInsets.top;
2418     }
2419
2420     WebCore::FloatPoint newCenter = CGPointMake(topLeft.x + CGRectGetWidth(self.bounds) / 2, topLeft.y + CGRectGetHeight(self.bounds) / 2);
2421
2422     if (scale != currentScale)
2423         _page->willStartUserTriggeredZooming();
2424
2425     LOG_WITH_STREAM(VisibleRects, stream << "_zoomToFocusRect: zooming to " << newCenter << " scale:" << scale);
2426
2427     // The newCenter has been computed in the new scale, but _zoomToCenter expected the center to be in the original scale.
2428     newCenter.scale(1 / scale);
2429     [_scrollView _zoomToCenter:newCenter scale:scale duration:formControlZoomAnimationDuration force:YES];
2430 }
2431
2432 - (CGFloat)_targetContentZoomScaleForRect:(const WebCore::FloatRect&)targetRect currentScale:(double)currentScale fitEntireRect:(BOOL)fitEntireRect minimumScale:(double)minimumScale maximumScale:(double)maximumScale
2433 {
2434     WebCore::FloatSize unobscuredContentSize([self _contentRectForUserInteraction].size);
2435     double horizontalScale = unobscuredContentSize.width() * currentScale / targetRect.width();
2436     double verticalScale = unobscuredContentSize.height() * currentScale / targetRect.height();
2437
2438     horizontalScale = std::min(std::max(horizontalScale, minimumScale), maximumScale);
2439     verticalScale = std::min(std::max(verticalScale, minimumScale), maximumScale);
2440
2441     return fitEntireRect ? std::min(horizontalScale, verticalScale) : horizontalScale;
2442 }
2443
2444 - (BOOL)_zoomToRect:(WebCore::FloatRect)targetRect withOrigin:(WebCore::FloatPoint)origin fitEntireRect:(BOOL)fitEntireRect minimumScale:(double)minimumScale maximumScale:(double)maximumScale minimumScrollDistance:(float)minimumScrollDistance
2445 {
2446     const float maximumScaleFactorDeltaForPanScroll = 0.02;
2447
2448     double currentScale = contentZoomScale(self);
2449     double targetScale = [self _targetContentZoomScaleForRect:targetRect currentScale:currentScale fitEntireRect:fitEntireRect minimumScale:minimumScale maximumScale:maximumScale];
2450
2451     if (fabs(targetScale - currentScale) < maximumScaleFactorDeltaForPanScroll) {
2452         if ([self _scrollToRect:targetRect origin:origin minimumScrollDistance:minimumScrollDistance])
2453             return true;
2454     } else if (targetScale != currentScale) {
2455         [self _zoomToRect:targetRect atScale:targetScale origin:origin animated:YES];
2456         return true;
2457     }
2458
2459     return false;
2460 }
2461
2462 - (void)didMoveToWindow
2463 {
2464     _page->activityStateDidChange(WebCore::ActivityState::allFlags());
2465 }
2466
2467 - (void)setOpaque:(BOOL)opaque
2468 {
2469     BOOL oldOpaque = self.opaque;
2470
2471     [super setOpaque:opaque];
2472     [_contentView setOpaque:opaque];
2473
2474     if (oldOpaque == opaque)
2475         return;
2476
2477     if (!_page)
2478         return;
2479
2480     _page->setDrawsBackground(opaque);
2481     [self _updateScrollViewBackground];
2482 }
2483
2484 - (void)setBackgroundColor:(UIColor *)backgroundColor
2485 {
2486     [super setBackgroundColor:backgroundColor];
2487     [_contentView setBackgroundColor:backgroundColor];
2488 }
2489
2490 - (BOOL)_allowsDoubleTapGestures
2491 {
2492     if (_fastClickingIsDisabled)
2493         return YES;
2494
2495     // If the page is not user scalable, we don't allow double tap gestures.
2496     if (![_scrollView isZoomEnabled] || [_scrollView minimumZoomScale] >= [_scrollView maximumZoomScale])
2497         return NO;
2498
2499     // If the viewport width was not explicit, we allow double tap gestures.
2500     if (!_viewportMetaTagWidthWasExplicit || _viewportMetaTagCameFromImageDocument)
2501         return YES;
2502
2503     // If the page set a viewport width that wasn't the device width, then it was
2504     // scaled and thus will probably need to zoom.
2505     if (_viewportMetaTagWidth != WebCore::ViewportArguments::ValueDeviceWidth)
2506         return YES;
2507
2508     // At this point, we have a page that asked for width = device-width. However,
2509     // if the content's width and height were large, we might have had to shrink it.
2510     // We'll enable double tap zoom whenever we're not at the actual initial scale.
2511     return !areEssentiallyEqualAsFloat(contentZoomScale(self), _initialScaleFactor);
2512 }
2513
2514 - (BOOL)_stylusTapGestureShouldCreateEditableImage
2515 {
2516     return [_configuration _editableImagesEnabled];
2517 }
2518
2519 #pragma mark - UIScrollViewDelegate
2520
2521 - (BOOL)usesStandardContentView
2522 {
2523     return !_customContentView && !_passwordView;
2524 }
2525
2526 - (CGSize)scrollView:(UIScrollView*)scrollView contentSizeForZoomScale:(CGFloat)scale withProposedSize:(CGSize)proposedSize
2527 {
2528     return roundScrollViewContentSize(*_page, proposedSize);
2529 }
2530
2531 - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
2532 {
2533     ASSERT(_scrollView == scrollView);
2534     return self._currentContentView;
2535 }
2536
2537 - (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view
2538 {
2539     if (![self usesStandardContentView]) {
2540         if ([_customContentView respondsToSelector:@selector(web_scrollViewWillBeginZooming:withView:)])
2541             [_customContentView web_scrollViewWillBeginZooming:scrollView withView:view];
2542         return;
2543     }
2544
2545     if (scrollView.pinchGestureRecognizer.state == UIGestureRecognizerStateBegan) {
2546         _page->willStartUserTriggeredZooming();
2547         [_contentView scrollViewWillStartPanOrPinchGesture];
2548     }
2549     [_contentView willStartZoomOrScroll];
2550 }
2551
2552 - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
2553 {
2554     if (![self usesStandardContentView])
2555         return;
2556
2557     if (scrollView.panGestureRecognizer.state == UIGestureRecognizerStateBegan)
2558         [_contentView scrollViewWillStartPanOrPinchGesture];
2559
2560     [_contentView willStartZoomOrScroll];
2561 #if ENABLE(CSS_SCROLL_SNAP) && ENABLE(ASYNC_SCROLLING)
2562     // FIXME: We will want to detect whether snapping will occur before beginning to drag. See WebPageProxy::didCommitLayerTree.
2563     WebKit::RemoteScrollingCoordinatorProxy* coordinator = _page->scrollingCoordinatorProxy();
2564     ASSERT(scrollView == _scrollView.get());
2565     CGFloat scrollDecelerationFactor = (coordinator && coordinator->shouldSetScrollViewDecelerationRateFast()) ? UIScrollViewDecelerationRateFast : UIScrollViewDecelerationRateNormal;
2566     scrollView.horizontalScrollDecelerationFactor = scrollDecelerationFactor;
2567     scrollView.verticalScrollDecelerationFactor = scrollDecelerationFactor;
2568 #endif
2569 }
2570
2571 - (void)_didFinishScrolling
2572 {
2573     if (![self usesStandardContentView])
2574         return;
2575
2576     [self _scheduleVisibleContentRectUpdate];
2577     [_contentView didFinishScrolling];
2578 }
2579
2580 - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
2581 {
2582     // Work around <rdar://problem/16374753> by avoiding deceleration while
2583     // zooming. We'll animate to the right place once the zoom finishes.
2584     if ([scrollView isZooming])
2585         *targetContentOffset = [scrollView contentOffset];
2586 #if ENABLE(CSS_SCROLL_SNAP) && ENABLE(ASYNC_SCROLLING)
2587     if (WebKit::RemoteScrollingCoordinatorProxy* coordinator = _page->scrollingCoordinatorProxy()) {
2588         // FIXME: Here, I'm finding the maximum horizontal/vertical scroll offsets. There's probably a better way to do this.
2589         CGSize maxScrollOffsets = CGSizeMake(scrollView.contentSize.width - scrollView.bounds.size.width, scrollView.contentSize.height - scrollView.bounds.size.height);
2590
2591         UIEdgeInsets obscuredInset;
2592
2593         id<WKUIDelegatePrivate> uiDelegatePrivate = static_cast<id <WKUIDelegatePrivate>>([self UIDelegate]);
2594         if ([uiDelegatePrivate respondsToSelector:@selector(_webView:finalObscuredInsetsForScrollView:withVelocity:targetContentOffset:)])
2595             obscuredInset = [uiDelegatePrivate _webView:self finalObscuredInsetsForScrollView:scrollView withVelocity:velocity targetContentOffset:targetContentOffset];
2596         else
2597             obscuredInset = [self _computedObscuredInset];
2598
2599         CGRect unobscuredRect = UIEdgeInsetsInsetRect(self.bounds, obscuredInset);
2600
2601         coordinator->adjustTargetContentOffsetForSnapping(maxScrollOffsets, velocity, unobscuredRect.origin.y, targetContentOffset);
2602     }
2603 #endif
2604 }
2605
2606 - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
2607 {
2608     // If we're decelerating, scroll offset will be updated when scrollViewDidFinishDecelerating: is called.
2609     if (!decelerate)
2610         [self _didFinishScrolling];
2611 }
2612
2613 - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
2614 {
2615     [self _didFinishScrolling];
2616 }
2617
2618 - (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView
2619 {
2620     [self _didFinishScrolling];
2621 }
2622
2623 - (void)scrollViewDidScroll:(UIScrollView *)scrollView
2624 {
2625     if (![self usesStandardContentView] && [_customContentView respondsToSelector:@selector(web_scrollViewDidScroll:)])
2626         [_customContentView web_scrollViewDidScroll:(UIScrollView *)scrollView];
2627
2628     [self _scheduleVisibleContentRectUpdateAfterScrollInView:scrollView];
2629
2630     if (WebKit::RemoteLayerTreeScrollingPerformanceData* scrollPerfData = _page->scrollingPerformanceData())
2631         scrollPerfData->didScroll([self visibleRectInViewCoordinates]);
2632 }
2633
2634 - (void)scrollViewDidZoom:(UIScrollView *)scrollView
2635 {
2636     if (![self usesStandardContentView] && [_customContentView respondsToSelector:@selector(web_scrollViewDidZoom:)])
2637         [_customContentView web_scrollViewDidZoom:scrollView];
2638
2639     [self _updateScrollViewBackground];
2640     [self _scheduleVisibleContentRectUpdateAfterScrollInView:scrollView];
2641 }
2642
2643 - (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale
2644 {
2645     if (![self usesStandardContentView] && [_customContentView respondsToSelector:@selector(web_scrollViewDidEndZooming:withView:atScale:)])
2646         [_customContentView web_scrollViewDidEndZooming:scrollView withView:view atScale:scale];
2647
2648     ASSERT(scrollView == _scrollView);
2649     // FIXME: remove when rdar://problem/36065495 is fixed.
2650     // When rotating with two fingers down, UIScrollView can set a bogus content view position.
2651     // "Center" is top left because we set the anchorPoint to 0,0.
2652     [_contentView setCenter:self.bounds.origin];
2653
2654     [self _scheduleVisibleContentRectUpdateAfterScrollInView:scrollView];
2655     [_contentView didZoomToScale:scale];
2656 }
2657
2658 - (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
2659 {
2660     [self _didFinishScrolling];
2661 }
2662
2663 - (void)_scrollViewDidInterruptDecelerating:(UIScrollView *)scrollView
2664 {
2665     if (![self usesStandardContentView])
2666         return;
2667
2668     [_contentView didInterruptScrolling];
2669     [self _scheduleVisibleContentRectUpdateAfterScrollInView:scrollView];
2670 }
2671
2672 - (UIView *)_enclosingViewForExposedRectComputation
2673 {
2674     return [self _scroller];
2675 }
2676
2677 - (CGRect)_visibleRectInEnclosingView:(UIView *)enclosingView
2678 {
2679     if (!enclosingView)
2680         return self.bounds;
2681
2682     CGRect exposedRect = [enclosingView convertRect:enclosingView.bounds toView:self];
2683     return CGRectIntersectsRect(exposedRect, self.bounds) ? CGRectIntersection(exposedRect, self.bounds) : CGRectZero;
2684 }
2685
2686 - (CGRect)_visibleContentRect
2687 {
2688     if (_frozenVisibleContentRect)
2689         return _frozenVisibleContentRect.value();
2690
2691     CGRect visibleRectInContentCoordinates = [self convertRect:self.bounds toView:_contentView.get()];
2692
2693     if (UIView *enclosingView = [self _enclosingViewForExposedRectComputation]) {
2694         CGRect viewVisibleRect = [self _visibleRectInEnclosingView:enclosingView];
2695         CGRect viewVisibleContentRect = [self convertRect:viewVisibleRect toView:_contentView.get()];
2696         visibleRectInContentCoordinates = CGRectIntersection(visibleRectInContentCoordinates, viewVisibleContentRect);
2697     }
2698
2699     return visibleRectInContentCoordinates;
2700 }
2701
2702 // Called when some ancestor UIScrollView scrolls.
2703 - (void)_didScroll
2704 {
2705     [self _scheduleVisibleContentRectUpdateAfterScrollInView:[self _scroller]];
2706
2707     const NSTimeInterval ScrollingEndedTimerInterval = 0.032;
2708     if (!_enclosingScrollViewScrollTimer) {
2709         _enclosingScrollViewScrollTimer = adoptNS([[NSTimer alloc] initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:ScrollingEndedTimerInterval]
2710             interval:0 target:self selector:@selector(_enclosingScrollerScrollingEnded:) userInfo:nil repeats:YES]);
2711         [[NSRunLoop mainRunLoop] addTimer:_enclosingScrollViewScrollTimer.get() forMode:NSDefaultRunLoopMode];
2712     }
2713     _didScrollSinceLastTimerFire = YES;
2714 }
2715
2716 - (void)_enclosingScrollerScrollingEnded:(NSTimer *)timer
2717 {
2718     if (_didScrollSinceLastTimerFire) {
2719         _didScrollSinceLastTimerFire = NO;
2720         return;
2721     }
2722
2723     [self _scheduleVisibleContentRectUpdate];
2724     [_enclosingScrollViewScrollTimer invalidate];
2725     _enclosingScrollViewScrollTimer = nil;
2726 }
2727
2728 - (UIEdgeInsets)_scrollViewSystemContentInset
2729 {
2730     // It's not safe to access the scroll view's safeAreaInsets or _systemContentInset from
2731     // inside our layoutSubviews implementation, because they aren't updated until afterwards.
2732     // Instead, depend on the fact that the UIScrollView and WKWebView are in the same coordinate
2733     // space, and map the WKWebView's own insets into the scroll view manually.
2734     return UIEdgeInsetsAdd([_scrollView _contentScrollInset], self.safeAreaInsets, [_scrollView _edgesApplyingSafeAreaInsetsToContentInset]);
2735 }
2736
2737 - (WebCore::FloatSize)activeViewLayoutSize:(const CGRect&)bounds
2738 {
2739     if (_overridesViewLayoutSize)
2740         return WebCore::FloatSize(_viewLayoutSizeOverride);
2741
2742 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000
2743     return WebCore::FloatSize(UIEdgeInsetsInsetRect(CGRectMake(0, 0, bounds.size.width, bounds.size.height), self._scrollViewSystemContentInset).size);
2744 #else
2745     return WebCore::FloatSize { bounds.size };
2746 #endif
2747 }
2748
2749 - (void)_dispatchSetViewLayoutSize:(WebCore::FloatSize)viewLayoutSize
2750 {
2751     if (_lastSentViewLayoutSize && CGSizeEqualToSize(_lastSentViewLayoutSize.value(), viewLayoutSize))
2752         return;
2753
2754     LOG_WITH_STREAM(VisibleRects, stream << "-[WKWebView " << _page->pageID() << " _dispatchSetViewLayoutSize:] " << viewLayoutSize << " contentZoomScale " << contentZoomScale(self));
2755     _page->setViewportConfigurationViewLayoutSize(viewLayoutSize, _page->layoutSizeScaleFactor(), _minimumEffectiveDeviceWidth);
2756     _lastSentViewLayoutSize = viewLayoutSize;
2757 }
2758
2759 - (void)_dispatchSetMaximumUnobscuredSize:(WebCore::FloatSize)maximumUnobscuredSize
2760 {
2761     if (_lastSentMaximumUnobscuredSize && CGSizeEqualToSize(_lastSentMaximumUnobscuredSize.value(), maximumUnobscuredSize))
2762         return;
2763
2764     _page->setMaximumUnobscuredSize(maximumUnobscuredSize);
2765     _lastSentMaximumUnobscuredSize = maximumUnobscuredSize;
2766 }
2767
2768 - (void)_dispatchSetDeviceOrientation:(int32_t)deviceOrientation
2769 {
2770     if (_lastSentDeviceOrientation && _lastSentDeviceOrientation.value() == deviceOrientation)
2771         return;
2772
2773     _page->setDeviceOrientation(deviceOrientation);
2774     _lastSentDeviceOrientation = deviceOrientation;
2775 }
2776
2777 - (void)_frameOrBoundsChanged
2778 {
2779     CGRect bounds = self.bounds;
2780     [_scrollView setFrame:bounds];
2781
2782     if (_dynamicViewportUpdateMode == WebKit::DynamicViewportUpdateMode::NotResizing) {
2783         if (!_overridesViewLayoutSize)
2784             [self _dispatchSetViewLayoutSize:[self activeViewLayoutSize:self.bounds]];
2785         if (!_overridesMaximumUnobscuredSize)
2786             [self _dispatchSetMaximumUnobscuredSize:WebCore::FloatSize(bounds.size)];
2787
2788         BOOL sizeChanged = NO;
2789         if (auto drawingArea = _page->drawingArea())
2790             sizeChanged = drawingArea->setSize(WebCore::IntSize(bounds.size));
2791
2792         if (sizeChanged & [self usesStandardContentView])
2793             [_contentView setSizeChangedSinceLastVisibleContentRectUpdate:YES];
2794     }
2795
2796     [_customContentView web_setMinimumSize:bounds.size];
2797     [self _scheduleVisibleContentRectUpdate];
2798 }
2799
2800 // 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.
2801 - (CGRect)_contentRectForUserInteraction
2802 {
2803     // FIXME: handle split keyboard.
2804     UIEdgeInsets obscuredInsets = _obscuredInsets;
2805     obscuredInsets.bottom = std::max(_obscuredInsets.bottom, _inputViewBounds.size.height);
2806     CGRect unobscuredRect = UIEdgeInsetsInsetRect(self.bounds, obscuredInsets);
2807     return [self convertRect:unobscuredRect toView:self._currentContentView];
2808 }
2809
2810 // Ideally UIScrollView would expose this for us: <rdar://problem/21394567>.
2811 - (BOOL)_scrollViewIsRubberBanding
2812 {
2813     float deviceScaleFactor = _page->deviceScaleFactor();
2814
2815     CGPoint contentOffset = [_scrollView contentOffset];
2816     CGPoint boundedOffset = contentOffsetBoundedInValidRange(_scrollView.get(), contentOffset);
2817     return !pointsEqualInDevicePixels(contentOffset, boundedOffset, deviceScaleFactor);
2818 }
2819
2820 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000
2821 - (void)safeAreaInsetsDidChange
2822 {
2823     [super safeAreaInsetsDidChange];
2824
2825     [self _scheduleVisibleContentRectUpdate];
2826 }
2827 #endif
2828
2829 - (void)_scheduleVisibleContentRectUpdate
2830 {
2831     // For visible rect updates not associated with a specific UIScrollView, just consider our own scroller.
2832     [self _scheduleVisibleContentRectUpdateAfterScrollInView:_scrollView.get()];
2833 }
2834
2835 - (BOOL)_scrollViewIsInStableState:(UIScrollView *)scrollView
2836 {
2837     BOOL isStableState = !([scrollView isDragging] || [scrollView isDecelerating] || [scrollView isZooming] || [scrollView _isAnimatingZoom] || [scrollView _isScrollingToTop]);
2838
2839     if (isStableState && scrollView == _scrollView.get())
2840         isStableState = !_isChangingObscuredInsetsInteractively;
2841
2842     if (isStableState && scrollView == _scrollView.get())
2843         isStableState = ![self _scrollViewIsRubberBanding];
2844
2845     if (isStableState)
2846         isStableState = !scrollView._isInterruptingDeceleration;
2847
2848     if (NSNumber *stableOverride = self._stableStateOverride)
2849         isStableState = stableOverride.boolValue;
2850
2851     return isStableState;
2852 }
2853
2854 - (void)_addUpdateVisibleContentRectPreCommitHandler
2855 {
2856     auto retainedSelf = retainPtr(self);
2857     [CATransaction addCommitHandler:[retainedSelf] {
2858         WKWebView *webView = retainedSelf.get();
2859         if (![webView _isValid]) {
2860             RELEASE_LOG_IF(webView._page && webView._page->isAlwaysOnLoggingAllowed(), ViewState, "In CATransaction preCommitHandler, WKWebView %p is invalid", webView);
2861             return;
2862         }
2863
2864         @try {
2865             [webView _updateVisibleContentRects];
2866         } @catch (NSException *exception) {
2867             RELEASE_LOG_IF(webView._page && webView._page->isAlwaysOnLoggingAllowed(), ViewState, "In CATransaction preCommitHandler, -[WKWebView %p _updateVisibleContentRects] threw an exception", webView);
2868         }
2869         webView->_hasScheduledVisibleRectUpdate = NO;
2870     } forPhase:kCATransactionPhasePreCommit];
2871 }
2872
2873 - (void)_scheduleVisibleContentRectUpdateAfterScrollInView:(UIScrollView *)scrollView
2874 {
2875     _visibleContentRectUpdateScheduledFromScrollViewInStableState = [self _scrollViewIsInStableState:scrollView];
2876
2877     if (_hasScheduledVisibleRectUpdate) {
2878         auto timeNow = MonotonicTime::now();
2879         if ((timeNow - _timeOfRequestForVisibleContentRectUpdate) > delayBeforeNoVisibleContentsRectsLogging) {
2880             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",
2881                 self, (timeNow - _timeOfRequestForVisibleContentRectUpdate).value(), (timeNow - _timeOfLastVisibleContentRectUpdate).value(), [self _isValid]);
2882         }
2883         return;
2884     }
2885
2886     _hasScheduledVisibleRectUpdate = YES;
2887     _timeOfRequestForVisibleContentRectUpdate = MonotonicTime::now();
2888
2889     CATransactionPhase transactionPhase = [CATransaction currentPhase];
2890     if (transactionPhase == kCATransactionPhaseNull || transactionPhase == kCATransactionPhasePreLayout) {
2891         [self _addUpdateVisibleContentRectPreCommitHandler];
2892         return;
2893     }
2894
2895     dispatch_async(dispatch_get_main_queue(), [retainedSelf = retainPtr(self)] {
2896         WKWebView *webView = retainedSelf.get();
2897         if (![webView _isValid])
2898             return;
2899         [webView _addUpdateVisibleContentRectPreCommitHandler];
2900     });
2901 }
2902
2903 static bool scrollViewCanScroll(UIScrollView *scrollView)
2904 {
2905     if (!scrollView)
2906         return NO;
2907
2908     UIEdgeInsets contentInset = scrollView.contentInset;
2909     CGSize contentSize = scrollView.contentSize;
2910     CGSize boundsSize = scrollView.bounds.size;
2911
2912     return (contentSize.width + contentInset.left + contentInset.right) > boundsSize.width
2913         || (contentSize.height + contentInset.top + contentInset.bottom) > boundsSize.height;
2914 }
2915
2916 - (CGRect)_contentBoundsExtendedForRubberbandingWithScale:(CGFloat)scaleFactor
2917 {
2918     CGPoint contentOffset = [_scrollView contentOffset];
2919     CGPoint boundedOffset = contentOffsetBoundedInValidRange(_scrollView.get(), contentOffset);
2920
2921     CGFloat horiontalRubberbandAmountInContentCoordinates = (contentOffset.x - boundedOffset.x) / scaleFactor;
2922     CGFloat verticalRubberbandAmountInContentCoordinates = (contentOffset.y - boundedOffset.y) / scaleFactor;
2923
2924     CGRect extendedBounds = [_contentView bounds];
2925
2926     if (horiontalRubberbandAmountInContentCoordinates < 0) {
2927         extendedBounds.origin.x += horiontalRubberbandAmountInContentCoordinates;
2928         extendedBounds.size.width -= horiontalRubberbandAmountInContentCoordinates;
2929     } else if (horiontalRubberbandAmountInContentCoordinates > 0)
2930         extendedBounds.size.width += horiontalRubberbandAmountInContentCoordinates;
2931
2932     if (verticalRubberbandAmountInContentCoordinates < 0) {
2933         extendedBounds.origin.y += verticalRubberbandAmountInContentCoordinates;
2934         extendedBounds.size.height -= verticalRubberbandAmountInContentCoordinates;
2935     } else if (verticalRubberbandAmountInContentCoordinates > 0)
2936         extendedBounds.size.height += verticalRubberbandAmountInContentCoordinates;
2937
2938     return extendedBounds;
2939 }
2940
2941 - (void)_updateVisibleContentRects
2942 {
2943     BOOL inStableState = _visibleContentRectUpdateScheduledFromScrollViewInStableState;
2944
2945     if (![self usesStandardContentView]) {
2946         [_passwordView setFrame:self.bounds];
2947         [_customContentView web_computedContentInsetDidChange];
2948         RELEASE_LOG_IF_ALLOWED("%p -[WKWebView _updateVisibleContentRects:] - usesStandardContentView is NO, bailing", self);
2949         return;
2950     }
2951
2952     if (_delayUpdateVisibleContentRects) {
2953         _hadDelayedUpdateVisibleContentRects = YES;
2954         RELEASE_LOG_IF_ALLOWED("%p -[WKWebView _updateVisibleContentRects:] - _delayUpdateVisibleContentRects is YES, bailing", self);
2955         return;
2956     }
2957
2958     if (_dynamicViewportUpdateMode != WebKit::DynamicViewportUpdateMode::NotResizing
2959         || (_needsResetViewStateAfterCommitLoadForMainFrame && ![_contentView sizeChangedSinceLastVisibleContentRectUpdate])
2960         || [_scrollView isZoomBouncing]
2961         || _currentlyAdjustingScrollViewInsetsForKeyboard) {
2962         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)",
2963             self, _dynamicViewportUpdateMode, _needsResetViewStateAfterCommitLoadForMainFrame, [_contentView sizeChangedSinceLastVisibleContentRectUpdate], [_scrollView isZoomBouncing], _currentlyAdjustingScrollViewInsetsForKeyboard);
2964         return;
2965     }
2966
2967     CGRect visibleRectInContentCoordinates = [self _visibleContentRect];
2968
2969     UIEdgeInsets computedContentInsetUnadjustedForKeyboard = [self _computedObscuredInset];
2970     if (!_haveSetObscuredInsets)
2971         computedContentInsetUnadjustedForKeyboard.bottom -= _totalScrollViewBottomInsetAdjustmentForKeyboard;
2972
2973     CGFloat scaleFactor = contentZoomScale(self);
2974
2975     CGRect unobscuredRect = UIEdgeInsetsInsetRect(self.bounds, computedContentInsetUnadjustedForKeyboard);
2976     CGRect unobscuredRectInContentCoordinates = _frozenUnobscuredContentRect ? _frozenUnobscuredContentRect.value() : [self convertRect:unobscuredRect toView:_contentView.get()];
2977     unobscuredRectInContentCoordinates = CGRectIntersection(unobscuredRectInContentCoordinates, [self _contentBoundsExtendedForRubberbandingWithScale:scaleFactor]);
2978
2979     // The following logic computes the extent to which the bottom, top, left and right content insets are visible.
2980     auto scrollViewInsets = [_scrollView contentInset];
2981     auto scrollViewBounds = [_scrollView bounds];
2982     auto scrollViewContentSize = [_scrollView contentSize];
2983     auto scrollViewOriginIncludingInset = UIEdgeInsetsInsetRect(scrollViewBounds, computedContentInsetUnadjustedForKeyboard).origin;
2984     auto maximumVerticalScrollExtentWithoutRevealingBottomContentInset = scrollViewContentSize.height - CGRectGetHeight(scrollViewBounds);
2985     auto maximumHorizontalScrollExtentWithoutRevealingRightContentInset = scrollViewContentSize.width - CGRectGetWidth(scrollViewBounds);
2986     auto contentInsets = UIEdgeInsetsZero;
2987     if (scrollViewInsets.left > 0 && scrollViewOriginIncludingInset.x < 0)
2988         contentInsets.left = std::min(-scrollViewOriginIncludingInset.x, scrollViewInsets.left) / scaleFactor;
2989
2990     if (scrollViewInsets.top > 0 && scrollViewOriginIncludingInset.y < 0)
2991         contentInsets.top = std::min(-scrollViewOriginIncludingInset.y, scrollViewInsets.top) / scaleFactor;
2992
2993     if (scrollViewInsets.right > 0 && scrollViewOriginIncludingInset.x > maximumHorizontalScrollExtentWithoutRevealingRightContentInset)
2994         contentInsets.right = std::min(scrollViewOriginIncludingInset.x - maximumHorizontalScrollExtentWithoutRevealingRightContentInset, scrollViewInsets.right) / scaleFactor;
2995
2996     if (scrollViewInsets.bottom > 0 && scrollViewOriginIncludingInset.y > maximumVerticalScrollExtentWithoutRevealingBottomContentInset)
2997         contentInsets.bottom = std::min(scrollViewOriginIncludingInset.y - maximumVerticalScrollExtentWithoutRevealingBottomContentInset, scrollViewInsets.bottom) / scaleFactor;
2998
2999 #if ENABLE(CSS_SCROLL_SNAP) && ENABLE(ASYNC_SCROLLING)
3000     if (inStableState) {
3001         WebKit::RemoteScrollingCoordinatorProxy* coordinator = _page->scrollingCoordinatorProxy();
3002         if (coordinator && coordinator->hasActiveSnapPoint()) {
3003             CGPoint currentPoint = [_scrollView contentOffset];
3004             CGPoint activePoint = coordinator->nearestActiveContentInsetAdjustedSnapPoint(unobscuredRect.origin.y, currentPoint);
3005
3006             if (!CGPointEqualToPoint(activePoint, currentPoint)) {
3007                 RetainPtr<WKScrollView> strongScrollView = _scrollView;
3008                 dispatch_async(dispatch_get_main_queue(), [strongScrollView, activePoint] {
3009                     [strongScrollView setContentOffset:activePoint animated:NO];
3010                 });
3011             }
3012         }
3013     }
3014 #endif
3015
3016     [_contentView didUpdateVisibleRect:visibleRectInContentCoordinates
3017         unobscuredRect:unobscuredRectInContentCoordinates
3018         contentInsets:contentInsets
3019         unobscuredRectInScrollViewCoordinates:unobscuredRect
3020         obscuredInsets:_obscuredInsets
3021         unobscuredSafeAreaInsets:[self _computedUnobscuredSafeAreaInset]
3022         inputViewBounds:_inputViewBounds
3023         scale:scaleFactor minimumScale:[_scrollView minimumZoomScale]
3024         inStableState:inStableState
3025         isChangingObscuredInsetsInteractively:_isChangingObscuredInsetsInteractively
3026         enclosedInScrollableAncestorView:scrollViewCanScroll([self _scroller])];
3027
3028     while (!_visibleContentRectUpdateCallbacks.isEmpty()) {
3029         auto callback = _visibleContentRectUpdateCallbacks.takeLast();
3030         callback();
3031     }
3032     
3033     auto timeNow = MonotonicTime::now();
3034     if ((timeNow - _timeOfRequestForVisibleContentRectUpdate) > delayBeforeNoVisibleContentsRectsLogging)
3035         RELEASE_LOG_IF_ALLOWED("%p -[WKWebView _updateVisibleContentRects:] finally ran %.2fs after being scheduled", self, (timeNow - _timeOfRequestForVisibleContentRectUpdate).value());
3036
3037     _timeOfLastVisibleContentRectUpdate = timeNow;
3038 }
3039
3040 - (void)_didStartProvisionalLoadForMainFrame
3041 {
3042     if (_gestureController)
3043         _gestureController->didStartProvisionalLoadForMainFrame();
3044 }
3045
3046 static WebCore::FloatSize activeMaximumUnobscuredSize(WKWebView *webView, const CGRect& bounds)
3047 {
3048     return WebCore::FloatSize(webView->_overridesMaximumUnobscuredSize ? webView->_maximumUnobscuredSizeOverride : bounds.size);
3049 }
3050
3051 static int32_t activeOrientation(WKWebView *webView)
3052 {
3053     return webView->_overridesInterfaceOrientation ? deviceOrientationForUIInterfaceOrientation(webView->_interfaceOrientationOverride) : webView->_page->deviceOrientation();
3054 }
3055
3056 - (void)_cancelAnimatedResize
3057 {
3058     LOG_WITH_STREAM(VisibleRects, stream << "-[WKWebView " << _page->pageID() << " _cancelAnimatedResize:] " << " _dynamicViewportUpdateMode " << (int)_dynamicViewportUpdateMode);
3059     if (_dynamicViewportUpdateMode == WebKit::DynamicViewportUpdateMode::NotResizing)
3060         return;
3061
3062     if (!_customContentView) {
3063         if (_resizeAnimationView) {
3064             NSUInteger indexOfResizeAnimationView = [[_scrollView subviews] indexOfObject:_resizeAnimationView.get()];
3065             [_scrollView insertSubview:_contentView.get() atIndex:indexOfResizeAnimationView];
3066             [_scrollView insertSubview:[_contentView unscaledView] atIndex:indexOfResizeAnimationView + 1];
3067             [_resizeAnimationView removeFromSuperview];
3068             _resizeAnimationView = nil;
3069         }
3070
3071         [_contentView setHidden:NO];
3072         _resizeAnimationTransformAdjustments = CATransform3DIdentity;
3073     }
3074
3075     _dynamicViewportUpdateMode = WebKit::DynamicViewportUpdateMode::NotResizing;
3076     [self _scheduleVisibleContentRectUpdate];
3077 }
3078
3079 - (void)_didCompleteAnimatedResize
3080 {
3081     if (_dynamicViewportUpdateMode == WebKit::DynamicViewportUpdateMode::NotResizing)
3082         return;
3083
3084     [_contentView setHidden:NO];
3085
3086     if (!_resizeAnimationView) {
3087         // Paranoia. If _resizeAnimationView is null we'll end up setting a zero scale on the content view.
3088         RELEASE_LOG_IF_ALLOWED("%p -[WKWebView _didCompleteAnimatedResize:] - _resizeAnimationView is nil", self);
3089         [self _cancelAnimatedResize];
3090         return;
3091     }
3092
3093     NSUInteger indexOfResizeAnimationView = [[_scrollView subviews] indexOfObject:_resizeAnimationView.get()];
3094     [_scrollView insertSubview:_contentView.get() atIndex:indexOfResizeAnimationView];
3095     [_scrollView insertSubview:[_contentView unscaledView] atIndex:indexOfResizeAnimationView + 1];
3096
3097     CALayer *contentLayer = [_contentView layer];
3098     CGFloat adjustmentScale = _resizeAnimationTransformAdjustments.m11;
3099     contentLayer.sublayerTransform = CATransform3DIdentity;
3100
3101     CGFloat animatingScaleTarget = [[_resizeAnimationView layer] transform].m11;
3102     CATransform3D contentLayerTransform = contentLayer.transform;
3103     CGFloat currentScale = [[_resizeAnimationView layer] transform].m11 * contentLayerTransform.m11;
3104
3105     // We cannot use [UIScrollView setZoomScale:] directly because the UIScrollView delegate would get a callback with
3106     // an invalid contentOffset. The real content offset is only set below.
3107     // Since there is no public API for setting both the zoomScale and the contentOffset, we set the zoomScale manually
3108     // on the zoom layer and then only change the contentOffset.
3109     CGFloat adjustedScale = adjustmentScale * currentScale;
3110     contentLayerTransform.m11 = adjustedScale;
3111     contentLayerTransform.m22 = adjustedScale;
3112     contentLayer.transform = contentLayerTransform;
3113
3114     CGPoint currentScrollOffset = [_scrollView contentOffset];
3115     double horizontalScrollAdjustement = _resizeAnimationTransformAdjustments.m41 * animatingScaleTarget;
3116     double verticalScrollAdjustment = _resizeAnimationTransformAdjustments.m42 * animatingScaleTarget;
3117
3118     [_scrollView setContentSize:roundScrollViewContentSize(*_page, [_contentView frame].size)];
3119     [_scrollView setContentOffset:CGPointMake(currentScrollOffset.x - horizontalScrollAdjustement, currentScrollOffset.y - verticalScrollAdjustment)];
3120
3121     [_resizeAnimationView removeFromSuperview];
3122     _resizeAnimationView = nil;
3123     _resizeAnimationTransformAdjustments = CATransform3DIdentity;
3124
3125     _dynamicViewportUpdateMode = WebKit::DynamicViewportUpdateMode::NotResizing;
3126     [self _scheduleVisibleContentRectUpdate];
3127
3128     CGRect newBounds = self.bounds;
3129     auto newViewLayoutSize = [self activeViewLayoutSize:newBounds];
3130     auto newMaximumUnobscuredSize = activeMaximumUnobscuredSize(self, newBounds);
3131     int32_t newOrientation = activeOrientation(self);
3132
3133     if (!_lastSentViewLayoutSize || newViewLayoutSize != _lastSentViewLayoutSize.value())
3134         [self _dispatchSetViewLayoutSize:newViewLayoutSize];
3135
3136     if (!_lastSentMaximumUnobscuredSize || newMaximumUnobscuredSize != _lastSentMaximumUnobscuredSize.value())
3137         [self _dispatchSetMaximumUnobscuredSize:WebCore::FloatSize(newMaximumUnobscuredSize)];
3138
3139     if (!_lastSentDeviceOrientation || newOrientation != _lastSentDeviceOrientation.value())
3140         [self _dispatchSetDeviceOrientation:newOrientation];
3141
3142     while (!_callbacksDeferredDuringResize.isEmpty())
3143         _callbacksDeferredDuringResize.takeLast()();
3144 }
3145
3146 - (void)_didFinishLoadForMainFrame
3147 {
3148     if (_gestureController)
3149         _gestureController->didFinishLoadForMainFrame();
3150 }
3151
3152 - (void)_didFailLoadForMainFrame
3153 {
3154     if (_gestureController)
3155         _gestureController->didFailLoadForMainFrame();
3156 }
3157
3158 - (void)_didSameDocumentNavigationForMainFrame:(WebKit::SameDocumentNavigationType)navigationType
3159 {
3160     [_customContentView web_didSameDocumentNavigation:toAPI(navigationType)];
3161
3162     if (_gestureController)
3163         _gestureController->didSameDocumentNavigationForMainFrame(navigationType);
3164 }
3165
3166 - (void)_keyboardChangedWithInfo:(NSDictionary *)keyboardInfo adjustScrollView:(BOOL)adjustScrollView
3167 {
3168     NSValue *endFrameValue = [keyboardInfo objectForKey:UIKeyboardFrameEndUserInfoKey];
3169     if (!endFrameValue)
3170         return;
3171
3172     // The keyboard rect is always in screen coordinates. In the view services case the window does not
3173     // have the interface orientation rotation transformation; its host does. So, it makes no sense to
3174     // clip the keyboard rect against its screen.
3175     if ([[self window] _isHostedInAnotherProcess])
3176         _inputViewBounds = [self.window convertRect:[endFrameValue CGRectValue] fromWindow:nil];
3177     else
3178         _inputViewBounds = [self.window convertRect:CGRectIntersection([endFrameValue CGRectValue], self.window.screen.bounds) fromWindow:nil];
3179
3180     if ([[UIPeripheralHost sharedInstance] isUndocked])
3181         _inputViewBounds = CGRectZero;
3182
3183     if (adjustScrollView) {
3184         CGFloat bottomInsetBeforeAdjustment = [_scrollView contentInset].bottom;
3185         SetForScope<BOOL> insetAdjustmentGuard(_currentlyAdjustingScrollViewInsetsForKeyboard, YES);
3186         [_scrollView _adjustForAutomaticKeyboardInfo:keyboardInfo animated:YES lastAdjustment:&_lastAdjustmentForScroller];
3187         CGFloat bottomInsetAfterAdjustment = [_scrollView contentInset].bottom;
3188         if (bottomInsetBeforeAdjustment != bottomInsetAfterAdjustment)
3189             _totalScrollViewBottomInsetAdjustmentForKeyboard += bottomInsetAfterAdjustment - bottomInsetBeforeAdjustment;
3190     }
3191
3192     [self _scheduleVisibleContentRectUpdate];
3193 }
3194
3195 - (BOOL)_shouldUpdateKeyboardWithInfo:(NSDictionary *)keyboardInfo
3196 {
3197     if ([_contentView isFocusingElement])
3198         return YES;
3199
3200     NSNumber *isLocalKeyboard = [keyboardInfo valueForKey:UIKeyboardIsLocalUserInfoKey];
3201     return isLocalKeyboard && !isLocalKeyboard.boolValue;
3202 }
3203
3204 - (void)_keyboardWillChangeFrame:(NSNotification *)notification
3205 {
3206     if ([self _shouldUpdateKeyboardWithInfo:notification.userInfo])
3207         [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES];
3208 }
3209
3210 - (void)_keyboardDidChangeFrame:(NSNotification *)notification
3211 {
3212     [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:NO];
3213 }
3214
3215 - (void)_keyboardWillShow:(NSNotification *)notification
3216 {
3217     if ([self _shouldUpdateKeyboardWithInfo:notification.userInfo])
3218         [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES];
3219
3220     _page->setIsKeyboardAnimatingIn(true);
3221 }
3222
3223 - (void)_keyboardDidShow:(NSNotification *)notification
3224 {
3225     _page->setIsKeyboardAnimatingIn(false);
3226 }
3227
3228 - (void)_keyboardWillHide:(NSNotification *)notification
3229 {
3230     // Ignore keyboard will hide notifications sent during rotation. They're just there for
3231     // backwards compatibility reasons and processing the will hide notification would
3232     // temporarily screw up the unobscured view area.
3233     if ([[UIPeripheralHost sharedInstance] rotationState])
3234         return;
3235
3236     [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES];
3237 }
3238
3239 static void hardwareKeyboardAvailabilityChangedCallback(CFNotificationCenterRef, void* observer, CFStringRef, const void*, CFDictionaryRef)
3240 {
3241     ASSERT(observer);
3242     WKWebView *webView = (__bridge WKWebView *)observer;
3243     webView._page->hardwareKeyboardAvailabilityChanged();
3244 }
3245
3246 - (void)_windowDidRotate:(NSNotification *)notification
3247 {
3248     if (!_overridesInterfaceOrientation)
3249         [self _dispatchSetDeviceOrientation:deviceOrientation()];
3250 }
3251
3252 - (void)_contentSizeCategoryDidChange:(NSNotification *)notification
3253 {
3254     _page->contentSizeCategoryDidChange([self _contentSizeCategory]);
3255 }
3256
3257 - (NSString *)_contentSizeCategory
3258 {
3259     return [[UIApplication sharedApplication] preferredContentSizeCategory];
3260 }
3261
3262 - (void)_accessibilitySettingsDidChange:(NSNotification *)notification
3263 {
3264     _page->accessibilitySettingsDidChange();
3265 }
3266
3267 - (void)setAllowsBackForwardNavigationGestures:(BOOL)allowsBackForwardNavigationGestures
3268 {
3269     if (_allowsBackForwardNavigationGestures == allowsBackForwardNavigationGestures)
3270         return;
3271
3272     _allowsBackForwardNavigationGestures = allowsBackForwardNavigationGestures;
3273
3274     if (allowsBackForwardNavigationGestures && !_gestureController) {
3275         _gestureController = std::make_unique<WebKit::ViewGestureController>(*_page);
3276         _gestureController->installSwipeHandler(self, [self scrollView]);
3277         if (WKWebView *alternateWebView = [_configuration _alternateWebViewForNavigationGestures])
3278             _gestureController->setAlternateBackForwardListSourcePage(alternateWebView->_page.get());
3279     }
3280
3281     if (_gestureController)
3282         _gestureController->setSwipeGestureEnabled(allowsBackForwardNavigationGestures);
3283
3284     _page->setShouldRecordNavigationSnapshots(allowsBackForwardNavigationGestures);
3285 }
3286
3287 - (BOOL)allowsBackForwardNavigationGestures
3288 {
3289     return _allowsBackForwardNavigationGestures;
3290 }
3291
3292 - (BOOL)_isNavigationSwipeGestureRecognizer:(UIGestureRecognizer *)recognizer
3293 {
3294     if (!_gestureController)
3295         return NO;
3296     return _gestureController->isNavigationSwipeGestureRecognizer(recognizer);
3297 }
3298
3299 - (void)_navigationGestureDidBegin
3300 {
3301     // During a back/forward swipe, there's a view interposed between this view and the content view that has
3302     // an offset and animation on it, which results in computing incorrect rectangles. Work around by using
3303     // frozen rects during swipes.
3304     CGRect fullViewRect = self.bounds;
3305     CGRect unobscuredRect = UIEdgeInsetsInsetRect(fullViewRect, [self _computedObscuredInset]);
3306
3307     _frozenVisibleContentRect = [self convertRect:fullViewRect toView:_contentView.get()];
3308     _frozenUnobscuredContentRect = [self convertRect:unobscuredRect toView:_contentView.get()];
3309
3310     LOG_WITH_STREAM(VisibleRects, stream << "_navigationGestureDidBegin: freezing visibleContentRect " << WebCore::FloatRect(_frozenVisibleContentRect.value()) << " UnobscuredContentRect " << WebCore::FloatRect(_frozenUnobscuredContentRect.value()));
3311 }
3312
3313 - (void)_navigationGestureDidEnd
3314 {
3315     _frozenVisibleContentRect = WTF::nullopt;
3316     _frozenUnobscuredContentRect = WTF::nullopt;
3317 }
3318
3319 - (void)_showPasswordViewWithDocumentName:(NSString *)documentName passwordHandler:(void (^)(NSString *))passwordHandler
3320 {
3321     ASSERT(!_passwordView);
3322     _passwordView = adoptNS([[WKPasswordView alloc] initWithFrame:self.bounds documentName:documentName]);
3323     [_passwordView setUserDidEnterPassword:passwordHandler];
3324     [_passwordView showInScrollView:_scrollView.get()];
3325     self._currentContentView.hidden = YES;
3326 }
3327
3328 - (void)_hidePasswordView
3329 {
3330     if (!_passwordView)
3331         return;
3332
3333     self._currentContentView.hidden = NO;
3334     [_passwordView hide];
3335     _passwordView = nil;
3336 }
3337
3338 - (WKPasswordView *)_passwordView
3339 {
3340     return _passwordView.get();
3341 }
3342
3343 - (void)_updateScrollViewInsetAdjustmentBehavior
3344 {
3345 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000
3346     if (![_scrollView _contentInsetAdjustmentBehaviorWasExternallyOverridden])
3347         [_scrollView _setContentInsetAdjustmentBehaviorInternal:self._safeAreaShouldAffectObscuredInsets ? UIScrollViewContentInsetAdjustmentAlways : UIScrollViewContentInsetAdjustmentNever];
3348 #endif
3349 }
3350
3351 - (void)_setAvoidsUnsafeArea:(BOOL)avoidsUnsafeArea
3352 {
3353     if (_avoidsUnsafeArea == avoidsUnsafeArea)
3354         return;
3355
3356     _avoidsUnsafeArea = avoidsUnsafeArea;
3357
3358     [self _updateScrollViewInsetAdjustmentBehavior];
3359     [self _scheduleVisibleContentRectUpdate];
3360
3361     id <WKUIDelegatePrivate> uiDelegate = (id <WKUIDelegatePrivate>)[self UIDelegate];
3362     if ([uiDelegate respondsToSelector:@selector(_webView:didChangeSafeAreaShouldAffectObscuredInsets:)])
3363         [uiDelegate _webView:self didChangeSafeAreaShouldAffectObscuredInsets:avoidsUnsafeArea];
3364 }
3365
3366 - (BOOL)_haveSetObscuredInsets
3367 {
3368     return _haveSetObscuredInsets;
3369 }
3370
3371 #endif // PLATFORM(IOS_FAMILY)
3372
3373 #if ENABLE(ACCESSIBILITY_EVENTS)
3374
3375 static void accessibilityEventsEnabledChangedCallback(CFNotificationCenterRef, void* observer, CFStringRef, const void*, CFDictionaryRef)
3376 {
3377     ASSERT(observer);
3378     WKWebView *webview = (__bridge WKWebView *)observer;
3379     [webview _updateAccessibilityEventsEnabled];
3380 }
3381
3382 - (void)_updateAccessibilityEventsEnabled
3383 {
3384     if (!isNullFunctionPointer(_AXSWebAccessibilityEventsEnabled))
3385         _page->updateAccessibilityEventsEnabled(_AXSWebAccessibilityEventsEnabled());
3386 }
3387
3388 #endif
3389
3390 #pragma mark OS X-specific methods
3391
3392 #if PLATFORM(MAC)
3393
3394 - (BOOL)acceptsFirstResponder
3395 {
3396     return _impl->acceptsFirstResponder();
3397 }
3398
3399 - (BOOL)becomeFirstResponder
3400 {
3401     return _impl->becomeFirstResponder();
3402 }
3403
3404 - (BOOL)resignFirstResponder
3405 {
3406     return _impl->resignFirstResponder();
3407 }
3408
3409 - (void)viewWillStartLiveResize
3410 {
3411     _impl->viewWillStartLiveResize();
3412 }
3413
3414 - (void)viewDidEndLiveResize
3415 {
3416     _impl->viewDidEndLiveResize();
3417 }
3418
3419 - (BOOL)isFlipped
3420 {
3421     return YES;
3422 }
3423
3424 - (NSSize)intrinsicContentSize
3425 {
3426     return NSSizeFromCGSize(_impl->intrinsicContentSize());
3427 }
3428
3429 - (void)prepareContentInRect:(NSRect)rect
3430 {
3431     _impl->prepareContentInRect(NSRectToCGRect(rect));
3432 }
3433
3434 - (void)setFrameSize:(NSSize)size
3435 {
3436     [super setFrameSize:size];
3437     [_safeBrowsingWarning setFrame:self.bounds];
3438     _impl->setFrameSize(NSSizeToCGSize(size));
3439 }
3440
3441 IGNORE_WARNINGS_BEGIN("deprecated-implementations")
3442 - (void)renewGState
3443 IGNORE_WARNINGS_END
3444 {
3445     _impl->renewGState();
3446     [super renewGState];
3447 }
3448
3449 #define WEBCORE_COMMAND(command) - (void)command:(id)sender { _impl->executeEditCommandForSelector(_cmd); }
3450
3451 WEBCORE_COMMAND(alignCenter)
3452 WEBCORE_COMMAND(alignJustified)
3453 WEBCORE_COMMAND(alignLeft)
3454 WEBCORE_COMMAND(alignRight)
3455 WEBCORE_COMMAND(copy)
3456 WEBCORE_COMMAND(cut)
3457 WEBCORE_COMMAND(delete)
3458 WEBCORE_COMMAND(deleteBackward)
3459 WEBCORE_COMMAND(deleteBackwardByDecomposingPreviousCharacter)
3460 WEBCORE_COMMAND(deleteForward)
3461 WEBCORE_COMMAND(deleteToBeginningOfLine)
3462 WEBCORE_COMMAND(deleteToBeginningOfParagraph)
3463 WEBCORE_COMMAND(deleteToEndOfLine)
3464 WEBCORE_COMMAND(deleteToEndOfParagraph)
3465 WEBCORE_COMMAND(deleteToMark)
3466 WEBCORE_COMMAND(deleteWordBackward)
3467 WEBCORE_COMMAND(deleteWordForward)
3468 WEBCORE_COMMAND(ignoreSpelling)
3469 WEBCORE_COMMAND(indent)
3470 WEBCORE_COMMAND(insertBacktab)
3471 WEBCORE_COMMAND(insertLineBreak)
3472 WEBCORE_COMMAND(insertNewline)
3473 WEBCORE_COMMAND(insertNewlineIgnoringFieldEditor)
3474 WEBCORE_COMMAND(insertParagraphSeparator)
3475 WEBCORE_COMMAND(insertTab)
3476 WEBCORE_COMMAND(insertTabIgnoringFieldEditor)
3477 WEBCORE_COMMAND(makeTextWritingDirectionLeftToRight)
3478 WEBCORE_COMMAND(makeTextWritingDirectionNatural)
3479 WEBCORE_COMMAND(makeTextWritingDirectionRightToLeft)
3480 WEBCORE_COMMAND(moveBackward)
3481 WEBCORE_COMMAND(moveBackwardAndModifySelection)
3482 WEBCORE_COMMAND(moveDown)
3483 WEBCORE_COMMAND(moveDownAndModifySelection)
3484 WEBCORE_COMMAND(moveForward)
3485 WEBCORE_COMMAND(moveForwardAndModifySelection)
3486 WEBCORE_COMMAND(moveLeft)
3487 WEBCORE_COMMAND(moveLeftAndModifySelection)
3488 WEBCORE_COMMAND(moveParagraphBackwardAndModifySelection)
3489 WEBCORE_COMMAND(moveParagraphForwardAndModifySelection)
3490 WEBCORE_COMMAND(moveRight)
3491 WEBCORE_COMMAND(moveRightAndModifySelection)
3492 WEBCORE_COMMAND(moveToBeginningOfDocument)
3493 WEBCORE_COMMAND(moveToBeginningOfDocumentAndModifySelection)
3494 WEBCORE_COMMAND(moveToBeginningOfLine)
3495 WEBCORE_COMMAND(moveToBeginningOfLineAndModifySelection)
3496 WEBCORE_COMMAND(moveToBeginningOfParagraph)
3497 WEBCORE_COMMAND(moveToBeginningOfParagraphAndModifySelection)
3498 WEBCORE_COMMAND(moveToBeginningOfSentence)
3499 WEBCORE_COMMAND(moveToBeginningOfSentenceAndModifySelection)
3500 WEBCORE_COMMAND(moveToEndOfDocument)
3501 WEBCORE_COMMAND(moveToEndOfDocumentAndModifySelection)
3502 WEBCORE_COMMAND(moveToEndOfLine)
3503 WEBCORE_COMMAND(moveToEndOfLineAndModifySelection)
3504 WEBCORE_COMMAND(moveToEndOfParagraph)
3505 WEBCORE_COMMAND(moveToEndOfParagraphAndModifySelection)
3506 WEBCORE_COMMAND(moveToEndOfSentence)
3507 WEBCORE_COMMAND(moveToEndOfSentenceAndModifySelection)
3508 WEBCORE_COMMAND(moveToLeftEndOfLine)
3509 WEBCORE_COMMAND(moveToLeftEndOfLineAndModifySelection)
3510 WEBCORE_COMMAND(moveToRightEndOfLine)
3511 WEBCORE_COMMAND(moveToRightEndOfLineAndModifySelection)
3512 WEBCORE_COMMAND(moveUp)
3513 WEBCORE_COMMAND(moveUpAndModifySelection)
3514 WEBCORE_COMMAND(moveWordBackward)
3515 WEBCORE_COMMAND(moveWordBackwardAndModifySelection)
3516 WEBCORE_COMMAND(moveWordForward)
3517 WEBCORE_COMMAND(moveWordForwardAndModifySelection)
3518 WEBCORE_COMMAND(moveWordLeft)
3519 WEBCORE_COMMAND(moveWordLeftAndModifySelection)
3520 WEBCORE_COMMAND(moveWordRight)
3521 WEBCORE_COMMAND(moveWordRightAndModifySelection)
3522 WEBCORE_COMMAND(outdent)
3523 WEBCORE_COMMAND(pageDown)
3524 WEBCORE_COMMAND(pageDownAndModifySelection)
3525 WEBCORE_COMMAND(pageUp)
3526 WEBCORE_COMMAND(pageUpAndModifySelection)
3527 WEBCORE_COMMAND(paste)
3528 WEBCORE_COMMAND(pasteAsPlainText)
3529 WEBCORE_COMMAND(scrollPageDown)
3530 WEBCORE_COMMAND(scrollPageUp)
3531 WEBCORE_COMMAND(scrollLineDown)
3532 WEBCORE_COMMAND(scrollLineUp)
3533 WEBCORE_COMMAND(scrollToBeginningOfDocument)
3534 WEBCORE_COMMAND(scrollToEndOfDocument)
3535 WEBCORE_COMMAND(selectAll)
3536 WEBCORE_COMMAND(selectLine)
3537 WEBCORE_COMMAND(selectParagraph)
3538 WEBCORE_COMMAND(selectSentence)
3539 WEBCORE_COMMAND(selectToMark)
3540 WEBCORE_COMMAND(selectWord)
3541 WEBCORE_COMMAND(setMark)
3542 WEBCORE_COMMAND(subscript)
3543 WEBCORE_COMMAND(superscript)
3544 WEBCORE_COMMAND(swapWithMark)
3545 WEBCORE_COMMAND(takeFindStringFromSelection)
3546 WEBCORE_COMMAND(transpose)
3547 WEBCORE_COMMAND(underline)
3548 WEBCORE_COMMAND(unscript)
3549 WEBCORE_COMMAND(yank)
3550 WEBCORE_COMMAND(yankAndSelect)
3551
3552 #undef WEBCORE_COMMAND
3553
3554 - (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pasteboard types:(NSArray *)types
3555 {
3556     return _impl->writeSelectionToPasteboard(pasteboard, types);
3557 }
3558
3559 - (void)centerSelectionInVisibleArea:(id)sender
3560 {
3561     _impl->centerSelectionInVisibleArea();
3562 }
3563
3564 - (id)validRequestorForSendType:(NSString *)sendType returnType:(NSString *)returnType
3565 {
3566     return _impl->validRequestorForSendAndReturnTypes(sendType, returnType);
3567 }
3568
3569 - (BOOL)readSelectionFromPasteboard:(NSPasteboard *)pasteboard
3570 {
3571     return _impl->readSelectionFromPasteboard(pasteboard);
3572 }
3573
3574 - (void)changeFont:(id)sender
3575 {
3576     _impl->changeFontFromFontManager();
3577 }
3578
3579 - (void)changeColor:(id)sender
3580 {
3581     _impl->changeFontColorFromSender(sender);
3582 }
3583
3584 - (void)changeAttributes:(id)sender
3585 {
3586     _impl->changeFontAttributesFromSender(sender);
3587 }
3588
3589 - (IBAction)startSpeaking:(id)sender
3590 {
3591     _impl->startSpeaking();
3592 }
3593
3594 - (IBAction)stopSpeaking:(id)sender
3595 {
3596     _impl->stopSpeaking(sender);
3597 }
3598
3599 - (IBAction)showGuessPanel:(id)sender
3600 {
3601     _impl->showGuessPanel(sender);
3602 }
3603
3604 - (IBAction)checkSpelling:(id)sender
3605 {
3606     _impl->checkSpelling();
3607 }
3608
3609 - (void)changeSpelling:(id)sender
3610 {
3611     _impl->changeSpelling(sender);
3612 }
3613
3614 - (IBAction)toggleContinuousSpellChecking:(id)sender
3615 {
3616     _impl->toggleContinuousSpellChecking();
3617 }
3618
3619 - (BOOL)isGrammarCheckingEnabled
3620 {
3621     return _impl->isGrammarCheckingEnabled();
3622 }
3623
3624 - (void)setGrammarCheckingEnabled:(BOOL)flag
3625 {
3626     _impl->setGrammarCheckingEnabled(flag);
3627 }
3628
3629 - (IBAction)toggleGrammarChecking:(id)sender
3630 {
3631     _impl->toggleGrammarChecking();
3632 }
3633
3634 - (IBAction)toggleAutomaticSpellingCorrection:(id)sender
3635 {
3636     _impl->toggleAutomaticSpellingCorrection();
3637 }
3638
3639 - (void)orderFrontSubstitutionsPanel:(id)sender
3640 {
3641     _impl->orderFrontSubstitutionsPanel(sender);
3642 }
3643
3644 - (IBAction)toggleSmartInsertDelete:(id)sender
3645 {
3646     _impl->toggleSmartInsertDelete();
3647 }
3648
3649 - (BOOL)isAutomaticQuoteSubstitutionEnabled
3650 {
3651     return _impl->isAutomaticQuoteSubstitutionEnabled();
3652 }
3653
3654 - (void)setAutomaticQuoteSubstitutionEnabled:(BOOL)flag
3655 {
3656     _impl->setAutomaticQuoteSubstitutionEnabled(flag);
3657 }
3658
3659 - (void)toggleAutomaticQuoteSubstitution:(id)sender
3660 {
3661     _impl->toggleAutomaticQuoteSubstitution();
3662 }
3663
3664 - (BOOL)isAutomaticDashSubstitutionEnabled
3665 {
3666     return _impl->isAutomaticDashSubstitutionEnabled();
3667 }
3668
3669 - (void)setAutomaticDashSubstitutionEnabled:(BOOL)flag
3670 {
3671     _impl->setAutomaticDashSubstitutionEnabled(flag);
3672 }
3673
3674 - (void)toggleAutomaticDashSubstitution:(id)sender
3675 {
3676     _impl->toggleAutomaticDashSubstitution();
3677 }
3678
3679 - (BOOL)isAutomaticLinkDetectionEnabled
3680 {
3681     return _impl->isAutomaticLinkDetectionEnabled();
3682 }
3683
3684 - (void)setAutomaticLinkDetectionEnabled:(BOOL)flag
3685 {
3686     _impl->setAutomaticLinkDetectionEnabled(flag);
3687 }
3688
3689 - (void)toggleAutomaticLinkDetection:(id)sender
3690 {
3691     _impl->toggleAutomaticLinkDetection();
3692 }
3693
3694 - (BOOL)isAutomaticTextReplacementEnabled
3695 {
3696     return _impl->isAutomaticTextReplacementEnabled();
3697 }
3698
3699 - (void)setAutomaticTextReplacementEnabled:(BOOL)flag
3700 {
3701     _impl->setAutomaticTextReplacementEnabled(flag);
3702 }
3703
3704 - (void)toggleAutomaticTextReplacement:(id)sender
3705 {
3706     _impl->toggleAutomaticTextReplacement();
3707 }
3708
3709 - (void)uppercaseWord:(id)sender
3710 {
3711     _impl->uppercaseWord();
3712 }
3713
3714 - (void)lowercaseWord:(id)sender
3715 {
3716     _impl->lowercaseWord();
3717 }
3718
3719 - (void)capitalizeWord:(id)sender
3720 {
3721     _impl->capitalizeWord();
3722 }
3723
3724 - (BOOL)_wantsKeyDownForEvent:(NSEvent *)event
3725 {
3726     return _impl->wantsKeyDownForEvent(event);
3727 }
3728
3729 - (void)scrollWheel:(NSEvent *)event
3730 {
3731     _impl->scrollWheel(event);
3732 }
3733
3734 - (void)swipeWithEvent:(NSEvent *)event
3735 {
3736     _impl->swipeWithEvent(event);
3737 }
3738
3739 - (void)mouseMoved:(NSEvent *)event
3740 {
3741     _impl->mouseMoved(event);
3742 }
3743
3744 - (void)mouseDown:(NSEvent *)event
3745 {
3746     _impl->mouseDown(event);
3747 }
3748
3749 - (void)mouseUp:(NSEvent *)event
3750 {
3751     _impl->mouseUp(event);
3752 }
3753
3754 - (void)mouseDragged:(NSEvent *)event
3755 {
3756     _impl->mouseDragged(event);
3757 }
3758
3759 - (void)mouseEntered:(NSEvent *)event
3760 {
3761     _impl->mouseEntered(event);
3762 }
3763
3764 - (void)mouseExited:(NSEvent *)event
3765 {
3766     _impl->mouseExited(event);
3767 }
3768
3769 - (void)otherMouseDown:(NSEvent *)event
3770 {
3771     _impl->otherMouseDown(event);
3772 }
3773
3774 - (void)otherMouseDragged:(NSEvent *)event
3775 {
3776     _impl->otherMouseDragged(event);
3777 }
3778
3779 - (void)otherMouseUp:(NSEvent *)event
3780 {
3781     _impl->otherMouseUp(event);
3782 }
3783
3784 - (void)rightMouseDown:(NSEvent *)event
3785 {
3786     _impl->rightMouseDown(event);
3787 }
3788
3789 - (void)rightMouseDragged:(NSEvent *)event
3790 {
3791     _impl->rightMouseDragged(event);
3792 }
3793
3794 - (void)rightMouseUp:(NSEvent *)event
3795 {
3796     _impl->rightMouseUp(event);
3797 }
3798
3799 - (void)pressureChangeWithEvent:(NSEvent *)event
3800 {
3801     _impl->pressureChangeWithEvent(event);
3802 }
3803
3804 - (BOOL)acceptsFirstMouse:(NSEvent *)event
3805 {
3806     return _impl->acceptsFirstMouse(event);
3807 }
3808
3809 - (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent *)event
3810 {
3811     return _impl->shouldDelayWindowOrderingForEvent(event);
3812 }
3813
3814 - (void)doCommandBySelector:(SEL)selector
3815 {
3816     _impl->doCommandBySelector(selector);
3817 }
3818
3819 - (void)insertText:(id)string
3820 {
3821     _impl->insertText(string);
3822 }
3823
3824 - (void)insertText:(id)string replacementRange:(NSRange)replacementRange
3825 {
3826     _impl->insertText(string, replacementRange);
3827 }
3828
3829 - (NSTextInputContext *)inputContext
3830 {
3831     if (!_impl)
3832         return nil;
3833     return _impl->inputContext();
3834 }
3835
3836 - (BOOL)performKeyEquivalent:(NSEvent *)event
3837 {
3838     return _impl->performKeyEquivalent(event);
3839 }
3840
3841 - (void)keyUp:(NSEvent *)theEvent
3842 {
3843     _impl->keyUp(theEvent);
3844 }
3845
3846 - (void)keyDown:(NSEvent *)theEvent
3847 {
3848     _impl->keyDown(theEvent);
3849 }
3850
3851 - (void)flagsChanged:(NSEvent *)theEvent
3852 {
3853     _impl->flagsChanged(theEvent);
3854 }
3855
3856 - (void)setMarkedText:(id)string selectedRange:(NSRange)newSelectedRange replacementRange:(NSRange)replacementRange
3857 {
3858     _impl->setMarkedText(string, newSelectedRange, replacementRange);
3859 }
3860
3861 - (void)unmarkText
3862 {
3863     _impl->unmarkText();
3864 }
3865
3866 - (NSRange)selectedRange
3867 {
3868     return _impl->selectedRange();
3869 }
3870
3871 - (BOOL)hasMarkedText
3872 {
3873     return _impl->hasMarkedText();
3874 }
3875
3876 - (NSRange)markedRange
3877 {
3878     return _impl->markedRange();
3879 }
3880
3881 - (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)nsRange actualRange:(NSRangePointer)actualRange
3882 {
3883     return _impl->attributedSubstringForProposedRange(nsRange, actualRange);
3884 }
3885
3886 - (NSUInteger)characterIndexForPoint:(NSPoint)thePoint
3887 {
3888     return _impl->characterIndexForPoint(thePoint);
3889 }
3890
3891 - (void)typingAttributesWithCompletionHandler:(void(^)(NSDictionary<NSString *, id> *))completion
3892 {
3893     _impl->typingAttributesWithCompletionHandler(completion);
3894 }
3895
3896 - (NSRect)firstRectForCharacterRange:(NSRange)theRange actualRange:(NSRangePointer)actualRange
3897 {
3898     return _impl->firstRectForCharacterRange(theRange, actualRange);
3899 }
3900
3901 - (void)selectedRangeWithCompletionHandler:(void(^)(NSRange selectedRange))completionHandlerPtr
3902 {
3903     _impl->selectedRangeWithCompletionHandler(completionHandlerPtr);
3904 }
3905
3906 - (void)markedRangeWithCompletionHandler:(void(^)(NSRange markedRange))completionHandlerPtr
3907 {
3908     _impl->markedRangeWithCompletionHandler(completionHandlerPtr);
3909 }
3910
3911 - (void)hasMarkedTextWithCompletionHandler:(void(^)(BOOL hasMarkedText))completionHandlerPtr
3912 {
3913     _impl->hasMarkedTextWithCompletionHandler(completionHandlerPtr);
3914 }
3915
3916 - (void)attributedSubstringForProposedRange:(NSRange)nsRange completionHandler:(void(^)(NSAttributedString *attrString, NSRange actualRange))completionHandlerPtr
3917 {
3918     _impl->attributedSubstringForProposedRange(nsRange, completionHandlerPtr);
3919 }
3920
3921 - (void)firstRectForCharacterRange:(NSRange)theRange completionHandler:(void(^)(NSRect firstRect, NSRange actualRange))completionHandlerPtr
3922 {
3923     _impl->firstRectForCharacterRange(theRange, completionHandlerPtr);
3924 }
3925
3926 - (void)characterIndexForPoint:(NSPoint)thePoint completionHandler:(void(^)(NSUInteger))completionHandlerPtr
3927 {
3928     _impl->characterIndexForPoint(thePoint, completionHandlerPtr);
3929 }
3930
3931 - (NSArray *)validAttributesForMarkedText
3932 {
3933     return _impl->validAttributesForMarkedText();
3934 }
3935
3936 #if ENABLE(DRAG_SUPPORT)
3937 IGNORE_WARNINGS_BEGIN("deprecated-implementations")
3938 - (void)draggedImage:(NSImage *)image endedAt:(NSPoint)endPoint operation:(NSDragOperation)operation
3939 IGNORE_WARNINGS_END
3940 {
3941     _impl->draggedImage(image, NSPointToCGPoint(endPoint), operation);
3942 }
3943
3944 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)draggingInfo
3945 {
3946     return _impl->draggingEntered(draggingInfo);
3947 }
3948
3949 - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)draggingInfo
3950 {
3951     return _impl->draggingUpdated(draggingInfo);
3952 }
3953
3954 - (void)draggingExited:(id <NSDraggingInfo>)draggingInfo
3955 {
3956     _impl->draggingExited(draggingInfo);
3957 }
3958
3959 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)draggingInfo
3960 {
3961     return _impl->prepareForDragOperation(draggingInfo);
3962 }
3963
3964 - (BOOL)performDragOperation:(id <NSDraggingInfo>)draggingInfo
3965 {
3966     return _impl->performDragOperation(draggingInfo);
3967 }
3968
3969 - (NSView *)_hitTest:(NSPoint *)point dragTypes:(NSSet *)types
3970 {
3971     return _impl->hitTestForDragTypes(NSPointToCGPoint(*point), types);
3972 }
3973 #endif // ENABLE(DRAG_SUPPORT)
3974
3975 - (BOOL)_windowResizeMouseLocationIsInVisibleScrollerThumb:(NSPoint)point
3976 {
3977     return _impl->windowResizeMouseLocationIsInVisibleScrollerThumb(NSPointToCGPoint(point));
3978 }
3979
3980 - (void)viewWillMoveToWindow:(NSWindow *)window
3981 {
3982     _impl->viewWillMoveToWindow(window);
3983 }
3984
3985 - (void)viewDidMoveToWindow
3986 {
3987     _impl->viewDidMoveToWindow();
3988 }
3989
3990 - (void)drawRect:(NSRect)rect
3991 {
3992     _impl->drawRect(NSRectToCGRect(rect));
3993 }
3994
3995 - (BOOL)isOpaque
3996 {
3997     return _impl->isOpaque();
3998 }
3999
4000 - (BOOL)mouseDownCanMoveWindow
4001 {
4002     return WebKit::WebViewImpl::mouseDownCanMoveWindow();
4003 }
4004
4005 - (void)viewDidHide
4006 {
4007     _impl->viewDidHide();
4008 }
4009
4010 - (void)viewDidUnhide
4011 {
4012     _impl->viewDidUnhide();
4013 }
4014
4015 - (void)viewDidChangeBackingProperties
4016 {
4017     _impl->viewDidChangeBackingProperties();
4018 }
4019
4020 - (void)_activeSpaceDidChange:(NSNotification *)notification
4021 {
4022     _impl->activeSpaceDidChange();
4023 }
4024
4025 - (id)accessibilityFocusedUIElement
4026 {
4027     return _impl->accessibilityFocusedUIElement();
4028 }
4029
4030 IGNORE_WARNINGS_BEGIN("deprecated-implementations")
4031 - (BOOL)accessibilityIsIgnored
4032 IGNORE_WARNINGS_END
4033 {
4034     return _impl->accessibilityIsIgnored();
4035 }
4036
4037 - (id)accessibilityHitTest:(NSPoint)point
4038 {
4039     return _impl->accessibilityHitTest(NSPointToCGPoint(point));
4040 }
4041
4042 IGNORE_WARNINGS_BEGIN("deprecated-implementations")
4043 - (id)accessibilityAttributeValue:(NSString *)attribute
4044 IGNORE_WARNINGS_END
4045 {
4046     return _impl->accessibilityAttributeValue(attribute);
4047 }
4048
4049 IGNORE_WARNINGS_BEGIN("deprecated-implementations")
4050 - (id)accessibilityAttributeValue:(NSString *)attribute forParameter:(id)parameter
4051 IGNORE_WARNINGS_END
4052 {
4053     return _impl->accessibilityAttributeValue(attribute, parameter);
4054 }
4055
4056 IGNORE_WARNINGS_BEGIN("deprecated-implementations")
4057 - (NSArray<NSString *> *)accessibilityParameterizedAttributeNames
4058 IGNORE_WARNINGS_END
4059 {
4060     NSArray<NSString *> *names = [super accessibilityParameterizedAttributeNames];
4061     return [names arrayByAddingObject:@"AXConvertRelativeFrame"];
4062 }
4063
4064 - (NSView *)hitTest:(NSPoint)point
4065 {
4066     if (!_impl)
4067         return [super hitTest:point];