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