2 * Copyright (C) 2014, 2015 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 "LegacySessionStateCoding.h"
38 #import "NavigationState.h"
39 #import "RemoteLayerTreeScrollingPerformanceData.h"
40 #import "RemoteLayerTreeTransaction.h"
41 #import "RemoteObjectRegistry.h"
42 #import "RemoteObjectRegistryMessages.h"
43 #import "UIDelegate.h"
44 #import "ViewGestureController.h"
45 #import "ViewSnapshotStore.h"
46 #import "WKBackForwardListInternal.h"
47 #import "WKBackForwardListItemInternal.h"
48 #import "WKBrowsingContextHandleInternal.h"
49 #import "WKErrorInternal.h"
50 #import "WKHistoryDelegatePrivate.h"
51 #import "WKLayoutMode.h"
53 #import "WKNSURLExtras.h"
54 #import "WKNavigationDelegate.h"
55 #import "WKNavigationInternal.h"
56 #import "WKPreferencesInternal.h"
57 #import "WKProcessPoolInternal.h"
58 #import "WKSharedAPICast.h"
59 #import "WKUIDelegate.h"
60 #import "WKUIDelegatePrivate.h"
61 #import "WKUserContentControllerInternal.h"
62 #import "WKWebViewConfigurationInternal.h"
63 #import "WKWebViewContentProvider.h"
64 #import "WKWebsiteDataStoreInternal.h"
65 #import "WebBackForwardList.h"
66 #import "WebCertificateInfo.h"
67 #import "WebFormSubmissionListenerProxy.h"
68 #import "WebKitSystemInterface.h"
69 #import "WebPageGroup.h"
70 #import "WebPageProxy.h"
71 #import "WebPreferencesKeys.h"
72 #import "WebProcessPool.h"
73 #import "WebProcessProxy.h"
74 #import "_WKDiagnosticLoggingDelegate.h"
75 #import "_WKFindDelegate.h"
76 #import "_WKFormDelegate.h"
77 #import "_WKRemoteObjectRegistryInternal.h"
78 #import "_WKSessionStateInternal.h"
79 #import "_WKVisitedLinkStoreInternal.h"
80 #import <WebCore/IOSurface.h>
81 #import <wtf/HashMap.h>
82 #import <wtf/MathExtras.h>
83 #import <wtf/NeverDestroyed.h>
84 #import <wtf/Optional.h>
85 #import <wtf/RetainPtr.h>
88 #import "_WKFrameHandleInternal.h"
89 #import "_WKWebViewPrintFormatter.h"
91 #import "ProcessThrottler.h"
92 #import "RemoteLayerTreeDrawingAreaProxy.h"
93 #import "RemoteScrollingCoordinatorProxy.h"
95 #import "WKContentViewInteraction.h"
97 #import "WKScrollView.h"
98 #import "WKWebViewContentProviderRegistry.h"
99 #import "WebPageMessages.h"
100 #import "WebVideoFullscreenManagerProxy.h"
101 #import <UIKit/UIApplication.h>
102 #import <WebCore/CoreGraphicsSPI.h>
103 #import <WebCore/FrameLoaderTypes.h>
104 #import <WebCore/InspectorOverlay.h>
105 #import <WebCore/QuartzCoreSPI.h>
107 @interface UIScrollView (UIScrollViewInternal)
108 - (void)_adjustForAutomaticKeyboardInfo:(NSDictionary*)info animated:(BOOL)animated lastAdjustment:(CGFloat*)lastAdjustment;
109 - (BOOL)_isScrollingToTop;
110 - (BOOL)_isInterruptingDeceleration;
111 - (CGPoint)_animatedTargetOffset;
114 @interface UIPeripheralHost(UIKitInternal)
115 - (CGFloat)getVerticalOverlapForView:(UIView *)view usingKeyboardInfo:(NSDictionary *)info;
118 @interface UIView (UIViewInternal)
119 - (UIViewController *)_viewControllerForAncestor;
122 @interface UIWindow (UIWindowInternal)
123 - (BOOL)_isHostedInAnotherProcess;
126 @interface UIViewController (UIViewControllerInternal)
127 - (UIViewController *)_rootAncestorViewController;
128 - (UIViewController *)_viewControllerForSupportedInterfaceOrientations;
131 enum class DynamicViewportUpdateMode {
133 ResizingWithAnimation,
134 ResizingWithDocumentHidden,
140 #import "WKViewInternal.h"
141 #import <WebCore/ColorMac.h>
144 static HashMap<WebKit::WebPageProxy*, WKWebView *>& pageToViewMap()
146 static NeverDestroyed<HashMap<WebKit::WebPageProxy*, WKWebView *>> map;
150 WKWebView* fromWebPageProxy(WebKit::WebPageProxy& page)
152 return pageToViewMap().get(&page);
155 @implementation WKWebView {
156 std::unique_ptr<WebKit::NavigationState> _navigationState;
157 std::unique_ptr<WebKit::UIDelegate> _uiDelegate;
159 RetainPtr<_WKRemoteObjectRegistry> _remoteObjectRegistry;
160 _WKRenderingProgressEvents _observedRenderingProgressEvents;
162 WebKit::WeakObjCPtr<id <_WKFormDelegate>> _formDelegate;
164 RetainPtr<WKScrollView> _scrollView;
165 RetainPtr<WKContentView> _contentView;
167 BOOL _overridesMinimumLayoutSize;
168 CGSize _minimumLayoutSizeOverride;
169 BOOL _overridesMaximumUnobscuredSize;
170 CGSize _maximumUnobscuredSizeOverride;
171 CGRect _inputViewBounds;
172 CGFloat _viewportMetaTagWidth;
173 BOOL _allowsLinkPreview;
175 UIEdgeInsets _obscuredInsets;
176 BOOL _haveSetObscuredInsets;
177 BOOL _isChangingObscuredInsetsInteractively;
179 UIInterfaceOrientation _interfaceOrientationOverride;
180 BOOL _overridesInterfaceOrientation;
182 BOOL _hasCommittedLoadForMainFrame;
183 BOOL _needsResetViewStateAfterCommitLoadForMainFrame;
184 uint64_t _firstPaintAfterCommitLoadTransactionID;
185 DynamicViewportUpdateMode _dynamicViewportUpdateMode;
186 CATransform3D _resizeAnimationTransformAdjustments;
187 uint64_t _resizeAnimationTransformTransactionID;
188 RetainPtr<UIView> _resizeAnimationView;
189 CGFloat _lastAdjustmentForScroller;
190 Optional<CGRect> _frozenVisibleContentRect;
191 Optional<CGRect> _frozenUnobscuredContentRect;
193 BOOL _needsToRestoreExposedRect;
194 WebCore::FloatRect _exposedRectToRestore;
195 BOOL _needsToRestoreUnobscuredCenter;
196 WebCore::FloatPoint _unobscuredCenterToRestore;
197 uint64_t _firstTransactionIDAfterPageRestore;
198 double _scaleToRestore;
200 std::unique_ptr<WebKit::ViewGestureController> _gestureController;
201 BOOL _allowsBackForwardNavigationGestures;
203 RetainPtr<UIView <WKWebViewContentProvider>> _customContentView;
204 RetainPtr<UIView> _customContentFixedOverlayView;
206 WebCore::Color _scrollViewBackgroundColor;
208 BOOL _delayUpdateVisibleContentRects;
209 BOOL _hadDelayedUpdateVisibleContentRects;
211 BOOL _pageIsPrintingToPDF;
212 RetainPtr<CGPDFDocumentRef> _printedDocument;
213 Vector<std::function<void ()>> _snapshotsDeferredDuringResize;
216 RetainPtr<WKView> _wkView;
220 - (instancetype)initWithFrame:(CGRect)frame
222 return [self initWithFrame:frame configuration:adoptNS([[WKWebViewConfiguration alloc] init]).get()];
226 static int32_t deviceOrientationForUIInterfaceOrientation(UIInterfaceOrientation orientation)
228 switch (orientation) {
229 case UIInterfaceOrientationUnknown:
230 case UIInterfaceOrientationPortrait:
232 case UIInterfaceOrientationPortraitUpsideDown:
234 case UIInterfaceOrientationLandscapeLeft:
236 case UIInterfaceOrientationLandscapeRight:
241 static int32_t deviceOrientation()
243 return deviceOrientationForUIInterfaceOrientation([[UIApplication sharedApplication] statusBarOrientation]);
246 - (BOOL)_isShowingVideoPictureInPicture
248 if (!_page || !_page->videoFullscreenManager())
251 return _page->videoFullscreenManager()->hasMode(WebCore::HTMLMediaElementEnums::VideoFullscreenModePictureInPicture);
254 - (BOOL)_mayAutomaticallyShowVideoPictureInPicture
256 #if (__IPHONE_OS_VERSION_MIN_REQUIRED <= 80200) || !HAVE(AVKIT)
259 if (!_page || !_page->videoFullscreenManager())
262 return _page->videoFullscreenManager()->mayAutomaticallyShowVideoPictureInPicture();
266 static bool shouldAllowPictureInPictureMediaPlayback()
268 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 90000
269 static bool shouldAllowPictureInPictureMediaPlayback = iosExecutableWasLinkedOnOrAfterVersion(wkIOSSystemVersion_9_0);
270 return shouldAllowPictureInPictureMediaPlayback;
278 - (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration
280 if (!(self = [super initWithFrame:frame]))
284 [NSException raise:NSInvalidArgumentException format:@"Configuration cannot be nil"];
286 _configuration = adoptNS([configuration copy]);
288 if (WKWebView *relatedWebView = [_configuration _relatedWebView]) {
289 WKProcessPool *processPool = [_configuration processPool];
290 WKProcessPool *relatedWebViewProcessPool = [relatedWebView->_configuration processPool];
291 if (processPool && processPool != relatedWebViewProcessPool)
292 [NSException raise:NSInvalidArgumentException format:@"Related web view %@ has process pool %@ but configuration specifies a different process pool %@", relatedWebView, relatedWebViewProcessPool, configuration.processPool];
294 [_configuration setProcessPool:relatedWebViewProcessPool];
297 [_configuration _validate];
299 CGRect bounds = self.bounds;
301 WebKit::WebProcessPool& processPool = *[_configuration processPool]->_processPool;
303 auto pageConfiguration = API::PageConfiguration::create();
305 pageConfiguration->setProcessPool(&processPool);
306 pageConfiguration->setPreferences([_configuration preferences]->_preferences.get());
307 if (WKWebView *relatedWebView = [_configuration _relatedWebView])
308 pageConfiguration->setRelatedPage(relatedWebView->_page.get());
310 pageConfiguration->setUserContentController([_configuration userContentController]->_userContentControllerProxy.get());
311 pageConfiguration->setVisitedLinkStore([_configuration _visitedLinkStore]->_visitedLinkStore.get());
312 pageConfiguration->setWebsiteDataStore([_configuration websiteDataStore]->_websiteDataStore.get());
313 pageConfiguration->setTreatsSHA1SignedCertificatesAsInsecure([_configuration _treatsSHA1SignedCertificatesAsInsecure]);
315 RefPtr<WebKit::WebPageGroup> pageGroup;
316 NSString *groupIdentifier = configuration._groupIdentifier;
317 if (groupIdentifier.length) {
318 pageGroup = WebKit::WebPageGroup::create(configuration._groupIdentifier);
319 pageConfiguration->setPageGroup(pageGroup.get());
322 pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::suppressesIncrementalRenderingKey(), WebKit::WebPreferencesStore::Value(!![_configuration suppressesIncrementalRendering]));
325 pageConfiguration->setAlwaysRunsAtForegroundPriority([_configuration _alwaysRunsAtForegroundPriority]);
327 pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::allowsInlineMediaPlaybackKey(), WebKit::WebPreferencesStore::Value(!![_configuration allowsInlineMediaPlayback]));
328 pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::inlineMediaPlaybackRequiresPlaysInlineAttributeKey(), WebKit::WebPreferencesStore::Value(!![_configuration _inlineMediaPlaybackRequiresPlaysInlineAttribute]));
329 pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::allowsPictureInPictureMediaPlaybackKey(), WebKit::WebPreferencesStore::Value(!![_configuration allowsPictureInPictureMediaPlayback] && shouldAllowPictureInPictureMediaPlayback()));
330 pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::requiresUserGestureForMediaPlaybackKey(), WebKit::WebPreferencesStore::Value(!![_configuration requiresUserActionForMediaPlayback]));
331 pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::mediaDataLoadsAutomaticallyKey(), WebKit::WebPreferencesStore::Value(!![_configuration _mediaDataLoadsAutomatically]));
333 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
334 pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::allowsAirPlayForMediaPlaybackKey(), WebKit::WebPreferencesStore::Value(!![_configuration allowsAirPlayForMediaPlayback]));
338 _scrollView = adoptNS([[WKScrollView alloc] initWithFrame:bounds]);
339 [_scrollView setInternalDelegate:self];
340 [_scrollView setBouncesZoom:YES];
342 [self addSubview:_scrollView.get()];
344 _contentView = adoptNS([[WKContentView alloc] initWithFrame:bounds processPool:processPool configuration:WTF::move(pageConfiguration) webView:self]);
346 _page = [_contentView page];
347 _page->setDeviceOrientation(deviceOrientation());
349 [_contentView layer].anchorPoint = CGPointZero;
350 [_contentView setFrame:bounds];
351 [_scrollView addSubview:_contentView.get()];
352 [_scrollView addSubview:[_contentView unscaledView]];
353 [self _updateScrollViewBackground];
355 _viewportMetaTagWidth = -1;
357 [self _frameOrBoundsChanged];
359 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
360 [center addObserver:self selector:@selector(_keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];
361 [center addObserver:self selector:@selector(_keyboardDidChangeFrame:) name:UIKeyboardDidChangeFrameNotification object:nil];
362 [center addObserver:self selector:@selector(_keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
363 [center addObserver:self selector:@selector(_keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
364 [center addObserver:self selector:@selector(_windowDidRotate:) name:UIWindowDidRotateNotification object:nil];
365 [center addObserver:self selector:@selector(_contentSizeCategoryDidChange:) name:UIContentSizeCategoryDidChangeNotification object:nil];
366 _page->contentSizeCategoryDidChange([self _contentSizeCategory]);
368 [[_configuration _contentProviderRegistry] addPage:*_page];
372 _wkView = adoptNS([[WKView alloc] initWithFrame:bounds processPool:processPool configuration:WTF::move(pageConfiguration) webView:self]);
373 [self addSubview:_wkView.get()];
374 _page = WebKit::toImpl([_wkView pageRef]);
376 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
377 [_wkView _setAutomaticallyAdjustsContentInsets:YES];
381 _page->setBackgroundExtendsBeyondPage(true);
383 if (NSString *applicationNameForUserAgent = configuration.applicationNameForUserAgent)
384 _page->setApplicationNameForUserAgent(applicationNameForUserAgent);
386 _navigationState = std::make_unique<WebKit::NavigationState>(self);
387 _uiDelegate = std::make_unique<WebKit::UIDelegate>(self);
388 _page->setFindClient(std::make_unique<WebKit::FindClient>(self));
389 _page->setDiagnosticLoggingClient(std::make_unique<WebKit::DiagnosticLoggingClient>(self));
391 pageToViewMap().add(_page.get(), self);
396 - (instancetype)initWithCoder:(NSCoder *)coder
404 if (_remoteObjectRegistry)
405 _page->process().processPool().removeMessageReceiver(Messages::RemoteObjectRegistry::messageReceiverName(), _page->pageID());
409 [_remoteObjectRegistry _invalidate];
411 [[_configuration _contentProviderRegistry] removePage:*_page];
412 [[NSNotificationCenter defaultCenter] removeObserver:self];
413 [_scrollView setInternalDelegate:nil];
416 pageToViewMap().remove(_page.get());
421 - (WKWebViewConfiguration *)configuration
423 return [[_configuration copy] autorelease];
426 - (WKBackForwardList *)backForwardList
428 return wrapper(_page->backForwardList());
431 - (id <WKNavigationDelegate>)navigationDelegate
433 return _navigationState->navigationDelegate().autorelease();
436 - (void)setNavigationDelegate:(id <WKNavigationDelegate>)navigationDelegate
438 _page->setNavigationClient(_navigationState->createNavigationClient());
439 _navigationState->setNavigationDelegate(navigationDelegate);
442 - (id <WKUIDelegate>)UIDelegate
444 return _uiDelegate->delegate().autorelease();
447 - (void)setUIDelegate:(id<WKUIDelegate>)UIDelegate
449 _page->setUIClient(_uiDelegate->createUIClient());
450 _uiDelegate->setDelegate(UIDelegate);
453 - (WKNavigation *)loadRequest:(NSURLRequest *)request
455 auto navigation = _page->loadRequest(request);
459 return [wrapper(*navigation.release().leakRef()) autorelease];
462 - (WKNavigation *)loadFileURL:(NSURL *)URL allowingReadAccessToURL:(NSURL *)readAccessURL
464 if (![URL isFileURL])
465 [NSException raise:NSInvalidArgumentException format:@"%@ is not a file URL", URL];
467 if (![readAccessURL isFileURL])
468 [NSException raise:NSInvalidArgumentException format:@"%@ is not a file URL", readAccessURL];
470 auto navigation = _page->loadFile([URL _web_originalDataAsWTFString], [readAccessURL _web_originalDataAsWTFString]);
474 return [wrapper(*navigation.release().leakRef()) autorelease];
477 - (WKNavigation *)loadHTMLString:(NSString *)string baseURL:(NSURL *)baseURL
479 NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
481 return [self loadData:data MIMEType:@"text/html" characterEncodingName:@"UTF-8" baseURL:baseURL];
484 - (WKNavigation *)loadData:(NSData *)data MIMEType:(NSString *)MIMEType characterEncodingName:(NSString *)characterEncodingName baseURL:(NSURL *)baseURL
486 auto navigation = _page->loadData(API::Data::createWithoutCopying(data).ptr(), MIMEType, characterEncodingName, baseURL.absoluteString);
490 return [wrapper(*navigation.release().leakRef()) autorelease];
493 - (WKNavigation *)goToBackForwardListItem:(WKBackForwardListItem *)item
495 auto navigation = _page->goToBackForwardItem(&item._item);
499 return [wrapper(*navigation.release().leakRef()) autorelease];
504 return _page->pageLoadState().title();
509 return [NSURL _web_URLWithWTFString:_page->pageLoadState().activeURL()];
514 return _page->pageLoadState().isLoading();
517 - (double)estimatedProgress
519 return _page->pageLoadState().estimatedProgress();
522 - (BOOL)hasOnlySecureContent
524 return _page->pageLoadState().hasOnlySecureContent();
527 - (NSArray *)certificateChain
529 auto certificateInfo = _page->pageLoadState().certificateInfo();
530 if (!certificateInfo)
533 return (NSArray *)certificateInfo->certificateInfo().certificateChain() ?: @[ ];
538 return _page->pageLoadState().canGoBack();
543 return _page->pageLoadState().canGoForward();
546 - (WKNavigation *)goBack
548 auto navigation = _page->goBack();
552 return [wrapper(*navigation.release().leakRef()) autorelease];
555 - (WKNavigation *)goForward
557 auto navigation = _page->goForward();
561 return [wrapper(*navigation.release().leakRef()) autorelease];
564 - (WKNavigation *)reload
566 auto navigation = _page->reload(false);
570 return [wrapper(*navigation.release().leakRef()) autorelease];
573 - (WKNavigation *)reloadFromOrigin
575 auto navigation = _page->reload(true);
579 return [wrapper(*navigation.release().leakRef()) autorelease];
584 _page->stopLoading();
587 static WKErrorCode callbackErrorCode(WebKit::CallbackBase::Error error)
590 case WebKit::CallbackBase::Error::None:
591 ASSERT_NOT_REACHED();
592 return WKErrorUnknown;
594 case WebKit::CallbackBase::Error::Unknown:
595 return WKErrorUnknown;
597 case WebKit::CallbackBase::Error::ProcessExited:
598 return WKErrorWebContentProcessTerminated;
600 case WebKit::CallbackBase::Error::OwnerWasInvalidated:
601 return WKErrorWebViewInvalidated;
605 - (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^)(id, NSError *))completionHandler
607 auto handler = adoptNS([completionHandler copy]);
609 _page->runJavaScriptInMainFrame(javaScriptString, [handler](API::SerializedScriptValue* serializedScriptValue, bool hadException, WebKit::ScriptValueCallback::Error errorCode) {
613 if (errorCode != WebKit::ScriptValueCallback::Error::None) {
614 auto error = createNSError(callbackErrorCode(errorCode));
615 if (errorCode == WebKit::ScriptValueCallback::Error::OwnerWasInvalidated) {
616 // The OwnerWasInvalidated callback is synchronous. We don't want to call the block from within it
617 // because that can trigger re-entrancy bugs in WebKit.
618 // FIXME: It would be even better if GenericCallback did this for us.
619 dispatch_async(dispatch_get_main_queue(), [handler, error] {
620 auto rawHandler = (void (^)(id, NSError *))handler.get();
621 rawHandler(nil, error.get());
626 auto rawHandler = (void (^)(id, NSError *))handler.get();
627 rawHandler(nil, error.get());
631 auto rawHandler = (void (^)(id, NSError *))handler.get();
633 ASSERT(!serializedScriptValue);
634 rawHandler(nil, createNSError(WKErrorJavaScriptExceptionOccurred).get());
638 if (!serializedScriptValue) {
639 rawHandler(nil, createNSError(WKErrorJavaScriptResultTypeIsUnsupported).get());
643 id body = API::SerializedScriptValue::deserialize(*serializedScriptValue->internalRepresentation(), 0);
644 rawHandler(body, nil);
648 - (NSString *)customUserAgent
650 return _page->customUserAgent();
653 - (void)setCustomUserAgent:(NSString *)customUserAgent
655 _page->setCustomUserAgent(customUserAgent);
658 - (WKPageRef)_pageForTesting
660 return toAPI(_page.get());
663 - (BOOL)allowsLinkPreview
666 return [_wkView allowsLinkPreview];
668 return _allowsLinkPreview;
672 - (void)setAllowsLinkPreview:(BOOL)allowsLinkPreview
675 [_wkView setAllowsLinkPreview:allowsLinkPreview];
678 if (_allowsLinkPreview == allowsLinkPreview)
681 _allowsLinkPreview = allowsLinkPreview;
683 #if HAVE(LINK_PREVIEW)
684 if (_allowsLinkPreview)
685 [_contentView _registerPreview];
687 [_contentView _unregisterPreview];
688 #endif // HAVE(LINK_PREVIEW)
689 #endif // PLATFORM(IOS)
692 #pragma mark iOS-specific methods
695 - (void)setFrame:(CGRect)frame
697 CGRect oldFrame = self.frame;
698 [super setFrame:frame];
700 if (!CGSizeEqualToSize(oldFrame.size, frame.size))
701 [self _frameOrBoundsChanged];
704 - (void)setBounds:(CGRect)bounds
706 CGRect oldBounds = self.bounds;
707 [super setBounds:bounds];
708 [_customContentFixedOverlayView setFrame:self.bounds];
710 if (!CGSizeEqualToSize(oldBounds.size, bounds.size))
711 [self _frameOrBoundsChanged];
714 - (UIScrollView *)scrollView
716 return _scrollView.get();
719 - (WKBrowsingContextController *)browsingContextController
721 return [_contentView browsingContextController];
724 - (BOOL)becomeFirstResponder
726 return [_contentView becomeFirstResponder] || [super becomeFirstResponder];
729 static inline CGFloat floorToDevicePixel(CGFloat input, float deviceScaleFactor)
731 return CGFloor(input * deviceScaleFactor) / deviceScaleFactor;
734 static inline bool pointsEqualInDevicePixels(CGPoint a, CGPoint b, float deviceScaleFactor)
736 return fabs(a.x * deviceScaleFactor - b.x * deviceScaleFactor) < std::numeric_limits<float>::epsilon()
737 && fabs(a.y * deviceScaleFactor - b.y * deviceScaleFactor) < std::numeric_limits<float>::epsilon();
740 static CGSize roundScrollViewContentSize(const WebKit::WebPageProxy& page, CGSize contentSize)
742 float deviceScaleFactor = page.deviceScaleFactor();
743 return CGSizeMake(floorToDevicePixel(contentSize.width, deviceScaleFactor), floorToDevicePixel(contentSize.height, deviceScaleFactor));
746 - (UIView *)_currentContentView
748 return _customContentView ? _customContentView.get() : _contentView.get();
751 - (void)_setHasCustomContentView:(BOOL)pageHasCustomContentView loadedMIMEType:(const WTF::String&)mimeType
753 if (pageHasCustomContentView) {
754 [_customContentView removeFromSuperview];
755 [_customContentFixedOverlayView removeFromSuperview];
757 Class representationClass = [[_configuration _contentProviderRegistry] providerForMIMEType:mimeType];
758 ASSERT(representationClass);
759 _customContentView = adoptNS([[representationClass alloc] web_initWithFrame:self.bounds webView:self]);
760 _customContentFixedOverlayView = adoptNS([[UIView alloc] initWithFrame:self.bounds]);
761 [_customContentFixedOverlayView setUserInteractionEnabled:NO];
763 [[_contentView unscaledView] removeFromSuperview];
764 [_contentView removeFromSuperview];
765 [_scrollView addSubview:_customContentView.get()];
766 [self addSubview:_customContentFixedOverlayView.get()];
768 [_customContentView web_setMinimumSize:self.bounds.size];
769 [_customContentView web_setFixedOverlayView:_customContentFixedOverlayView.get()];
770 } else if (_customContentView) {
771 [_customContentView removeFromSuperview];
772 _customContentView = nullptr;
774 [_customContentFixedOverlayView removeFromSuperview];
775 _customContentFixedOverlayView = nullptr;
777 [_scrollView addSubview:_contentView.get()];
778 [_scrollView addSubview:[_contentView unscaledView]];
779 [_scrollView setContentSize:roundScrollViewContentSize(*_page, [_contentView frame].size)];
781 [_customContentFixedOverlayView setFrame:self.bounds];
782 [self addSubview:_customContentFixedOverlayView.get()];
786 - (void)_didFinishLoadingDataForCustomContentProviderWithSuggestedFilename:(const String&)suggestedFilename data:(NSData *)data
788 ASSERT(_customContentView);
789 [_customContentView web_setContentProviderData:data suggestedFilename:suggestedFilename];
791 // FIXME: It may make more sense for custom content providers to invoke this when they're ready,
792 // because there's no guarantee that all custom content providers will lay out synchronously.
793 _page->didLayoutForCustomContentProvider();
796 - (void)_setViewportMetaTagWidth:(float)newWidth
798 _viewportMetaTagWidth = newWidth;
801 - (void)_willInvokeUIScrollViewDelegateCallback
803 _delayUpdateVisibleContentRects = YES;
806 - (void)_didInvokeUIScrollViewDelegateCallback
808 _delayUpdateVisibleContentRects = NO;
809 if (_hadDelayedUpdateVisibleContentRects) {
810 _hadDelayedUpdateVisibleContentRects = NO;
811 [self _updateVisibleContentRects];
815 static CGFloat contentZoomScale(WKWebView *webView)
817 CGFloat scale = webView._currentContentView.layer.affineTransform.a;
818 ASSERT(scale == [webView->_scrollView zoomScale]);
822 static WebCore::Color baseScrollViewBackgroundColor(WKWebView *webView)
824 if (webView->_customContentView)
825 return [webView->_customContentView backgroundColor].CGColor;
827 if (webView->_gestureController) {
828 WebCore::Color color = webView->_gestureController->backgroundColorForCurrentSnapshot();
833 return webView->_page->pageExtendedBackgroundColor();
836 static WebCore::Color scrollViewBackgroundColor(WKWebView *webView)
839 return WebCore::Color::transparent;
841 WebCore::Color color = baseScrollViewBackgroundColor(webView);
843 if (!color.isValid())
844 color = WebCore::Color::white;
846 CGFloat zoomScale = contentZoomScale(webView);
847 CGFloat minimumZoomScale = [webView->_scrollView minimumZoomScale];
848 if (zoomScale < minimumZoomScale) {
850 CGFloat opacity = std::max<CGFloat>(1 - slope * (minimumZoomScale - zoomScale), 0);
851 color = WebCore::colorWithOverrideAlpha(color.rgb(), opacity);
857 - (void)_updateScrollViewBackground
859 WebCore::Color color = scrollViewBackgroundColor(self);
861 if (_scrollViewBackgroundColor == color)
864 _scrollViewBackgroundColor = color;
866 auto uiBackgroundColor = adoptNS([[UIColor alloc] initWithCGColor:cachedCGColor(color, WebCore::ColorSpaceDeviceRGB)]);
867 [_scrollView setBackgroundColor:uiBackgroundColor.get()];
869 // Update the indicator style based on the lightness/darkness of the background color.
870 double hue, saturation, lightness;
871 color.getHSL(hue, saturation, lightness);
872 if (lightness <= .5 && color.alpha() > 0)
873 [_scrollView setIndicatorStyle:UIScrollViewIndicatorStyleWhite];
875 [_scrollView setIndicatorStyle:UIScrollViewIndicatorStyleDefault];
878 - (CGPoint)_adjustedContentOffset:(CGPoint)point
880 CGPoint result = point;
881 UIEdgeInsets contentInset = [self _computedContentInset];
883 result.x -= contentInset.left;
884 result.y -= contentInset.top;
889 - (UIEdgeInsets)_computedContentInset
891 if (_haveSetObscuredInsets)
892 return _obscuredInsets;
894 return [_scrollView contentInset];
897 - (void)_processDidExit
899 if (!_customContentView && _dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing) {
900 NSUInteger indexOfResizeAnimationView = [[_scrollView subviews] indexOfObject:_resizeAnimationView.get()];
901 [_scrollView insertSubview:_contentView.get() atIndex:indexOfResizeAnimationView];
902 [_scrollView insertSubview:[_contentView unscaledView] atIndex:indexOfResizeAnimationView + 1];
903 [_resizeAnimationView removeFromSuperview];
904 _resizeAnimationView = nil;
906 _resizeAnimationTransformAdjustments = CATransform3DIdentity;
908 [_contentView setFrame:self.bounds];
909 [_scrollView setBackgroundColor:[UIColor whiteColor]];
910 [_scrollView setContentOffset:[self _adjustedContentOffset:CGPointZero]];
911 [_scrollView setZoomScale:1];
913 _viewportMetaTagWidth = -1;
914 _hasCommittedLoadForMainFrame = NO;
915 _needsResetViewStateAfterCommitLoadForMainFrame = NO;
916 _dynamicViewportUpdateMode = DynamicViewportUpdateMode::NotResizing;
917 [_contentView setHidden:NO];
918 _needsToRestoreExposedRect = NO;
919 _needsToRestoreUnobscuredCenter = NO;
920 _scrollViewBackgroundColor = WebCore::Color();
921 _delayUpdateVisibleContentRects = NO;
922 _hadDelayedUpdateVisibleContentRects = NO;
925 - (void)_didCommitLoadForMainFrame
927 _firstPaintAfterCommitLoadTransactionID = downcast<WebKit::RemoteLayerTreeDrawingAreaProxy>(*_page->drawingArea()).nextLayerTreeTransactionID();
929 _hasCommittedLoadForMainFrame = YES;
930 _needsResetViewStateAfterCommitLoadForMainFrame = YES;
933 static CGPoint contentOffsetBoundedInValidRange(UIScrollView *scrollView, CGPoint contentOffset)
935 UIEdgeInsets contentInsets = scrollView.contentInset;
936 CGSize contentSize = scrollView.contentSize;
937 CGSize scrollViewSize = scrollView.bounds.size;
939 CGFloat maxHorizontalOffset = contentSize.width + contentInsets.right - scrollViewSize.width;
940 contentOffset.x = std::min(maxHorizontalOffset, contentOffset.x);
941 contentOffset.x = std::max(-contentInsets.left, contentOffset.x);
943 CGFloat maxVerticalOffset = contentSize.height + contentInsets.bottom - scrollViewSize.height;
944 contentOffset.y = std::min(maxVerticalOffset, contentOffset.y);
945 contentOffset.y = std::max(-contentInsets.top, contentOffset.y);
946 return contentOffset;
949 static void changeContentOffsetBoundedInValidRange(UIScrollView *scrollView, WebCore::FloatPoint contentOffset)
951 scrollView.contentOffset = contentOffsetBoundedInValidRange(scrollView, contentOffset);
954 - (WebCore::FloatRect)visibleRectInViewCoordinates
956 WebCore::FloatRect bounds = self.bounds;
957 bounds.moveBy([_scrollView contentOffset]);
958 WebCore::FloatRect contentViewBounds = [_contentView bounds];
959 bounds.intersect(contentViewBounds);
963 // WebCore stores the page scale factor as float instead of double. When we get a scale from WebCore,
964 // we need to ignore differences that are within a small rounding error on floats.
965 static inline bool areEssentiallyEqualAsFloat(float a, float b)
967 return WTF::areEssentiallyEqual(a, b);
970 - (void)_didCommitLayerTree:(const WebKit::RemoteLayerTreeTransaction&)layerTreeTransaction
972 if (_customContentView)
975 if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing) {
976 if (layerTreeTransaction.transactionID() >= _resizeAnimationTransformTransactionID) {
977 [_resizeAnimationView layer].sublayerTransform = _resizeAnimationTransformAdjustments;
978 if (_dynamicViewportUpdateMode == DynamicViewportUpdateMode::ResizingWithDocumentHidden) {
979 [_contentView setHidden:NO];
980 [self _endAnimatedResize];
986 CGSize newContentSize = roundScrollViewContentSize(*_page, [_contentView frame].size);
987 [_scrollView _setContentSizePreservingContentOffsetDuringRubberband:newContentSize];
989 [_scrollView setMinimumZoomScale:layerTreeTransaction.minimumScaleFactor()];
990 [_scrollView setMaximumZoomScale:layerTreeTransaction.maximumScaleFactor()];
991 [_scrollView setZoomEnabled:layerTreeTransaction.allowsUserScaling()];
992 if (!layerTreeTransaction.scaleWasSetByUIProcess() && ![_scrollView isZooming] && ![_scrollView isZoomBouncing] && ![_scrollView _isAnimatingZoom])
993 [_scrollView setZoomScale:layerTreeTransaction.pageScaleFactor()];
995 [self _updateScrollViewBackground];
997 if (_gestureController)
998 _gestureController->setRenderTreeSize(layerTreeTransaction.renderTreeSize());
1000 if (_needsResetViewStateAfterCommitLoadForMainFrame && layerTreeTransaction.transactionID() >= _firstPaintAfterCommitLoadTransactionID) {
1001 _needsResetViewStateAfterCommitLoadForMainFrame = NO;
1002 [_scrollView setContentOffset:[self _adjustedContentOffset:CGPointZero]];
1003 [self _updateVisibleContentRects];
1004 if (_observedRenderingProgressEvents & _WKRenderingProgressEventFirstPaint)
1005 _navigationState->didFirstPaint();
1008 bool isTransactionAfterPageRestore = layerTreeTransaction.transactionID() >= _firstTransactionIDAfterPageRestore;
1010 if (_needsToRestoreExposedRect && isTransactionAfterPageRestore) {
1011 _needsToRestoreExposedRect = NO;
1013 if (areEssentiallyEqualAsFloat(contentZoomScale(self), _scaleToRestore)) {
1014 WebCore::FloatPoint exposedPosition = _exposedRectToRestore.location();
1015 exposedPosition.scale(_scaleToRestore, _scaleToRestore);
1017 changeContentOffsetBoundedInValidRange(_scrollView.get(), exposedPosition);
1018 if (_gestureController)
1019 _gestureController->didRestoreScrollPosition();
1021 [self _updateVisibleContentRects];
1024 if (_needsToRestoreUnobscuredCenter && isTransactionAfterPageRestore) {
1025 _needsToRestoreUnobscuredCenter = NO;
1027 if (areEssentiallyEqualAsFloat(contentZoomScale(self), _scaleToRestore)) {
1028 CGRect unobscuredRect = UIEdgeInsetsInsetRect(self.bounds, _obscuredInsets);
1029 WebCore::FloatSize unobscuredContentSizeAtNewScale(unobscuredRect.size.width / _scaleToRestore, unobscuredRect.size.height / _scaleToRestore);
1030 WebCore::FloatPoint topLeftInDocumentCoordinates(_unobscuredCenterToRestore.x() - unobscuredContentSizeAtNewScale.width() / 2, _unobscuredCenterToRestore.y() - unobscuredContentSizeAtNewScale.height() / 2);
1032 topLeftInDocumentCoordinates.scale(_scaleToRestore, _scaleToRestore);
1033 topLeftInDocumentCoordinates.moveBy(WebCore::FloatPoint(-_obscuredInsets.left, -_obscuredInsets.top));
1035 changeContentOffsetBoundedInValidRange(_scrollView.get(), topLeftInDocumentCoordinates);
1036 if (_gestureController)
1037 _gestureController->didRestoreScrollPosition();
1039 [self _updateVisibleContentRects];
1042 if (WebKit::RemoteLayerTreeScrollingPerformanceData* scrollPerfData = _page->scrollingPerformanceData())
1043 scrollPerfData->didCommitLayerTree([self visibleRectInViewCoordinates]);
1046 - (void)_dynamicViewportUpdateChangedTargetToScale:(double)newScale position:(CGPoint)newScrollPosition nextValidLayerTreeTransactionID:(uint64_t)nextValidLayerTreeTransactionID
1048 if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing) {
1049 CGFloat animatingScaleTarget = [[_resizeAnimationView layer] transform].m11;
1050 double currentTargetScale = animatingScaleTarget * [[_contentView layer] transform].m11;
1051 double scale = newScale / currentTargetScale;
1052 _resizeAnimationTransformAdjustments = CATransform3DMakeScale(scale, scale, 1);
1054 CGPoint newContentOffset = [self _adjustedContentOffset:CGPointMake(newScrollPosition.x * newScale, newScrollPosition.y * newScale)];
1055 CGPoint currentContentOffset = [_scrollView contentOffset];
1057 _resizeAnimationTransformAdjustments.m41 = (currentContentOffset.x - newContentOffset.x) / animatingScaleTarget;
1058 _resizeAnimationTransformAdjustments.m42 = (currentContentOffset.y - newContentOffset.y) / animatingScaleTarget;
1059 _resizeAnimationTransformTransactionID = nextValidLayerTreeTransactionID;
1063 - (void)_couldNotRestorePageState
1065 // The gestureController may be waiting for the scroll position to be restored
1066 // in order to remove the swipe snapshot. Since the scroll position could not be
1067 // restored, tell the gestureController it was restored so that it no longer waits
1069 if (_gestureController)
1070 _gestureController->didRestoreScrollPosition();
1073 - (void)_restorePageStateToExposedRect:(WebCore::FloatRect)exposedRect scale:(double)scale
1075 if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing)
1078 if (_customContentView)
1081 _needsToRestoreUnobscuredCenter = NO;
1082 _needsToRestoreExposedRect = YES;
1083 _firstTransactionIDAfterPageRestore = downcast<WebKit::RemoteLayerTreeDrawingAreaProxy>(*_page->drawingArea()).nextLayerTreeTransactionID();
1084 _exposedRectToRestore = exposedRect;
1085 _scaleToRestore = scale;
1088 - (void)_restorePageStateToUnobscuredCenter:(WebCore::FloatPoint)center scale:(double)scale
1090 if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing)
1093 if (_customContentView)
1096 _needsToRestoreExposedRect = NO;
1097 _needsToRestoreUnobscuredCenter = YES;
1098 _firstTransactionIDAfterPageRestore = downcast<WebKit::RemoteLayerTreeDrawingAreaProxy>(*_page->drawingArea()).nextLayerTreeTransactionID();
1099 _unobscuredCenterToRestore = center;
1100 _scaleToRestore = scale;
1103 - (PassRefPtr<WebKit::ViewSnapshot>)_takeViewSnapshot
1105 float deviceScale = WKGetScreenScaleFactor();
1106 WebCore::FloatSize snapshotSize(self.bounds.size);
1107 snapshotSize.scale(deviceScale, deviceScale);
1109 CATransform3D transform = CATransform3DMakeScale(deviceScale, deviceScale, 1);
1112 auto surface = WebCore::IOSurface::create(WebCore::expandedIntSize(snapshotSize), WebCore::ColorSpaceDeviceRGB);
1113 CARenderServerRenderLayerWithTransform(MACH_PORT_NULL, self.layer.context.contextId, reinterpret_cast<uint64_t>(self.layer), surface->surface(), 0, 0, &transform);
1115 return WebKit::ViewSnapshot::create(nullptr);
1117 uint32_t slotID = [WebKit::ViewSnapshotStore::snapshottingContext() createImageSlot:snapshotSize hasAlpha:YES];
1122 CARenderServerCaptureLayerWithTransform(MACH_PORT_NULL, self.layer.context.contextId, (uint64_t)self.layer, slotID, 0, 0, &transform);
1123 WebCore::IntSize imageSize = WebCore::expandedIntSize(WebCore::FloatSize(snapshotSize));
1124 return WebKit::ViewSnapshot::create(slotID, imageSize, imageSize.width() * imageSize.height() * 4);
1128 - (void)_zoomToPoint:(WebCore::FloatPoint)point atScale:(double)scale animated:(BOOL)animated
1130 CFTimeInterval duration = 0;
1131 CGFloat zoomScale = contentZoomScale(self);
1134 const double maximumZoomDuration = 0.4;
1135 const double minimumZoomDuration = 0.1;
1136 const double zoomDurationFactor = 0.3;
1138 duration = std::min(fabs(log(zoomScale) - log(scale)) * zoomDurationFactor + minimumZoomDuration, maximumZoomDuration);
1141 if (scale != zoomScale)
1142 _page->willStartUserTriggeredZooming();
1144 [_scrollView _zoomToCenter:point scale:scale duration:duration];
1147 - (void)_zoomToRect:(WebCore::FloatRect)targetRect atScale:(double)scale origin:(WebCore::FloatPoint)origin animated:(BOOL)animated
1149 // FIXME: Some of this could be shared with _scrollToRect.
1150 const double visibleRectScaleChange = contentZoomScale(self) / scale;
1151 const WebCore::FloatRect visibleRect([self convertRect:self.bounds toView:self._currentContentView]);
1152 const WebCore::FloatRect unobscuredRect([self _contentRectForUserInteraction]);
1154 const WebCore::FloatSize topLeftObscuredInsetAfterZoom((unobscuredRect.minXMinYCorner() - visibleRect.minXMinYCorner()) * visibleRectScaleChange);
1155 const WebCore::FloatSize bottomRightObscuredInsetAfterZoom((visibleRect.maxXMaxYCorner() - unobscuredRect.maxXMaxYCorner()) * visibleRectScaleChange);
1157 const WebCore::FloatSize unobscuredRectSizeAfterZoom(unobscuredRect.size() * visibleRectScaleChange);
1159 // Center to the target rect.
1160 WebCore::FloatPoint unobscuredRectLocationAfterZoom = targetRect.location() - (unobscuredRectSizeAfterZoom - targetRect.size()) * 0.5;
1162 // Center to the tap point instead in case the target rect won't fit in a direction.
1163 if (targetRect.width() > unobscuredRectSizeAfterZoom.width())
1164 unobscuredRectLocationAfterZoom.setX(origin.x() - unobscuredRectSizeAfterZoom.width() / 2);
1165 if (targetRect.height() > unobscuredRectSizeAfterZoom.height())
1166 unobscuredRectLocationAfterZoom.setY(origin.y() - unobscuredRectSizeAfterZoom.height() / 2);
1168 // We have computed where we want the unobscured rect to be. Now adjust for the obscuring insets.
1169 WebCore::FloatRect visibleRectAfterZoom(unobscuredRectLocationAfterZoom, unobscuredRectSizeAfterZoom);
1170 visibleRectAfterZoom.move(-topLeftObscuredInsetAfterZoom);
1171 visibleRectAfterZoom.expand(topLeftObscuredInsetAfterZoom + bottomRightObscuredInsetAfterZoom);
1173 [self _zoomToPoint:visibleRectAfterZoom.center() atScale:scale animated:animated];
1176 static WebCore::FloatPoint constrainContentOffset(WebCore::FloatPoint contentOffset, WebCore::FloatSize contentSize, WebCore::FloatSize unobscuredContentSize)
1178 WebCore::FloatSize maximumContentOffset = contentSize - unobscuredContentSize;
1179 contentOffset = contentOffset.shrunkTo(WebCore::FloatPoint(maximumContentOffset.width(), maximumContentOffset.height()));
1180 contentOffset = contentOffset.expandedTo(WebCore::FloatPoint());
1181 return contentOffset;
1184 - (void)_scrollToContentOffset:(WebCore::FloatPoint)contentOffsetInPageCoordinates scrollOrigin:(WebCore::IntPoint)scrollOrigin
1186 if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing)
1189 WebCore::FloatPoint contentOffsetRespectingOrigin = scrollOrigin + toFloatSize(contentOffsetInPageCoordinates);
1191 WebCore::FloatPoint scaledOffset = contentOffsetRespectingOrigin;
1192 CGFloat zoomScale = contentZoomScale(self);
1193 scaledOffset.scale(zoomScale, zoomScale);
1195 CGPoint contentOffsetInScrollViewCoordinates = [self _adjustedContentOffset:scaledOffset];
1196 contentOffsetInScrollViewCoordinates = contentOffsetBoundedInValidRange(_scrollView.get(), contentOffsetInScrollViewCoordinates);
1198 [_scrollView _stopScrollingAndZoomingAnimations];
1200 if (!CGPointEqualToPoint(contentOffsetInScrollViewCoordinates, [_scrollView contentOffset]))
1201 [_scrollView setContentOffset:contentOffsetInScrollViewCoordinates];
1203 // If we haven't changed anything, there would not be any VisibleContentRect update sent to the content.
1204 // The WebProcess would keep the invalid contentOffset as its scroll position.
1205 // To synchronize the WebProcess with what is on screen, we send the VisibleContentRect again.
1206 _page->resendLastVisibleContentRects();
1210 - (BOOL)_scrollToRect:(WebCore::FloatRect)targetRect origin:(WebCore::FloatPoint)origin minimumScrollDistance:(float)minimumScrollDistance
1212 WebCore::FloatRect unobscuredContentRect([self _contentRectForUserInteraction]);
1213 WebCore::FloatPoint unobscuredContentOffset = unobscuredContentRect.location();
1214 WebCore::FloatSize contentSize([self._currentContentView bounds].size);
1216 // Center the target rect in the scroll view.
1217 // If the target doesn't fit in the scroll view, center on the gesture location instead.
1218 WebCore::FloatPoint newUnobscuredContentOffset;
1219 if (targetRect.width() <= unobscuredContentRect.width())
1220 newUnobscuredContentOffset.setX(targetRect.x() - (unobscuredContentRect.width() - targetRect.width()) / 2);
1222 newUnobscuredContentOffset.setX(origin.x() - unobscuredContentRect.width() / 2);
1223 if (targetRect.height() <= unobscuredContentRect.height())
1224 newUnobscuredContentOffset.setY(targetRect.y() - (unobscuredContentRect.height() - targetRect.height()) / 2);
1226 newUnobscuredContentOffset.setY(origin.y() - unobscuredContentRect.height() / 2);
1227 newUnobscuredContentOffset = constrainContentOffset(newUnobscuredContentOffset, contentSize, unobscuredContentRect.size());
1229 if (unobscuredContentOffset == newUnobscuredContentOffset) {
1230 if (targetRect.width() > unobscuredContentRect.width())
1231 newUnobscuredContentOffset.setX(origin.x() - unobscuredContentRect.width() / 2);
1232 if (targetRect.height() > unobscuredContentRect.height())
1233 newUnobscuredContentOffset.setY(origin.y() - unobscuredContentRect.height() / 2);
1234 newUnobscuredContentOffset = constrainContentOffset(newUnobscuredContentOffset, contentSize, unobscuredContentRect.size());
1237 WebCore::FloatSize scrollViewOffsetDelta = newUnobscuredContentOffset - unobscuredContentOffset;
1238 scrollViewOffsetDelta.scale(contentZoomScale(self));
1240 float scrollDistance = scrollViewOffsetDelta.diagonalLength();
1241 if (scrollDistance < minimumScrollDistance)
1244 [_contentView willStartZoomOrScroll];
1246 [_scrollView setContentOffset:([_scrollView contentOffset] + scrollViewOffsetDelta) animated:YES];
1250 - (void)_scrollByOffset:(WebCore::FloatPoint)offset
1252 CGPoint currentOffset = ([_scrollView _isAnimatingScroll]) ? [_scrollView _animatedTargetOffset] : [_scrollView contentOffset];
1254 CGPoint boundedOffset = contentOffsetBoundedInValidRange(_scrollView.get(), currentOffset + offset);
1256 if (CGPointEqualToPoint(boundedOffset, currentOffset))
1258 [_contentView willStartZoomOrScroll];
1259 [_scrollView setContentOffset:boundedOffset animated:YES];
1262 - (void)_zoomOutWithOrigin:(WebCore::FloatPoint)origin animated:(BOOL)animated
1264 [self _zoomToPoint:origin atScale:[_scrollView minimumZoomScale] animated:animated];
1267 // focusedElementRect and selectionRect are both in document coordinates.
1268 - (void)_zoomToFocusRect:(WebCore::FloatRect)focusedElementRectInDocumentCoordinates selectionRect:(WebCore::FloatRect)selectionRectInDocumentCoordinates fontSize:(float)fontSize minimumScale:(double)minimumScale maximumScale:(double)maximumScale allowScaling:(BOOL)allowScaling forceScroll:(BOOL)forceScroll
1270 const double WKWebViewStandardFontSize = 16;
1271 const double kMinimumHeightToShowContentAboveKeyboard = 106;
1272 const CFTimeInterval UIWebFormAnimationDuration = 0.25;
1273 const double CaretOffsetFromWindowEdge = 20;
1275 // Zoom around the element's bounding frame. We use a "standard" size to determine the proper frame.
1276 double scale = allowScaling ? std::min(std::max(WKWebViewStandardFontSize / fontSize, minimumScale), maximumScale) : contentZoomScale(self);
1277 CGFloat documentWidth = [_contentView bounds].size.width;
1278 scale = CGRound(documentWidth * scale) / documentWidth;
1280 UIWindow *window = [_scrollView window];
1282 WebCore::FloatRect focusedElementRectInNewScale = focusedElementRectInDocumentCoordinates;
1283 focusedElementRectInNewScale.scale(scale);
1284 focusedElementRectInNewScale.moveBy([_contentView frame].origin);
1286 // Find the portion of the view that is visible on the screen.
1287 UIViewController *topViewController = [[[_scrollView _viewControllerForAncestor] _rootAncestorViewController] _viewControllerForSupportedInterfaceOrientations];
1288 UIView *fullScreenView = topViewController.view;
1289 if (!fullScreenView)
1290 fullScreenView = window;
1292 CGRect unobscuredScrollViewRectInWebViewCoordinates = UIEdgeInsetsInsetRect([self bounds], _obscuredInsets);
1293 CGRect visibleScrollViewBoundsInWebViewCoordinates = CGRectIntersection(unobscuredScrollViewRectInWebViewCoordinates, [fullScreenView convertRect:[fullScreenView bounds] toView:self]);
1294 CGRect formAssistantFrameInWebViewCoordinates = [window convertRect:_inputViewBounds toView:self];
1295 CGRect intersectionBetweenScrollViewAndFormAssistant = CGRectIntersection(visibleScrollViewBoundsInWebViewCoordinates, formAssistantFrameInWebViewCoordinates);
1296 CGSize visibleSize = visibleScrollViewBoundsInWebViewCoordinates.size;
1298 CGFloat visibleOffsetFromTop = 0;
1299 if (!CGRectIsEmpty(intersectionBetweenScrollViewAndFormAssistant)) {
1300 CGFloat heightVisibleAboveFormAssistant = CGRectGetMinY(intersectionBetweenScrollViewAndFormAssistant) - CGRectGetMinY(visibleScrollViewBoundsInWebViewCoordinates);
1301 CGFloat heightVisibleBelowFormAssistant = CGRectGetMaxY(visibleScrollViewBoundsInWebViewCoordinates) - CGRectGetMaxY(intersectionBetweenScrollViewAndFormAssistant);
1303 if (heightVisibleAboveFormAssistant >= kMinimumHeightToShowContentAboveKeyboard || heightVisibleBelowFormAssistant < heightVisibleAboveFormAssistant)
1304 visibleSize.height = heightVisibleAboveFormAssistant;
1306 visibleSize.height = heightVisibleBelowFormAssistant;
1307 visibleOffsetFromTop = CGRectGetMaxY(intersectionBetweenScrollViewAndFormAssistant) - CGRectGetMinY(visibleScrollViewBoundsInWebViewCoordinates);
1311 BOOL selectionRectIsNotNull = !selectionRectInDocumentCoordinates.isZero();
1313 CGRect currentlyVisibleRegionInWebViewCoordinates;
1314 currentlyVisibleRegionInWebViewCoordinates.origin = unobscuredScrollViewRectInWebViewCoordinates.origin;
1315 currentlyVisibleRegionInWebViewCoordinates.origin.y += visibleOffsetFromTop;
1316 currentlyVisibleRegionInWebViewCoordinates.size = visibleSize;
1318 // Don't bother scrolling if the entire node is already visible, whether or not we got a selectionRect.
1319 if (CGRectContainsRect(currentlyVisibleRegionInWebViewCoordinates, [self convertRect:focusedElementRectInDocumentCoordinates fromView:_contentView.get()]))
1322 // Don't bother scrolling if we have a valid selectionRect and it is already visible.
1323 if (selectionRectIsNotNull && CGRectContainsRect(currentlyVisibleRegionInWebViewCoordinates, [self convertRect:selectionRectInDocumentCoordinates fromView:_contentView.get()]))
1327 // We want to zoom to the left/top corner of the DOM node, with as much spacing on all sides as we
1328 // can get based on the visible area after zooming (workingFrame). The spacing in either dimension is half the
1329 // difference between the size of the DOM node and the size of the visible frame.
1330 CGFloat horizontalSpaceInWebViewCoordinates = std::max((visibleSize.width - focusedElementRectInNewScale.width()) / 2.0, 0.0);
1331 CGFloat verticalSpaceInWebViewCoordinates = std::max((visibleSize.height - focusedElementRectInNewScale.height()) / 2.0, 0.0);
1334 topLeft.x = focusedElementRectInNewScale.x() - horizontalSpaceInWebViewCoordinates;
1335 topLeft.y = focusedElementRectInNewScale.y() - verticalSpaceInWebViewCoordinates - visibleOffsetFromTop;
1337 CGFloat minimumAllowableHorizontalOffsetInWebViewCoordinates = -INFINITY;
1338 CGFloat minimumAllowableVerticalOffsetInWebViewCoordinates = -INFINITY;
1339 if (selectionRectIsNotNull) {
1340 WebCore::FloatRect selectionRectInNewScale = selectionRectInDocumentCoordinates;
1341 selectionRectInNewScale.scale(scale);
1342 selectionRectInNewScale.moveBy([_contentView frame].origin);
1343 minimumAllowableHorizontalOffsetInWebViewCoordinates = CGRectGetMaxX(selectionRectInNewScale) + CaretOffsetFromWindowEdge - visibleSize.width;
1344 minimumAllowableVerticalOffsetInWebViewCoordinates = CGRectGetMaxY(selectionRectInNewScale) + CaretOffsetFromWindowEdge - visibleSize.height - visibleOffsetFromTop;
1347 WebCore::FloatRect documentBoundsInNewScale = [_contentView bounds];
1348 documentBoundsInNewScale.scale(scale);
1349 documentBoundsInNewScale.moveBy([_contentView frame].origin);
1351 // Constrain the left edge in document coordinates so that:
1352 // - it isn't so small that the scrollVisibleRect isn't visible on the screen
1353 // - it isn't so great that the document's right edge is less than the right edge of the screen
1354 if (selectionRectIsNotNull && topLeft.x < minimumAllowableHorizontalOffsetInWebViewCoordinates)
1355 topLeft.x = minimumAllowableHorizontalOffsetInWebViewCoordinates;
1357 CGFloat maximumAllowableHorizontalOffset = CGRectGetMaxX(documentBoundsInNewScale) - visibleSize.width;
1358 if (topLeft.x > maximumAllowableHorizontalOffset)
1359 topLeft.x = maximumAllowableHorizontalOffset;
1362 // Constrain the top edge in document coordinates so that:
1363 // - it isn't so small that the scrollVisibleRect isn't visible on the screen
1364 // - it isn't so great that the document's bottom edge is higher than the top of the form assistant
1365 if (selectionRectIsNotNull && topLeft.y < minimumAllowableVerticalOffsetInWebViewCoordinates)
1366 topLeft.y = minimumAllowableVerticalOffsetInWebViewCoordinates;
1368 CGFloat maximumAllowableVerticalOffset = CGRectGetMaxY(documentBoundsInNewScale) - visibleSize.height;
1369 if (topLeft.y > maximumAllowableVerticalOffset)
1370 topLeft.y = maximumAllowableVerticalOffset;
1373 WebCore::FloatPoint newCenter = CGPointMake(topLeft.x + unobscuredScrollViewRectInWebViewCoordinates.size.width / 2.0, topLeft.y + unobscuredScrollViewRectInWebViewCoordinates.size.height / 2.0);
1375 if (scale != contentZoomScale(self))
1376 _page->willStartUserTriggeredZooming();
1378 // The newCenter has been computed in the new scale, but _zoomToCenter expected the center to be in the original scale.
1379 newCenter.scale(1 / scale, 1 / scale);
1380 [_scrollView _zoomToCenter:newCenter
1382 duration:UIWebFormAnimationDuration
1386 - (BOOL)_zoomToRect:(WebCore::FloatRect)targetRect withOrigin:(WebCore::FloatPoint)origin fitEntireRect:(BOOL)fitEntireRect minimumScale:(double)minimumScale maximumScale:(double)maximumScale minimumScrollDistance:(float)minimumScrollDistance
1388 const float maximumScaleFactorDeltaForPanScroll = 0.02;
1390 double currentScale = contentZoomScale(self);
1392 WebCore::FloatSize unobscuredContentSize([self _contentRectForUserInteraction].size);
1393 double horizontalScale = unobscuredContentSize.width() * currentScale / targetRect.width();
1394 double verticalScale = unobscuredContentSize.height() * currentScale / targetRect.height();
1396 horizontalScale = std::min(std::max(horizontalScale, minimumScale), maximumScale);
1397 verticalScale = std::min(std::max(verticalScale, minimumScale), maximumScale);
1399 double targetScale = fitEntireRect ? std::min(horizontalScale, verticalScale) : horizontalScale;
1400 if (fabs(targetScale - currentScale) < maximumScaleFactorDeltaForPanScroll) {
1401 if ([self _scrollToRect:targetRect origin:origin minimumScrollDistance:minimumScrollDistance])
1403 } else if (targetScale != currentScale) {
1404 [self _zoomToRect:targetRect atScale:targetScale origin:origin animated:YES];
1411 - (void)didMoveToWindow
1413 _page->viewStateDidChange(WebCore::ViewState::AllFlags);
1416 - (void)setOpaque:(BOOL)opaque
1418 BOOL oldOpaque = self.opaque;
1420 [super setOpaque:opaque];
1421 [_contentView setOpaque:opaque];
1423 if (oldOpaque == opaque)
1426 _page->setDrawsBackground(opaque);
1427 [self _updateScrollViewBackground];
1430 - (void)setBackgroundColor:(UIColor *)backgroundColor
1432 [super setBackgroundColor:backgroundColor];
1433 [_contentView setBackgroundColor:backgroundColor];
1436 #pragma mark - UIScrollViewDelegate
1438 - (BOOL)usesStandardContentView
1440 return !_customContentView;
1443 - (CGSize)scrollView:(UIScrollView*)scrollView contentSizeForZoomScale:(CGFloat)scale withProposedSize:(CGSize)proposedSize
1445 return roundScrollViewContentSize(*_page, proposedSize);
1448 - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
1450 ASSERT(_scrollView == scrollView);
1452 if (_customContentView)
1453 return _customContentView.get();
1455 return _contentView.get();
1458 - (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view
1460 if (![self usesStandardContentView])
1463 if (scrollView.pinchGestureRecognizer.state == UIGestureRecognizerStateBegan) {
1464 _page->willStartUserTriggeredZooming();
1465 [_contentView scrollViewWillStartPanOrPinchGesture];
1467 [_contentView willStartZoomOrScroll];
1470 - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
1472 if (![self usesStandardContentView])
1475 if (scrollView.panGestureRecognizer.state == UIGestureRecognizerStateBegan)
1476 [_contentView scrollViewWillStartPanOrPinchGesture];
1478 [_contentView willStartZoomOrScroll];
1479 #if ENABLE(CSS_SCROLL_SNAP) && ENABLE(ASYNC_SCROLLING)
1480 // FIXME: We will want to detect whether snapping will occur before beginning to drag. See WebPageProxy::didCommitLayerTree.
1481 WebKit::RemoteScrollingCoordinatorProxy* coordinator = _page->scrollingCoordinatorProxy();
1482 ASSERT(scrollView == _scrollView.get());
1483 CGFloat scrollDecelerationFactor = (coordinator && coordinator->shouldSetScrollViewDecelerationRateFast()) ? UIScrollViewDecelerationRateFast : [_scrollView preferredScrollDecelerationFactor];
1484 scrollView.horizontalScrollDecelerationFactor = scrollDecelerationFactor;
1485 scrollView.verticalScrollDecelerationFactor = scrollDecelerationFactor;
1489 - (void)_didFinishScrolling
1491 if (![self usesStandardContentView])
1494 [self _updateVisibleContentRects];
1495 [_contentView didFinishScrolling];
1498 - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
1500 // Work around <rdar://problem/16374753> by avoiding deceleration while
1501 // zooming. We'll animate to the right place once the zoom finishes.
1502 if ([scrollView isZooming])
1503 *targetContentOffset = [scrollView contentOffset];
1504 #if ENABLE(CSS_SCROLL_SNAP) && ENABLE(ASYNC_SCROLLING)
1505 if (WebKit::RemoteScrollingCoordinatorProxy* coordinator = _page->scrollingCoordinatorProxy()) {
1506 // FIXME: Here, I'm finding the maximum horizontal/vertical scroll offsets. There's probably a better way to do this.
1507 CGSize maxScrollOffsets = CGSizeMake(scrollView.contentSize.width - scrollView.bounds.size.width, scrollView.contentSize.height - scrollView.bounds.size.height);
1509 CGRect fullViewRect = self.bounds;
1511 UIEdgeInsets contentInset;
1513 id<WKUIDelegatePrivate> uiDelegatePrivate = static_cast<id <WKUIDelegatePrivate>>([self UIDelegate]);
1514 if ([uiDelegatePrivate respondsToSelector:@selector(_webView:finalObscuredInsetsForScrollView:withVelocity:targetContentOffset:)])
1515 contentInset = [uiDelegatePrivate _webView:self finalObscuredInsetsForScrollView:scrollView withVelocity:velocity targetContentOffset:targetContentOffset];
1517 contentInset = [self _computedContentInset];
1519 CGRect unobscuredRect = UIEdgeInsetsInsetRect(fullViewRect, contentInset);
1521 coordinator->adjustTargetContentOffsetForSnapping(maxScrollOffsets, velocity, unobscuredRect.origin.y, targetContentOffset);
1526 - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
1528 // If we're decelerating, scroll offset will be updated when scrollViewDidFinishDecelerating: is called.
1530 [self _didFinishScrolling];
1533 - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
1535 [self _didFinishScrolling];
1538 - (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView
1540 [self _didFinishScrolling];
1543 - (void)scrollViewDidScroll:(UIScrollView *)scrollView
1545 if (![self usesStandardContentView])
1546 [_customContentView scrollViewDidScroll:(UIScrollView *)scrollView];
1548 [self _updateVisibleContentRects];
1550 if (WebKit::RemoteLayerTreeScrollingPerformanceData* scrollPerfData = _page->scrollingPerformanceData())
1551 scrollPerfData->didScroll([self visibleRectInViewCoordinates]);
1554 - (void)scrollViewDidZoom:(UIScrollView *)scrollView
1556 [self _updateScrollViewBackground];
1557 [self _updateVisibleContentRects];
1560 - (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale
1562 ASSERT(scrollView == _scrollView);
1563 [self _updateVisibleContentRects];
1564 [_contentView didZoomToScale:scale];
1567 - (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
1569 [self _didFinishScrolling];
1572 - (void)_scrollViewDidInterruptDecelerating:(UIScrollView *)scrollView
1574 if (![self usesStandardContentView])
1577 [_contentView didInterruptScrolling];
1578 [self _updateVisibleContentRects];
1581 - (void)_frameOrBoundsChanged
1583 CGRect bounds = self.bounds;
1584 [_scrollView setFrame:bounds];
1586 if (_dynamicViewportUpdateMode == DynamicViewportUpdateMode::NotResizing) {
1587 if (!_overridesMinimumLayoutSize)
1588 _page->setViewportConfigurationMinimumLayoutSize(WebCore::FloatSize(bounds.size));
1589 if (!_overridesMaximumUnobscuredSize)
1590 _page->setMaximumUnobscuredSize(WebCore::FloatSize(bounds.size));
1591 if (WebKit::DrawingAreaProxy* drawingArea = _page->drawingArea())
1592 drawingArea->setSize(WebCore::IntSize(bounds.size), WebCore::IntSize(), WebCore::IntSize());
1595 [_customContentView web_setMinimumSize:bounds.size];
1596 [self _updateVisibleContentRects];
1599 // Unobscured content rect where the user can interact. When the keyboard is up, this should be the area above or bellow the keyboard, wherever there is enough space.
1600 - (CGRect)_contentRectForUserInteraction
1602 // FIXME: handle split keyboard.
1603 UIEdgeInsets obscuredInsets = _obscuredInsets;
1604 obscuredInsets.bottom = std::max(_obscuredInsets.bottom, _inputViewBounds.size.height);
1605 CGRect unobscuredRect = UIEdgeInsetsInsetRect(self.bounds, obscuredInsets);
1606 return [self convertRect:unobscuredRect toView:self._currentContentView];
1609 // Ideally UIScrollView would expose this for us: <rdar://problem/21394567>.
1610 - (BOOL)_scrollViewIsRubberBanding
1612 float deviceScaleFactor = _page->deviceScaleFactor();
1614 CGPoint contentOffset = [_scrollView contentOffset];
1615 CGPoint boundedOffset = contentOffsetBoundedInValidRange(_scrollView.get(), contentOffset);
1616 return !pointsEqualInDevicePixels(contentOffset, boundedOffset, deviceScaleFactor);
1619 - (void)_updateVisibleContentRects
1621 if (![self usesStandardContentView]) {
1622 [_customContentView web_computedContentInsetDidChange];
1626 if (_delayUpdateVisibleContentRects) {
1627 _hadDelayedUpdateVisibleContentRects = YES;
1631 if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing)
1634 if (_needsResetViewStateAfterCommitLoadForMainFrame)
1637 if ([_scrollView isZoomBouncing])
1640 CGRect fullViewRect = self.bounds;
1641 CGRect visibleRectInContentCoordinates = _frozenVisibleContentRect ? _frozenVisibleContentRect.value() : [self convertRect:fullViewRect toView:_contentView.get()];
1643 CGRect unobscuredRect = UIEdgeInsetsInsetRect(fullViewRect, [self _computedContentInset]);
1644 CGRect unobscuredRectInContentCoordinates = _frozenUnobscuredContentRect ? _frozenUnobscuredContentRect.value() : [self convertRect:unobscuredRect toView:_contentView.get()];
1646 CGFloat scaleFactor = contentZoomScale(self);
1648 BOOL isStableState = !(_isChangingObscuredInsetsInteractively || [_scrollView isDragging] || [_scrollView isDecelerating] || [_scrollView isZooming] || [_scrollView _isAnimatingZoom] || [_scrollView _isScrollingToTop] || [self _scrollViewIsRubberBanding]);
1650 // FIXME: this can be made static after we stop supporting iOS 8.x.
1651 if (isStableState && [_scrollView respondsToSelector:@selector(_isInterruptingDeceleration)])
1652 isStableState = ![_scrollView performSelector:@selector(_isInterruptingDeceleration)];
1654 #if ENABLE(CSS_SCROLL_SNAP) && ENABLE(ASYNC_SCROLLING)
1655 if (isStableState) {
1656 WebKit::RemoteScrollingCoordinatorProxy* coordinator = _page->scrollingCoordinatorProxy();
1657 if (coordinator && coordinator->hasActiveSnapPoint()) {
1658 CGRect fullViewRect = self.bounds;
1659 CGRect unobscuredRect = UIEdgeInsetsInsetRect(fullViewRect, [self _computedContentInset]);
1661 CGPoint currentPoint = [_scrollView contentOffset];
1662 CGPoint activePoint = coordinator->nearestActiveContentInsetAdjustedSnapPoint(unobscuredRect.origin.y, currentPoint);
1664 if (!CGPointEqualToPoint(activePoint, currentPoint)) {
1665 RetainPtr<WKScrollView> strongScrollView = _scrollView;
1666 dispatch_async(dispatch_get_main_queue(), [strongScrollView, activePoint] {
1667 [strongScrollView setContentOffset:activePoint animated:NO];
1674 [_contentView didUpdateVisibleRect:visibleRectInContentCoordinates
1675 unobscuredRect:unobscuredRectInContentCoordinates
1676 unobscuredRectInScrollViewCoordinates:unobscuredRect
1677 scale:scaleFactor minimumScale:[_scrollView minimumZoomScale]
1678 inStableState:isStableState isChangingObscuredInsetsInteractively:_isChangingObscuredInsetsInteractively];
1681 - (void)_didFinishLoadForMainFrame
1683 if (_gestureController)
1684 _gestureController->didFinishLoadForMainFrame();
1687 - (void)_didFailLoadForMainFrame
1689 if (_gestureController)
1690 _gestureController->didFailLoadForMainFrame();
1693 - (void)_didSameDocumentNavigationForMainFrame:(WebKit::SameDocumentNavigationType)navigationType
1695 [_customContentView web_didSameDocumentNavigation:toAPI(navigationType)];
1697 if (_gestureController)
1698 _gestureController->didSameDocumentNavigationForMainFrame(navigationType);
1701 - (void)_keyboardChangedWithInfo:(NSDictionary *)keyboardInfo adjustScrollView:(BOOL)adjustScrollView
1703 NSValue *endFrameValue = [keyboardInfo objectForKey:UIKeyboardFrameEndUserInfoKey];
1707 // The keyboard rect is always in screen coordinates. In the view services case the window does not
1708 // have the interface orientation rotation transformation; its host does. So, it makes no sense to
1709 // clip the keyboard rect against its screen.
1710 if ([[self window] _isHostedInAnotherProcess])
1711 _inputViewBounds = [self.window convertRect:[endFrameValue CGRectValue] fromWindow:nil];
1713 _inputViewBounds = [self.window convertRect:CGRectIntersection([endFrameValue CGRectValue], self.window.screen.bounds) fromWindow:nil];
1715 [self _updateVisibleContentRects];
1717 if (adjustScrollView)
1718 [_scrollView _adjustForAutomaticKeyboardInfo:keyboardInfo animated:YES lastAdjustment:&_lastAdjustmentForScroller];
1721 - (BOOL)_shouldUpdateKeyboardWithInfo:(NSDictionary *)keyboardInfo
1723 if ([_contentView isAssistingNode])
1726 NSNumber *isLocalKeyboard = [keyboardInfo valueForKey:UIKeyboardIsLocalUserInfoKey];
1727 return isLocalKeyboard && !isLocalKeyboard.boolValue;
1730 - (void)_keyboardWillChangeFrame:(NSNotification *)notification
1732 if ([self _shouldUpdateKeyboardWithInfo:notification.userInfo])
1733 [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES];
1736 - (void)_keyboardDidChangeFrame:(NSNotification *)notification
1738 [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:NO];
1741 - (void)_keyboardWillShow:(NSNotification *)notification
1743 if ([self _shouldUpdateKeyboardWithInfo:notification.userInfo])
1744 [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES];
1747 - (void)_keyboardWillHide:(NSNotification *)notification
1749 // Ignore keyboard will hide notifications sent during rotation. They're just there for
1750 // backwards compatibility reasons and processing the will hide notification would
1751 // temporarily screw up the the unobscured view area.
1752 if ([[UIPeripheralHost sharedInstance] rotationState])
1755 [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES];
1758 - (void)_windowDidRotate:(NSNotification *)notification
1760 if (!_overridesInterfaceOrientation)
1761 _page->setDeviceOrientation(deviceOrientation());
1764 - (void)_contentSizeCategoryDidChange:(NSNotification *)notification
1766 _page->contentSizeCategoryDidChange([self _contentSizeCategory]);
1769 - (NSString *)_contentSizeCategory
1771 return [[UIApplication sharedApplication] preferredContentSizeCategory];
1774 - (void)setAllowsBackForwardNavigationGestures:(BOOL)allowsBackForwardNavigationGestures
1776 if (_allowsBackForwardNavigationGestures == allowsBackForwardNavigationGestures)
1779 _allowsBackForwardNavigationGestures = allowsBackForwardNavigationGestures;
1781 if (allowsBackForwardNavigationGestures) {
1782 if (!_gestureController) {
1783 _gestureController = std::make_unique<WebKit::ViewGestureController>(*_page);
1784 _gestureController->installSwipeHandler(self, [self scrollView]);
1785 _gestureController->setAlternateBackForwardListSourceView([_configuration _alternateWebViewForNavigationGestures]);
1788 _gestureController = nullptr;
1790 _page->setShouldRecordNavigationSnapshots(allowsBackForwardNavigationGestures);
1793 - (BOOL)allowsBackForwardNavigationGestures
1795 return _allowsBackForwardNavigationGestures;
1798 - (void)_navigationGestureDidBegin
1800 // During a back/forward swipe, there's a view interposed between this view and the content view that has
1801 // an offset and animation on it, which results in computing incorrect rectangles. Work around by using
1802 // frozen rects during swipes.
1803 CGRect fullViewRect = self.bounds;
1804 CGRect unobscuredRect = UIEdgeInsetsInsetRect(fullViewRect, [self _computedContentInset]);
1806 _frozenVisibleContentRect = [self convertRect:fullViewRect toView:_contentView.get()];
1807 _frozenUnobscuredContentRect = [self convertRect:unobscuredRect toView:_contentView.get()];
1810 - (void)_navigationGestureDidEnd
1812 _frozenVisibleContentRect = Nullopt;
1813 _frozenUnobscuredContentRect = Nullopt;
1816 #endif // PLATFORM(IOS)
1818 #pragma mark OS X-specific methods
1822 - (BOOL)becomeFirstResponder
1824 return [[self window] makeFirstResponder: _wkView.get()];
1827 - (BOOL)acceptsFirstResponder
1829 return [_wkView acceptsFirstResponder];
1832 - (void)resizeSubviewsWithOldSize:(NSSize)oldSize
1834 [_wkView setFrame:self.bounds];
1837 - (void)setAllowsBackForwardNavigationGestures:(BOOL)allowsBackForwardNavigationGestures
1839 [_wkView setAllowsBackForwardNavigationGestures:allowsBackForwardNavigationGestures];
1842 - (BOOL)allowsBackForwardNavigationGestures
1844 return [_wkView allowsBackForwardNavigationGestures];
1847 - (void)setAllowsMagnification:(BOOL)allowsMagnification
1849 [_wkView setAllowsMagnification:allowsMagnification];
1852 - (BOOL)allowsMagnification
1854 return [_wkView allowsMagnification];
1857 - (void)setMagnification:(CGFloat)magnification
1859 [_wkView setMagnification:magnification];
1862 - (CGFloat)magnification
1864 return [_wkView magnification];
1867 - (void)setMagnification:(CGFloat)magnification centeredAtPoint:(CGPoint)point
1869 [_wkView setMagnification:magnification centeredAtPoint:NSPointFromCGPoint(point)];
1872 - (BOOL)_ignoresNonWheelEvents
1874 return [_wkView _ignoresNonWheelEvents];
1877 - (void)_setIgnoresNonWheelEvents:(BOOL)ignoresNonWheelEvents
1879 [_wkView _setIgnoresNonWheelEvents:ignoresNonWheelEvents];
1882 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
1884 return [_wkView draggingEntered:sender];
1887 - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
1889 return [_wkView draggingUpdated:sender];
1892 - (void)draggingExited:(id <NSDraggingInfo>)sender
1894 [_wkView draggingExited:sender];
1897 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
1899 return [_wkView prepareForDragOperation:sender];
1902 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
1904 return [_wkView performDragOperation:sender];
1906 #endif // PLATFORM(MAC)
1910 @implementation WKWebView (WKPrivate)
1914 return _page->isEditable();
1917 - (void)_setEditable:(BOOL)editable
1919 _page->setEditable(editable);
1921 [_wkView _addFontPanelObserver];
1925 - (_WKRemoteObjectRegistry *)_remoteObjectRegistry
1927 if (!_remoteObjectRegistry) {
1928 _remoteObjectRegistry = adoptNS([[_WKRemoteObjectRegistry alloc] _initWithMessageSender:*_page]);
1929 _page->process().processPool().addMessageReceiver(Messages::RemoteObjectRegistry::messageReceiverName(), _page->pageID(), [_remoteObjectRegistry remoteObjectRegistry]);
1932 return _remoteObjectRegistry.get();
1935 - (WKBrowsingContextHandle *)_handle
1937 return [[[WKBrowsingContextHandle alloc] _initWithPageID:_page->pageID()] autorelease];
1940 - (_WKRenderingProgressEvents)_observedRenderingProgressEvents
1942 return _observedRenderingProgressEvents;
1945 - (id <WKHistoryDelegatePrivate>)_historyDelegate
1947 return _navigationState->historyDelegate().autorelease();
1950 - (void)_setHistoryDelegate:(id <WKHistoryDelegatePrivate>)historyDelegate
1952 _page->setHistoryClient(_navigationState->createHistoryClient());
1953 _navigationState->setHistoryDelegate(historyDelegate);
1956 - (NSURL *)_unreachableURL
1958 return [NSURL _web_URLWithWTFString:_page->pageLoadState().unreachableURL()];
1961 - (void)_loadAlternateHTMLString:(NSString *)string baseURL:(NSURL *)baseURL forUnreachableURL:(NSURL *)unreachableURL
1963 _page->loadAlternateHTMLString(string, [baseURL _web_originalDataAsWTFString], [unreachableURL _web_originalDataAsWTFString]);
1966 - (NSArray *)_certificateChain
1968 if (WebKit::WebFrameProxy* mainFrame = _page->mainFrame())
1969 return mainFrame->certificateInfo() ? (NSArray *)mainFrame->certificateInfo()->certificateInfo().certificateChain() : nil;
1974 - (NSURL *)_committedURL
1976 return [NSURL _web_URLWithWTFString:_page->pageLoadState().url()];
1979 - (NSString *)_MIMEType
1981 if (_page->mainFrame())
1982 return _page->mainFrame()->mimeType();
1987 - (NSString *)_userAgent
1989 return _page->userAgent();
1992 - (NSString *)_applicationNameForUserAgent
1994 return _page->applicationNameForUserAgent();
1997 - (void)_setApplicationNameForUserAgent:(NSString *)applicationNameForUserAgent
1999 _page->setApplicationNameForUserAgent(applicationNameForUserAgent);
2002 - (NSString *)_customUserAgent
2004 return self.customUserAgent;
2007 - (void)_setCustomUserAgent:(NSString *)customUserAgent
2009 self.customUserAgent = customUserAgent;
2012 - (void)_setUserContentExtensionsEnabled:(BOOL)userContentExtensionsEnabled
2014 _page->setUserContentExtensionsEnabled(userContentExtensionsEnabled);
2017 - (BOOL)_userContentExtensionsEnabled
2019 return _page->userContentExtensionsEnabled();
2022 - (pid_t)_webProcessIdentifier
2024 return _page->isValid() ? _page->processIdentifier() : 0;
2027 - (void)_killWebContentProcess
2029 if (!_page->isValid())
2032 _page->process().terminate();
2035 - (void)_killWebContentProcessAndResetState
2037 _page->terminateProcess();
2041 static WebCore::FloatSize activeMinimumLayoutSize(WKWebView *webView, const CGRect& bounds)
2043 return WebCore::FloatSize(webView->_overridesMinimumLayoutSize ? webView->_minimumLayoutSizeOverride : bounds.size);
2046 static WebCore::FloatSize activeMaximumUnobscuredSize(WKWebView *webView, const CGRect& bounds)
2048 return WebCore::FloatSize(webView->_overridesMaximumUnobscuredSize ? webView->_maximumUnobscuredSizeOverride : bounds.size);
2051 static int32_t activeOrientation(WKWebView *webView)
2053 return webView->_overridesInterfaceOrientation ? deviceOrientationForUIInterfaceOrientation(webView->_interfaceOrientationOverride) : webView->_page->deviceOrientation();
2056 - (void (^)(void))_retainActiveFocusedState
2058 ++_activeFocusedStateRetainCount;
2060 // FIXME: Use something like CompletionHandlerCallChecker to ensure that the returned block is called before it's released.
2062 --_activeFocusedStateRetainCount;
2063 } copy] autorelease];
2066 - (void)_becomeFirstResponderWithSelectionMovingForward:(BOOL)selectingForward completionHandler:(void (^)(BOOL didBecomeFirstResponder))completionHandler
2068 typeof(completionHandler) completionHandlerCopy = nil;
2069 if (completionHandler)
2070 completionHandlerCopy = Block_copy(completionHandler);
2072 [_contentView _becomeFirstResponderWithSelectionMovingForward:selectingForward completionHandler:[completionHandlerCopy](BOOL didBecomeFirstResponder) {
2073 if (!completionHandlerCopy)
2076 completionHandlerCopy(didBecomeFirstResponder);
2077 Block_release(completionHandlerCopy);
2081 - (id)_snapshotLayerContentsForBackForwardListItem:(WKBackForwardListItem *)item
2083 if (_page->backForwardList().currentItem() == &item._item)
2084 _page->recordNavigationSnapshot(*_page->backForwardList().currentItem());
2086 if (auto* viewSnapshot = item._item.snapshot())
2087 return viewSnapshot->asLayerContents();
2094 - (void)_didRelaunchProcess
2097 CGRect bounds = self.bounds;
2098 WebCore::FloatSize minimalLayoutSize = activeMinimumLayoutSize(self, bounds);
2099 _page->setViewportConfigurationMinimumLayoutSize(minimalLayoutSize);
2100 _page->setMaximumUnobscuredSize(activeMaximumUnobscuredSize(self, bounds));
2104 - (NSData *)_sessionStateData
2106 WebKit::SessionState sessionState = _page->sessionState();
2108 // FIXME: This should not use the legacy session state encoder.
2109 return [wrapper(*WebKit::encodeLegacySessionState(sessionState).release().leakRef()) autorelease];
2112 - (_WKSessionState *)_sessionState
2114 return adoptNS([[_WKSessionState alloc] _initWithSessionState:_page->sessionState()]).autorelease();
2117 - (void)_restoreFromSessionStateData:(NSData *)sessionStateData
2119 // FIXME: This should not use the legacy session state decoder.
2120 WebKit::SessionState sessionState;
2121 if (!WebKit::decodeLegacySessionState(static_cast<const uint8_t*>(sessionStateData.bytes), sessionStateData.length, sessionState))
2124 _page->restoreFromSessionState(WTF::move(sessionState), true);
2127 - (WKNavigation *)_restoreSessionState:(_WKSessionState *)sessionState andNavigate:(BOOL)navigate
2129 auto navigation = _page->restoreFromSessionState(sessionState->_sessionState, navigate);
2133 return [wrapper(*navigation.release().leakRef()) autorelease];
2141 - (BOOL)_allowsRemoteInspection
2143 #if ENABLE(REMOTE_INSPECTOR)
2144 return _page->allowsRemoteInspection();
2150 - (void)_setAllowsRemoteInspection:(BOOL)allow
2152 #if ENABLE(REMOTE_INSPECTOR)
2153 _page->setAllowsRemoteInspection(allow);
2157 - (BOOL)_addsVisitedLinks
2159 return _page->addsVisitedLinks();
2162 - (void)_setAddsVisitedLinks:(BOOL)addsVisitedLinks
2164 _page->setAddsVisitedLinks(addsVisitedLinks);
2167 - (BOOL)_networkRequestsInProgress
2169 return _page->pageLoadState().networkRequestsInProgress();
2172 static inline WebCore::LayoutMilestones layoutMilestones(_WKRenderingProgressEvents events)
2174 WebCore::LayoutMilestones milestones = 0;
2176 if (events & _WKRenderingProgressEventFirstLayout)
2177 milestones |= WebCore::DidFirstLayout;
2179 if (events & _WKRenderingProgressEventFirstVisuallyNonEmptyLayout)
2180 milestones |= WebCore::DidFirstVisuallyNonEmptyLayout;
2182 if (events & _WKRenderingProgressEventFirstPaintWithSignificantArea)
2183 milestones |= WebCore::DidHitRelevantRepaintedObjectsAreaThreshold;
2185 if (events & _WKRenderingProgressEventReachedSessionRestorationRenderTreeSizeThreshold)
2186 milestones |= WebCore::ReachedSessionRestorationRenderTreeSizeThreshold;
2188 if (events & _WKRenderingProgressEventFirstLayoutAfterSuppressedIncrementalRendering)
2189 milestones |= WebCore::DidFirstLayoutAfterSuppressedIncrementalRendering;
2191 if (events & _WKRenderingProgressEventFirstPaintAfterSuppressedIncrementalRendering)
2192 milestones |= WebCore::DidFirstPaintAfterSuppressedIncrementalRendering;
2197 - (void)_setObservedRenderingProgressEvents:(_WKRenderingProgressEvents)observedRenderingProgressEvents
2199 _observedRenderingProgressEvents = observedRenderingProgressEvents;
2200 _page->listenForLayoutMilestones(layoutMilestones(observedRenderingProgressEvents));
2203 - (void)_getMainResourceDataWithCompletionHandler:(void (^)(NSData *, NSError *))completionHandler
2205 auto handler = adoptNS([completionHandler copy]);
2207 _page->getMainResourceDataOfFrame(_page->mainFrame(), [handler](API::Data* data, WebKit::CallbackBase::Error error) {
2208 void (^completionHandlerBlock)(NSData *, NSError *) = (void (^)(NSData *, NSError *))handler.get();
2209 if (error != WebKit::CallbackBase::Error::None) {
2210 // FIXME: Pipe a proper error in from the WebPageProxy.
2211 RetainPtr<NSError> error = adoptNS([[NSError alloc] init]);
2212 completionHandlerBlock(nil, error.get());
2214 completionHandlerBlock(wrapper(*data), nil);
2218 - (void)_getWebArchiveDataWithCompletionHandler:(void (^)(NSData *, NSError *))completionHandler
2220 auto handler = adoptNS([completionHandler copy]);
2222 _page->getWebArchiveOfFrame(_page->mainFrame(), [handler](API::Data* data, WebKit::CallbackBase::Error error) {
2223 void (^completionHandlerBlock)(NSData *, NSError *) = (void (^)(NSData *, NSError *))handler.get();
2224 if (error != WebKit::CallbackBase::Error::None) {
2225 // FIXME: Pipe a proper error in from the WebPageProxy.
2226 RetainPtr<NSError> error = adoptNS([[NSError alloc] init]);
2227 completionHandlerBlock(nil, error.get());
2229 completionHandlerBlock(wrapper(*data), nil);
2233 - (_WKPaginationMode)_paginationMode
2235 switch (_page->paginationMode()) {
2236 case WebCore::Pagination::Unpaginated:
2237 return _WKPaginationModeUnpaginated;
2238 case WebCore::Pagination::LeftToRightPaginated:
2239 return _WKPaginationModeLeftToRight;
2240 case WebCore::Pagination::RightToLeftPaginated:
2241 return _WKPaginationModeRightToLeft;
2242 case WebCore::Pagination::TopToBottomPaginated:
2243 return _WKPaginationModeTopToBottom;
2244 case WebCore::Pagination::BottomToTopPaginated:
2245 return _WKPaginationModeBottomToTop;
2248 ASSERT_NOT_REACHED();
2249 return _WKPaginationModeUnpaginated;
2252 - (void)_setPaginationMode:(_WKPaginationMode)paginationMode
2254 WebCore::Pagination::Mode mode;
2255 switch (paginationMode) {
2256 case _WKPaginationModeUnpaginated:
2257 mode = WebCore::Pagination::Unpaginated;
2259 case _WKPaginationModeLeftToRight:
2260 mode = WebCore::Pagination::LeftToRightPaginated;
2262 case _WKPaginationModeRightToLeft:
2263 mode = WebCore::Pagination::RightToLeftPaginated;
2265 case _WKPaginationModeTopToBottom:
2266 mode = WebCore::Pagination::TopToBottomPaginated;
2268 case _WKPaginationModeBottomToTop:
2269 mode = WebCore::Pagination::BottomToTopPaginated;
2275 _page->setPaginationMode(mode);
2278 - (BOOL)_paginationBehavesLikeColumns
2280 return _page->paginationBehavesLikeColumns();
2283 - (void)_setPaginationBehavesLikeColumns:(BOOL)behavesLikeColumns
2285 _page->setPaginationBehavesLikeColumns(behavesLikeColumns);
2288 - (CGFloat)_pageLength
2290 return _page->pageLength();
2293 - (void)_setPageLength:(CGFloat)pageLength
2295 _page->setPageLength(pageLength);
2298 - (CGFloat)_gapBetweenPages
2300 return _page->gapBetweenPages();
2303 - (void)_setGapBetweenPages:(CGFloat)gapBetweenPages
2305 _page->setGapBetweenPages(gapBetweenPages);
2308 - (NSUInteger)_pageCount
2310 return _page->pageCount();
2313 - (BOOL)_supportsTextZoom
2315 return _page->supportsTextZoom();
2318 - (double)_textZoomFactor
2320 return _page->textZoomFactor();
2323 - (void)_setTextZoomFactor:(double)zoomFactor
2325 _page->setTextZoomFactor(zoomFactor);
2328 - (double)_pageZoomFactor
2330 return _page->pageZoomFactor();
2333 - (void)_setPageZoomFactor:(double)zoomFactor
2335 _page->setPageZoomFactor(zoomFactor);
2338 - (id <_WKDiagnosticLoggingDelegate>)_diagnosticLoggingDelegate
2340 return [static_cast<WebKit::DiagnosticLoggingClient&>(_page->diagnosticLoggingClient()).delegate().leakRef() autorelease];
2343 - (void)_setDiagnosticLoggingDelegate:(id<_WKDiagnosticLoggingDelegate>)diagnosticLoggingDelegate
2345 static_cast<WebKit::DiagnosticLoggingClient&>(_page->diagnosticLoggingClient()).setDelegate(diagnosticLoggingDelegate);
2348 - (id <_WKFindDelegate>)_findDelegate
2350 return [static_cast<WebKit::FindClient&>(_page->findClient()).delegate().leakRef() autorelease];
2353 - (void)_setFindDelegate:(id<_WKFindDelegate>)findDelegate
2355 static_cast<WebKit::FindClient&>(_page->findClient()).setDelegate(findDelegate);
2358 static inline WebKit::FindOptions toFindOptions(_WKFindOptions wkFindOptions)
2360 unsigned findOptions = 0;
2362 if (wkFindOptions & _WKFindOptionsCaseInsensitive)
2363 findOptions |= WebKit::FindOptionsCaseInsensitive;
2364 if (wkFindOptions & _WKFindOptionsAtWordStarts)
2365 findOptions |= WebKit::FindOptionsAtWordStarts;
2366 if (wkFindOptions & _WKFindOptionsTreatMedialCapitalAsWordStart)
2367 findOptions |= WebKit::FindOptionsTreatMedialCapitalAsWordStart;
2368 if (wkFindOptions & _WKFindOptionsBackwards)
2369 findOptions |= WebKit::FindOptionsBackwards;
2370 if (wkFindOptions & _WKFindOptionsWrapAround)
2371 findOptions |= WebKit::FindOptionsWrapAround;
2372 if (wkFindOptions & _WKFindOptionsShowOverlay)
2373 findOptions |= WebKit::FindOptionsShowOverlay;
2374 if (wkFindOptions & _WKFindOptionsShowFindIndicator)
2375 findOptions |= WebKit::FindOptionsShowFindIndicator;
2376 if (wkFindOptions & _WKFindOptionsShowHighlight)
2377 findOptions |= WebKit::FindOptionsShowHighlight;
2378 if (wkFindOptions & _WKFindOptionsDetermineMatchIndex)
2379 findOptions |= WebKit::FindOptionsDetermineMatchIndex;
2381 return static_cast<WebKit::FindOptions>(findOptions);
2384 - (void)_countStringMatches:(NSString *)string options:(_WKFindOptions)options maxCount:(NSUInteger)maxCount
2387 if (_customContentView) {
2388 [_customContentView web_countStringMatches:string options:options maxCount:maxCount];
2392 _page->countStringMatches(string, toFindOptions(options), maxCount);
2395 - (void)_findString:(NSString *)string options:(_WKFindOptions)options maxCount:(NSUInteger)maxCount
2398 if (_customContentView) {
2399 [_customContentView web_findString:string options:options maxCount:maxCount];
2403 _page->findString(string, toFindOptions(options), maxCount);
2409 if (_customContentView) {
2410 [_customContentView web_hideFindUI];
2414 _page->hideFindUI();
2417 - (void)_saveBackForwardSnapshotForItem:(WKBackForwardListItem *)item
2419 _page->recordNavigationSnapshot(item._item);
2422 - (id <_WKFormDelegate>)_formDelegate
2424 return _formDelegate.getAutoreleased();
2427 - (void)_setFormDelegate:(id <_WKFormDelegate>)formDelegate
2429 _formDelegate = formDelegate;
2431 class FormClient : public API::FormClient {
2433 explicit FormClient(WKWebView *webView)
2434 : m_webView(webView)
2438 virtual ~FormClient() { }
2440 virtual void willSubmitForm(WebKit::WebPageProxy&, WebKit::WebFrameProxy&, WebKit::WebFrameProxy& sourceFrame, const Vector<std::pair<WTF::String, WTF::String>>& textFieldValues, API::Object* userData, Ref<WebKit::WebFormSubmissionListenerProxy>&& listener) override
2442 if (userData && userData->type() != API::Object::Type::Data) {
2443 ASSERT(!userData || userData->type() == API::Object::Type::Data);
2444 m_webView->_page->process().connection()->markCurrentlyDispatchedMessageAsInvalid();
2445 listener->continueSubmission();
2449 auto formDelegate = m_webView->_formDelegate.get();
2451 if (![formDelegate respondsToSelector:@selector(_webView:willSubmitFormValues:userObject:submissionHandler:)]) {
2452 listener->continueSubmission();
2456 auto valueMap = adoptNS([[NSMutableDictionary alloc] initWithCapacity:textFieldValues.size()]);
2457 for (const auto& pair : textFieldValues)
2458 [valueMap setObject:pair.second forKey:pair.first];
2460 NSObject <NSSecureCoding> *userObject = nil;
2461 if (API::Data* data = static_cast<API::Data*>(userData)) {
2462 auto nsData = adoptNS([[NSData alloc] initWithBytesNoCopy:const_cast<void*>(static_cast<const void*>(data->bytes())) length:data->size() freeWhenDone:NO]);
2463 auto unarchiver = adoptNS([[NSKeyedUnarchiver alloc] initForReadingWithData:nsData.get()]);
2464 [unarchiver setRequiresSecureCoding:YES];
2466 userObject = [unarchiver decodeObjectOfClass:[NSObject class] forKey:@"userObject"];
2467 } @catch (NSException *exception) {
2468 LOG_ERROR("Failed to decode user data: %@", exception);
2472 RefPtr<WebKit::WebFormSubmissionListenerProxy> localListener = WTF::move(listener);
2473 RefPtr<WebKit::CompletionHandlerCallChecker> checker = WebKit::CompletionHandlerCallChecker::create(formDelegate.get(), @selector(_webView:willSubmitFormValues:userObject:submissionHandler:));
2474 [formDelegate _webView:m_webView willSubmitFormValues:valueMap.get() userObject:userObject submissionHandler:[localListener, checker] {
2475 checker->didCallCompletionHandler();
2476 localListener->continueSubmission();
2481 WKWebView *m_webView;
2485 _page->setFormClient(std::make_unique<FormClient>(self));
2487 _page->setFormClient(nullptr);
2490 - (BOOL)_isDisplayingStandaloneImageDocument
2492 if (auto* mainFrame = _page->mainFrame())
2493 return mainFrame->isDisplayingStandaloneImageDocument();
2497 - (BOOL)_isDisplayingStandaloneMediaDocument
2499 if (auto* mainFrame = _page->mainFrame())
2500 return mainFrame->isDisplayingStandaloneMediaDocument();
2504 - (BOOL)_isShowingNavigationGestureSnapshot
2506 return _page->isShowingNavigationGestureSnapshot();
2509 - (_WKLayoutMode)_layoutMode
2512 switch ([_wkView _layoutMode]) {
2513 case kWKLayoutModeFixedSize:
2514 return _WKLayoutModeFixedSize;
2515 case kWKLayoutModeDynamicSizeComputedFromViewScale:
2516 return _WKLayoutModeDynamicSizeComputedFromViewScale;
2517 case kWKLayoutModeDynamicSizeComputedFromMinimumDocumentSize:
2518 return _WKLayoutModeDynamicSizeComputedFromMinimumDocumentSize;
2519 case kWKLayoutModeViewSize:
2521 return _WKLayoutModeViewSize;
2524 return _page->useFixedLayout() ? _WKLayoutModeFixedSize : _WKLayoutModeViewSize;
2528 - (void)_setLayoutMode:(_WKLayoutMode)layoutMode
2531 WKLayoutMode wkViewLayoutMode;
2532 switch (layoutMode) {
2533 case _WKLayoutModeFixedSize:
2534 wkViewLayoutMode = kWKLayoutModeFixedSize;
2536 case _WKLayoutModeDynamicSizeComputedFromViewScale:
2537 wkViewLayoutMode = kWKLayoutModeDynamicSizeComputedFromViewScale;
2539 case _WKLayoutModeDynamicSizeComputedFromMinimumDocumentSize:
2540 wkViewLayoutMode = kWKLayoutModeDynamicSizeComputedFromMinimumDocumentSize;
2542 case _WKLayoutModeViewSize:
2544 wkViewLayoutMode = kWKLayoutModeViewSize;
2547 [_wkView _setLayoutMode:wkViewLayoutMode];
2549 _page->setUseFixedLayout(layoutMode == _WKLayoutModeFixedSize || layoutMode == _WKLayoutModeDynamicSizeComputedFromViewScale);
2553 - (CGSize)_fixedLayoutSize
2555 return _page->fixedLayoutSize();
2558 - (void)_setFixedLayoutSize:(CGSize)fixedLayoutSize
2560 _page->setFixedLayoutSize(WebCore::expandedIntSize(WebCore::FloatSize(fixedLayoutSize)));
2563 - (CGFloat)_viewScale
2565 return _page->viewScaleFactor();
2568 - (void)_setViewScale:(CGFloat)viewScale
2571 [_wkView _setViewScale:viewScale];
2573 if (viewScale <= 0 || isnan(viewScale) || isinf(viewScale))
2574 [NSException raise:NSInvalidArgumentException format:@"View scale should be a positive number"];
2576 _page->scaleView(viewScale);
2580 #pragma mark scrollperf methods
2582 - (void)_setScrollPerformanceDataCollectionEnabled:(BOOL)enabled
2584 _page->setScrollPerformanceDataCollectionEnabled(enabled);
2587 - (BOOL)_scrollPerformanceDataCollectionEnabled
2589 return _page->scrollPerformanceDataCollectionEnabled();
2592 - (NSArray *)_scrollPerformanceData
2595 if (WebKit::RemoteLayerTreeScrollingPerformanceData* scrollPerfData = _page->scrollingPerformanceData())
2596 return scrollPerfData->data();
2601 #pragma mark media playback restrictions
2603 - (BOOL)_allowsMediaDocumentInlinePlayback
2606 return _page->allowsMediaDocumentInlinePlayback();
2612 - (void)_setAllowsMediaDocumentInlinePlayback:(BOOL)flag
2615 _page->setAllowsMediaDocumentInlinePlayback(flag);
2619 #pragma mark iOS-specific methods
2623 - (CGSize)_minimumLayoutSizeOverride
2625 ASSERT(_overridesMinimumLayoutSize);
2626 return _minimumLayoutSizeOverride;
2629 - (void)_setMinimumLayoutSizeOverride:(CGSize)minimumLayoutSizeOverride
2631 _overridesMinimumLayoutSize = YES;
2632 if (CGSizeEqualToSize(_minimumLayoutSizeOverride, minimumLayoutSizeOverride))
2635 _minimumLayoutSizeOverride = minimumLayoutSizeOverride;
2636 if (_dynamicViewportUpdateMode == DynamicViewportUpdateMode::NotResizing)
2637 _page->setViewportConfigurationMinimumLayoutSize(WebCore::FloatSize(minimumLayoutSizeOverride));
2640 - (UIEdgeInsets)_obscuredInsets
2642 return _obscuredInsets;
2645 - (void)_setObscuredInsets:(UIEdgeInsets)obscuredInsets
2647 ASSERT(obscuredInsets.top >= 0);
2648 ASSERT(obscuredInsets.left >= 0);
2649 ASSERT(obscuredInsets.bottom >= 0);
2650 ASSERT(obscuredInsets.right >= 0);
2652 _haveSetObscuredInsets = YES;
2654 if (UIEdgeInsetsEqualToEdgeInsets(_obscuredInsets, obscuredInsets))
2657 _obscuredInsets = obscuredInsets;
2659 [self _updateVisibleContentRects];
2662 - (void)_setInterfaceOrientationOverride:(UIInterfaceOrientation)interfaceOrientation
2664 if (!_overridesInterfaceOrientation)
2665 [[NSNotificationCenter defaultCenter] removeObserver:self name:UIWindowDidRotateNotification object:nil];
2667 _overridesInterfaceOrientation = YES;
2669 if (interfaceOrientation == _interfaceOrientationOverride)
2672 _interfaceOrientationOverride = interfaceOrientation;
2674 if (_dynamicViewportUpdateMode == DynamicViewportUpdateMode::NotResizing)
2675 _page->setDeviceOrientation(deviceOrientationForUIInterfaceOrientation(_interfaceOrientationOverride));
2678 - (UIInterfaceOrientation)_interfaceOrientationOverride
2680 ASSERT(_overridesInterfaceOrientation);
2681 return _interfaceOrientationOverride;
2684 - (CGSize)_maximumUnobscuredSizeOverride
2686 ASSERT(_overridesMaximumUnobscuredSize);
2687 return _maximumUnobscuredSizeOverride;
2690 - (void)_setMaximumUnobscuredSizeOverride:(CGSize)size
2692 ASSERT(size.width <= self.bounds.size.width && size.height <= self.bounds.size.height);
2693 _overridesMaximumUnobscuredSize = YES;
2694 if (CGSizeEqualToSize(_maximumUnobscuredSizeOverride, size))
2697 _maximumUnobscuredSizeOverride = size;
2698 if (_dynamicViewportUpdateMode == DynamicViewportUpdateMode::NotResizing)
2699 _page->setMaximumUnobscuredSize(WebCore::FloatSize(size));
2702 - (void)_setBackgroundExtendsBeyondPage:(BOOL)backgroundExtends
2704 _page->setBackgroundExtendsBeyondPage(backgroundExtends);
2707 - (BOOL)_backgroundExtendsBeyondPage
2709 return _page->backgroundExtendsBeyondPage();
2712 - (void)_beginInteractiveObscuredInsetsChange
2714 ASSERT(!_isChangingObscuredInsetsInteractively);
2715 _isChangingObscuredInsetsInteractively = YES;
2718 - (void)_endInteractiveObscuredInsetsChange
2720 ASSERT(_isChangingObscuredInsetsInteractively);
2721 _isChangingObscuredInsetsInteractively = NO;
2722 [self _updateVisibleContentRects];
2725 - (void)_hideContentUntilNextUpdate
2727 if (auto* area = _page->drawingArea())
2728 area->hideContentUntilAnyUpdate();
2731 - (void)_beginAnimatedResizeWithUpdates:(void (^)(void))updateBlock
2733 CGRect oldBounds = self.bounds;
2734 WebCore::FloatRect oldUnobscuredContentRect = _page->unobscuredContentRect();
2736 if (_customContentView || !_hasCommittedLoadForMainFrame || CGRectIsEmpty(oldBounds) || oldUnobscuredContentRect.isEmpty()) {
2741 _dynamicViewportUpdateMode = DynamicViewportUpdateMode::ResizingWithAnimation;
2743 WebCore::FloatSize oldMinimumLayoutSize = activeMinimumLayoutSize(self, oldBounds);
2744 WebCore::FloatSize oldMaximumUnobscuredSize = activeMaximumUnobscuredSize(self, oldBounds);
2745 int32_t oldOrientation = activeOrientation(self);
2746 UIEdgeInsets oldObscuredInsets = _obscuredInsets;
2750 CGRect newBounds = self.bounds;
2751 WebCore::FloatSize newMinimumLayoutSize = activeMinimumLayoutSize(self, newBounds);
2752 WebCore::FloatSize newMaximumUnobscuredSize = activeMaximumUnobscuredSize(self, newBounds);
2753 int32_t newOrientation = activeOrientation(self);
2754 UIEdgeInsets newObscuredInsets = _obscuredInsets;
2755 CGRect futureUnobscuredRectInSelfCoordinates = UIEdgeInsetsInsetRect(newBounds, _obscuredInsets);
2756 CGRect contentViewBounds = [_contentView bounds];
2758 ASSERT_WITH_MESSAGE(!(_overridesMinimumLayoutSize && newMinimumLayoutSize.isEmpty()), "Clients controlling the layout size should maintain a valid layout size to minimize layouts.");
2759 if (CGRectIsEmpty(newBounds) || newMinimumLayoutSize.isEmpty() || CGRectIsEmpty(futureUnobscuredRectInSelfCoordinates) || CGRectIsEmpty(contentViewBounds)) {
2760 _dynamicViewportUpdateMode = DynamicViewportUpdateMode::NotResizing;
2761 [self _frameOrBoundsChanged];
2762 if (_overridesMinimumLayoutSize)
2763 _page->setViewportConfigurationMinimumLayoutSize(WebCore::FloatSize(newMinimumLayoutSize));
2764 if (_overridesMaximumUnobscuredSize)
2765 _page->setMaximumUnobscuredSize(WebCore::FloatSize(newMaximumUnobscuredSize));
2766 if (_overridesInterfaceOrientation)
2767 _page->setDeviceOrientation(newOrientation);
2768 [self _updateVisibleContentRects];
2772 if (CGRectEqualToRect(oldBounds, newBounds)
2773 && oldMinimumLayoutSize == newMinimumLayoutSize
2774 && oldMaximumUnobscuredSize == newMaximumUnobscuredSize
2775 && oldOrientation == newOrientation
2776 && UIEdgeInsetsEqualToEdgeInsets(oldObscuredInsets, newObscuredInsets)) {
2777 _dynamicViewportUpdateMode = DynamicViewportUpdateMode::NotResizing;
2778 [self _updateVisibleContentRects];
2782 _resizeAnimationTransformAdjustments = CATransform3DIdentity;
2784 NSUInteger indexOfContentView = [[_scrollView subviews] indexOfObject:_contentView.get()];
2785 _resizeAnimationView = adoptNS([[UIView alloc] init]);
2786 [_scrollView insertSubview:_resizeAnimationView.get() atIndex:indexOfContentView];
2787 [_resizeAnimationView addSubview:_contentView.get()];
2788 [_resizeAnimationView addSubview:[_contentView unscaledView]];
2790 CGSize contentSizeInContentViewCoordinates = contentViewBounds.size;
2791 [_scrollView setMinimumZoomScale:std::min(newMinimumLayoutSize.width() / contentSizeInContentViewCoordinates.width, [_scrollView minimumZoomScale])];
2792 [_scrollView setMaximumZoomScale:std::max(newMinimumLayoutSize.width() / contentSizeInContentViewCoordinates.width, [_scrollView maximumZoomScale])];
2794 // Compute the new scale to keep the current content width in the scrollview.
2795 CGFloat oldWebViewWidthInContentViewCoordinates = oldUnobscuredContentRect.width();
2796 CGFloat visibleContentViewWidthInContentCoordinates = std::min(contentSizeInContentViewCoordinates.width, oldWebViewWidthInContentViewCoordinates);
2797 CGFloat targetScale = newMinimumLayoutSize.width() / visibleContentViewWidthInContentCoordinates;
2798 CGFloat resizeAnimationViewAnimationScale = targetScale / contentZoomScale(self);
2799 [_resizeAnimationView setTransform:CGAffineTransformMakeScale(resizeAnimationViewAnimationScale, resizeAnimationViewAnimationScale)];
2801 // Compute a new position to keep the content centered.
2802 CGPoint originalContentCenter = oldUnobscuredContentRect.center();
2803 CGPoint originalContentCenterInSelfCoordinates = [self convertPoint:originalContentCenter fromView:_contentView.get()];
2804 CGPoint futureUnobscuredRectCenterInSelfCoordinates = CGPointMake(futureUnobscuredRectInSelfCoordinates.origin.x + futureUnobscuredRectInSelfCoordinates.size.width / 2, futureUnobscuredRectInSelfCoordinates.origin.y + futureUnobscuredRectInSelfCoordinates.size.height / 2);
2806 CGPoint originalContentOffset = [_scrollView contentOffset];
2807 CGPoint contentOffset = originalContentOffset;
2808 contentOffset.x += (originalContentCenterInSelfCoordinates.x - futureUnobscuredRectCenterInSelfCoordinates.x);
2809 contentOffset.y += (originalContentCenterInSelfCoordinates.y - futureUnobscuredRectCenterInSelfCoordinates.y);
2811 // Limit the new offset within the scrollview, we do not want to rubber band programmatically.
2812 CGSize futureContentSizeInSelfCoordinates = CGSizeMake(contentSizeInContentViewCoordinates.width * targetScale, contentSizeInContentViewCoordinates.height * targetScale);
2813 CGFloat maxHorizontalOffset = futureContentSizeInSelfCoordinates.width - newBounds.size.width + _obscuredInsets.right;
2814 contentOffset.x = std::min(contentOffset.x, maxHorizontalOffset);
2815 CGFloat maxVerticalOffset = futureContentSizeInSelfCoordinates.height - newBounds.size.height + _obscuredInsets.bottom;
2816 contentOffset.y = std::min(contentOffset.y, maxVerticalOffset);
2818 contentOffset.x = std::max(contentOffset.x, -_obscuredInsets.left);
2819 contentOffset.y = std::max(contentOffset.y, -_obscuredInsets.top);
2821 // Make the top/bottom edges "sticky" within 1 pixel.
2822 if (oldUnobscuredContentRect.maxY() > contentSizeInContentViewCoordinates.height - 1)
2823 contentOffset.y = maxVerticalOffset;
2824 if (oldUnobscuredContentRect.y() < 1)
2825 contentOffset.y = -_obscuredInsets.top;
2827 // FIXME: if we have content centered after double tap to zoom, we should also try to keep that rect in view.
2828 [_scrollView setContentSize:roundScrollViewContentSize(*_page, futureContentSizeInSelfCoordinates)];
2829 [_scrollView setContentOffset:contentOffset];
2831 CGRect visibleRectInContentCoordinates = [self convertRect:newBounds toView:_contentView.get()];
2832 CGRect unobscuredRectInContentCoordinates = [self convertRect:futureUnobscuredRectInSelfCoordinates toView:_contentView.get()];
2834 _page->dynamicViewportSizeUpdate(newMinimumLayoutSize, newMaximumUnobscuredSize, visibleRectInContentCoordinates, unobscuredRectInContentCoordinates, futureUnobscuredRectInSelfCoordinates, targetScale, newOrientation);
2835 if (WebKit::DrawingAreaProxy* drawingArea = _page->drawingArea())
2836 drawingArea->setSize(WebCore::IntSize(newBounds.size), WebCore::IntSize(), WebCore::IntSize());
2839 - (void)_endAnimatedResize
2841 if (_dynamicViewportUpdateMode == DynamicViewportUpdateMode::NotResizing)
2844 _page->synchronizeDynamicViewportUpdate();
2846 NSUInteger indexOfResizeAnimationView = [[_scrollView subviews] indexOfObject:_resizeAnimationView.get()];
2847 [_scrollView insertSubview:_contentView.get() atIndex:indexOfResizeAnimationView];
2848 [_scrollView insertSubview:[_contentView unscaledView] atIndex:indexOfResizeAnimationView + 1];
2850 CALayer *contentViewLayer = [_contentView layer];
2851 CGFloat adjustmentScale = _resizeAnimationTransformAdjustments.m11;
2852 contentViewLayer.sublayerTransform = CATransform3DIdentity;
2854 CGFloat animatingScaleTarget = [[_resizeAnimationView layer] transform].m11;
2855 CALayer *contentLayer = [_contentView layer];
2856 CATransform3D contentLayerTransform = contentLayer.transform;
2857 CGFloat currentScale = [[_resizeAnimationView layer] transform].m11 * contentLayerTransform.m11;
2859 // We cannot use [UIScrollView setZoomScale:] directly because the UIScrollView delegate would get a callback with
2860 // an invalid contentOffset. The real content offset is only set below.
2861 // Since there is no public API for setting both the zoomScale and the contentOffset, we set the zoomScale manually
2862 // on the zoom layer and then only change the contentOffset.
2863 CGFloat adjustedScale = adjustmentScale * currentScale;
2864 contentLayerTransform.m11 = adjustedScale;
2865 contentLayerTransform.m22 = adjustedScale;
2866 contentLayer.transform = contentLayerTransform;
2868 CGPoint currentScrollOffset = [_scrollView contentOffset];
2869 double horizontalScrollAdjustement = _resizeAnimationTransformAdjustments.m41 * animatingScaleTarget;
2870 double verticalScrollAdjustment = _resizeAnimationTransformAdjustments.m42 * animatingScaleTarget;
2872 [_scrollView setContentSize:roundScrollViewContentSize(*_page, [_contentView frame].size)];
2873 [_scrollView setContentOffset:CGPointMake(currentScrollOffset.x - horizontalScrollAdjustement, currentScrollOffset.y - verticalScrollAdjustment)];
2875 [_resizeAnimationView removeFromSuperview];
2876 _resizeAnimationView = nil;
2877 _resizeAnimationTransformAdjustments = CATransform3DIdentity;
2879 _dynamicViewportUpdateMode = DynamicViewportUpdateMode::NotResizing;
2880 [_contentView setHidden:NO];
2881 [self _updateVisibleContentRects];
2883 while (!_snapshotsDeferredDuringResize.isEmpty())
2884 _snapshotsDeferredDuringResize.takeLast()();
2887 - (void)_resizeWhileHidingContentWithUpdates:(void (^)(void))updateBlock
2889 [self _beginAnimatedResizeWithUpdates:updateBlock];
2890 if (_dynamicViewportUpdateMode == DynamicViewportUpdateMode::ResizingWithAnimation) {
2891 [_contentView setHidden:YES];
2892 _dynamicViewportUpdateMode = DynamicViewportUpdateMode::ResizingWithDocumentHidden;
2896 - (void)_setOverlaidAccessoryViewsInset:(CGSize)inset
2898 [_customContentView web_setOverlaidAccessoryViewsInset:inset];
2901 - (void)_snapshotRect:(CGRect)rectInViewCoordinates intoImageOfWidth:(CGFloat)imageWidth completionHandler:(void(^)(CGImageRef))completionHandler
2903 if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing) {
2904 // Defer snapshotting until after the current resize completes.
2905 void (^copiedCompletionHandler)(CGImageRef) = [completionHandler copy];
2906 RetainPtr<WKWebView> retainedSelf = self;
2907 _snapshotsDeferredDuringResize.append([retainedSelf, rectInViewCoordinates, imageWidth, copiedCompletionHandler] {
2908 [retainedSelf _snapshotRect:rectInViewCoordinates intoImageOfWidth:imageWidth completionHandler:copiedCompletionHandler];
2909 [copiedCompletionHandler release];
2914 CGRect snapshotRectInContentCoordinates = [self convertRect:rectInViewCoordinates toView:self._currentContentView];
2915 CGFloat imageScale = imageWidth / snapshotRectInContentCoordinates.size.width;
2916 CGFloat imageHeight = imageScale * snapshotRectInContentCoordinates.size.height;
2917 CGSize imageSize = CGSizeMake(imageWidth, imageHeight);
2920 // If we are parented and thus won't incur a significant penalty from paging in tiles, snapshot the view hierarchy directly.
2921 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 90000
2922 if (CADisplay *display = self.window.screen._display) {
2926 auto surface = WebCore::IOSurface::create(WebCore::expandedIntSize(WebCore::FloatSize(imageSize)), WebCore::ColorSpaceDeviceRGB);
2927 CGFloat imageScaleInViewCoordinates = imageWidth / rectInViewCoordinates.size.width;
2928 CATransform3D transform = CATransform3DMakeScale(imageScaleInViewCoordinates, imageScaleInViewCoordinates, 1);
2929 transform = CATransform3DTranslate(transform, -rectInViewCoordinates.origin.x, -rectInViewCoordinates.origin.y, 0);
2930 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 90000
2931 CARenderServerRenderDisplayLayerWithTransformAndTimeOffset(MACH_PORT_NULL, (CFStringRef)display.name, self.layer.context.contextId, reinterpret_cast<uint64_t>(self.layer), surface->surface(), 0, 0, &transform, 0);
2933 CARenderServerRenderLayerWithTransform(MACH_PORT_NULL, self.layer.context.contextId, reinterpret_cast<uint64_t>(self.layer), surface->surface(), 0, 0, &transform);
2935 completionHandler(surface->createImage().get());
2940 if (_customContentView) {
2941 UIGraphicsBeginImageContextWithOptions(imageSize, YES, 1);
2943 UIView *customContentView = _customContentView.get();
2944 [customContentView.backgroundColor set];
2945 UIRectFill(CGRectMake(0, 0, imageWidth, imageHeight));
2947 CGContextRef context = UIGraphicsGetCurrentContext();
2948 CGContextTranslateCTM(context, -snapshotRectInContentCoordinates.origin.x * imageScale, -snapshotRectInContentCoordinates.origin.y * imageScale);
2949 CGContextScaleCTM(context, imageScale, imageScale);
2950 [customContentView.layer renderInContext:context];
2952 completionHandler([UIGraphicsGetImageFromCurrentImageContext() CGImage]);
2954 UIGraphicsEndImageContext();
2958 void(^copiedCompletionHandler)(CGImageRef) = [completionHandler copy];
2959 _page->takeSnapshot(WebCore::enclosingIntRect(snapshotRectInContentCoordinates), WebCore::expandedIntSize(WebCore::FloatSize(imageSize)), WebKit::SnapshotOptionsExcludeDeviceScaleFactor, [=](const WebKit::ShareableBitmap::Handle& imageHandle, WebKit::CallbackBase::Error) {
2960 if (imageHandle.isNull()) {
2961 copiedCompletionHandler(nullptr);
2962 [copiedCompletionHandler release];
2966 RefPtr<WebKit::ShareableBitmap> bitmap = WebKit::ShareableBitmap::create(imageHandle, WebKit::SharedMemory::Protection::ReadOnly);
2969 copiedCompletionHandler(nullptr);
2970 [copiedCompletionHandler release];
2974 RetainPtr<CGImageRef> cgImage;
2975 cgImage = bitmap->makeCGImage();
2976 copiedCompletionHandler(cgImage.get());
2977 [copiedCompletionHandler release];
2981 - (void)_overrideLayoutParametersWithMinimumLayoutSize:(CGSize)minimumLayoutSize minimumLayoutSizeForMinimalUI:(CGSize)minimumLayoutSizeForMinimalUI maximumUnobscuredSizeOverride:(CGSize)maximumUnobscuredSizeOverride
2983 UNUSED_PARAM(minimumLayoutSizeForMinimalUI);
2984 [self _overrideLayoutParametersWithMinimumLayoutSize:minimumLayoutSize maximumUnobscuredSizeOverride:maximumUnobscuredSizeOverride];
2987 - (void)_overrideLayoutParametersWithMinimumLayoutSize:(CGSize)minimumLayoutSize maximumUnobscuredSizeOverride:(CGSize)maximumUnobscuredSizeOverride
2989 [self _setMinimumLayoutSizeOverride:minimumLayoutSize];
2990 [self _setMaximumUnobscuredSizeOverride:maximumUnobscuredSizeOverride];
2993 - (UIView *)_viewForFindUI
2995 return [self viewForZoomingInScrollView:[self scrollView]];
2998 - (BOOL)_isDisplayingPDF
3000 return [_customContentView isKindOfClass:[WKPDFView class]];
3003 - (NSData *)_dataForDisplayedPDF
3005 if (![self _isDisplayingPDF])
3007 CGPDFDocumentRef pdfDocument = [(WKPDFView *)_customContentView pdfDocument];
3008 return [(NSData *)CGDataProviderCopyData(CGPDFDocumentGetDataProvider(pdfDocument)) autorelease];
3011 - (NSString *)_suggestedFilenameForDisplayedPDF
3013 if (![self _isDisplayingPDF])
3015 return [(WKPDFView *)_customContentView.get() suggestedFilename];
3018 - (CGFloat)_viewportMetaTagWidth
3020 return _viewportMetaTagWidth;
3023 - (_WKWebViewPrintFormatter *)_webViewPrintFormatter
3025 UIViewPrintFormatter *viewPrintFormatter = self.viewPrintFormatter;
3026 ASSERT([viewPrintFormatter isKindOfClass:[_WKWebViewPrintFormatter class]]);
3027 return (_WKWebViewPrintFormatter *)viewPrintFormatter;
3030 #else // #if PLATFORM(IOS)
3032 #pragma mark - OS X-specific methods
3034 - (NSColor *)_pageExtendedBackgroundColor
3036 WebCore::Color color = _page->pageExtendedBackgroundColor();
3037 if (!color.isValid())
3040 return nsColor(color);
3043 - (BOOL)_drawsTransparentBackground
3045 return _page->drawsTransparentBackground();
3048 - (void)_setDrawsTransparentBackground:(BOOL)drawsTransparentBackground
3050 _page->setDrawsTransparentBackground(drawsTransparentBackground);
3053 - (void)_setOverrideDeviceScaleFactor:(CGFloat)deviceScaleFactor
3055 [_wkView _setOverrideDeviceScaleFactor:deviceScaleFactor];
3058 - (CGFloat)_overrideDeviceScaleFactor
3060 return [_wkView _overrideDeviceScaleFactor];
3063 - (void)_setTopContentInset:(CGFloat)contentInset
3065 [_wkView _setTopContentInset:contentInset];
3068 - (CGFloat)_topContentInset
3070 return [_wkView _topContentInset];
3073 - (BOOL)_windowOcclusionDetectionEnabled
3075 return [_wkView windowOcclusionDetectionEnabled];
3078 - (void)_setWindowOcclusionDetectionEnabled:(BOOL)flag
3080 [_wkView setWindowOcclusionDetectionEnabled:flag];
3083 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
3085 - (void)_setAutomaticallyAdjustsContentInsets:(BOOL)automaticallyAdjustsContentInsets
3087 [_wkView _setAutomaticallyAdjustsContentInsets:automaticallyAdjustsContentInsets];
3090 - (BOOL)_automaticallyAdjustsContentInsets
3092 return [_wkView _automaticallyAdjustsContentInsets];
3101 #if !TARGET_OS_IPHONE
3103 @implementation WKWebView (WKIBActions)
3105 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
3107 SEL action = item.action;
3109 if (action == @selector(goBack:))
3110 return !!_page->backForwardList().backItem();
3112 if (action == @selector(goForward:))
3113 return !!_page->backForwardList().forwardItem();
3115 if (action == @selector(stopLoading:)) {
3116 // FIXME: Return no if we're stopped.
3120 if (action == @selector(reload:) || action == @selector(reloadFromOrigin:)) {
3121 // FIXME: Return no if we're loading.
3128 - (IBAction)goBack:(id)sender
3133 - (IBAction)goForward:(id)sender
3138 - (IBAction)reload:(id)sender
3143 - (IBAction)reloadFromOrigin:(id)sender
3145 [self reloadFromOrigin];
3148 - (IBAction)stopLoading:(id)sender
3150 _page->stopLoading();
3158 @implementation WKWebView (_WKWebViewPrintFormatter)
3160 - (Class)_printFormatterClass
3162 return [_WKWebViewPrintFormatter class];
3165 - (NSInteger)_computePageCountAndStartDrawingToPDFForFrame:(_WKFrameHandle *)frame printInfo:(const WebKit::PrintInfo&)printInfo firstPage:(uint32_t)firstPage computedTotalScaleFactor:(double&)totalScaleFactor
3167 if ([self _isDisplayingPDF])
3168 return CGPDFDocumentGetNumberOfPages([(WKPDFView *)_customContentView pdfDocument]);
3170 _pageIsPrintingToPDF = YES;
3171 Vector<WebCore::IntRect> pageRects;
3172 uint64_t frameID = frame ? frame._frameID : _page->mainFrame()->frameID();
3173 if (!_page->sendSync(Messages::WebPage::ComputePagesForPrintingAndStartDrawingToPDF(frameID, printInfo, firstPage), Messages::WebPage::ComputePagesForPrintingAndStartDrawingToPDF::Reply(pageRects, totalScaleFactor)))
3175 return pageRects.size();
3178 - (void)_endPrinting
3180 _pageIsPrintingToPDF = NO;
3181 _printedDocument = nullptr;
3182 _page->send(Messages::WebPage::EndPrinting());
3185 // FIXME: milliseconds::max() overflows when converted to nanoseconds, causing condition_variable::wait_for() to believe
3186 // a timeout occurred on any spurious wakeup. Use nanoseconds::max() (converted to ms) to avoid this. We should perhaps
3187 // change waitForAndDispatchImmediately() to take nanoseconds to avoid this issue.
3188 static constexpr std::chrono::milliseconds didFinishLoadingTimeout = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::nanoseconds::max());
3190 - (CGPDFDocumentRef)_printedDocument
3192 if ([self _isDisplayingPDF]) {
3193 ASSERT(!_pageIsPrintingToPDF);
3194 return [(WKPDFView *)_customContentView pdfDocument];
3197 if (_pageIsPrintingToPDF) {
3198 if (!_page->process().connection()->waitForAndDispatchImmediately<Messages::WebPageProxy::DidFinishDrawingPagesToPDF>(_page->pageID(), didFinishLoadingTimeout)) {
3199 ASSERT_NOT_REACHED();
3202 ASSERT(!_pageIsPrintingToPDF);
3204 return _printedDocument.get();
3207 - (void)_setPrintedDocument:(CGPDFDocumentRef)printedDocument
3209 if (!_pageIsPrintingToPDF)
3211 ASSERT(![self _isDisplayingPDF]);
3212 _printedDocument = printedDocument;
3213 _pageIsPrintingToPDF = NO;
3219 #endif // WK_API_ENABLED