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