2 * Copyright (C) 2014-2018 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #import "WKWebViewInternal.h"
31 #import "APIFormClient.h"
32 #import "APIPageConfiguration.h"
33 #import "APISerializedScriptValue.h"
34 #import "CompletionHandlerCallChecker.h"
35 #import "DiagnosticLoggingClient.h"
36 #import "FindClient.h"
37 #import "FrontBoardServicesSPI.h"
38 #import "FullscreenClient.h"
39 #import "IconLoadingDelegate.h"
40 #import "LegacySessionStateCoding.h"
42 #import "NavigationState.h"
43 #import "ObjCObjectGraph.h"
44 #import "RemoteLayerTreeScrollingPerformanceData.h"
45 #import "RemoteLayerTreeTransaction.h"
46 #import "RemoteObjectRegistry.h"
47 #import "RemoteObjectRegistryMessages.h"
48 #import "SandboxUtilities.h"
49 #import "UIDelegate.h"
50 #import "VersionChecks.h"
51 #import "ViewGestureController.h"
52 #import "ViewSnapshotStore.h"
53 #import "WKBackForwardListInternal.h"
54 #import "WKBackForwardListItemInternal.h"
55 #import "WKBrowsingContextHandleInternal.h"
56 #import "WKDragDestinationAction.h"
57 #import "WKErrorInternal.h"
58 #import "WKHistoryDelegatePrivate.h"
59 #import "WKLayoutMode.h"
61 #import "WKNSURLExtras.h"
62 #import "WKNavigationDelegate.h"
63 #import "WKNavigationInternal.h"
64 #import "WKPreferencesInternal.h"
65 #import "WKProcessPoolInternal.h"
66 #import "WKSharedAPICast.h"
67 #import "WKSnapshotConfiguration.h"
68 #import "WKUIDelegate.h"
69 #import "WKUIDelegatePrivate.h"
70 #import "WKUserContentControllerInternal.h"
71 #import "WKWebViewConfigurationInternal.h"
72 #import "WKWebViewContentProvider.h"
73 #import "WKWebsiteDataStoreInternal.h"
74 #import "WebBackForwardList.h"
75 #import "WebCertificateInfo.h"
76 #import "WebFullScreenManagerProxy.h"
77 #import "WebPageGroup.h"
78 #import "WebPageProxy.h"
79 #import "WebPreferencesKeys.h"
80 #import "WebProcessPool.h"
81 #import "WebProcessProxy.h"
82 #import "WebURLSchemeHandlerCocoa.h"
83 #import "WebViewImpl.h"
84 #import "_WKActivatedElementInfoInternal.h"
85 #import "_WKDiagnosticLoggingDelegate.h"
86 #import "_WKFindDelegate.h"
87 #import "_WKFrameHandleInternal.h"
88 #import "_WKFullscreenDelegate.h"
89 #import "_WKHitTestResultInternal.h"
90 #import "_WKInputDelegate.h"
91 #import "_WKRemoteObjectRegistryInternal.h"
92 #import "_WKSessionStateInternal.h"
93 #import "_WKVisitedLinkStoreInternal.h"
94 #import "_WKWebsitePoliciesInternal.h"
95 #import <WebCore/AttachmentTypes.h>
96 #import <WebCore/GraphicsContextCG.h>
97 #import <WebCore/IOSurface.h>
98 #import <WebCore/JSDOMBinding.h>
99 #import <WebCore/JSDOMExceptionHandling.h>
100 #import <WebCore/PlatformScreen.h>
101 #import <WebCore/RuntimeApplicationChecks.h>
102 #import <WebCore/SQLiteDatabaseTracker.h>
103 #import <WebCore/SchemeRegistry.h>
104 #import <WebCore/Settings.h>
105 #import <WebCore/SharedBuffer.h>
106 #import <WebCore/ValidationBubble.h>
107 #import <WebCore/ViewportArguments.h>
108 #import <WebCore/WritingMode.h>
109 #import <pal/spi/cocoa/NSKeyedArchiverSPI.h>
110 #import <pal/spi/mac/NSTextFinderSPI.h>
111 #import <wtf/BlockPtr.h>
112 #import <wtf/HashMap.h>
113 #import <wtf/MathExtras.h>
114 #import <wtf/NeverDestroyed.h>
115 #import <wtf/Optional.h>
116 #import <wtf/RetainPtr.h>
117 #import <wtf/SetForScope.h>
119 #import <wtf/spi/darwin/dyldSPI.h>
120 #import <wtf/text/TextStream.h>
122 #if ENABLE(APPLICATION_MANIFEST)
123 #import "_WKApplicationManifestInternal.h"
126 #if ENABLE(DATA_DETECTION)
127 #import "WKDataDetectorTypesInternal.h"
131 #import "InteractionInformationAtPosition.h"
132 #import "InteractionInformationRequest.h"
133 #import "ProcessThrottler.h"
134 #import "RemoteLayerTreeDrawingAreaProxy.h"
135 #import "RemoteScrollingCoordinatorProxy.h"
137 #import "VideoFullscreenManagerProxy.h"
138 #import "WKContentViewInteraction.h"
139 #import "WKPDFView.h"
140 #import "WKPasswordView.h"
141 #import "WKScrollView.h"
142 #import "WKWebViewContentProviderRegistry.h"
143 #import "_WKWebViewPrintFormatter.h"
144 #import <UIKit/UIApplication.h>
145 #import <WebCore/FrameLoaderTypes.h>
146 #import <WebCore/InspectorOverlay.h>
147 #import <WebCore/ScrollableArea.h>
148 #import <WebCore/WebBackgroundTaskController.h>
149 #import <WebCore/WebSQLiteDatabaseTrackerClient.h>
150 #import <pal/spi/cg/CoreGraphicsSPI.h>
151 #import <pal/spi/cocoa/QuartzCoreSPI.h>
153 #if __has_include(<AccessibilitySupport.h>)
154 #include <AccessibilitySupport.h>
157 CFStringRef kAXSAllowForceWebScalingEnabledNotification;
161 #define RELEASE_LOG_IF_ALLOWED(...) RELEASE_LOG_IF(_page && _page->isAlwaysOnLoggingAllowed(), ViewState, __VA_ARGS__)
163 @interface UIScrollView (UIScrollViewInternal)
164 - (void)_adjustForAutomaticKeyboardInfo:(NSDictionary*)info animated:(BOOL)animated lastAdjustment:(CGFloat*)lastAdjustment;
165 - (BOOL)_isScrollingToTop;
166 - (CGPoint)_animatedTargetOffset;
169 @interface UIPeripheralHost(UIKitInternal)
170 - (CGFloat)getVerticalOverlapForView:(UIView *)view usingKeyboardInfo:(NSDictionary *)info;
173 @interface UIView (UIViewInternal)
174 - (UIViewController *)_viewControllerForAncestor;
177 @interface UIWindow (UIWindowInternal)
178 - (BOOL)_isHostedInAnotherProcess;
181 @interface UIViewController (UIViewControllerInternal)
182 - (UIViewController *)_rootAncestorViewController;
183 - (UIViewController *)_viewControllerForSupportedInterfaceOrientations;
186 enum class DynamicViewportUpdateMode {
188 ResizingWithAnimation,
189 ResizingWithDocumentHidden,
192 struct ActiveViewportLayoutSizes {
193 WebCore::FloatSize minimumLayoutSize;
194 WebCore::FloatSize viewSize;
197 #endif // PLATFORM(IOS)
200 static const uint32_t firstSDKVersionWithLinkPreviewEnabledByDefault = 0xA0000;
204 #import "WKTextFinderClient.h"
205 #import "WKViewInternal.h"
206 #import <WebCore/ColorMac.h>
208 @interface WKWebView () <WebViewImplDelegate, NSTextInputClient>
212 @interface WKWebView () <NSTouchBarProvider>
214 #endif // HAVE(TOUCH_BAR)
215 #endif // PLATFORM(MAC)
217 static HashMap<WebKit::WebPageProxy*, WKWebView *>& pageToViewMap()
219 static NeverDestroyed<HashMap<WebKit::WebPageProxy*, WKWebView *>> map;
223 WKWebView* fromWebPageProxy(WebKit::WebPageProxy& page)
225 return pageToViewMap().get(&page);
229 static _WKOverlayScrollbarStyle toAPIScrollbarStyle(std::optional<WebCore::ScrollbarOverlayStyle> coreScrollbarStyle)
231 if (!coreScrollbarStyle)
232 return _WKOverlayScrollbarStyleAutomatic;
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;
242 ASSERT_NOT_REACHED();
243 return _WKOverlayScrollbarStyleAutomatic;
246 static std::optional<WebCore::ScrollbarOverlayStyle> toCoreScrollbarStyle(_WKOverlayScrollbarStyle scrollbarStyle)
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:
262 @implementation WKWebView {
263 std::unique_ptr<WebKit::NavigationState> _navigationState;
264 std::unique_ptr<WebKit::UIDelegate> _uiDelegate;
265 std::unique_ptr<WebKit::IconLoadingDelegate> _iconLoadingDelegate;
267 _WKRenderingProgressEvents _observedRenderingProgressEvents;
269 WebKit::WeakObjCPtr<id <_WKInputDelegate>> _inputDelegate;
272 RetainPtr<_WKRemoteObjectRegistry> _remoteObjectRegistry;
274 RetainPtr<WKScrollView> _scrollView;
275 RetainPtr<WKContentView> _contentView;
277 #if ENABLE(FULLSCREEN_API)
278 RetainPtr<WKFullScreenWindowController> _fullScreenWindowController;
281 BOOL _overridesMinimumLayoutSize;
282 CGSize _minimumLayoutSizeOverride;
283 std::optional<WebCore::FloatSize> _lastSentMinimumLayoutSize;
284 BOOL _overridesMaximumUnobscuredSize;
285 CGSize _maximumUnobscuredSizeOverride;
286 std::optional<WebCore::FloatSize> _lastSentMaximumUnobscuredSize;
287 CGRect _inputViewBounds;
288 CGFloat _viewportMetaTagWidth;
289 BOOL _viewportMetaTagWidthWasExplicit;
290 BOOL _viewportMetaTagCameFromImageDocument;
291 CGFloat _initialScaleFactor;
292 BOOL _fastClickingIsDisabled;
294 BOOL _allowsLinkPreview;
296 UIEdgeInsets _obscuredInsets;
297 BOOL _haveSetObscuredInsets;
298 BOOL _isChangingObscuredInsetsInteractively;
300 UIEdgeInsets _unobscuredSafeAreaInsets;
301 BOOL _haveSetUnobscuredSafeAreaInsets;
302 BOOL _avoidsUnsafeArea;
303 UIRectEdge _obscuredInsetEdgesAffectedBySafeArea;
305 UIInterfaceOrientation _interfaceOrientationOverride;
306 BOOL _overridesInterfaceOrientation;
307 std::optional<int32_t> _lastSentDeviceOrientation;
309 BOOL _allowsViewportShrinkToFit;
310 BOOL _forceHorizontalViewportShrinkToFit;
311 CGFloat _minimumAllowedLayoutWidth;
313 BOOL _hasCommittedLoadForMainFrame;
314 BOOL _needsResetViewStateAfterCommitLoadForMainFrame;
315 uint64_t _firstPaintAfterCommitLoadTransactionID;
316 DynamicViewportUpdateMode _dynamicViewportUpdateMode;
317 CATransform3D _resizeAnimationTransformAdjustments;
318 std::optional<uint64_t> _resizeAnimationTransformTransactionID;
319 RetainPtr<UIView> _resizeAnimationView;
320 CGFloat _lastAdjustmentForScroller;
321 std::optional<CGRect> _frozenVisibleContentRect;
322 std::optional<CGRect> _frozenUnobscuredContentRect;
324 BOOL _commitDidRestoreScrollPosition;
325 std::optional<WebCore::FloatPoint> _scrollOffsetToRestore;
326 WebCore::FloatBoxExtent _obscuredInsetsWhenSaved;
328 std::optional<WebCore::FloatPoint> _unobscuredCenterToRestore;
329 std::optional<uint64_t> _firstTransactionIDAfterPageRestore;
330 double _scaleToRestore;
332 std::unique_ptr<WebKit::ViewGestureController> _gestureController;
333 BOOL _allowsBackForwardNavigationGestures;
335 RetainPtr<UIView <WKWebViewContentProvider>> _customContentView;
336 RetainPtr<UIView> _customContentFixedOverlayView;
338 RetainPtr<NSTimer> _enclosingScrollViewScrollTimer;
339 BOOL _didScrollSinceLastTimerFire;
341 WebCore::Color _scrollViewBackgroundColor;
343 // This value tracks the current adjustment added to the bottom inset due to the keyboard sliding out from the bottom
344 // when computing obscured content insets. This is used when updating the visible content rects where we should not
345 // include this adjustment.
346 CGFloat _totalScrollViewBottomInsetAdjustmentForKeyboard;
347 BOOL _currentlyAdjustingScrollViewInsetsForKeyboard;
349 BOOL _delayUpdateVisibleContentRects;
350 BOOL _hadDelayedUpdateVisibleContentRects;
352 int _activeAnimatedResizeCount;
354 Vector<WTF::Function<void ()>> _snapshotsDeferredDuringResize;
355 RetainPtr<NSMutableArray> _stableStatePresentationUpdateCallbacks;
357 RetainPtr<WKPasswordView> _passwordView;
359 BOOL _hasScheduledVisibleRectUpdate;
360 BOOL _visibleContentRectUpdateScheduledFromScrollViewInStableState;
361 Vector<BlockPtr<void ()>> _visibleContentRectUpdateCallbacks;
364 std::unique_ptr<WebKit::WebViewImpl> _impl;
365 RetainPtr<WKTextFinderClient> _textFinderClient;
369 _WKDragInteractionPolicy _dragInteractionPolicy;
373 - (instancetype)initWithFrame:(CGRect)frame
375 return [self initWithFrame:frame configuration:adoptNS([[WKWebViewConfiguration alloc] init]).get()];
380 return _page && _page->isValid();
384 static int32_t deviceOrientationForUIInterfaceOrientation(UIInterfaceOrientation orientation)
386 switch (orientation) {
387 case UIInterfaceOrientationUnknown:
388 case UIInterfaceOrientationPortrait:
390 case UIInterfaceOrientationPortraitUpsideDown:
392 case UIInterfaceOrientationLandscapeLeft:
394 case UIInterfaceOrientationLandscapeRight:
399 static int32_t deviceOrientation()
401 return deviceOrientationForUIInterfaceOrientation([[UIApplication sharedApplication] statusBarOrientation]);
404 - (BOOL)_isShowingVideoPictureInPicture
409 if (!_page || !_page->videoFullscreenManager())
412 return _page->videoFullscreenManager()->hasMode(WebCore::HTMLMediaElementEnums::VideoFullscreenModePictureInPicture);
416 - (BOOL)_mayAutomaticallyShowVideoPictureInPicture
421 if (!_page || !_page->videoFullscreenManager())
424 return _page->videoFullscreenManager()->mayAutomaticallyShowVideoPictureInPicture();
428 static bool shouldAllowPictureInPictureMediaPlayback()
430 static bool shouldAllowPictureInPictureMediaPlayback = dyld_get_program_sdk_version() >= DYLD_IOS_VERSION_9_0;
431 return shouldAllowPictureInPictureMediaPlayback;
434 static bool shouldAllowSettingAnyXHRHeaderFromFileURLs()
436 static bool shouldAllowSettingAnyXHRHeaderFromFileURLs = (WebCore::IOSApplication::isCardiogram() || WebCore::IOSApplication::isNike()) && !linkedOnOrAfter(WebKit::SDKVersion::FirstThatDisallowsSettingAnyXHRHeaderFromFileURLs);
437 return shouldAllowSettingAnyXHRHeaderFromFileURLs;
442 static bool shouldRequireUserGestureToLoadVideo()
445 static bool shouldRequireUserGestureToLoadVideo = dyld_get_program_sdk_version() >= DYLD_IOS_VERSION_10_0;
446 return shouldRequireUserGestureToLoadVideo;
453 static uint32_t convertUserInterfaceDirectionPolicy(WKUserInterfaceDirectionPolicy policy)
456 case WKUserInterfaceDirectionPolicyContent:
457 return static_cast<uint32_t>(WebCore::UserInterfaceDirectionPolicy::Content);
458 case WKUserInterfaceDirectionPolicySystem:
459 return static_cast<uint32_t>(WebCore::UserInterfaceDirectionPolicy::System);
461 return static_cast<uint32_t>(WebCore::UserInterfaceDirectionPolicy::Content);
464 static uint32_t convertSystemLayoutDirection(NSUserInterfaceLayoutDirection direction)
467 case NSUserInterfaceLayoutDirectionLeftToRight:
468 return static_cast<uint32_t>(WebCore::UserInterfaceLayoutDirection::LTR);
469 case NSUserInterfaceLayoutDirectionRightToLeft:
470 return static_cast<uint32_t>(WebCore::UserInterfaceLayoutDirection::RTL);
472 return static_cast<uint32_t>(WebCore::UserInterfaceLayoutDirection::LTR);
476 static void validate(WKWebViewConfiguration *configuration)
478 if (!configuration.processPool)
479 [NSException raise:NSInvalidArgumentException format:@"configuration.processPool is nil"];
481 if (!configuration.preferences)
482 [NSException raise:NSInvalidArgumentException format:@"configuration.preferences is nil"];
484 if (!configuration.userContentController)
485 [NSException raise:NSInvalidArgumentException format:@"configuration.userContentController is nil"];
487 if (!configuration.websiteDataStore)
488 [NSException raise:NSInvalidArgumentException format:@"configuration.websiteDataStore is nil"];
490 if (!configuration._visitedLinkStore)
491 [NSException raise:NSInvalidArgumentException format:@"configuration._visitedLinkStore is nil"];
494 if (!configuration._contentProviderRegistry)
495 [NSException raise:NSInvalidArgumentException format:@"configuration._contentProviderRegistry is nil"];
499 - (void)_initializeWithConfiguration:(WKWebViewConfiguration *)configuration
502 [NSException raise:NSInvalidArgumentException format:@"Configuration cannot be nil"];
504 _configuration = adoptNS([configuration copy]);
506 if (WKWebView *relatedWebView = [_configuration _relatedWebView]) {
507 WKProcessPool *processPool = [_configuration processPool];
508 WKProcessPool *relatedWebViewProcessPool = [relatedWebView->_configuration processPool];
509 if (processPool && processPool != relatedWebViewProcessPool)
510 [NSException raise:NSInvalidArgumentException format:@"Related web view %@ has process pool %@ but configuration specifies a different process pool %@", relatedWebView, relatedWebViewProcessPool, configuration.processPool];
512 [_configuration setProcessPool:relatedWebViewProcessPool];
515 validate(_configuration.get());
517 WebKit::WebProcessPool& processPool = *[_configuration processPool]->_processPool;
518 processPool.setResourceLoadStatisticsEnabled(configuration.websiteDataStore._resourceLoadStatisticsEnabled);
520 auto pageConfiguration = API::PageConfiguration::create();
522 pageConfiguration->setProcessPool(&processPool);
523 pageConfiguration->setPreferences([_configuration preferences]->_preferences.get());
524 if (WKWebView *relatedWebView = [_configuration _relatedWebView])
525 pageConfiguration->setRelatedPage(relatedWebView->_page.get());
527 pageConfiguration->setUserContentController([_configuration userContentController]->_userContentControllerProxy.get());
528 pageConfiguration->setVisitedLinkStore([_configuration _visitedLinkStore]->_visitedLinkStore.get());
529 pageConfiguration->setWebsiteDataStore([_configuration websiteDataStore]->_websiteDataStore.get());
530 pageConfiguration->setTreatsSHA1SignedCertificatesAsInsecure([_configuration _treatsSHA1SignedCertificatesAsInsecure]);
532 if (NSString *overrideContentSecurityPolicy = configuration._overrideContentSecurityPolicy)
533 pageConfiguration->setOverrideContentSecurityPolicy(overrideContentSecurityPolicy);
536 if (auto pageGroup = WebKit::toImpl([configuration _pageGroup])) {
537 pageConfiguration->setPageGroup(pageGroup);
538 pageConfiguration->setUserContentController(&pageGroup->userContentController());
542 NSString *groupIdentifier = configuration._groupIdentifier;
543 if (groupIdentifier.length)
544 pageConfiguration->setPageGroup(WebKit::WebPageGroup::create(configuration._groupIdentifier).ptr());
547 pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::suppressesIncrementalRenderingKey(), WebKit::WebPreferencesStore::Value(!![_configuration suppressesIncrementalRendering]));
549 pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::shouldRespectImageOrientationKey(), WebKit::WebPreferencesStore::Value(!![_configuration _respectsImageOrientation]));
550 pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::shouldPrintBackgroundsKey(), WebKit::WebPreferencesStore::Value(!![_configuration _printsBackgrounds]));
551 pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::incrementalRenderingSuppressionTimeoutKey(), WebKit::WebPreferencesStore::Value([_configuration _incrementalRenderingSuppressionTimeout]));
552 pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::javaScriptMarkupEnabledKey(), WebKit::WebPreferencesStore::Value(!![_configuration _allowsJavaScriptMarkup]));
553 pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::shouldConvertPositionStyleOnCopyKey(), WebKit::WebPreferencesStore::Value(!![_configuration _convertsPositionStyleOnCopy]));
554 pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::httpEquivEnabledKey(), WebKit::WebPreferencesStore::Value(!![_configuration _allowsMetaRefresh]));
555 pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::allowUniversalAccessFromFileURLsKey(), WebKit::WebPreferencesStore::Value(!![_configuration _allowUniversalAccessFromFileURLs]));
556 pageConfiguration->setInitialCapitalizationEnabled([_configuration _initialCapitalizationEnabled]);
557 pageConfiguration->setWaitsForPaintAfterViewDidMoveToWindow([_configuration _waitsForPaintAfterViewDidMoveToWindow]);
558 pageConfiguration->setControlledByAutomation([_configuration _isControlledByAutomation]);
560 #if ENABLE(APPLICATION_MANIFEST)
561 pageConfiguration->setApplicationManifest([_configuration _applicationManifest] ? [configuration _applicationManifest]->_applicationManifest.get() : nullptr);
565 if (auto cpuLimit = [_configuration _cpuLimit])
566 pageConfiguration->setCPULimit(cpuLimit);
567 pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::showsURLsInToolTipsEnabledKey(), WebKit::WebPreferencesStore::Value(!![_configuration _showsURLsInToolTips]));
568 pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::serviceControlsEnabledKey(), WebKit::WebPreferencesStore::Value(!![_configuration _serviceControlsEnabled]));
569 pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::imageControlsEnabledKey(), WebKit::WebPreferencesStore::Value(!![_configuration _imageControlsEnabled]));
571 pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::userInterfaceDirectionPolicyKey(), WebKit::WebPreferencesStore::Value(convertUserInterfaceDirectionPolicy([_configuration userInterfaceDirectionPolicy])));
572 // We are in the View's initialization routine, so our client hasn't had time to set our user interface direction.
573 // Therefore, according to the docs[1], "this property contains the value reported by the app's userInterfaceLayoutDirection property."
574 // [1] http://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSView_Class/index.html#//apple_ref/doc/uid/20000014-SW222
575 pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::systemLayoutDirectionKey(), WebKit::WebPreferencesStore::Value(convertSystemLayoutDirection(self.userInterfaceLayoutDirection)));
579 pageConfiguration->setAlwaysRunsAtForegroundPriority([_configuration _alwaysRunsAtForegroundPriority]);
581 pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::allowsInlineMediaPlaybackKey(), WebKit::WebPreferencesStore::Value(!![_configuration allowsInlineMediaPlayback]));
582 pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::allowsInlineMediaPlaybackAfterFullscreenKey(), WebKit::WebPreferencesStore::Value(!![_configuration _allowsInlineMediaPlaybackAfterFullscreen]));
583 pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::inlineMediaPlaybackRequiresPlaysInlineAttributeKey(), WebKit::WebPreferencesStore::Value(!![_configuration _inlineMediaPlaybackRequiresPlaysInlineAttribute]));
584 pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::allowsPictureInPictureMediaPlaybackKey(), WebKit::WebPreferencesStore::Value(!![_configuration allowsPictureInPictureMediaPlayback] && shouldAllowPictureInPictureMediaPlayback()));
585 pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::userInterfaceDirectionPolicyKey(), WebKit::WebPreferencesStore::Value(static_cast<uint32_t>(WebCore::UserInterfaceDirectionPolicy::Content)));
586 pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::systemLayoutDirectionKey(), WebKit::WebPreferencesStore::Value(static_cast<uint32_t>(WebCore::LTR)));
587 pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::allowSettingAnyXHRHeaderFromFileURLsKey(), WebKit::WebPreferencesStore::Value(shouldAllowSettingAnyXHRHeaderFromFileURLs()));
590 WKAudiovisualMediaTypes mediaTypesRequiringUserGesture = [_configuration mediaTypesRequiringUserActionForPlayback];
591 pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::requiresUserGestureForVideoPlaybackKey(), WebKit::WebPreferencesStore::Value((mediaTypesRequiringUserGesture & WKAudiovisualMediaTypeVideo) == WKAudiovisualMediaTypeVideo));
592 pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::requiresUserGestureForAudioPlaybackKey(), WebKit::WebPreferencesStore::Value(((mediaTypesRequiringUserGesture & WKAudiovisualMediaTypeAudio) == WKAudiovisualMediaTypeAudio)));
593 pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::requiresUserGestureToLoadVideoKey(), WebKit::WebPreferencesStore::Value(shouldRequireUserGestureToLoadVideo()));
594 pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::mainContentUserGestureOverrideEnabledKey(), WebKit::WebPreferencesStore::Value(!![_configuration _mainContentUserGestureOverrideEnabled]));
595 pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::invisibleAutoplayNotPermittedKey(), WebKit::WebPreferencesStore::Value(!![_configuration _invisibleAutoplayNotPermitted]));
596 pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::mediaDataLoadsAutomaticallyKey(), WebKit::WebPreferencesStore::Value(!![_configuration _mediaDataLoadsAutomatically]));
597 pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::attachmentElementEnabledKey(), WebKit::WebPreferencesStore::Value(!![_configuration _attachmentElementEnabled]));
599 #if ENABLE(DATA_DETECTION) && PLATFORM(IOS)
600 pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::dataDetectorTypesKey(), WebKit::WebPreferencesStore::Value(static_cast<uint32_t>(fromWKDataDetectorTypes([_configuration dataDetectorTypes]))));
602 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
603 pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::allowsAirPlayForMediaPlaybackKey(), WebKit::WebPreferencesStore::Value(!![_configuration allowsAirPlayForMediaPlayback]));
606 #if ENABLE(APPLE_PAY)
607 pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::applePayEnabledKey(), WebKit::WebPreferencesStore::Value(!![_configuration _applePayEnabled]));
610 pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::needsStorageAccessFromFileURLsQuirkKey(), WebKit::WebPreferencesStore::Value(!![_configuration _needsStorageAccessFromFileURLsQuirk]));
611 pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::mediaContentTypesRequiringHardwareSupportKey(), WebKit::WebPreferencesStore::Value(String([_configuration _mediaContentTypesRequiringHardwareSupport])));
612 pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::allowMediaContentTypesRequiringHardwareSupportAsFallbackKey(), WebKit::WebPreferencesStore::Value(!![_configuration _allowMediaContentTypesRequiringHardwareSupportAsFallback]));
614 #if ENABLE(LEGACY_ENCRYPTED_MEDIA)
615 pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::legacyEncryptedMediaAPIEnabledKey(), WebKit::WebPreferencesStore::Value(!![_configuration _legacyEncryptedMediaAPIEnabled]));
618 #if PLATFORM(IOS) && ENABLE(SERVICE_WORKER)
619 if (!WebKit::processHasEntitlement(@"com.apple.developer.WebKit.ServiceWorkers"))
620 pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::serviceWorkersEnabledKey(), WebKit::WebPreferencesStore::Value(false));
624 CGRect bounds = self.bounds;
625 _scrollView = adoptNS([[WKScrollView alloc] initWithFrame:bounds]);
626 [_scrollView setInternalDelegate:self];
627 [_scrollView setBouncesZoom:YES];
629 _avoidsUnsafeArea = YES;
630 [self _updateScrollViewInsetAdjustmentBehavior];
632 [self addSubview:_scrollView.get()];
634 static uint32_t programSDKVersion = dyld_get_program_sdk_version();
635 _allowsLinkPreview = programSDKVersion >= firstSDKVersionWithLinkPreviewEnabledByDefault;
637 _contentView = adoptNS([[WKContentView alloc] initWithFrame:bounds processPool:processPool configuration:WTFMove(pageConfiguration) webView:self]);
639 _page = [_contentView page];
640 [self _dispatchSetDeviceOrientation:deviceOrientation()];
641 _page->setDrawsBackground(self.opaque);
643 [_contentView layer].anchorPoint = CGPointZero;
644 [_contentView setFrame:bounds];
645 [_scrollView addSubview:_contentView.get()];
646 [_scrollView addSubview:[_contentView unscaledView]];
647 [self _updateScrollViewBackground];
648 _obscuredInsetEdgesAffectedBySafeArea = UIRectEdgeTop | UIRectEdgeLeft | UIRectEdgeRight;
650 _viewportMetaTagWidth = WebCore::ViewportArguments::ValueAuto;
651 _initialScaleFactor = 1;
652 _fastClickingIsDisabled = [[NSUserDefaults standardUserDefaults] boolForKey:@"WebKitFastClickingDisabled"];
654 [self _frameOrBoundsChanged];
656 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
657 [center addObserver:self selector:@selector(_keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];
658 [center addObserver:self selector:@selector(_keyboardDidChangeFrame:) name:UIKeyboardDidChangeFrameNotification object:nil];
659 [center addObserver:self selector:@selector(_keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
660 [center addObserver:self selector:@selector(_keyboardDidShow:) name:UIKeyboardDidShowNotification object:nil];
661 [center addObserver:self selector:@selector(_keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
662 [center addObserver:self selector:@selector(_windowDidRotate:) name:UIWindowDidRotateNotification object:nil];
663 [center addObserver:self selector:@selector(_contentSizeCategoryDidChange:) name:UIContentSizeCategoryDidChangeNotification object:nil];
664 _page->contentSizeCategoryDidChange([self _contentSizeCategory]);
666 [center addObserver:self selector:@selector(_accessibilitySettingsDidChange:) name:UIAccessibilityGrayscaleStatusDidChangeNotification object:nil];
667 [center addObserver:self selector:@selector(_accessibilitySettingsDidChange:) name:UIAccessibilityInvertColorsStatusDidChangeNotification object:nil];
668 [center addObserver:self selector:@selector(_accessibilitySettingsDidChange:) name:UIAccessibilityReduceMotionStatusDidChangeNotification object:nil];
670 [[_configuration _contentProviderRegistry] addPage:*_page];
671 _page->setForceAlwaysUserScalable([_configuration ignoresViewportScaleLimits]);
675 _impl = std::make_unique<WebKit::WebViewImpl>(self, self, processPool, WTFMove(pageConfiguration));
676 _page = &_impl->page();
678 _impl->setAutomaticallyAdjustsContentInsets(true);
679 _impl->setRequiresUserActionForEditingControlsManager([configuration _requiresUserActionForEditingControlsManager]);
682 _page->setBackgroundExtendsBeyondPage(true);
684 if (NSString *applicationNameForUserAgent = configuration.applicationNameForUserAgent)
685 _page->setApplicationNameForUserAgent(applicationNameForUserAgent);
687 _navigationState = std::make_unique<WebKit::NavigationState>(self);
688 _page->setNavigationClient(_navigationState->createNavigationClient());
690 _uiDelegate = std::make_unique<WebKit::UIDelegate>(self);
691 _page->setFindClient(std::make_unique<WebKit::FindClient>(self));
692 _page->setDiagnosticLoggingClient(std::make_unique<WebKit::DiagnosticLoggingClient>(self));
694 _iconLoadingDelegate = std::make_unique<WebKit::IconLoadingDelegate>(self);
697 [self _setUpSQLiteDatabaseTrackerClient];
700 auto *handlers = _configuration.get()._urlSchemeHandlers;
701 for (NSString *key in handlers)
702 _page->setURLSchemeHandlerForScheme(WebKit::WebURLSchemeHandlerCocoa::create(handlers[key]), key);
704 pageToViewMap().add(_page.get(), self);
707 _dragInteractionPolicy = _WKDragInteractionPolicyDefault;
708 #if ENABLE(EXTRA_ZOOM_MODE)
709 _minimumAllowedLayoutWidth = 320;
710 _allowsViewportShrinkToFit = YES;
711 _forceHorizontalViewportShrinkToFit = YES;
713 _minimumAllowedLayoutWidth = 0;
714 _allowsViewportShrinkToFit = NO;
715 _forceHorizontalViewportShrinkToFit = NO;
717 #endif // PLATFORM(IOS)
720 - (void)_setUpSQLiteDatabaseTrackerClient
723 WebBackgroundTaskController *controller = [WebBackgroundTaskController sharedController];
724 if (controller.backgroundTaskStartBlock)
727 controller.backgroundTaskStartBlock = ^NSUInteger (void (^expirationHandler)())
729 return [[UIApplication sharedApplication] beginBackgroundTaskWithName:@"com.apple.WebKit.DatabaseActivity" expirationHandler:expirationHandler];
731 controller.backgroundTaskEndBlock = ^(UIBackgroundTaskIdentifier taskIdentifier)
733 [[UIApplication sharedApplication] endBackgroundTask:taskIdentifier];
735 controller.invalidBackgroundTaskIdentifier = UIBackgroundTaskInvalid;
737 WebCore::SQLiteDatabaseTracker::setClient(&WebCore::WebSQLiteDatabaseTrackerClient::sharedWebSQLiteDatabaseTrackerClient());
741 - (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration
743 if (!(self = [super initWithFrame:frame]))
746 [self _initializeWithConfiguration:configuration];
751 - (instancetype)initWithCoder:(NSCoder *)coder
753 if (!(self = [super initWithCoder:coder]))
756 WKWebViewConfiguration *configuration = decodeObjectOfClassForKeyFromCoder([WKWebViewConfiguration class], @"configuration", coder);
757 [self _initializeWithConfiguration:configuration];
759 self.allowsBackForwardNavigationGestures = [coder decodeBoolForKey:@"allowsBackForwardNavigationGestures"];
760 self.customUserAgent = decodeObjectOfClassForKeyFromCoder([NSString class], @"customUserAgent", coder);
761 self.allowsLinkPreview = [coder decodeBoolForKey:@"allowsLinkPreview"];
764 self.allowsMagnification = [coder decodeBoolForKey:@"allowsMagnification"];
765 self.magnification = [coder decodeDoubleForKey:@"magnification"];
771 - (void)encodeWithCoder:(NSCoder *)coder
773 [super encodeWithCoder:coder];
775 [coder encodeObject:_configuration.get() forKey:@"configuration"];
777 [coder encodeBool:self.allowsBackForwardNavigationGestures forKey:@"allowsBackForwardNavigationGestures"];
778 [coder encodeObject:self.customUserAgent forKey:@"customUserAgent"];
779 [coder encodeBool:self.allowsLinkPreview forKey:@"allowsLinkPreview"];
782 [coder encodeBool:self.allowsMagnification forKey:@"allowsMagnification"];
783 [coder encodeDouble:self.magnification forKey:@"magnification"];
790 [_textFinderClient willDestroyView:self];
794 [_contentView _webViewDestroyed];
796 if (_remoteObjectRegistry)
797 _page->process().processPool().removeMessageReceiver(Messages::RemoteObjectRegistry::messageReceiverName(), _page->pageID());
803 [_remoteObjectRegistry _invalidate];
804 [[_configuration _contentProviderRegistry] removePage:*_page];
805 CFNotificationCenterRemoveObserver(CFNotificationCenterGetDarwinNotifyCenter(), (__bridge const void *)(self), nullptr, nullptr);
806 [[NSNotificationCenter defaultCenter] removeObserver:self];
807 [_scrollView setInternalDelegate:nil];
810 pageToViewMap().remove(_page.get());
815 - (WKWebViewConfiguration *)configuration
817 return [[_configuration copy] autorelease];
820 - (WKBackForwardList *)backForwardList
822 return wrapper(_page->backForwardList());
825 - (id <WKNavigationDelegate>)navigationDelegate
827 return _navigationState->navigationDelegate().autorelease();
830 - (void)setNavigationDelegate:(id <WKNavigationDelegate>)navigationDelegate
832 _page->setNavigationClient(_navigationState->createNavigationClient());
833 _navigationState->setNavigationDelegate(navigationDelegate);
836 - (id <WKUIDelegate>)UIDelegate
838 return _uiDelegate->delegate().autorelease();
841 - (void)setUIDelegate:(id<WKUIDelegate>)UIDelegate
843 _uiDelegate->setDelegate(UIDelegate);
844 #if ENABLE(CONTEXT_MENUS)
845 _page->setContextMenuClient(_uiDelegate->createContextMenuClient());
847 _page->setUIClient(_uiDelegate->createUIClient());
850 - (id <_WKIconLoadingDelegate>)_iconLoadingDelegate
852 return _iconLoadingDelegate->delegate().autorelease();
855 - (void)_setIconLoadingDelegate:(id<_WKIconLoadingDelegate>)iconLoadingDelegate
857 _page->setIconLoadingClient(_iconLoadingDelegate->createIconLoadingClient());
858 _iconLoadingDelegate->setDelegate(iconLoadingDelegate);
861 - (WKNavigation *)loadRequest:(NSURLRequest *)request
863 auto navigation = _page->loadRequest(request);
867 return [wrapper(*navigation.leakRef()) autorelease];
870 - (WKNavigation *)loadFileURL:(NSURL *)URL allowingReadAccessToURL:(NSURL *)readAccessURL
872 if (![URL isFileURL])
873 [NSException raise:NSInvalidArgumentException format:@"%@ is not a file URL", URL];
875 if (![readAccessURL isFileURL])
876 [NSException raise:NSInvalidArgumentException format:@"%@ is not a file URL", readAccessURL];
878 auto navigation = _page->loadFile([URL _web_originalDataAsWTFString], [readAccessURL _web_originalDataAsWTFString]);
882 return [wrapper(*navigation.leakRef()) autorelease];
885 - (WKNavigation *)loadHTMLString:(NSString *)string baseURL:(NSURL *)baseURL
887 NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
889 return [self loadData:data MIMEType:@"text/html" characterEncodingName:@"UTF-8" baseURL:baseURL];
892 - (WKNavigation *)loadData:(NSData *)data MIMEType:(NSString *)MIMEType characterEncodingName:(NSString *)characterEncodingName baseURL:(NSURL *)baseURL
894 auto navigation = _page->loadData(API::Data::createWithoutCopying(data).ptr(), MIMEType, characterEncodingName, baseURL.absoluteString);
898 return [wrapper(*navigation.leakRef()) autorelease];
901 - (WKNavigation *)goToBackForwardListItem:(WKBackForwardListItem *)item
903 auto navigation = _page->goToBackForwardItem(&item._item);
907 return [wrapper(*navigation.leakRef()) autorelease];
912 return _page->pageLoadState().title();
917 return [NSURL _web_URLWithWTFString:_page->pageLoadState().activeURL()];
922 return _page->pageLoadState().isLoading();
925 - (double)estimatedProgress
927 return _page->pageLoadState().estimatedProgress();
930 - (BOOL)hasOnlySecureContent
932 return _page->pageLoadState().hasOnlySecureContent();
935 - (SecTrustRef)serverTrust
937 #if HAVE(SEC_TRUST_SERIALIZATION)
938 auto certificateInfo = _page->pageLoadState().certificateInfo();
939 if (!certificateInfo)
942 return certificateInfo->certificateInfo().trust();
950 return _page->pageLoadState().canGoBack();
955 return _page->pageLoadState().canGoForward();
958 - (WKNavigation *)goBack
960 auto navigation = _page->goBack();
964 return [wrapper(*navigation.leakRef()) autorelease];
967 - (WKNavigation *)goForward
969 auto navigation = _page->goForward();
973 return [wrapper(*navigation.leakRef()) autorelease];
976 - (WKNavigation *)reload
978 OptionSet<WebCore::ReloadOption> reloadOptions;
979 if (linkedOnOrAfter(WebKit::SDKVersion::FirstWithExpiredOnlyReloadBehavior))
980 reloadOptions |= WebCore::ReloadOption::ExpiredOnly;
982 auto navigation = _page->reload(reloadOptions);
986 return [wrapper(*navigation.leakRef()) autorelease];
989 - (WKNavigation *)reloadFromOrigin
991 auto navigation = _page->reload(WebCore::ReloadOption::FromOrigin);
995 return [wrapper(*navigation.leakRef()) autorelease];
1000 _page->stopLoading();
1003 static WKErrorCode callbackErrorCode(WebKit::CallbackBase::Error error)
1006 case WebKit::CallbackBase::Error::None:
1007 ASSERT_NOT_REACHED();
1008 return WKErrorUnknown;
1010 case WebKit::CallbackBase::Error::Unknown:
1011 return WKErrorUnknown;
1013 case WebKit::CallbackBase::Error::ProcessExited:
1014 return WKErrorWebContentProcessTerminated;
1016 case WebKit::CallbackBase::Error::OwnerWasInvalidated:
1017 return WKErrorWebViewInvalidated;
1021 - (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^)(id, NSError *))completionHandler
1023 [self _evaluateJavaScript:javaScriptString forceUserGesture:YES completionHandler:completionHandler];
1026 - (void)_evaluateJavaScript:(NSString *)javaScriptString forceUserGesture:(BOOL)forceUserGesture completionHandler:(void (^)(id, NSError *))completionHandler
1028 auto handler = adoptNS([completionHandler copy]);
1030 _page->runJavaScriptInMainFrame(javaScriptString, forceUserGesture, [handler](API::SerializedScriptValue* serializedScriptValue, bool hadException, const WebCore::ExceptionDetails& details, WebKit::ScriptValueCallback::Error errorCode) {
1034 if (errorCode != WebKit::ScriptValueCallback::Error::None) {
1035 auto error = createNSError(callbackErrorCode(errorCode));
1036 if (errorCode == WebKit::ScriptValueCallback::Error::OwnerWasInvalidated) {
1037 // The OwnerWasInvalidated callback is synchronous. We don't want to call the block from within it
1038 // because that can trigger re-entrancy bugs in WebKit.
1039 // FIXME: It would be even better if GenericCallback did this for us.
1040 dispatch_async(dispatch_get_main_queue(), [handler, error] {
1041 auto rawHandler = (void (^)(id, NSError *))handler.get();
1042 rawHandler(nil, error.get());
1047 auto rawHandler = (void (^)(id, NSError *))handler.get();
1048 rawHandler(nil, error.get());
1052 auto rawHandler = (void (^)(id, NSError *))handler.get();
1054 ASSERT(!serializedScriptValue);
1056 RetainPtr<NSMutableDictionary> userInfo = adoptNS([[NSMutableDictionary alloc] init]);
1058 [userInfo setObject:localizedDescriptionForErrorCode(WKErrorJavaScriptExceptionOccurred) forKey:NSLocalizedDescriptionKey];
1059 [userInfo setObject:static_cast<NSString *>(details.message) forKey:_WKJavaScriptExceptionMessageErrorKey];
1060 [userInfo setObject:@(details.lineNumber) forKey:_WKJavaScriptExceptionLineNumberErrorKey];
1061 [userInfo setObject:@(details.columnNumber) forKey:_WKJavaScriptExceptionColumnNumberErrorKey];
1063 if (!details.sourceURL.isEmpty())
1064 [userInfo setObject:[NSURL _web_URLWithWTFString:details.sourceURL] forKey:_WKJavaScriptExceptionSourceURLErrorKey];
1066 rawHandler(nil, adoptNS([[NSError alloc] initWithDomain:WKErrorDomain code:WKErrorJavaScriptExceptionOccurred userInfo:userInfo.get()]).get());
1070 if (!serializedScriptValue) {
1071 rawHandler(nil, createNSError(WKErrorJavaScriptResultTypeIsUnsupported).get());
1075 id body = API::SerializedScriptValue::deserialize(serializedScriptValue->internalRepresentation(), 0);
1076 rawHandler(body, nil);
1081 - (void)takeSnapshotWithConfiguration:(WKSnapshotConfiguration *)snapshotConfiguration completionHandler:(void(^)(NSImage *, NSError *))completionHandler
1083 CGRect rectInViewCoordinates = snapshotConfiguration && !CGRectIsNull(snapshotConfiguration.rect) ? snapshotConfiguration.rect : self.bounds;
1084 CGFloat snapshotWidth;
1085 if (snapshotConfiguration)
1086 snapshotWidth = snapshotConfiguration.snapshotWidth.doubleValue ?: rectInViewCoordinates.size.width;
1088 snapshotWidth = self.bounds.size.width;
1090 auto handler = makeBlockPtr(completionHandler);
1091 CGFloat imageScale = snapshotWidth / rectInViewCoordinates.size.width;
1092 CGFloat imageHeight = imageScale * rectInViewCoordinates.size.height;
1094 // Need to scale by device scale factor or the image will be distorted.
1095 CGFloat deviceScale = _page->deviceScaleFactor();
1096 WebCore::IntSize bitmapSize(snapshotWidth, imageHeight);
1097 bitmapSize.scale(deviceScale, deviceScale);
1099 // Software snapshot will not capture elements rendered with hardware acceleration (WebGL, video, etc).
1100 _page->takeSnapshot(WebCore::enclosingIntRect(rectInViewCoordinates), bitmapSize, WebKit::SnapshotOptionsInViewCoordinates, [handler, snapshotWidth, imageHeight](const WebKit::ShareableBitmap::Handle& imageHandle, WebKit::CallbackBase::Error errorCode) {
1101 if (errorCode != WebKit::ScriptValueCallback::Error::None) {
1102 auto error = createNSError(callbackErrorCode(errorCode));
1103 handler(nil, error.get());
1107 RefPtr<WebKit::ShareableBitmap> bitmap = WebKit::ShareableBitmap::create(imageHandle, WebKit::SharedMemory::Protection::ReadOnly);
1108 RetainPtr<CGImageRef> cgImage = bitmap ? bitmap->makeCGImage() : nullptr;
1109 RetainPtr<NSImage> nsImage = adoptNS([[NSImage alloc] initWithCGImage:cgImage.get() size:NSMakeSize(snapshotWidth, imageHeight)]);
1110 handler(nsImage.get(), nil);
1115 - (void)takeSnapshotWithConfiguration:(WKSnapshotConfiguration *)snapshotConfiguration completionHandler:(void(^)(UIImage *, NSError *))completionHandler
1117 CGRect rectInViewCoordinates = snapshotConfiguration && !CGRectIsNull(snapshotConfiguration.rect) ? snapshotConfiguration.rect : self.bounds;
1118 CGFloat snapshotWidth;
1119 if (snapshotConfiguration)
1120 snapshotWidth = snapshotConfiguration.snapshotWidth.doubleValue ?: rectInViewCoordinates.size.width;
1122 snapshotWidth = self.bounds.size.width;
1124 auto handler = makeBlockPtr(completionHandler);
1125 CGFloat deviceScale = _page->deviceScaleFactor();
1127 [self _snapshotRect:rectInViewCoordinates intoImageOfWidth:(snapshotWidth * deviceScale) completionHandler:^(CGImageRef snapshotImage) {
1128 RetainPtr<NSError> error;
1129 RetainPtr<UIImage> uiImage;
1132 error = createNSError(WKErrorUnknown);
1134 uiImage = adoptNS([[UIImage alloc] initWithCGImage:snapshotImage scale:deviceScale orientation:UIImageOrientationUp]);
1136 handler(uiImage.get(), error.get());
1141 - (NSString *)customUserAgent
1143 return _page->customUserAgent();
1146 - (void)setCustomUserAgent:(NSString *)customUserAgent
1148 _page->setCustomUserAgent(customUserAgent);
1151 - (WKPageRef)_pageForTesting
1153 return toAPI(_page.get());
1156 - (WebKit::WebPageProxy *)_page
1161 - (BOOL)allowsLinkPreview
1164 return _impl->allowsLinkPreview();
1166 return _allowsLinkPreview;
1170 - (void)setAllowsLinkPreview:(BOOL)allowsLinkPreview
1173 _impl->setAllowsLinkPreview(allowsLinkPreview);
1176 if (_allowsLinkPreview == allowsLinkPreview)
1179 _allowsLinkPreview = allowsLinkPreview;
1181 #if HAVE(LINK_PREVIEW)
1182 if (_allowsLinkPreview)
1183 [_contentView _registerPreview];
1185 [_contentView _unregisterPreview];
1186 #endif // HAVE(LINK_PREVIEW)
1187 #endif // PLATFORM(IOS)
1190 - (CGSize)_viewportSizeForCSSViewportUnits
1192 return _page->viewportSizeForCSSViewportUnits();
1195 - (void)_setViewportSizeForCSSViewportUnits:(CGSize)viewportSize
1197 auto viewportSizeForViewportUnits = WebCore::IntSize(viewportSize);
1198 if (viewportSizeForViewportUnits.isEmpty())
1199 [NSException raise:NSInvalidArgumentException format:@"Viewport size should not be empty"];
1201 _page->setViewportSizeForCSSViewportUnits(viewportSizeForViewportUnits);
1204 static NSTextAlignment nsTextAlignment(WebKit::TextAlignment alignment)
1206 switch (alignment) {
1207 case WebKit::NoAlignment:
1208 return NSTextAlignmentNatural;
1209 case WebKit::LeftAlignment:
1210 return NSTextAlignmentLeft;
1211 case WebKit::RightAlignment:
1212 return NSTextAlignmentRight;
1213 case WebKit::CenterAlignment:
1214 return NSTextAlignmentCenter;
1215 case WebKit::JustifiedAlignment:
1216 return NSTextAlignmentJustified;
1218 ASSERT_NOT_REACHED();
1219 return NSTextAlignmentNatural;
1222 static NSDictionary *dictionaryRepresentationForEditorState(const WebKit::EditorState& state)
1224 if (state.isMissingPostLayoutData)
1225 return @{ @"post-layout-data" : @NO };
1227 auto& postLayoutData = state.postLayoutData();
1229 @"post-layout-data" : @YES,
1230 @"bold": postLayoutData.typingAttributes & WebKit::AttributeBold ? @YES : @NO,
1231 @"italic": postLayoutData.typingAttributes & WebKit::AttributeItalics ? @YES : @NO,
1232 @"underline": postLayoutData.typingAttributes & WebKit::AttributeUnderline ? @YES : @NO,
1233 @"text-alignment": @(nsTextAlignment(static_cast<WebKit::TextAlignment>(postLayoutData.textAlignment))),
1234 @"text-color": (NSString *)postLayoutData.textColor.cssText()
1238 - (void)_didChangeEditorState
1240 id <WKUIDelegatePrivate> uiDelegate = (id <WKUIDelegatePrivate>)self.UIDelegate;
1241 if ([uiDelegate respondsToSelector:@selector(_webView:editorStateDidChange:)])
1242 [uiDelegate _webView:self editorStateDidChange:dictionaryRepresentationForEditorState(_page->editorState())];
1245 #if ENABLE(ATTACHMENT_ELEMENT)
1247 - (void)_didInsertAttachment:(NSString *)identifier withSource:(NSString *)source
1249 id <WKUIDelegatePrivate> uiDelegate = (id <WKUIDelegatePrivate>)self.UIDelegate;
1250 if ([uiDelegate respondsToSelector:@selector(_webView:didInsertAttachment:withSource:)])
1251 [uiDelegate _webView:self didInsertAttachment:[wrapper(API::Attachment::create(identifier, *_page).leakRef()) autorelease] withSource:source];
1252 else if ([uiDelegate respondsToSelector:@selector(_webView:didInsertAttachment:)])
1253 [uiDelegate _webView:self didInsertAttachment:[wrapper(API::Attachment::create(identifier, *_page).leakRef()) autorelease]];
1256 - (void)_didRemoveAttachment:(NSString *)identifier
1258 id <WKUIDelegatePrivate> uiDelegate = (id <WKUIDelegatePrivate>)self.UIDelegate;
1259 if ([uiDelegate respondsToSelector:@selector(_webView:didRemoveAttachment:)])
1260 [uiDelegate _webView:self didRemoveAttachment:[wrapper(API::Attachment::create(identifier, *_page).leakRef()) autorelease]];
1263 #endif // ENABLE(ATTACHMENT_ELEMENT)
1265 #pragma mark iOS-specific methods
1269 - (_WKDragInteractionPolicy)_dragInteractionPolicy
1271 return _dragInteractionPolicy;
1274 - (void)_setDragInteractionPolicy:(_WKDragInteractionPolicy)policy
1276 if (_dragInteractionPolicy == policy)
1279 _dragInteractionPolicy = policy;
1280 #if ENABLE(DRAG_SUPPORT)
1281 [_contentView _didChangeDragInteractionPolicy];
1285 - (void)_populateArchivedSubviews:(NSMutableSet *)encodedViews
1287 [super _populateArchivedSubviews:encodedViews];
1290 [encodedViews removeObject:_scrollView.get()];
1291 if (_customContentFixedOverlayView)
1292 [encodedViews removeObject:_customContentFixedOverlayView.get()];
1295 - (BOOL)_isBackground
1297 if ([self _isDisplayingPDF])
1298 return [(WKPDFView *)_customContentView isBackground];
1300 return [_contentView isBackground];
1303 - (void)setFrame:(CGRect)frame
1305 CGRect oldFrame = self.frame;
1306 [super setFrame:frame];
1308 if (!CGSizeEqualToSize(oldFrame.size, frame.size))
1309 [self _frameOrBoundsChanged];
1312 - (void)setBounds:(CGRect)bounds
1314 CGRect oldBounds = self.bounds;
1315 [super setBounds:bounds];
1316 [_customContentFixedOverlayView setFrame:self.bounds];
1318 if (!CGSizeEqualToSize(oldBounds.size, bounds.size))
1319 [self _frameOrBoundsChanged];
1322 - (void)layoutSubviews
1324 [super layoutSubviews];
1325 [self _frameOrBoundsChanged];
1328 - (UIScrollView *)scrollView
1330 return _scrollView.get();
1333 - (WKBrowsingContextController *)browsingContextController
1335 return [_contentView browsingContextController];
1338 - (BOOL)becomeFirstResponder
1340 UIView *currentContentView = self._currentContentView;
1341 if (currentContentView == _contentView && [_contentView superview])
1342 return [_contentView becomeFirstResponderForWebView] || [super becomeFirstResponder];
1344 return [currentContentView becomeFirstResponder] || [super becomeFirstResponder];
1347 - (BOOL)canBecomeFirstResponder
1349 if (self._currentContentView == _contentView)
1350 return [_contentView canBecomeFirstResponderForWebView];
1355 - (BOOL)resignFirstResponder
1357 if ([_contentView isFirstResponder])
1358 return [_contentView resignFirstResponderForWebView];
1360 return [super resignFirstResponder];
1363 #define FORWARD_ACTION_TO_WKCONTENTVIEW(_action) \
1364 - (void)_action:(id)sender \
1366 if (self.usesStandardContentView) \
1367 [_contentView _action ## ForWebView:sender]; \
1370 FOR_EACH_WKCONTENTVIEW_ACTION(FORWARD_ACTION_TO_WKCONTENTVIEW)
1372 #undef FORWARD_ACTION_TO_WKCONTENTVIEW
1374 - (BOOL)canPerformAction:(SEL)action withSender:(id)sender
1376 #define FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW(_action) \
1377 if (action == @selector(_action:)) \
1378 return self.usesStandardContentView && [_contentView canPerformActionForWebView:action withSender:sender];
1380 FOR_EACH_WKCONTENTVIEW_ACTION(FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW)
1382 #undef FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW
1384 return [super canPerformAction:action withSender:sender];
1387 - (id)targetForAction:(SEL)action withSender:(id)sender
1389 #define FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW(_action) \
1390 if (action == @selector(_action:) && self.usesStandardContentView) \
1391 return [_contentView targetForActionForWebView:action withSender:sender];
1393 FOR_EACH_WKCONTENTVIEW_ACTION(FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW)
1395 #undef FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW
1397 return [super targetForAction:action withSender:sender];
1400 static inline CGFloat floorToDevicePixel(CGFloat input, float deviceScaleFactor)
1402 return CGFloor(input * deviceScaleFactor) / deviceScaleFactor;
1405 static inline bool pointsEqualInDevicePixels(CGPoint a, CGPoint b, float deviceScaleFactor)
1407 return fabs(a.x * deviceScaleFactor - b.x * deviceScaleFactor) < std::numeric_limits<float>::epsilon()
1408 && fabs(a.y * deviceScaleFactor - b.y * deviceScaleFactor) < std::numeric_limits<float>::epsilon();
1411 static CGSize roundScrollViewContentSize(const WebKit::WebPageProxy& page, CGSize contentSize)
1413 float deviceScaleFactor = page.deviceScaleFactor();
1414 return CGSizeMake(floorToDevicePixel(contentSize.width, deviceScaleFactor), floorToDevicePixel(contentSize.height, deviceScaleFactor));
1417 - (UIView *)_currentContentView
1419 return _customContentView ? _customContentView.get() : _contentView.get();
1422 - (WKWebViewContentProviderRegistry *)_contentProviderRegistry
1424 return [_configuration _contentProviderRegistry];
1427 - (WKSelectionGranularity)_selectionGranularity
1429 return [_configuration selectionGranularity];
1432 - (void)_setHasCustomContentView:(BOOL)pageHasCustomContentView loadedMIMEType:(const WTF::String&)mimeType
1434 if (pageHasCustomContentView) {
1435 [_customContentView removeFromSuperview];
1436 [_customContentFixedOverlayView removeFromSuperview];
1438 Class representationClass = [[_configuration _contentProviderRegistry] providerForMIMEType:mimeType];
1439 ASSERT(representationClass);
1440 _customContentView = adoptNS([[representationClass alloc] web_initWithFrame:self.bounds webView:self]);
1441 _customContentFixedOverlayView = adoptNS([[UIView alloc] initWithFrame:self.bounds]);
1442 [_customContentFixedOverlayView layer].name = @"CustomContentFixedOverlay";
1443 [_customContentFixedOverlayView setUserInteractionEnabled:NO];
1445 [[_contentView unscaledView] removeFromSuperview];
1446 [_contentView removeFromSuperview];
1447 [_scrollView addSubview:_customContentView.get()];
1448 [self addSubview:_customContentFixedOverlayView.get()];
1450 [_customContentView web_setMinimumSize:self.bounds.size];
1451 [_customContentView web_setFixedOverlayView:_customContentFixedOverlayView.get()];
1453 _scrollViewBackgroundColor = WebCore::Color();
1454 [_scrollView setContentOffset:[self _adjustedContentOffset:CGPointZero]];
1456 [self _setAvoidsUnsafeArea:NO];
1457 } else if (_customContentView) {
1458 [_customContentView removeFromSuperview];
1459 _customContentView = nullptr;
1461 [_customContentFixedOverlayView removeFromSuperview];
1462 _customContentFixedOverlayView = nullptr;
1464 [_scrollView addSubview:_contentView.get()];
1465 [_scrollView addSubview:[_contentView unscaledView]];
1466 [_scrollView setContentSize:roundScrollViewContentSize(*_page, [_contentView frame].size)];
1468 [_customContentFixedOverlayView setFrame:self.bounds];
1469 [self addSubview:_customContentFixedOverlayView.get()];
1472 if (self.isFirstResponder) {
1473 UIView *currentContentView = self._currentContentView;
1474 if (currentContentView == _contentView ? [_contentView canBecomeFirstResponderForWebView] : currentContentView.canBecomeFirstResponder)
1475 [currentContentView becomeFirstResponder];
1479 - (void)_didFinishLoadingDataForCustomContentProviderWithSuggestedFilename:(const String&)suggestedFilename data:(NSData *)data
1481 ASSERT(_customContentView);
1482 [_customContentView web_setContentProviderData:data suggestedFilename:suggestedFilename];
1484 // FIXME: It may make more sense for custom content providers to invoke this when they're ready,
1485 // because there's no guarantee that all custom content providers will lay out synchronously.
1486 _page->didLayoutForCustomContentProvider();
1489 - (void)_willInvokeUIScrollViewDelegateCallback
1491 _delayUpdateVisibleContentRects = YES;
1494 - (void)_didInvokeUIScrollViewDelegateCallback
1496 _delayUpdateVisibleContentRects = NO;
1497 if (_hadDelayedUpdateVisibleContentRects) {
1498 _hadDelayedUpdateVisibleContentRects = NO;
1499 [self _scheduleVisibleContentRectUpdate];
1503 static CGFloat contentZoomScale(WKWebView *webView)
1505 CGFloat scale = webView._currentContentView.layer.affineTransform.a;
1506 ASSERT(scale == [webView->_scrollView zoomScale]);
1510 static WebCore::Color baseScrollViewBackgroundColor(WKWebView *webView)
1512 if (webView->_customContentView)
1513 return [webView->_customContentView backgroundColor].CGColor;
1515 if (webView->_gestureController) {
1516 WebCore::Color color = webView->_gestureController->backgroundColorForCurrentSnapshot();
1517 if (color.isValid())
1521 return webView->_page->pageExtendedBackgroundColor();
1524 static WebCore::Color scrollViewBackgroundColor(WKWebView *webView)
1526 if (!webView.opaque)
1527 return WebCore::Color::transparent;
1529 WebCore::Color color = baseScrollViewBackgroundColor(webView);
1531 if (!color.isValid())
1532 color = WebCore::Color::white;
1534 CGFloat zoomScale = contentZoomScale(webView);
1535 CGFloat minimumZoomScale = [webView->_scrollView minimumZoomScale];
1536 if (zoomScale < minimumZoomScale) {
1538 CGFloat opacity = std::max<CGFloat>(1 - slope * (minimumZoomScale - zoomScale), 0);
1539 color = WebCore::colorWithOverrideAlpha(color.rgb(), opacity);
1545 - (void)_updateScrollViewBackground
1547 WebCore::Color color = scrollViewBackgroundColor(self);
1549 if (_scrollViewBackgroundColor == color)
1552 _scrollViewBackgroundColor = color;
1554 auto uiBackgroundColor = adoptNS([[UIColor alloc] initWithCGColor:cachedCGColor(color)]);
1555 [_scrollView setBackgroundColor:uiBackgroundColor.get()];
1557 // Update the indicator style based on the lightness/darkness of the background color.
1558 double hue, saturation, lightness;
1559 color.getHSL(hue, saturation, lightness);
1560 if (lightness <= .5 && color.isVisible())
1561 [_scrollView setIndicatorStyle:UIScrollViewIndicatorStyleWhite];
1563 [_scrollView setIndicatorStyle:UIScrollViewIndicatorStyleDefault];
1566 - (CGPoint)_adjustedContentOffset:(CGPoint)point
1568 CGPoint result = point;
1569 UIEdgeInsets contentInset = [self _computedContentInset];
1571 result.x -= contentInset.left;
1572 result.y -= contentInset.top;
1577 - (UIRectEdge)_effectiveObscuredInsetEdgesAffectedBySafeArea
1579 if (![self usesStandardContentView])
1580 return UIRectEdgeAll;
1581 return _obscuredInsetEdgesAffectedBySafeArea;
1584 - (UIEdgeInsets)_computedContentInset
1586 if (_haveSetObscuredInsets)
1587 return _obscuredInsets;
1589 UIEdgeInsets insets = [_scrollView contentInset];
1591 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000
1592 if (self._safeAreaShouldAffectObscuredInsets)
1593 insets = UIEdgeInsetsAdd(insets, self._scrollViewSystemContentInset, self._effectiveObscuredInsetEdgesAffectedBySafeArea);
1599 - (UIEdgeInsets)_computedUnobscuredSafeAreaInset
1601 if (_haveSetUnobscuredSafeAreaInsets)
1602 return _unobscuredSafeAreaInsets;
1604 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000
1605 if (!self._safeAreaShouldAffectObscuredInsets)
1606 return self.safeAreaInsets;
1609 return UIEdgeInsetsZero;
1612 - (void)_processDidExit
1614 [self _hidePasswordView];
1615 if (!_customContentView && _dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing) {
1616 NSUInteger indexOfResizeAnimationView = [[_scrollView subviews] indexOfObject:_resizeAnimationView.get()];
1617 [_scrollView insertSubview:_contentView.get() atIndex:indexOfResizeAnimationView];
1618 [_scrollView insertSubview:[_contentView unscaledView] atIndex:indexOfResizeAnimationView + 1];
1619 [_resizeAnimationView removeFromSuperview];
1620 _resizeAnimationView = nil;
1622 _resizeAnimationTransformAdjustments = CATransform3DIdentity;
1624 [_contentView setFrame:self.bounds];
1625 [_scrollView setBackgroundColor:[UIColor whiteColor]];
1626 [_scrollView setContentOffset:[self _adjustedContentOffset:CGPointZero]];
1627 [_scrollView setZoomScale:1];
1629 _viewportMetaTagWidth = WebCore::ViewportArguments::ValueAuto;
1630 _initialScaleFactor = 1;
1631 _hasCommittedLoadForMainFrame = NO;
1632 _needsResetViewStateAfterCommitLoadForMainFrame = NO;
1633 _dynamicViewportUpdateMode = DynamicViewportUpdateMode::NotResizing;
1634 [_contentView setHidden:NO];
1635 _scrollOffsetToRestore = std::nullopt;
1636 _unobscuredCenterToRestore = std::nullopt;
1637 _scrollViewBackgroundColor = WebCore::Color();
1638 _delayUpdateVisibleContentRects = NO;
1639 _hadDelayedUpdateVisibleContentRects = NO;
1640 _lastSentMinimumLayoutSize = std::nullopt;
1641 _lastSentMaximumUnobscuredSize = std::nullopt;
1642 _lastSentDeviceOrientation = std::nullopt;
1644 _frozenVisibleContentRect = std::nullopt;
1645 _frozenUnobscuredContentRect = std::nullopt;
1647 _firstPaintAfterCommitLoadTransactionID = 0;
1648 _firstTransactionIDAfterPageRestore = std::nullopt;
1649 _resizeAnimationTransformTransactionID = std::nullopt;
1651 _hasScheduledVisibleRectUpdate = NO;
1652 _commitDidRestoreScrollPosition = NO;
1654 _avoidsUnsafeArea = YES;
1657 - (void)_didCommitLoadForMainFrame
1659 _firstPaintAfterCommitLoadTransactionID = downcast<WebKit::RemoteLayerTreeDrawingAreaProxy>(*_page->drawingArea()).nextLayerTreeTransactionID();
1661 _hasCommittedLoadForMainFrame = YES;
1662 _needsResetViewStateAfterCommitLoadForMainFrame = YES;
1664 [_scrollView _stopScrollingAndZoomingAnimations];
1667 static CGPoint contentOffsetBoundedInValidRange(UIScrollView *scrollView, CGPoint contentOffset)
1669 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000
1670 UIEdgeInsets contentInsets = scrollView.adjustedContentInset;
1672 UIEdgeInsets contentInsets = scrollView.contentInset;
1675 CGSize contentSize = scrollView.contentSize;
1676 CGSize scrollViewSize = scrollView.bounds.size;
1678 CGPoint minimumContentOffset = CGPointMake(-contentInsets.left, -contentInsets.top);
1679 CGPoint maximumContentOffset = CGPointMake(std::max(minimumContentOffset.x, contentSize.width + contentInsets.right - scrollViewSize.width), std::max(minimumContentOffset.y, contentSize.height + contentInsets.bottom - scrollViewSize.height));
1681 return CGPointMake(std::max(std::min(contentOffset.x, maximumContentOffset.x), minimumContentOffset.x), std::max(std::min(contentOffset.y, maximumContentOffset.y), minimumContentOffset.y));
1684 static void changeContentOffsetBoundedInValidRange(UIScrollView *scrollView, WebCore::FloatPoint contentOffset)
1686 scrollView.contentOffset = contentOffsetBoundedInValidRange(scrollView, contentOffset);
1689 - (WebCore::FloatRect)visibleRectInViewCoordinates
1691 WebCore::FloatRect bounds = self.bounds;
1692 bounds.moveBy([_scrollView contentOffset]);
1693 WebCore::FloatRect contentViewBounds = [_contentView bounds];
1694 bounds.intersect(contentViewBounds);
1698 // WebCore stores the page scale factor as float instead of double. When we get a scale from WebCore,
1699 // we need to ignore differences that are within a small rounding error on floats.
1700 static inline bool areEssentiallyEqualAsFloat(float a, float b)
1702 return WTF::areEssentiallyEqual(a, b);
1705 - (void)_didCommitLayerTree:(const WebKit::RemoteLayerTreeTransaction&)layerTreeTransaction
1707 if (![self usesStandardContentView])
1710 LOG_WITH_STREAM(VisibleRects, stream << "-[WKWebView " << _page->pageID() << " _didCommitLayerTree:] transactionID " << layerTreeTransaction.transactionID() << " _dynamicViewportUpdateMode " << (int)_dynamicViewportUpdateMode);
1712 bool needUpdateVisibleContentRects = _page->updateLayoutViewportParameters(layerTreeTransaction);
1714 if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing) {
1715 if (_resizeAnimationTransformTransactionID && layerTreeTransaction.transactionID() >= _resizeAnimationTransformTransactionID.value()) {
1716 _resizeAnimationTransformTransactionID = std::nullopt;
1717 [_resizeAnimationView layer].sublayerTransform = _resizeAnimationTransformAdjustments;
1718 if (_dynamicViewportUpdateMode == DynamicViewportUpdateMode::ResizingWithDocumentHidden) {
1719 [_contentView setHidden:NO];
1720 [self _endAnimatedResize];
1726 if (_activeAnimatedResizeCount)
1727 RELEASE_LOG_IF_ALLOWED("%p -[WKWebView _didCommitLayerTree:] - %d animated resizes in flight", self, _activeAnimatedResizeCount);
1729 CGSize newContentSize = roundScrollViewContentSize(*_page, [_contentView frame].size);
1730 [_scrollView _setContentSizePreservingContentOffsetDuringRubberband:newContentSize];
1732 [_scrollView setMinimumZoomScale:layerTreeTransaction.minimumScaleFactor()];
1733 [_scrollView setMaximumZoomScale:layerTreeTransaction.maximumScaleFactor()];
1734 [_scrollView setZoomEnabled:layerTreeTransaction.allowsUserScaling()];
1735 if (!layerTreeTransaction.scaleWasSetByUIProcess() && ![_scrollView isZooming] && ![_scrollView isZoomBouncing] && ![_scrollView _isAnimatingZoom] && [_scrollView zoomScale] != layerTreeTransaction.pageScaleFactor()) {
1736 LOG_WITH_STREAM(VisibleRects, stream << " updating scroll view with pageScaleFactor " << layerTreeTransaction.pageScaleFactor());
1737 [_scrollView setZoomScale:layerTreeTransaction.pageScaleFactor()];
1740 _viewportMetaTagWidth = layerTreeTransaction.viewportMetaTagWidth();
1741 _viewportMetaTagWidthWasExplicit = layerTreeTransaction.viewportMetaTagWidthWasExplicit();
1742 _viewportMetaTagCameFromImageDocument = layerTreeTransaction.viewportMetaTagCameFromImageDocument();
1743 _initialScaleFactor = layerTreeTransaction.initialScaleFactor();
1744 if (_page->inStableState() && layerTreeTransaction.isInStableState() && [_stableStatePresentationUpdateCallbacks count]) {
1745 for (dispatch_block_t action in _stableStatePresentationUpdateCallbacks.get())
1748 [_stableStatePresentationUpdateCallbacks removeAllObjects];
1749 _stableStatePresentationUpdateCallbacks = nil;
1752 if (![_contentView _mayDisableDoubleTapGesturesDuringSingleTap])
1753 [_contentView _setDoubleTapGesturesEnabled:self._allowsDoubleTapGestures];
1755 [self _updateScrollViewBackground];
1756 [self _setAvoidsUnsafeArea:layerTreeTransaction.avoidsUnsafeArea()];
1758 if (_gestureController)
1759 _gestureController->setRenderTreeSize(layerTreeTransaction.renderTreeSize());
1761 if (_needsResetViewStateAfterCommitLoadForMainFrame && layerTreeTransaction.transactionID() >= _firstPaintAfterCommitLoadTransactionID) {
1762 _needsResetViewStateAfterCommitLoadForMainFrame = NO;
1763 [_scrollView setContentOffset:[self _adjustedContentOffset:CGPointZero]];
1764 if (_observedRenderingProgressEvents & _WKRenderingProgressEventFirstPaint)
1765 _navigationState->didFirstPaint();
1767 needUpdateVisibleContentRects = true;
1770 if (_firstTransactionIDAfterPageRestore && layerTreeTransaction.transactionID() >= _firstTransactionIDAfterPageRestore.value()) {
1771 if (_scrollOffsetToRestore) {
1772 WebCore::FloatPoint scaledScrollOffset = _scrollOffsetToRestore.value();
1773 _scrollOffsetToRestore = std::nullopt;
1775 if (areEssentiallyEqualAsFloat(contentZoomScale(self), _scaleToRestore)) {
1776 scaledScrollOffset.scale(_scaleToRestore);
1777 WebCore::FloatPoint contentOffsetInScrollViewCoordinates = scaledScrollOffset - WebCore::FloatSize(_obscuredInsetsWhenSaved.left(), _obscuredInsetsWhenSaved.top());
1779 changeContentOffsetBoundedInValidRange(_scrollView.get(), contentOffsetInScrollViewCoordinates);
1780 _commitDidRestoreScrollPosition = YES;
1783 needUpdateVisibleContentRects = true;
1786 if (_unobscuredCenterToRestore) {
1787 WebCore::FloatPoint unobscuredCenterToRestore = _unobscuredCenterToRestore.value();
1788 _unobscuredCenterToRestore = std::nullopt;
1790 if (areEssentiallyEqualAsFloat(contentZoomScale(self), _scaleToRestore)) {
1791 CGRect unobscuredRect = UIEdgeInsetsInsetRect(self.bounds, _obscuredInsets);
1792 WebCore::FloatSize unobscuredContentSizeAtNewScale = WebCore::FloatSize(unobscuredRect.size) / _scaleToRestore;
1793 WebCore::FloatPoint topLeftInDocumentCoordinates = unobscuredCenterToRestore - unobscuredContentSizeAtNewScale / 2;
1795 topLeftInDocumentCoordinates.scale(_scaleToRestore);
1796 topLeftInDocumentCoordinates.moveBy(WebCore::FloatPoint(-_obscuredInsets.left, -_obscuredInsets.top));
1798 changeContentOffsetBoundedInValidRange(_scrollView.get(), topLeftInDocumentCoordinates);
1801 needUpdateVisibleContentRects = true;
1804 if (_gestureController)
1805 _gestureController->didRestoreScrollPosition();
1807 _firstTransactionIDAfterPageRestore = std::nullopt;
1810 if (needUpdateVisibleContentRects)
1811 [self _scheduleVisibleContentRectUpdate];
1813 if (WebKit::RemoteLayerTreeScrollingPerformanceData* scrollPerfData = _page->scrollingPerformanceData())
1814 scrollPerfData->didCommitLayerTree([self visibleRectInViewCoordinates]);
1817 - (void)_layerTreeCommitComplete
1819 _commitDidRestoreScrollPosition = NO;
1822 - (void)_dynamicViewportUpdateChangedTargetToScale:(double)newScale position:(CGPoint)newScrollPosition nextValidLayerTreeTransactionID:(uint64_t)nextValidLayerTreeTransactionID
1824 LOG_WITH_STREAM(VisibleRects, stream << "-[WKWebView " << _page->pageID() << " _dynamicViewportUpdateChangedTargetToScale:] " << newScale << " _dynamicViewportUpdateMode " << (int)_dynamicViewportUpdateMode);
1826 if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing) {
1827 CGFloat animatingScaleTarget = [[_resizeAnimationView layer] transform].m11;
1828 double currentTargetScale = animatingScaleTarget * [[_contentView layer] transform].m11;
1829 double scale = newScale / currentTargetScale;
1830 _resizeAnimationTransformAdjustments = CATransform3DMakeScale(scale, scale, 1);
1832 CGPoint newContentOffset = [self _adjustedContentOffset:CGPointMake(newScrollPosition.x * newScale, newScrollPosition.y * newScale)];
1833 CGPoint currentContentOffset = [_scrollView contentOffset];
1835 _resizeAnimationTransformAdjustments.m41 = (currentContentOffset.x - newContentOffset.x) / animatingScaleTarget;
1836 _resizeAnimationTransformAdjustments.m42 = (currentContentOffset.y - newContentOffset.y) / animatingScaleTarget;
1837 _resizeAnimationTransformTransactionID = nextValidLayerTreeTransactionID;
1841 - (void)_couldNotRestorePageState
1843 // The gestureController may be waiting for the scroll position to be restored
1844 // in order to remove the swipe snapshot. Since the scroll position could not be
1845 // restored, tell the gestureController it was restored so that it no longer waits
1847 if (_gestureController)
1848 _gestureController->didRestoreScrollPosition();
1851 - (void)_restorePageScrollPosition:(std::optional<WebCore::FloatPoint>)scrollPosition scrollOrigin:(WebCore::FloatPoint)scrollOrigin previousObscuredInset:(WebCore::FloatBoxExtent)obscuredInsets scale:(double)scale
1853 if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing)
1856 if (![self usesStandardContentView])
1859 _firstTransactionIDAfterPageRestore = downcast<WebKit::RemoteLayerTreeDrawingAreaProxy>(*_page->drawingArea()).nextLayerTreeTransactionID();
1861 _scrollOffsetToRestore = WebCore::ScrollableArea::scrollOffsetFromPosition(WebCore::FloatPoint(scrollPosition.value()), WebCore::toFloatSize(scrollOrigin));
1863 _scrollOffsetToRestore = std::nullopt;
1865 _obscuredInsetsWhenSaved = obscuredInsets;
1866 _scaleToRestore = scale;
1869 - (void)_restorePageStateToUnobscuredCenter:(std::optional<WebCore::FloatPoint>)center scale:(double)scale
1871 if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing)
1874 if (![self usesStandardContentView])
1877 _firstTransactionIDAfterPageRestore = downcast<WebKit::RemoteLayerTreeDrawingAreaProxy>(*_page->drawingArea()).nextLayerTreeTransactionID();
1878 _unobscuredCenterToRestore = center.value();
1880 _scaleToRestore = scale;
1883 - (RefPtr<WebKit::ViewSnapshot>)_takeViewSnapshot
1885 float deviceScale = WebCore::screenScaleFactor();
1886 WebCore::FloatSize snapshotSize(self.bounds.size);
1887 snapshotSize.scale(deviceScale);
1889 CATransform3D transform = CATransform3DMakeScale(deviceScale, deviceScale, 1);
1892 WebCore::IOSurface::Format snapshotFormat = WebCore::screenSupportsExtendedColor() ? WebCore::IOSurface::Format::RGB10 : WebCore::IOSurface::Format::RGBA;
1893 auto surface = WebCore::IOSurface::create(WebCore::expandedIntSize(snapshotSize), WebCore::sRGBColorSpaceRef(), snapshotFormat);
1896 CARenderServerRenderLayerWithTransform(MACH_PORT_NULL, self.layer.context.contextId, reinterpret_cast<uint64_t>(self.layer), surface->surface(), 0, 0, &transform);
1898 #if HAVE(IOSURFACE_ACCELERATOR)
1899 WebCore::IOSurface::Format compressedFormat = WebCore::IOSurface::Format::YUV422;
1900 if (WebCore::IOSurface::allowConversionFromFormatToFormat(snapshotFormat, compressedFormat)) {
1901 RefPtr<WebKit::ViewSnapshot> viewSnapshot = WebKit::ViewSnapshot::create(nullptr);
1902 WebCore::IOSurface::convertToFormat(WTFMove(surface), WebCore::IOSurface::Format::YUV422, [viewSnapshot](std::unique_ptr<WebCore::IOSurface> convertedSurface) {
1903 if (convertedSurface)
1904 viewSnapshot->setSurface(WTFMove(convertedSurface));
1907 return viewSnapshot;
1909 #endif // HAVE(IOSURFACE_ACCELERATOR)
1911 return WebKit::ViewSnapshot::create(WTFMove(surface));
1912 #else // HAVE(IOSURFACE)
1913 uint32_t slotID = [WebKit::ViewSnapshotStore::snapshottingContext() createImageSlot:snapshotSize hasAlpha:YES];
1918 CARenderServerCaptureLayerWithTransform(MACH_PORT_NULL, self.layer.context.contextId, (uint64_t)self.layer, slotID, 0, 0, &transform);
1919 WebCore::IntSize imageSize = WebCore::expandedIntSize(WebCore::FloatSize(snapshotSize));
1920 return WebKit::ViewSnapshot::create(slotID, imageSize, (imageSize.area() * 4).unsafeGet());
1921 #endif // HAVE(IOSURFACE)
1924 - (void)_zoomToPoint:(WebCore::FloatPoint)point atScale:(double)scale animated:(BOOL)animated
1926 CFTimeInterval duration = 0;
1927 CGFloat zoomScale = contentZoomScale(self);
1930 const double maximumZoomDuration = 0.4;
1931 const double minimumZoomDuration = 0.1;
1932 const double zoomDurationFactor = 0.3;
1934 duration = std::min(fabs(log(zoomScale) - log(scale)) * zoomDurationFactor + minimumZoomDuration, maximumZoomDuration);
1937 if (scale != zoomScale)
1938 _page->willStartUserTriggeredZooming();
1940 LOG_WITH_STREAM(VisibleRects, stream << "_zoomToPoint:" << point << " scale: " << scale << " duration:" << duration);
1942 [_scrollView _zoomToCenter:point scale:scale duration:duration];
1945 - (void)_zoomToRect:(WebCore::FloatRect)targetRect atScale:(double)scale origin:(WebCore::FloatPoint)origin animated:(BOOL)animated
1947 // FIXME: Some of this could be shared with _scrollToRect.
1948 const double visibleRectScaleChange = contentZoomScale(self) / scale;
1949 const WebCore::FloatRect visibleRect([self convertRect:self.bounds toView:self._currentContentView]);
1950 const WebCore::FloatRect unobscuredRect([self _contentRectForUserInteraction]);
1952 const WebCore::FloatSize topLeftObscuredInsetAfterZoom((unobscuredRect.minXMinYCorner() - visibleRect.minXMinYCorner()) * visibleRectScaleChange);
1953 const WebCore::FloatSize bottomRightObscuredInsetAfterZoom((visibleRect.maxXMaxYCorner() - unobscuredRect.maxXMaxYCorner()) * visibleRectScaleChange);
1955 const WebCore::FloatSize unobscuredRectSizeAfterZoom(unobscuredRect.size() * visibleRectScaleChange);
1957 // Center to the target rect.
1958 WebCore::FloatPoint unobscuredRectLocationAfterZoom = targetRect.location() - (unobscuredRectSizeAfterZoom - targetRect.size()) * 0.5;
1960 // Center to the tap point instead in case the target rect won't fit in a direction.
1961 if (targetRect.width() > unobscuredRectSizeAfterZoom.width())
1962 unobscuredRectLocationAfterZoom.setX(origin.x() - unobscuredRectSizeAfterZoom.width() / 2);
1963 if (targetRect.height() > unobscuredRectSizeAfterZoom.height())
1964 unobscuredRectLocationAfterZoom.setY(origin.y() - unobscuredRectSizeAfterZoom.height() / 2);
1966 // We have computed where we want the unobscured rect to be. Now adjust for the obscuring insets.
1967 WebCore::FloatRect visibleRectAfterZoom(unobscuredRectLocationAfterZoom, unobscuredRectSizeAfterZoom);
1968 visibleRectAfterZoom.move(-topLeftObscuredInsetAfterZoom);
1969 visibleRectAfterZoom.expand(topLeftObscuredInsetAfterZoom + bottomRightObscuredInsetAfterZoom);
1971 [self _zoomToPoint:visibleRectAfterZoom.center() atScale:scale animated:animated];
1974 static WebCore::FloatPoint constrainContentOffset(WebCore::FloatPoint contentOffset, WebCore::FloatSize contentSize, WebCore::FloatSize unobscuredContentSize)
1976 WebCore::FloatSize maximumContentOffset = contentSize - unobscuredContentSize;
1977 return contentOffset.constrainedBetween(WebCore::FloatPoint(), WebCore::FloatPoint(maximumContentOffset));
1980 - (void)_scrollToContentScrollPosition:(WebCore::FloatPoint)scrollPosition scrollOrigin:(WebCore::IntPoint)scrollOrigin
1982 if (_commitDidRestoreScrollPosition || _dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing)
1985 WebCore::FloatPoint contentOffset = WebCore::ScrollableArea::scrollOffsetFromPosition(scrollPosition, toFloatSize(scrollOrigin));
1987 WebCore::FloatPoint scaledOffset = contentOffset;
1988 CGFloat zoomScale = contentZoomScale(self);
1989 scaledOffset.scale(zoomScale);
1991 CGPoint contentOffsetInScrollViewCoordinates = [self _adjustedContentOffset:scaledOffset];
1992 contentOffsetInScrollViewCoordinates = contentOffsetBoundedInValidRange(_scrollView.get(), contentOffsetInScrollViewCoordinates);
1994 [_scrollView _stopScrollingAndZoomingAnimations];
1996 if (!CGPointEqualToPoint(contentOffsetInScrollViewCoordinates, [_scrollView contentOffset]))
1997 [_scrollView setContentOffset:contentOffsetInScrollViewCoordinates];
1999 // If we haven't changed anything, there would not be any VisibleContentRect update sent to the content.
2000 // The WebProcess would keep the invalid contentOffset as its scroll position.
2001 // To synchronize the WebProcess with what is on screen, we send the VisibleContentRect again.
2002 _page->resendLastVisibleContentRects();
2006 - (BOOL)_scrollToRect:(WebCore::FloatRect)targetRect origin:(WebCore::FloatPoint)origin minimumScrollDistance:(float)minimumScrollDistance
2008 WebCore::FloatRect unobscuredContentRect([self _contentRectForUserInteraction]);
2009 WebCore::FloatPoint unobscuredContentOffset = unobscuredContentRect.location();
2010 WebCore::FloatSize contentSize([self._currentContentView bounds].size);
2012 // Center the target rect in the scroll view.
2013 // If the target doesn't fit in the scroll view, center on the gesture location instead.
2014 WebCore::FloatPoint newUnobscuredContentOffset;
2015 if (targetRect.width() <= unobscuredContentRect.width())
2016 newUnobscuredContentOffset.setX(targetRect.x() - (unobscuredContentRect.width() - targetRect.width()) / 2);
2018 newUnobscuredContentOffset.setX(origin.x() - unobscuredContentRect.width() / 2);
2019 if (targetRect.height() <= unobscuredContentRect.height())
2020 newUnobscuredContentOffset.setY(targetRect.y() - (unobscuredContentRect.height() - targetRect.height()) / 2);
2022 newUnobscuredContentOffset.setY(origin.y() - unobscuredContentRect.height() / 2);
2023 newUnobscuredContentOffset = constrainContentOffset(newUnobscuredContentOffset, contentSize, unobscuredContentRect.size());
2025 if (unobscuredContentOffset == newUnobscuredContentOffset) {
2026 if (targetRect.width() > unobscuredContentRect.width())
2027 newUnobscuredContentOffset.setX(origin.x() - unobscuredContentRect.width() / 2);
2028 if (targetRect.height() > unobscuredContentRect.height())
2029 newUnobscuredContentOffset.setY(origin.y() - unobscuredContentRect.height() / 2);
2030 newUnobscuredContentOffset = constrainContentOffset(newUnobscuredContentOffset, contentSize, unobscuredContentRect.size());
2033 WebCore::FloatSize scrollViewOffsetDelta = newUnobscuredContentOffset - unobscuredContentOffset;
2034 scrollViewOffsetDelta.scale(contentZoomScale(self));
2036 float scrollDistance = scrollViewOffsetDelta.diagonalLength();
2037 if (scrollDistance < minimumScrollDistance)
2040 [_contentView willStartZoomOrScroll];
2042 LOG_WITH_STREAM(VisibleRects, stream << "_scrollToRect: scrolling to " << [_scrollView contentOffset] + scrollViewOffsetDelta);
2044 [_scrollView setContentOffset:([_scrollView contentOffset] + scrollViewOffsetDelta) animated:YES];
2048 - (void)_scrollByContentOffset:(WebCore::FloatPoint)contentOffsetDelta
2050 WebCore::FloatPoint scaledOffsetDelta = contentOffsetDelta;
2051 CGFloat zoomScale = contentZoomScale(self);
2052 scaledOffsetDelta.scale(zoomScale);
2054 CGPoint currentOffset = [_scrollView _isAnimatingScroll] ? [_scrollView _animatedTargetOffset] : [_scrollView contentOffset];
2055 CGPoint boundedOffset = contentOffsetBoundedInValidRange(_scrollView.get(), currentOffset + scaledOffsetDelta);
2057 if (CGPointEqualToPoint(boundedOffset, currentOffset))
2059 [_contentView willStartZoomOrScroll];
2061 LOG_WITH_STREAM(VisibleRects, stream << "_scrollByContentOffset: scrolling to " << WebCore::FloatPoint(boundedOffset));
2063 [_scrollView setContentOffset:boundedOffset animated:YES];
2066 - (void)_zoomOutWithOrigin:(WebCore::FloatPoint)origin animated:(BOOL)animated
2068 [self _zoomToPoint:origin atScale:[_scrollView minimumZoomScale] animated:animated];
2071 - (void)_zoomToInitialScaleWithOrigin:(WebCore::FloatPoint)origin animated:(BOOL)animated
2073 ASSERT(_initialScaleFactor > 0);
2074 [self _zoomToPoint:origin atScale:_initialScaleFactor animated:animated];
2077 // focusedElementRect and selectionRect are both in document coordinates.
2078 - (void)_zoomToFocusRect:(WebCore::FloatRect)focusedElementRectInDocumentCoordinates selectionRect:(WebCore::FloatRect)selectionRectInDocumentCoordinates insideFixed:(BOOL)insideFixed
2079 fontSize:(float)fontSize minimumScale:(double)minimumScale maximumScale:(double)maximumScale allowScaling:(BOOL)allowScaling forceScroll:(BOOL)forceScroll
2081 LOG_WITH_STREAM(VisibleRects, stream << "_zoomToFocusRect:" << focusedElementRectInDocumentCoordinates << " selectionRect:" << selectionRectInDocumentCoordinates);
2082 UNUSED_PARAM(insideFixed);
2084 const double WKWebViewStandardFontSize = 16;
2085 const double kMinimumHeightToShowContentAboveKeyboard = 106;
2086 const CFTimeInterval UIWebFormAnimationDuration = 0.25;
2087 const double CaretOffsetFromWindowEdge = 20;
2089 // Zoom around the element's bounding frame. We use a "standard" size to determine the proper frame.
2090 double scale = allowScaling ? std::min(std::max(WKWebViewStandardFontSize / fontSize, minimumScale), maximumScale) : contentZoomScale(self);
2091 CGFloat documentWidth = [_contentView bounds].size.width;
2092 scale = CGRound(documentWidth * scale) / documentWidth;
2094 UIWindow *window = [_scrollView window];
2096 WebCore::FloatRect focusedElementRectInNewScale = focusedElementRectInDocumentCoordinates;
2097 focusedElementRectInNewScale.scale(scale);
2098 focusedElementRectInNewScale.moveBy([_contentView frame].origin);
2100 // Find the portion of the view that is visible on the screen.
2101 UIViewController *topViewController = [[[_scrollView _viewControllerForAncestor] _rootAncestorViewController] _viewControllerForSupportedInterfaceOrientations];
2102 UIView *fullScreenView = topViewController.view;
2103 if (!fullScreenView)
2104 fullScreenView = window;
2106 CGRect unobscuredScrollViewRectInWebViewCoordinates = UIEdgeInsetsInsetRect([self bounds], _obscuredInsets);
2107 CGRect visibleScrollViewBoundsInWebViewCoordinates = CGRectIntersection(unobscuredScrollViewRectInWebViewCoordinates, [fullScreenView convertRect:[fullScreenView bounds] toView:self]);
2108 CGRect formAssistantFrameInWebViewCoordinates = [window convertRect:_inputViewBounds toView:self];
2109 CGRect intersectionBetweenScrollViewAndFormAssistant = CGRectIntersection(visibleScrollViewBoundsInWebViewCoordinates, formAssistantFrameInWebViewCoordinates);
2110 CGSize visibleSize = visibleScrollViewBoundsInWebViewCoordinates.size;
2112 CGFloat visibleOffsetFromTop = 0;
2113 if (!CGRectIsEmpty(intersectionBetweenScrollViewAndFormAssistant)) {
2114 CGFloat heightVisibleAboveFormAssistant = CGRectGetMinY(intersectionBetweenScrollViewAndFormAssistant) - CGRectGetMinY(visibleScrollViewBoundsInWebViewCoordinates);
2115 CGFloat heightVisibleBelowFormAssistant = CGRectGetMaxY(visibleScrollViewBoundsInWebViewCoordinates) - CGRectGetMaxY(intersectionBetweenScrollViewAndFormAssistant);
2117 if (heightVisibleAboveFormAssistant >= kMinimumHeightToShowContentAboveKeyboard || heightVisibleBelowFormAssistant < heightVisibleAboveFormAssistant)
2118 visibleSize.height = heightVisibleAboveFormAssistant;
2120 visibleSize.height = heightVisibleBelowFormAssistant;
2121 visibleOffsetFromTop = CGRectGetMaxY(intersectionBetweenScrollViewAndFormAssistant) - CGRectGetMinY(visibleScrollViewBoundsInWebViewCoordinates);
2125 BOOL selectionRectIsNotNull = !selectionRectInDocumentCoordinates.isZero();
2127 CGRect currentlyVisibleRegionInWebViewCoordinates;
2128 currentlyVisibleRegionInWebViewCoordinates.origin = unobscuredScrollViewRectInWebViewCoordinates.origin;
2129 currentlyVisibleRegionInWebViewCoordinates.origin.y += visibleOffsetFromTop;
2130 currentlyVisibleRegionInWebViewCoordinates.size = visibleSize;
2132 // Don't bother scrolling if the entire node is already visible, whether or not we got a selectionRect.
2133 if (CGRectContainsRect(currentlyVisibleRegionInWebViewCoordinates, [self convertRect:focusedElementRectInDocumentCoordinates fromView:_contentView.get()]))
2136 // Don't bother scrolling if we have a valid selectionRect and it is already visible.
2137 if (selectionRectIsNotNull && CGRectContainsRect(currentlyVisibleRegionInWebViewCoordinates, [self convertRect:selectionRectInDocumentCoordinates fromView:_contentView.get()]))
2141 // We want to zoom to the left/top corner of the DOM node, with as much spacing on all sides as we
2142 // can get based on the visible area after zooming (workingFrame). The spacing in either dimension is half the
2143 // difference between the size of the DOM node and the size of the visible frame.
2144 CGFloat horizontalSpaceInWebViewCoordinates = std::max((visibleSize.width - focusedElementRectInNewScale.width()) / 2.0, 0.0);
2145 CGFloat verticalSpaceInWebViewCoordinates = std::max((visibleSize.height - focusedElementRectInNewScale.height()) / 2.0, 0.0);
2148 topLeft.x = focusedElementRectInNewScale.x() - horizontalSpaceInWebViewCoordinates;
2149 topLeft.y = focusedElementRectInNewScale.y() - verticalSpaceInWebViewCoordinates - visibleOffsetFromTop;
2151 CGFloat minimumAllowableHorizontalOffsetInWebViewCoordinates = -INFINITY;
2152 CGFloat minimumAllowableVerticalOffsetInWebViewCoordinates = -INFINITY;
2153 if (selectionRectIsNotNull) {
2154 WebCore::FloatRect selectionRectInNewScale = selectionRectInDocumentCoordinates;
2155 selectionRectInNewScale.scale(scale);
2156 selectionRectInNewScale.moveBy([_contentView frame].origin);
2157 minimumAllowableHorizontalOffsetInWebViewCoordinates = CGRectGetMaxX(selectionRectInNewScale) + CaretOffsetFromWindowEdge - visibleSize.width;
2158 minimumAllowableVerticalOffsetInWebViewCoordinates = CGRectGetMaxY(selectionRectInNewScale) + CaretOffsetFromWindowEdge - visibleSize.height - visibleOffsetFromTop;
2161 WebCore::FloatRect documentBoundsInNewScale = [_contentView bounds];
2162 documentBoundsInNewScale.scale(scale);
2163 documentBoundsInNewScale.moveBy([_contentView frame].origin);
2165 // Constrain the left edge in document coordinates so that:
2166 // - it isn't so small that the scrollVisibleRect isn't visible on the screen
2167 // - it isn't so great that the document's right edge is less than the right edge of the screen
2168 if (selectionRectIsNotNull && topLeft.x < minimumAllowableHorizontalOffsetInWebViewCoordinates)
2169 topLeft.x = minimumAllowableHorizontalOffsetInWebViewCoordinates;
2171 CGFloat maximumAllowableHorizontalOffset = CGRectGetMaxX(documentBoundsInNewScale) - visibleSize.width;
2172 if (topLeft.x > maximumAllowableHorizontalOffset)
2173 topLeft.x = maximumAllowableHorizontalOffset;
2176 // Constrain the top edge in document coordinates so that:
2177 // - it isn't so small that the scrollVisibleRect isn't visible on the screen
2178 // - it isn't so great that the document's bottom edge is higher than the top of the form assistant
2179 if (selectionRectIsNotNull && topLeft.y < minimumAllowableVerticalOffsetInWebViewCoordinates)
2180 topLeft.y = minimumAllowableVerticalOffsetInWebViewCoordinates;
2182 CGFloat maximumAllowableVerticalOffset = CGRectGetMaxY(documentBoundsInNewScale) - visibleSize.height;
2183 if (topLeft.y > maximumAllowableVerticalOffset)
2184 topLeft.y = maximumAllowableVerticalOffset;
2187 WebCore::FloatPoint newCenter = CGPointMake(topLeft.x + unobscuredScrollViewRectInWebViewCoordinates.size.width / 2.0, topLeft.y + unobscuredScrollViewRectInWebViewCoordinates.size.height / 2.0);
2189 if (scale != contentZoomScale(self))
2190 _page->willStartUserTriggeredZooming();
2192 LOG_WITH_STREAM(VisibleRects, stream << "_zoomToFocusRect: zooming to " << newCenter << " scale:" << scale);
2194 // The newCenter has been computed in the new scale, but _zoomToCenter expected the center to be in the original scale.
2195 newCenter.scale(1 / scale);
2196 [_scrollView _zoomToCenter:newCenter
2198 duration:UIWebFormAnimationDuration
2202 - (CGFloat)_targetContentZoomScaleForRect:(const WebCore::FloatRect&)targetRect currentScale:(double)currentScale fitEntireRect:(BOOL)fitEntireRect minimumScale:(double)minimumScale maximumScale:(double)maximumScale
2204 WebCore::FloatSize unobscuredContentSize([self _contentRectForUserInteraction].size);
2205 double horizontalScale = unobscuredContentSize.width() * currentScale / targetRect.width();
2206 double verticalScale = unobscuredContentSize.height() * currentScale / targetRect.height();
2208 horizontalScale = std::min(std::max(horizontalScale, minimumScale), maximumScale);
2209 verticalScale = std::min(std::max(verticalScale, minimumScale), maximumScale);
2211 return fitEntireRect ? std::min(horizontalScale, verticalScale) : horizontalScale;
2214 - (BOOL)_zoomToRect:(WebCore::FloatRect)targetRect withOrigin:(WebCore::FloatPoint)origin fitEntireRect:(BOOL)fitEntireRect minimumScale:(double)minimumScale maximumScale:(double)maximumScale minimumScrollDistance:(float)minimumScrollDistance
2216 const float maximumScaleFactorDeltaForPanScroll = 0.02;
2218 double currentScale = contentZoomScale(self);
2219 double targetScale = [self _targetContentZoomScaleForRect:targetRect currentScale:currentScale fitEntireRect:fitEntireRect minimumScale:minimumScale maximumScale:maximumScale];
2221 if (fabs(targetScale - currentScale) < maximumScaleFactorDeltaForPanScroll) {
2222 if ([self _scrollToRect:targetRect origin:origin minimumScrollDistance:minimumScrollDistance])
2224 } else if (targetScale != currentScale) {
2225 [self _zoomToRect:targetRect atScale:targetScale origin:origin animated:YES];
2232 - (void)didMoveToWindow
2234 _page->activityStateDidChange(WebCore::ActivityState::AllFlags);
2237 - (void)setOpaque:(BOOL)opaque
2239 BOOL oldOpaque = self.opaque;
2241 [super setOpaque:opaque];
2242 [_contentView setOpaque:opaque];
2244 if (oldOpaque == opaque)
2250 _page->setDrawsBackground(opaque);
2251 [self _updateScrollViewBackground];
2254 - (void)setBackgroundColor:(UIColor *)backgroundColor
2256 [super setBackgroundColor:backgroundColor];
2257 [_contentView setBackgroundColor:backgroundColor];
2260 - (BOOL)_allowsDoubleTapGestures
2262 if (_fastClickingIsDisabled)
2265 // If the page is not user scalable, we don't allow double tap gestures.
2266 if (![_scrollView isZoomEnabled] || [_scrollView minimumZoomScale] >= [_scrollView maximumZoomScale])
2269 // If the viewport width was not explicit, we allow double tap gestures.
2270 if (!_viewportMetaTagWidthWasExplicit || _viewportMetaTagCameFromImageDocument)
2273 // If the page set a viewport width that wasn't the device width, then it was
2274 // scaled and thus will probably need to zoom.
2275 if (_viewportMetaTagWidth != WebCore::ViewportArguments::ValueDeviceWidth)
2278 // At this point, we have a page that asked for width = device-width. However,
2279 // if the content's width and height were large, we might have had to shrink it.
2280 // Since we'll enable double tap zoom whenever we're not at the actual
2281 // initial scale, this simply becomes a test of the current scale against 1.
2282 return !areEssentiallyEqualAsFloat(contentZoomScale(self), 1);
2285 #pragma mark - UIScrollViewDelegate
2287 - (BOOL)usesStandardContentView
2289 return !_customContentView && !_passwordView;
2292 - (CGSize)scrollView:(UIScrollView*)scrollView contentSizeForZoomScale:(CGFloat)scale withProposedSize:(CGSize)proposedSize
2294 return roundScrollViewContentSize(*_page, proposedSize);
2297 - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
2299 ASSERT(_scrollView == scrollView);
2301 if (_customContentView)
2302 return _customContentView.get();
2304 return _contentView.get();
2307 - (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view
2309 if (![self usesStandardContentView])
2312 if (scrollView.pinchGestureRecognizer.state == UIGestureRecognizerStateBegan) {
2313 _page->willStartUserTriggeredZooming();
2314 [_contentView scrollViewWillStartPanOrPinchGesture];
2316 [_contentView willStartZoomOrScroll];
2319 - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
2321 if (![self usesStandardContentView])
2324 if (scrollView.panGestureRecognizer.state == UIGestureRecognizerStateBegan)
2325 [_contentView scrollViewWillStartPanOrPinchGesture];
2327 [_contentView willStartZoomOrScroll];
2328 #if ENABLE(CSS_SCROLL_SNAP) && ENABLE(ASYNC_SCROLLING)
2329 // FIXME: We will want to detect whether snapping will occur before beginning to drag. See WebPageProxy::didCommitLayerTree.
2330 WebKit::RemoteScrollingCoordinatorProxy* coordinator = _page->scrollingCoordinatorProxy();
2331 ASSERT(scrollView == _scrollView.get());
2332 CGFloat scrollDecelerationFactor = (coordinator && coordinator->shouldSetScrollViewDecelerationRateFast()) ? UIScrollViewDecelerationRateFast : UIScrollViewDecelerationRateNormal;
2333 scrollView.horizontalScrollDecelerationFactor = scrollDecelerationFactor;
2334 scrollView.verticalScrollDecelerationFactor = scrollDecelerationFactor;
2338 - (void)_didFinishScrolling
2340 if (![self usesStandardContentView])
2343 [self _scheduleVisibleContentRectUpdate];
2344 [_contentView didFinishScrolling];
2347 - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
2349 // Work around <rdar://problem/16374753> by avoiding deceleration while
2350 // zooming. We'll animate to the right place once the zoom finishes.
2351 if ([scrollView isZooming])
2352 *targetContentOffset = [scrollView contentOffset];
2353 #if ENABLE(CSS_SCROLL_SNAP) && ENABLE(ASYNC_SCROLLING)
2354 if (WebKit::RemoteScrollingCoordinatorProxy* coordinator = _page->scrollingCoordinatorProxy()) {
2355 // FIXME: Here, I'm finding the maximum horizontal/vertical scroll offsets. There's probably a better way to do this.
2356 CGSize maxScrollOffsets = CGSizeMake(scrollView.contentSize.width - scrollView.bounds.size.width, scrollView.contentSize.height - scrollView.bounds.size.height);
2358 CGRect fullViewRect = self.bounds;
2360 UIEdgeInsets contentInset;
2362 id<WKUIDelegatePrivate> uiDelegatePrivate = static_cast<id <WKUIDelegatePrivate>>([self UIDelegate]);
2363 if ([uiDelegatePrivate respondsToSelector:@selector(_webView:finalObscuredInsetsForScrollView:withVelocity:targetContentOffset:)])
2364 contentInset = [uiDelegatePrivate _webView:self finalObscuredInsetsForScrollView:scrollView withVelocity:velocity targetContentOffset:targetContentOffset];
2366 contentInset = [self _computedContentInset];
2368 CGRect unobscuredRect = UIEdgeInsetsInsetRect(fullViewRect, contentInset);
2370 coordinator->adjustTargetContentOffsetForSnapping(maxScrollOffsets, velocity, unobscuredRect.origin.y, targetContentOffset);
2375 - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
2377 // If we're decelerating, scroll offset will be updated when scrollViewDidFinishDecelerating: is called.
2379 [self _didFinishScrolling];
2382 - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
2384 [self _didFinishScrolling];
2387 - (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView
2389 [self _didFinishScrolling];
2392 - (void)scrollViewDidScroll:(UIScrollView *)scrollView
2394 if (![self usesStandardContentView])
2395 [_customContentView scrollViewDidScroll:(UIScrollView *)scrollView];
2397 [self _scheduleVisibleContentRectUpdateAfterScrollInView:scrollView];
2399 if (WebKit::RemoteLayerTreeScrollingPerformanceData* scrollPerfData = _page->scrollingPerformanceData())
2400 scrollPerfData->didScroll([self visibleRectInViewCoordinates]);
2403 - (void)scrollViewDidZoom:(UIScrollView *)scrollView
2405 [self _updateScrollViewBackground];
2406 [self _scheduleVisibleContentRectUpdateAfterScrollInView:scrollView];
2409 - (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale
2411 ASSERT(scrollView == _scrollView);
2412 // FIXME: remove when rdar://problem/36065495 is fixed.
2413 // When rotating with two fingers down, UIScrollView can set a bogus content view position.
2414 // "Center" is top left because we set the anchorPoint to 0,0.
2415 [_contentView setCenter:self.bounds.origin];
2417 [self _scheduleVisibleContentRectUpdateAfterScrollInView:scrollView];
2418 [_contentView didZoomToScale:scale];
2421 - (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
2423 [self _didFinishScrolling];
2426 - (void)_scrollViewDidInterruptDecelerating:(UIScrollView *)scrollView
2428 if (![self usesStandardContentView])
2431 [_contentView didInterruptScrolling];
2432 [self _scheduleVisibleContentRectUpdateAfterScrollInView:scrollView];
2435 - (UIView *)_enclosingViewForExposedRectComputation
2437 return [self _scroller];
2440 - (CGRect)_visibleRectInEnclosingView:(UIView *)enclosingView
2445 CGRect exposedRect = [enclosingView convertRect:enclosingView.bounds toView:self];
2446 return CGRectIntersectsRect(exposedRect, self.bounds) ? CGRectIntersection(exposedRect, self.bounds) : CGRectZero;
2449 - (CGRect)_visibleContentRect
2451 if (_frozenVisibleContentRect)
2452 return _frozenVisibleContentRect.value();
2454 CGRect visibleRectInContentCoordinates = [self convertRect:self.bounds toView:_contentView.get()];
2456 if (UIView *enclosingView = [self _enclosingViewForExposedRectComputation]) {
2457 CGRect viewVisibleRect = [self _visibleRectInEnclosingView:enclosingView];
2458 CGRect viewVisibleContentRect = [self convertRect:viewVisibleRect toView:_contentView.get()];
2459 visibleRectInContentCoordinates = CGRectIntersection(visibleRectInContentCoordinates, viewVisibleContentRect);
2462 return visibleRectInContentCoordinates;
2465 // Called when some ancestor UIScrollView scrolls.
2468 [self _scheduleVisibleContentRectUpdateAfterScrollInView:[self _scroller]];
2470 const NSTimeInterval ScrollingEndedTimerInterval = 0.032;
2471 if (!_enclosingScrollViewScrollTimer) {
2472 _enclosingScrollViewScrollTimer = adoptNS([[NSTimer alloc] initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:ScrollingEndedTimerInterval]
2473 interval:0 target:self selector:@selector(_enclosingScrollerScrollingEnded:) userInfo:nil repeats:YES]);
2474 [[NSRunLoop mainRunLoop] addTimer:_enclosingScrollViewScrollTimer.get() forMode:NSDefaultRunLoopMode];
2476 _didScrollSinceLastTimerFire = YES;
2479 - (void)_enclosingScrollerScrollingEnded:(NSTimer *)timer
2481 if (_didScrollSinceLastTimerFire) {
2482 _didScrollSinceLastTimerFire = NO;
2486 [self _scheduleVisibleContentRectUpdate];
2487 [_enclosingScrollViewScrollTimer invalidate];
2488 _enclosingScrollViewScrollTimer = nil;
2491 - (UIEdgeInsets)_scrollViewSystemContentInset
2493 // It's not safe to access the scroll view's safeAreaInsets or _systemContentInset from
2494 // inside our layoutSubviews implementation, because they aren't updated until afterwards.
2495 // Instead, depend on the fact that the UIScrollView and WKWebView are in the same coordinate
2496 // space, and map the WKWebView's own insets into the scroll view manually.
2497 return UIEdgeInsetsAdd([_scrollView _contentScrollInset], self.safeAreaInsets, [_scrollView _edgesApplyingSafeAreaInsetsToContentInset]);
2500 - (CGFloat)_minimumAllowedLayoutWidth
2502 return _minimumAllowedLayoutWidth;
2505 - (void)_setMinimumAllowedLayoutWidth:(CGFloat)minimumAllowedLayoutWidth
2507 if (_minimumAllowedLayoutWidth == minimumAllowedLayoutWidth)
2510 _minimumAllowedLayoutWidth = minimumAllowedLayoutWidth;
2512 auto sizes = [self activeMinimumLayoutSizes:self.bounds];
2513 [self _dispatchSetMinimumLayoutSize:sizes.minimumLayoutSize viewSize:sizes.viewSize];
2516 - (ActiveViewportLayoutSizes)activeMinimumLayoutSizes:(const CGRect&)bounds
2518 if (_overridesMinimumLayoutSize)
2519 return { WebCore::FloatSize(_minimumLayoutSizeOverride), WebCore::FloatSize(_minimumLayoutSizeOverride) };
2521 ActiveViewportLayoutSizes sizes;
2522 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000
2523 sizes.viewSize = WebCore::FloatSize(UIEdgeInsetsInsetRect(CGRectMake(0, 0, bounds.size.width, bounds.size.height), self._scrollViewSystemContentInset).size);
2525 sizes.viewSize = WebCore::FloatSize { bounds.size };
2528 sizes.minimumLayoutSize = { std::max<float>(sizes.viewSize.width(), self._minimumAllowedLayoutWidth), sizes.viewSize.height() };
2532 - (void)_dispatchSetMinimumLayoutSize:(WebCore::FloatSize)minimumLayoutSize viewSize:(WebCore::FloatSize)viewSize
2534 if (_lastSentMinimumLayoutSize && CGSizeEqualToSize(_lastSentMinimumLayoutSize.value(), minimumLayoutSize))
2537 LOG_WITH_STREAM(VisibleRects, stream << "-[WKWebView " << _page->pageID() << " _dispatchSetMinimumLayoutSize:] " << minimumLayoutSize << " viewSize " << viewSize << " contentZoomScale " << contentZoomScale(self));
2538 _page->setViewportConfigurationMinimumLayoutSize(minimumLayoutSize, viewSize);
2539 _lastSentMinimumLayoutSize = minimumLayoutSize;
2542 - (void)_dispatchSetMaximumUnobscuredSize:(WebCore::FloatSize)maximumUnobscuredSize
2544 if (_lastSentMaximumUnobscuredSize && CGSizeEqualToSize(_lastSentMaximumUnobscuredSize.value(), maximumUnobscuredSize))
2547 _page->setMaximumUnobscuredSize(maximumUnobscuredSize);
2548 _lastSentMaximumUnobscuredSize = maximumUnobscuredSize;
2551 - (void)_dispatchSetDeviceOrientation:(int32_t)deviceOrientation
2553 if (_lastSentDeviceOrientation && _lastSentDeviceOrientation.value() == deviceOrientation)
2556 _page->setDeviceOrientation(deviceOrientation);
2557 _lastSentDeviceOrientation = deviceOrientation;
2560 - (void)_frameOrBoundsChanged
2562 CGRect bounds = self.bounds;
2563 [_scrollView setFrame:bounds];
2565 if (_dynamicViewportUpdateMode == DynamicViewportUpdateMode::NotResizing) {
2566 if (!_overridesMinimumLayoutSize) {
2567 auto sizes = [self activeMinimumLayoutSizes:self.bounds];
2568 [self _dispatchSetMinimumLayoutSize:sizes.minimumLayoutSize viewSize:sizes.viewSize];
2570 if (!_overridesMaximumUnobscuredSize)
2571 [self _dispatchSetMaximumUnobscuredSize:WebCore::FloatSize(bounds.size)];
2573 BOOL sizeChanged = NO;
2574 if (auto drawingArea = _page->drawingArea())
2575 sizeChanged = drawingArea->setSize(WebCore::IntSize(bounds.size));
2577 if (sizeChanged & [self usesStandardContentView])
2578 [_contentView setSizeChangedSinceLastVisibleContentRectUpdate:YES];
2581 [_customContentView web_setMinimumSize:bounds.size];
2582 [self _scheduleVisibleContentRectUpdate];
2585 // 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.
2586 - (CGRect)_contentRectForUserInteraction
2588 // FIXME: handle split keyboard.
2589 UIEdgeInsets obscuredInsets = _obscuredInsets;
2590 obscuredInsets.bottom = std::max(_obscuredInsets.bottom, _inputViewBounds.size.height);
2591 CGRect unobscuredRect = UIEdgeInsetsInsetRect(self.bounds, obscuredInsets);
2592 return [self convertRect:unobscuredRect toView:self._currentContentView];
2595 // Ideally UIScrollView would expose this for us: <rdar://problem/21394567>.
2596 - (BOOL)_scrollViewIsRubberBanding
2598 float deviceScaleFactor = _page->deviceScaleFactor();
2600 CGPoint contentOffset = [_scrollView contentOffset];
2601 CGPoint boundedOffset = contentOffsetBoundedInValidRange(_scrollView.get(), contentOffset);
2602 return !pointsEqualInDevicePixels(contentOffset, boundedOffset, deviceScaleFactor);
2605 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000
2606 - (void)safeAreaInsetsDidChange
2608 [super safeAreaInsetsDidChange];
2610 [self _scheduleVisibleContentRectUpdate];
2614 - (void)_scheduleVisibleContentRectUpdate
2616 // For visible rect updates not associated with a specific UIScrollView, just consider our own scroller.
2617 [self _scheduleVisibleContentRectUpdateAfterScrollInView:_scrollView.get()];
2620 - (BOOL)_scrollViewIsInStableState:(UIScrollView *)scrollView
2622 BOOL isStableState = !([scrollView isDragging] || [scrollView isDecelerating] || [scrollView isZooming] || [scrollView _isAnimatingZoom] || [scrollView _isScrollingToTop]);
2624 if (isStableState && scrollView == _scrollView.get())
2625 isStableState = !_isChangingObscuredInsetsInteractively;
2627 if (isStableState && scrollView == _scrollView.get())
2628 isStableState = ![self _scrollViewIsRubberBanding];
2631 isStableState = !scrollView._isInterruptingDeceleration;
2633 if (NSNumber *stableOverride = self._stableStateOverride)
2634 isStableState = stableOverride.boolValue;
2636 return isStableState;
2639 - (void)_addUpdateVisibleContentRectPreCommitHandler
2641 auto retainedSelf = retainPtr(self);
2642 [CATransaction addCommitHandler:[retainedSelf] {
2643 WKWebView *webView = retainedSelf.get();
2644 if (![webView _isValid])
2646 [webView _updateVisibleContentRects];
2647 webView->_hasScheduledVisibleRectUpdate = NO;
2648 } forPhase:kCATransactionPhasePreCommit];
2651 - (void)_scheduleVisibleContentRectUpdateAfterScrollInView:(UIScrollView *)scrollView
2653 _visibleContentRectUpdateScheduledFromScrollViewInStableState = [self _scrollViewIsInStableState:scrollView];
2655 if (_hasScheduledVisibleRectUpdate)
2658 _hasScheduledVisibleRectUpdate = YES;
2660 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000
2661 CATransactionPhase transactionPhase = [CATransaction currentPhase];
2662 if (transactionPhase == kCATransactionPhaseNull || transactionPhase == kCATransactionPhasePreLayout) {
2663 [self _addUpdateVisibleContentRectPreCommitHandler];
2668 dispatch_async(dispatch_get_main_queue(), [retainedSelf = retainPtr(self)] {
2669 WKWebView *webView = retainedSelf.get();
2670 if (![webView _isValid])
2672 [webView _addUpdateVisibleContentRectPreCommitHandler];
2676 static bool scrollViewCanScroll(UIScrollView *scrollView)
2681 UIEdgeInsets contentInset = scrollView.contentInset;
2682 CGSize contentSize = scrollView.contentSize;
2683 CGSize boundsSize = scrollView.bounds.size;
2685 return (contentSize.width + contentInset.left + contentInset.right) > boundsSize.width
2686 || (contentSize.height + contentInset.top + contentInset.bottom) > boundsSize.height;
2689 - (CGRect)_contentBoundsExtendedForRubberbandingWithScale:(CGFloat)scaleFactor
2691 CGPoint contentOffset = [_scrollView contentOffset];
2692 CGPoint boundedOffset = contentOffsetBoundedInValidRange(_scrollView.get(), contentOffset);
2694 CGFloat horiontalRubberbandAmountInContentCoordinates = (contentOffset.x - boundedOffset.x) / scaleFactor;
2695 CGFloat verticalRubberbandAmountInContentCoordinates = (contentOffset.y - boundedOffset.y) / scaleFactor;
2697 CGRect extendedBounds = [_contentView bounds];
2699 if (horiontalRubberbandAmountInContentCoordinates < 0) {
2700 extendedBounds.origin.x += horiontalRubberbandAmountInContentCoordinates;
2701 extendedBounds.size.width -= horiontalRubberbandAmountInContentCoordinates;
2702 } else if (horiontalRubberbandAmountInContentCoordinates > 0)
2703 extendedBounds.size.width += horiontalRubberbandAmountInContentCoordinates;
2705 if (verticalRubberbandAmountInContentCoordinates < 0) {
2706 extendedBounds.origin.y += verticalRubberbandAmountInContentCoordinates;
2707 extendedBounds.size.height -= verticalRubberbandAmountInContentCoordinates;
2708 } else if (verticalRubberbandAmountInContentCoordinates > 0)
2709 extendedBounds.size.height += verticalRubberbandAmountInContentCoordinates;
2711 return extendedBounds;
2714 - (void)_updateVisibleContentRects
2716 BOOL inStableState = _visibleContentRectUpdateScheduledFromScrollViewInStableState;
2718 if (![self usesStandardContentView]) {
2719 [_passwordView setFrame:self.bounds];
2720 [_customContentView web_computedContentInsetDidChange];
2724 if (_delayUpdateVisibleContentRects) {
2725 _hadDelayedUpdateVisibleContentRects = YES;
2729 if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing
2730 || (_needsResetViewStateAfterCommitLoadForMainFrame && ![_contentView sizeChangedSinceLastVisibleContentRectUpdate])
2731 || [_scrollView isZoomBouncing]
2732 || _currentlyAdjustingScrollViewInsetsForKeyboard)
2735 CGRect fullViewRect = self.bounds;
2736 CGRect visibleRectInContentCoordinates = [self _visibleContentRect];
2738 UIEdgeInsets computedContentInsetUnadjustedForKeyboard = [self _computedContentInset];
2739 if (!_haveSetObscuredInsets)
2740 computedContentInsetUnadjustedForKeyboard.bottom -= _totalScrollViewBottomInsetAdjustmentForKeyboard;
2742 CGFloat scaleFactor = contentZoomScale(self);
2744 CGRect unobscuredRect = UIEdgeInsetsInsetRect(fullViewRect, computedContentInsetUnadjustedForKeyboard);
2745 CGRect unobscuredRectInContentCoordinates = _frozenUnobscuredContentRect ? _frozenUnobscuredContentRect.value() : [self convertRect:unobscuredRect toView:_contentView.get()];
2746 unobscuredRectInContentCoordinates = CGRectIntersection(unobscuredRectInContentCoordinates, [self _contentBoundsExtendedForRubberbandingWithScale:scaleFactor]);
2748 #if ENABLE(CSS_SCROLL_SNAP) && ENABLE(ASYNC_SCROLLING)
2749 if (inStableState) {
2750 WebKit::RemoteScrollingCoordinatorProxy* coordinator = _page->scrollingCoordinatorProxy();
2751 if (coordinator && coordinator->hasActiveSnapPoint()) {
2752 CGRect unobscuredRect = UIEdgeInsetsInsetRect(fullViewRect, computedContentInsetUnadjustedForKeyboard);
2754 CGPoint currentPoint = [_scrollView contentOffset];
2755 CGPoint activePoint = coordinator->nearestActiveContentInsetAdjustedSnapPoint(unobscuredRect.origin.y, currentPoint);
2757 if (!CGPointEqualToPoint(activePoint, currentPoint)) {
2758 RetainPtr<WKScrollView> strongScrollView = _scrollView;
2759 dispatch_async(dispatch_get_main_queue(), [strongScrollView, activePoint] {
2760 [strongScrollView setContentOffset:activePoint animated:NO];
2767 [_contentView didUpdateVisibleRect:visibleRectInContentCoordinates
2768 unobscuredRect:unobscuredRectInContentCoordinates
2769 unobscuredRectInScrollViewCoordinates:unobscuredRect
2770 obscuredInsets:_obscuredInsets
2771 unobscuredSafeAreaInsets:[self _computedUnobscuredSafeAreaInset]
2772 inputViewBounds:_inputViewBounds
2773 scale:scaleFactor minimumScale:[_scrollView minimumZoomScale]
2774 inStableState:inStableState
2775 isChangingObscuredInsetsInteractively:_isChangingObscuredInsetsInteractively
2776 enclosedInScrollableAncestorView:scrollViewCanScroll([self _scroller])];
2778 while (!_visibleContentRectUpdateCallbacks.isEmpty()) {
2779 auto callback = _visibleContentRectUpdateCallbacks.takeLast();
2784 - (void)_didFinishLoadForMainFrame
2786 if (_gestureController)
2787 _gestureController->didFinishLoadForMainFrame();
2790 - (void)_didFailLoadForMainFrame
2792 if (_gestureController)
2793 _gestureController->didFailLoadForMainFrame();
2796 - (void)_didSameDocumentNavigationForMainFrame:(WebKit::SameDocumentNavigationType)navigationType
2798 [_customContentView web_didSameDocumentNavigation:toAPI(navigationType)];
2800 if (_gestureController)
2801 _gestureController->didSameDocumentNavigationForMainFrame(navigationType);
2804 - (void)_keyboardChangedWithInfo:(NSDictionary *)keyboardInfo adjustScrollView:(BOOL)adjustScrollView
2806 NSValue *endFrameValue = [keyboardInfo objectForKey:UIKeyboardFrameEndUserInfoKey];
2810 // The keyboard rect is always in screen coordinates. In the view services case the window does not
2811 // have the interface orientation rotation transformation; its host does. So, it makes no sense to
2812 // clip the keyboard rect against its screen.
2813 if ([[self window] _isHostedInAnotherProcess])
2814 _inputViewBounds = [self.window convertRect:[endFrameValue CGRectValue] fromWindow:nil];
2816 _inputViewBounds = [self.window convertRect:CGRectIntersection([endFrameValue CGRectValue], self.window.screen.bounds) fromWindow:nil];
2818 if (adjustScrollView) {
2819 CGFloat bottomInsetBeforeAdjustment = [_scrollView contentInset].bottom;
2820 SetForScope<BOOL> insetAdjustmentGuard(_currentlyAdjustingScrollViewInsetsForKeyboard, YES);
2821 [_scrollView _adjustForAutomaticKeyboardInfo:keyboardInfo animated:YES lastAdjustment:&_lastAdjustmentForScroller];
2822 CGFloat bottomInsetAfterAdjustment = [_scrollView contentInset].bottom;
2823 if (bottomInsetBeforeAdjustment != bottomInsetAfterAdjustment)
2824 _totalScrollViewBottomInsetAdjustmentForKeyboard += bottomInsetAfterAdjustment - bottomInsetBeforeAdjustment;
2827 [self _scheduleVisibleContentRectUpdate];
2830 - (BOOL)_shouldUpdateKeyboardWithInfo:(NSDictionary *)keyboardInfo
2832 if ([_contentView isAssistingNode])
2835 NSNumber *isLocalKeyboard = [keyboardInfo valueForKey:UIKeyboardIsLocalUserInfoKey];
2836 return isLocalKeyboard && !isLocalKeyboard.boolValue;
2839 - (void)_keyboardWillChangeFrame:(NSNotification *)notification
2841 if ([self _shouldUpdateKeyboardWithInfo:notification.userInfo])
2842 [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES];
2845 - (void)_keyboardDidChangeFrame:(NSNotification *)notification
2847 [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:NO];
2850 - (void)_keyboardWillShow:(NSNotification *)notification
2852 if ([self _shouldUpdateKeyboardWithInfo:notification.userInfo])
2853 [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES];
2855 _page->setIsKeyboardAnimatingIn(true);
2858 - (void)_keyboardDidShow:(NSNotification *)notification
2860 _page->setIsKeyboardAnimatingIn(false);
2863 - (void)_keyboardWillHide:(NSNotification *)notification
2865 // Ignore keyboard will hide notifications sent during rotation. They're just there for
2866 // backwards compatibility reasons and processing the will hide notification would
2867 // temporarily screw up the unobscured view area.
2868 if ([[UIPeripheralHost sharedInstance] rotationState])
2871 [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES];
2874 - (void)_windowDidRotate:(NSNotification *)notification
2876 if (!_overridesInterfaceOrientation)
2877 [self _dispatchSetDeviceOrientation:deviceOrientation()];
2880 - (void)_contentSizeCategoryDidChange:(NSNotification *)notification
2882 _page->contentSizeCategoryDidChange([self _contentSizeCategory]);
2885 - (NSString *)_contentSizeCategory
2887 return [[UIApplication sharedApplication] preferredContentSizeCategory];
2890 - (void)_accessibilitySettingsDidChange:(NSNotification *)notification
2892 _page->accessibilitySettingsDidChange();
2895 - (void)setAllowsBackForwardNavigationGestures:(BOOL)allowsBackForwardNavigationGestures
2897 if (_allowsBackForwardNavigationGestures == allowsBackForwardNavigationGestures)
2900 _allowsBackForwardNavigationGestures = allowsBackForwardNavigationGestures;
2902 if (allowsBackForwardNavigationGestures) {
2903 if (!_gestureController) {
2904 _gestureController = std::make_unique<WebKit::ViewGestureController>(*_page);
2905 _gestureController->installSwipeHandler(self, [self scrollView]);
2906 if (WKWebView *alternateWebView = [_configuration _alternateWebViewForNavigationGestures])
2907 _gestureController->setAlternateBackForwardListSourcePage(alternateWebView->_page.get());
2910 _gestureController = nullptr;
2912 _page->setShouldRecordNavigationSnapshots(allowsBackForwardNavigationGestures);
2915 - (BOOL)allowsBackForwardNavigationGestures
2917 return _allowsBackForwardNavigationGestures;
2920 - (BOOL)_isNavigationSwipeGestureRecognizer:(UIGestureRecognizer *)recognizer
2922 if (!_gestureController)
2924 return _gestureController->isNavigationSwipeGestureRecognizer(recognizer);
2927 - (void)_navigationGestureDidBegin
2929 // During a back/forward swipe, there's a view interposed between this view and the content view that has
2930 // an offset and animation on it, which results in computing incorrect rectangles. Work around by using
2931 // frozen rects during swipes.
2932 CGRect fullViewRect = self.bounds;
2933 CGRect unobscuredRect = UIEdgeInsetsInsetRect(fullViewRect, [self _computedContentInset]);
2935 _frozenVisibleContentRect = [self convertRect:fullViewRect toView:_contentView.get()];
2936 _frozenUnobscuredContentRect = [self convertRect:unobscuredRect toView:_contentView.get()];
2938 LOG_WITH_STREAM(VisibleRects, stream << "_navigationGestureDidBegin: freezing visibleContentRect " << WebCore::FloatRect(_frozenVisibleContentRect.value()) << " UnobscuredContentRect " << WebCore::FloatRect(_frozenUnobscuredContentRect.value()));
2941 - (void)_navigationGestureDidEnd
2943 _frozenVisibleContentRect = std::nullopt;
2944 _frozenUnobscuredContentRect = std::nullopt;
2947 - (void)_showPasswordViewWithDocumentName:(NSString *)documentName passwordHandler:(void (^)(NSString *))passwordHandler
2949 ASSERT(!_passwordView);
2950 _passwordView = adoptNS([[WKPasswordView alloc] initWithFrame:self.bounds documentName:documentName]);
2951 [_passwordView setUserDidEnterPassword:passwordHandler];
2952 [_passwordView showInScrollView:_scrollView.get()];
2953 self._currentContentView.hidden = YES;
2956 - (void)_hidePasswordView
2961 self._currentContentView.hidden = NO;
2962 [_passwordView hide];
2963 _passwordView = nil;
2966 - (WKPasswordView *)_passwordView
2968 return _passwordView.get();
2971 - (void)_updateScrollViewInsetAdjustmentBehavior
2973 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000
2974 if (![_scrollView _contentInsetAdjustmentBehaviorWasExternallyOverridden])
2975 [_scrollView _setContentInsetAdjustmentBehaviorInternal:self._safeAreaShouldAffectObscuredInsets ? UIScrollViewContentInsetAdjustmentAlways : UIScrollViewContentInsetAdjustmentNever];
2979 - (void)_setAvoidsUnsafeArea:(BOOL)avoidsUnsafeArea
2981 if (_avoidsUnsafeArea == avoidsUnsafeArea)
2984 _avoidsUnsafeArea = avoidsUnsafeArea;
2986 [self _updateScrollViewInsetAdjustmentBehavior];
2987 [self _scheduleVisibleContentRectUpdate];
2989 id <WKUIDelegatePrivate> uiDelegate = (id <WKUIDelegatePrivate>)[self UIDelegate];
2990 if ([uiDelegate respondsToSelector:@selector(_webView:didChangeSafeAreaShouldAffectObscuredInsets:)])
2991 [uiDelegate _webView:self didChangeSafeAreaShouldAffectObscuredInsets:avoidsUnsafeArea];
2994 #endif // PLATFORM(IOS)
2996 #pragma mark OS X-specific methods
3000 - (BOOL)acceptsFirstResponder
3002 return _impl->acceptsFirstResponder();
3005 - (BOOL)becomeFirstResponder
3007 return _impl->becomeFirstResponder();
3010 - (BOOL)resignFirstResponder
3012 return _impl->resignFirstResponder();
3015 - (void)viewWillStartLiveResize
3017 _impl->viewWillStartLiveResize();
3020 - (void)viewDidEndLiveResize
3022 _impl->viewDidEndLiveResize();
3030 - (NSSize)intrinsicContentSize
3032 return NSSizeFromCGSize(_impl->intrinsicContentSize());
3035 - (void)prepareContentInRect:(NSRect)rect
3037 _impl->prepareContentInRect(NSRectToCGRect(rect));
3040 - (void)setFrameSize:(NSSize)size
3042 [super setFrameSize:size];
3043 _impl->setFrameSize(NSSizeToCGSize(size));
3048 _impl->renewGState();
3049 [super renewGState];
3052 #define WEBCORE_COMMAND(command) - (void)command:(id)sender { _impl->executeEditCommandForSelector(_cmd); }
3054 WEBCORE_COMMAND(alignCenter)
3055 WEBCORE_COMMAND(alignJustified)
3056 WEBCORE_COMMAND(alignLeft)
3057 WEBCORE_COMMAND(alignRight)
3058 WEBCORE_COMMAND(copy)
3059 WEBCORE_COMMAND(cut)
3060 WEBCORE_COMMAND(delete)
3061 WEBCORE_COMMAND(deleteBackward)
3062 WEBCORE_COMMAND(deleteBackwardByDecomposingPreviousCharacter)
3063 WEBCORE_COMMAND(deleteForward)
3064 WEBCORE_COMMAND(deleteToBeginningOfLine)
3065 WEBCORE_COMMAND(deleteToBeginningOfParagraph)
3066 WEBCORE_COMMAND(deleteToEndOfLine)
3067 WEBCORE_COMMAND(deleteToEndOfParagraph)
3068 WEBCORE_COMMAND(deleteToMark)
3069 WEBCORE_COMMAND(deleteWordBackward)
3070 WEBCORE_COMMAND(deleteWordForward)
3071 WEBCORE_COMMAND(ignoreSpelling)
3072 WEBCORE_COMMAND(indent)
3073 WEBCORE_COMMAND(insertBacktab)
3074 WEBCORE_COMMAND(insertLineBreak)
3075 WEBCORE_COMMAND(insertNewline)
3076 WEBCORE_COMMAND(insertNewlineIgnoringFieldEditor)
3077 WEBCORE_COMMAND(insertParagraphSeparator)
3078 WEBCORE_COMMAND(insertTab)
3079 WEBCORE_COMMAND(insertTabIgnoringFieldEditor)
3080 WEBCORE_COMMAND(makeTextWritingDirectionLeftToRight)
3081 WEBCORE_COMMAND(makeTextWritingDirectionNatural)
3082 WEBCORE_COMMAND(makeTextWritingDirectionRightToLeft)
3083 WEBCORE_COMMAND(moveBackward)
3084 WEBCORE_COMMAND(moveBackwardAndModifySelection)
3085 WEBCORE_COMMAND(moveDown)
3086 WEBCORE_COMMAND(moveDownAndModifySelection)
3087 WEBCORE_COMMAND(moveForward)
3088 WEBCORE_COMMAND(moveForwardAndModifySelection)
3089 WEBCORE_COMMAND(moveLeft)
3090 WEBCORE_COMMAND(moveLeftAndModifySelection)
3091 WEBCORE_COMMAND(moveParagraphBackwardAndModifySelection)
3092 WEBCORE_COMMAND(moveParagraphForwardAndModifySelection)
3093 WEBCORE_COMMAND(moveRight)
3094 WEBCORE_COMMAND(moveRightAndModifySelection)
3095 WEBCORE_COMMAND(moveToBeginningOfDocument)
3096 WEBCORE_COMMAND(moveToBeginningOfDocumentAndModifySelection)
3097 WEBCORE_COMMAND(moveToBeginningOfLine)
3098 WEBCORE_COMMAND(moveToBeginningOfLineAndModifySelection)
3099 WEBCORE_COMMAND(moveToBeginningOfParagraph)
3100 WEBCORE_COMMAND(moveToBeginningOfParagraphAndModifySelection)
3101 WEBCORE_COMMAND(moveToBeginningOfSentence)
3102 WEBCORE_COMMAND(moveToBeginningOfSentenceAndModifySelection)
3103 WEBCORE_COMMAND(moveToEndOfDocument)
3104 WEBCORE_COMMAND(moveToEndOfDocumentAndModifySelection)
3105 WEBCORE_COMMAND(moveToEndOfLine)
3106 WEBCORE_COMMAND(moveToEndOfLineAndModifySelection)
3107 WEBCORE_COMMAND(moveToEndOfParagraph)
3108 WEBCORE_COMMAND(moveToEndOfParagraphAndModifySelection)
3109 WEBCORE_COMMAND(moveToEndOfSentence)
3110 WEBCORE_COMMAND(moveToEndOfSentenceAndModifySelection)
3111 WEBCORE_COMMAND(moveToLeftEndOfLine)
3112 WEBCORE_COMMAND(moveToLeftEndOfLineAndModifySelection)
3113 WEBCORE_COMMAND(moveToRightEndOfLine)
3114 WEBCORE_COMMAND(moveToRightEndOfLineAndModifySelection)
3115 WEBCORE_COMMAND(moveUp)
3116 WEBCORE_COMMAND(moveUpAndModifySelection)
3117 WEBCORE_COMMAND(moveWordBackward)
3118 WEBCORE_COMMAND(moveWordBackwardAndModifySelection)
3119 WEBCORE_COMMAND(moveWordForward)
3120 WEBCORE_COMMAND(moveWordForwardAndModifySelection)
3121 WEBCORE_COMMAND(moveWordLeft)
3122 WEBCORE_COMMAND(moveWordLeftAndModifySelection)
3123 WEBCORE_COMMAND(moveWordRight)
3124 WEBCORE_COMMAND(moveWordRightAndModifySelection)
3125 WEBCORE_COMMAND(outdent)
3126 WEBCORE_COMMAND(pageDown)
3127 WEBCORE_COMMAND(pageDownAndModifySelection)
3128 WEBCORE_COMMAND(pageUp)
3129 WEBCORE_COMMAND(pageUpAndModifySelection)
3130 WEBCORE_COMMAND(paste)
3131 WEBCORE_COMMAND(pasteAsPlainText)
3132 WEBCORE_COMMAND(scrollPageDown)
3133 WEBCORE_COMMAND(scrollPageUp)
3134 WEBCORE_COMMAND(scrollLineDown)
3135 WEBCORE_COMMAND(scrollLineUp)
3136 WEBCORE_COMMAND(scrollToBeginningOfDocument)
3137 WEBCORE_COMMAND(scrollToEndOfDocument)
3138 WEBCORE_COMMAND(selectAll)
3139 WEBCORE_COMMAND(selectLine)
3140 WEBCORE_COMMAND(selectParagraph)
3141 WEBCORE_COMMAND(selectSentence)
3142 WEBCORE_COMMAND(selectToMark)
3143 WEBCORE_COMMAND(selectWord)
3144 WEBCORE_COMMAND(setMark)
3145 WEBCORE_COMMAND(subscript)
3146 WEBCORE_COMMAND(superscript)
3147 WEBCORE_COMMAND(swapWithMark)
3148 WEBCORE_COMMAND(takeFindStringFromSelection)
3149 WEBCORE_COMMAND(transpose)
3150 WEBCORE_COMMAND(underline)
3151 WEBCORE_COMMAND(unscript)
3152 WEBCORE_COMMAND(yank)
3153 WEBCORE_COMMAND(yankAndSelect)
3155 #undef WEBCORE_COMMAND
3157 - (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pasteboard types:(NSArray *)types
3159 return _impl->writeSelectionToPasteboard(pasteboard, types);
3162 - (void)centerSelectionInVisibleArea:(id)sender
3164 _impl->centerSelectionInVisibleArea();
3167 - (id)validRequestorForSendType:(NSString *)sendType returnType:(NSString *)returnType
3169 return _impl->validRequestorForSendAndReturnTypes(sendType, returnType);
3172 - (BOOL)readSelectionFromPasteboard:(NSPasteboard *)pasteboard
3174 return _impl->readSelectionFromPasteboard(pasteboard);
3177 - (void)changeFont:(id)sender
3179 _impl->changeFontFromFontPanel();
3182 - (IBAction)startSpeaking:(id)sender
3184 _impl->startSpeaking();
3187 - (IBAction)stopSpeaking:(id)sender
3189 _impl->stopSpeaking(sender);
3192 - (IBAction)showGuessPanel:(id)sender
3194 _impl->showGuessPanel(sender);
3197 - (IBAction)checkSpelling:(id)sender
3199 _impl->checkSpelling();
3202 - (void)changeSpelling:(id)sender
3204 _impl->changeSpelling(sender);
3207 - (IBAction)toggleContinuousSpellChecking:(id)sender
3209 _impl->toggleContinuousSpellChecking();
3212 - (BOOL)isGrammarCheckingEnabled
3214 return _impl->isGrammarCheckingEnabled();
3217 - (void)setGrammarCheckingEnabled:(BOOL)flag
3219 _impl->setGrammarCheckingEnabled(flag);
3222 - (IBAction)toggleGrammarChecking:(id)sender
3224 _impl->toggleGrammarChecking();
3227 - (IBAction)toggleAutomaticSpellingCorrection:(id)sender
3229 _impl->toggleAutomaticSpellingCorrection();
3232 - (void)orderFrontSubstitutionsPanel:(id)sender
3234 _impl->orderFrontSubstitutionsPanel(sender);
3237 - (IBAction)toggleSmartInsertDelete:(id)sender
3239 _impl->toggleSmartInsertDelete();
3242 - (BOOL)isAutomaticQuoteSubstitutionEnabled
3244 return _impl->isAutomaticQuoteSubstitutionEnabled();
3247 - (void)setAutomaticQuoteSubstitutionEnabled:(BOOL)flag
3249 _impl->setAutomaticQuoteSubstitutionEnabled(flag);
3252 - (void)toggleAutomaticQuoteSubstitution:(id)sender
3254 _impl->toggleAutomaticQuoteSubstitution();
3257 - (BOOL)isAutomaticDashSubstitutionEnabled
3259 return _impl->isAutomaticDashSubstitutionEnabled();
3262 - (void)setAutomaticDashSubstitutionEnabled:(BOOL)flag
3264 _impl->setAutomaticDashSubstitutionEnabled(flag);
3267 - (void)toggleAutomaticDashSubstitution:(id)sender
3269 _impl->toggleAutomaticDashSubstitution();
3272 - (BOOL)isAutomaticLinkDetectionEnabled
3274 return _impl->isAutomaticLinkDetectionEnabled();
3277 - (void)setAutomaticLinkDetectionEnabled:(BOOL)flag
3279 _impl->setAutomaticLinkDetectionEnabled(flag);
3282 - (void)toggleAutomaticLinkDetection:(id)sender
3284 _impl->toggleAutomaticLinkDetection();
3287 - (BOOL)isAutomaticTextReplacementEnabled
3289 return _impl->isAutomaticTextReplacementEnabled();
3292 - (void)setAutomaticTextReplacementEnabled:(BOOL)flag
3294 _impl->setAutomaticTextReplacementEnabled(flag);
3297 - (void)toggleAutomaticTextReplacement:(id)sender
3299 _impl->toggleAutomaticTextReplacement();
3302 - (void)uppercaseWord:(id)sender
3304 _impl->uppercaseWord();
3307 - (void)lowercaseWord:(id)sender
3309 _impl->lowercaseWord();
3312 - (void)capitalizeWord:(id)sender
3314 _impl->capitalizeWord();
3317 - (BOOL)_wantsKeyDownForEvent:(NSEvent *)event
3319 return _impl->wantsKeyDownForEvent(event);
3322 - (void)scrollWheel:(NSEvent *)event
3324 _impl->scrollWheel(event);
3327 - (void)swipeWithEvent:(NSEvent *)event
3329 _impl->swipeWithEvent(event);
3332 - (void)mouseMoved:(NSEvent *)event
3334 _impl->mouseMoved(event);
3337 - (void)mouseDown:(NSEvent *)event
3339 _impl->mouseDown(event);
3342 - (void)mouseUp:(NSEvent *)event
3344 _impl->mouseUp(event);
3347 - (void)mouseDragged:(NSEvent *)event
3349 _impl->mouseDragged(event);
3352 - (void)mouseEntered:(NSEvent *)event
3354 _impl->mouseEntered(event);
3357 - (void)mouseExited:(NSEvent *)event
3359 _impl->mouseExited(event);
3362 - (void)otherMouseDown:(NSEvent *)event
3364 _impl->otherMouseDown(event);
3367 - (void)otherMouseDragged:(NSEvent *)event
3369 _impl->otherMouseDragged(event);
3372 - (void)otherMouseUp:(NSEvent *)event
3374 _impl->otherMouseUp(event);
3377 - (void)rightMouseDown:(NSEvent *)event
3379 _impl->rightMouseDown(event);
3382 - (void)rightMouseDragged:(NSEvent *)event
3384 _impl->rightMouseDragged(event);
3387 - (void)rightMouseUp:(NSEvent *)event
3389 _impl->rightMouseUp(event);
3392 - (void)pressureChangeWithEvent:(NSEvent *)event
3394 _impl->pressureChangeWithEvent(event);
3397 - (BOOL)acceptsFirstMouse:(NSEvent *)event
3399 return _impl->acceptsFirstMouse(event);
3402 - (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent *)event
3404 return _impl->shouldDelayWindowOrderingForEvent(event);
3407 - (void)doCommandBySelector:(SEL)selector
3409 _impl->doCommandBySelector(selector);
3412 - (void)insertText:(id)string
3414 _impl->insertText(string);
3417 - (void)insertText:(id)string replacementRange:(NSRange)replacementRange
3419 _impl->insertText(string, replacementRange);
3422 - (NSTextInputContext *)inputContext
3426 return _impl->inputContext();
3429 - (BOOL)performKeyEquivalent:(NSEvent *)event
3431 return _impl->performKeyEquivalent(event);
3434 - (void)keyUp:(NSEvent *)theEvent
3436 _impl->keyUp(theEvent);
3439 - (void)keyDown:(NSEvent *)theEvent
3441 _impl->keyDown(theEvent);
3444 - (void)flagsChanged:(NSEvent *)theEvent
3446 _impl->flagsChanged(theEvent);
3449 - (void)setMarkedText:(id)string selectedRange:(NSRange)newSelectedRange replacementRange:(NSRange)replacementRange
3451 _impl->setMarkedText(string, newSelectedRange, replacementRange);
3456 _impl->unmarkText();
3459 - (NSRange)selectedRange
3461 return _impl->selectedRange();
3464 - (BOOL)hasMarkedText
3466 return _impl->hasMarkedText();
3469 - (NSRange)markedRange
3471 return _impl->markedRange();
3474 - (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)nsRange actualRange:(NSRangePointer)actualRange
3476 return _impl->attributedSubstringForProposedRange(nsRange, actualRange);
3479 - (NSUInteger)characterIndexForPoint:(NSPoint)thePoint
3481 return _impl->characterIndexForPoint(thePoint);
3484 - (NSRect)firstRectForCharacterRange:(NSRange)theRange actualRange:(NSRangePointer)actualRange
3486 return _impl->firstRectForCharacterRange(theRange, actualRange);
3489 - (void)selectedRangeWithCompletionHandler:(void(^)(NSRange selectedRange))completionHandlerPtr
3491 _impl->selectedRangeWithCompletionHandler(completionHandlerPtr);
3494 - (void)markedRangeWithCompletionHandler:(void(^)(NSRange markedRange))completionHandlerPtr
3496 _impl->markedRangeWithCompletionHandler(completionHandlerPtr);
3499 - (void)hasMarkedTextWithCompletionHandler:(void(^)(BOOL hasMarkedText))completionHandlerPtr