Add SPI for reloading without content blockers
[WebKit-https.git] / Source / WebKit2 / UIProcess / API / Cocoa / WKWebView.mm
1 /*
2  * Copyright (C) 2014, 2015 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "WKWebViewInternal.h"
28
29 #if WK_API_ENABLED
30
31 #import "APIFormClient.h"
32 #import "APIPageConfiguration.h"
33 #import "APISerializedScriptValue.h"
34 #import "CompletionHandlerCallChecker.h"
35 #import "DiagnosticLoggingClient.h"
36 #import "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"
52 #import "WKNSData.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>
86
87 #if PLATFORM(IOS)
88 #import "_WKFrameHandleInternal.h"
89 #import "_WKWebViewPrintFormatter.h"
90 #import "PrintInfo.h"
91 #import "ProcessThrottler.h"
92 #import "RemoteLayerTreeDrawingAreaProxy.h"
93 #import "RemoteScrollingCoordinatorProxy.h"
94 #import "UIKitSPI.h"
95 #import "WKContentViewInteraction.h"
96 #import "WKPDFView.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>
106
107 @interface UIScrollView (UIScrollViewInternal)
108 - (void)_adjustForAutomaticKeyboardInfo:(NSDictionary*)info animated:(BOOL)animated lastAdjustment:(CGFloat*)lastAdjustment;
109 - (BOOL)_isScrollingToTop;
110 - (BOOL)_isInterruptingDeceleration;
111 - (CGPoint)_animatedTargetOffset;
112 @end
113
114 @interface UIPeripheralHost(UIKitInternal)
115 - (CGFloat)getVerticalOverlapForView:(UIView *)view usingKeyboardInfo:(NSDictionary *)info;
116 @end
117
118 @interface UIView (UIViewInternal)
119 - (UIViewController *)_viewControllerForAncestor;
120 @end
121
122 @interface UIWindow (UIWindowInternal)
123 - (BOOL)_isHostedInAnotherProcess;
124 @end
125
126 @interface UIViewController (UIViewControllerInternal)
127 - (UIViewController *)_rootAncestorViewController;
128 - (UIViewController *)_viewControllerForSupportedInterfaceOrientations;
129 @end
130
131 enum class DynamicViewportUpdateMode {
132     NotResizing,
133     ResizingWithAnimation,
134     ResizingWithDocumentHidden,
135 };
136
137 #endif
138
139 #if PLATFORM(MAC)
140 #import "WKViewInternal.h"
141 #import <WebCore/ColorMac.h>
142 #endif
143
144 static HashMap<WebKit::WebPageProxy*, WKWebView *>& pageToViewMap()
145 {
146     static NeverDestroyed<HashMap<WebKit::WebPageProxy*, WKWebView *>> map;
147     return map;
148 }
149
150 WKWebView* fromWebPageProxy(WebKit::WebPageProxy& page)
151 {
152     return pageToViewMap().get(&page);
153 }
154
155 @implementation WKWebView {
156     std::unique_ptr<WebKit::NavigationState> _navigationState;
157     std::unique_ptr<WebKit::UIDelegate> _uiDelegate;
158
159     RetainPtr<_WKRemoteObjectRegistry> _remoteObjectRegistry;
160     _WKRenderingProgressEvents _observedRenderingProgressEvents;
161
162     WebKit::WeakObjCPtr<id <_WKFormDelegate>> _formDelegate;
163 #if PLATFORM(IOS)
164     RetainPtr<WKScrollView> _scrollView;
165     RetainPtr<WKContentView> _contentView;
166
167     BOOL _overridesMinimumLayoutSize;
168     CGSize _minimumLayoutSizeOverride;
169     BOOL _overridesMaximumUnobscuredSize;
170     CGSize _maximumUnobscuredSizeOverride;
171     CGRect _inputViewBounds;
172     CGFloat _viewportMetaTagWidth;
173     BOOL _allowsLinkPreview;
174
175     UIEdgeInsets _obscuredInsets;
176     BOOL _haveSetObscuredInsets;
177     BOOL _isChangingObscuredInsetsInteractively;
178
179     UIInterfaceOrientation _interfaceOrientationOverride;
180     BOOL _overridesInterfaceOrientation;
181
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;
192
193     BOOL _needsToRestoreExposedRect;
194     WebCore::FloatRect _exposedRectToRestore;
195     BOOL _needsToRestoreUnobscuredCenter;
196     WebCore::FloatPoint _unobscuredCenterToRestore;
197     uint64_t _firstTransactionIDAfterPageRestore;
198     double _scaleToRestore;
199
200     std::unique_ptr<WebKit::ViewGestureController> _gestureController;
201     BOOL _allowsBackForwardNavigationGestures;
202
203     RetainPtr<UIView <WKWebViewContentProvider>> _customContentView;
204     RetainPtr<UIView> _customContentFixedOverlayView;
205
206     WebCore::Color _scrollViewBackgroundColor;
207
208     BOOL _delayUpdateVisibleContentRects;
209     BOOL _hadDelayedUpdateVisibleContentRects;
210
211     BOOL _pageIsPrintingToPDF;
212     RetainPtr<CGPDFDocumentRef> _printedDocument;
213     Vector<std::function<void ()>> _snapshotsDeferredDuringResize;
214
215     BOOL _canAssistOnProgrammaticFocus;
216 #endif
217 #if PLATFORM(MAC)
218     RetainPtr<WKView> _wkView;
219 #endif
220 }
221
222 - (instancetype)initWithFrame:(CGRect)frame
223 {
224     return [self initWithFrame:frame configuration:adoptNS([[WKWebViewConfiguration alloc] init]).get()];
225 }
226
227 #if PLATFORM(IOS)
228 static int32_t deviceOrientationForUIInterfaceOrientation(UIInterfaceOrientation orientation)
229 {
230     switch (orientation) {
231     case UIInterfaceOrientationUnknown:
232     case UIInterfaceOrientationPortrait:
233         return 0;
234     case UIInterfaceOrientationPortraitUpsideDown:
235         return 180;
236     case UIInterfaceOrientationLandscapeLeft:
237         return -90;
238     case UIInterfaceOrientationLandscapeRight:
239         return 90;
240     }
241 }
242
243 static int32_t deviceOrientation()
244 {
245     return deviceOrientationForUIInterfaceOrientation([[UIApplication sharedApplication] statusBarOrientation]);
246 }
247
248 - (BOOL)_isShowingVideoPictureInPicture
249 {
250     if (!_page || !_page->videoFullscreenManager())
251         return false;
252     
253     return _page->videoFullscreenManager()->hasMode(WebCore::HTMLMediaElementEnums::VideoFullscreenModePictureInPicture);
254 }
255
256 - (BOOL)_mayAutomaticallyShowVideoPictureInPicture
257 {
258 #if !HAVE(AVKIT)
259     return false;
260 #else
261     if (!_page || !_page->videoFullscreenManager())
262         return false;
263
264     return _page->videoFullscreenManager()->mayAutomaticallyShowVideoPictureInPicture();
265 #endif
266 }
267
268 static bool shouldAllowPictureInPictureMediaPlayback()
269 {
270     static bool shouldAllowPictureInPictureMediaPlayback = iosExecutableWasLinkedOnOrAfterVersion(wkIOSSystemVersion_9_0);
271     return shouldAllowPictureInPictureMediaPlayback;
272 }
273
274 #endif
275
276 - (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration
277 {
278     if (!(self = [super initWithFrame:frame]))
279         return nil;
280
281     if (!configuration)
282         [NSException raise:NSInvalidArgumentException format:@"Configuration cannot be nil"];
283
284     _configuration = adoptNS([configuration copy]);
285
286     if (WKWebView *relatedWebView = [_configuration _relatedWebView]) {
287         WKProcessPool *processPool = [_configuration processPool];
288         WKProcessPool *relatedWebViewProcessPool = [relatedWebView->_configuration processPool];
289         if (processPool && processPool != relatedWebViewProcessPool)
290             [NSException raise:NSInvalidArgumentException format:@"Related web view %@ has process pool %@ but configuration specifies a different process pool %@", relatedWebView, relatedWebViewProcessPool, configuration.processPool];
291
292         [_configuration setProcessPool:relatedWebViewProcessPool];
293     }
294
295     [_configuration _validate];
296
297     CGRect bounds = self.bounds;
298
299     WebKit::WebProcessPool& processPool = *[_configuration processPool]->_processPool;
300     
301     auto pageConfiguration = API::PageConfiguration::create();
302
303     pageConfiguration->setProcessPool(&processPool);
304     pageConfiguration->setPreferences([_configuration preferences]->_preferences.get());
305     if (WKWebView *relatedWebView = [_configuration _relatedWebView])
306         pageConfiguration->setRelatedPage(relatedWebView->_page.get());
307
308     pageConfiguration->setUserContentController([_configuration userContentController]->_userContentControllerProxy.get());
309     pageConfiguration->setVisitedLinkStore([_configuration _visitedLinkStore]->_visitedLinkStore.get());
310     pageConfiguration->setWebsiteDataStore([_configuration websiteDataStore]->_websiteDataStore.get());
311     pageConfiguration->setTreatsSHA1SignedCertificatesAsInsecure([_configuration _treatsSHA1SignedCertificatesAsInsecure]);
312
313     RefPtr<WebKit::WebPageGroup> pageGroup;
314     NSString *groupIdentifier = configuration._groupIdentifier;
315     if (groupIdentifier.length) {
316         pageGroup = WebKit::WebPageGroup::create(configuration._groupIdentifier);
317         pageConfiguration->setPageGroup(pageGroup.get());
318     }
319
320     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::suppressesIncrementalRenderingKey(), WebKit::WebPreferencesStore::Value(!![_configuration suppressesIncrementalRendering]));
321
322 #if PLATFORM(IOS)
323     pageConfiguration->setAlwaysRunsAtForegroundPriority([_configuration _alwaysRunsAtForegroundPriority]);
324
325     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::allowsInlineMediaPlaybackKey(), WebKit::WebPreferencesStore::Value(!![_configuration allowsInlineMediaPlayback]));
326     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::inlineMediaPlaybackRequiresPlaysInlineAttributeKey(), WebKit::WebPreferencesStore::Value(!![_configuration _inlineMediaPlaybackRequiresPlaysInlineAttribute]));
327     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::allowsPictureInPictureMediaPlaybackKey(), WebKit::WebPreferencesStore::Value(!![_configuration allowsPictureInPictureMediaPlayback] && shouldAllowPictureInPictureMediaPlayback()));
328     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::requiresUserGestureForMediaPlaybackKey(), WebKit::WebPreferencesStore::Value(!![_configuration requiresUserActionForMediaPlayback]));
329     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::mediaDataLoadsAutomaticallyKey(), WebKit::WebPreferencesStore::Value(!![_configuration _mediaDataLoadsAutomatically]));
330 #endif
331 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
332     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::allowsAirPlayForMediaPlaybackKey(), WebKit::WebPreferencesStore::Value(!![_configuration allowsAirPlayForMediaPlayback]));
333 #endif
334
335 #if PLATFORM(IOS)
336     _scrollView = adoptNS([[WKScrollView alloc] initWithFrame:bounds]);
337     [_scrollView setInternalDelegate:self];
338     [_scrollView setBouncesZoom:YES];
339
340     [self addSubview:_scrollView.get()];
341
342     _contentView = adoptNS([[WKContentView alloc] initWithFrame:bounds processPool:processPool configuration:WTF::move(pageConfiguration) webView:self]);
343
344     _page = [_contentView page];
345     _page->setDeviceOrientation(deviceOrientation());
346
347     [_contentView layer].anchorPoint = CGPointZero;
348     [_contentView setFrame:bounds];
349     [_scrollView addSubview:_contentView.get()];
350     [_scrollView addSubview:[_contentView unscaledView]];
351     [self _updateScrollViewBackground];
352
353     _viewportMetaTagWidth = -1;
354
355     [self _frameOrBoundsChanged];
356
357     NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
358     [center addObserver:self selector:@selector(_keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];
359     [center addObserver:self selector:@selector(_keyboardDidChangeFrame:) name:UIKeyboardDidChangeFrameNotification object:nil];
360     [center addObserver:self selector:@selector(_keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
361     [center addObserver:self selector:@selector(_keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
362     [center addObserver:self selector:@selector(_windowDidRotate:) name:UIWindowDidRotateNotification object:nil];
363     [center addObserver:self selector:@selector(_contentSizeCategoryDidChange:) name:UIContentSizeCategoryDidChangeNotification object:nil];
364     _page->contentSizeCategoryDidChange([self _contentSizeCategory]);
365
366     [[_configuration _contentProviderRegistry] addPage:*_page];
367
368     [self setCanAssistOnProgrammaticFocus:[_configuration canAssistOnProgrammaticFocus]];
369 #endif
370
371 #if PLATFORM(MAC)
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]);
375
376 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
377     [_wkView _setAutomaticallyAdjustsContentInsets:YES];
378 #endif
379 #endif
380
381     _page->setBackgroundExtendsBeyondPage(true);
382
383     if (NSString *applicationNameForUserAgent = configuration.applicationNameForUserAgent)
384         _page->setApplicationNameForUserAgent(applicationNameForUserAgent);
385
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));
390
391     pageToViewMap().add(_page.get(), self);
392
393     return self;
394 }
395
396 - (instancetype)initWithCoder:(NSCoder *)coder
397 {
398     [self release];
399     return nil;
400 }
401
402 - (void)dealloc
403 {
404     if (_remoteObjectRegistry)
405         _page->process().processPool().removeMessageReceiver(Messages::RemoteObjectRegistry::messageReceiverName(), _page->pageID());
406
407     _page->close();
408
409     [_remoteObjectRegistry _invalidate];
410 #if PLATFORM(IOS)
411     [[_configuration _contentProviderRegistry] removePage:*_page];
412     [[NSNotificationCenter defaultCenter] removeObserver:self];
413     [_scrollView setInternalDelegate:nil];
414 #endif
415
416     pageToViewMap().remove(_page.get());
417
418     [super dealloc];
419 }
420
421 - (WKWebViewConfiguration *)configuration
422 {
423     return [[_configuration copy] autorelease];
424 }
425
426 - (WKBackForwardList *)backForwardList
427 {
428     return wrapper(_page->backForwardList());
429 }
430
431 - (id <WKNavigationDelegate>)navigationDelegate
432 {
433     return _navigationState->navigationDelegate().autorelease();
434 }
435
436 - (void)setNavigationDelegate:(id <WKNavigationDelegate>)navigationDelegate
437 {
438     _page->setNavigationClient(_navigationState->createNavigationClient());
439     _navigationState->setNavigationDelegate(navigationDelegate);
440 }
441
442 - (id <WKUIDelegate>)UIDelegate
443 {
444     return _uiDelegate->delegate().autorelease();
445 }
446
447 - (void)setUIDelegate:(id<WKUIDelegate>)UIDelegate
448 {
449     _page->setUIClient(_uiDelegate->createUIClient());
450     _uiDelegate->setDelegate(UIDelegate);
451 }
452
453 - (WKNavigation *)loadRequest:(NSURLRequest *)request
454 {
455     auto navigation = _page->loadRequest(request);
456     if (!navigation)
457         return nil;
458
459     return [wrapper(*navigation.release().leakRef()) autorelease];
460 }
461
462 - (WKNavigation *)loadFileURL:(NSURL *)URL allowingReadAccessToURL:(NSURL *)readAccessURL
463 {
464     if (![URL isFileURL])
465         [NSException raise:NSInvalidArgumentException format:@"%@ is not a file URL", URL];
466
467     if (![readAccessURL isFileURL])
468         [NSException raise:NSInvalidArgumentException format:@"%@ is not a file URL", readAccessURL];
469
470     auto navigation = _page->loadFile([URL _web_originalDataAsWTFString], [readAccessURL _web_originalDataAsWTFString]);
471     if (!navigation)
472         return nil;
473
474     return [wrapper(*navigation.release().leakRef()) autorelease];
475 }
476
477 - (WKNavigation *)loadHTMLString:(NSString *)string baseURL:(NSURL *)baseURL
478 {
479     NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
480
481     return [self loadData:data MIMEType:@"text/html" characterEncodingName:@"UTF-8" baseURL:baseURL];
482 }
483
484 - (WKNavigation *)loadData:(NSData *)data MIMEType:(NSString *)MIMEType characterEncodingName:(NSString *)characterEncodingName baseURL:(NSURL *)baseURL
485 {
486     auto navigation = _page->loadData(API::Data::createWithoutCopying(data).ptr(), MIMEType, characterEncodingName, baseURL.absoluteString);
487     if (!navigation)
488         return nil;
489
490     return [wrapper(*navigation.release().leakRef()) autorelease];
491 }
492
493 - (WKNavigation *)goToBackForwardListItem:(WKBackForwardListItem *)item
494 {
495     auto navigation = _page->goToBackForwardItem(&item._item);
496     if (!navigation)
497         return nil;
498
499     return [wrapper(*navigation.release().leakRef()) autorelease];
500 }
501
502 - (NSString *)title
503 {
504     return _page->pageLoadState().title();
505 }
506
507 - (NSURL *)URL
508 {
509     return [NSURL _web_URLWithWTFString:_page->pageLoadState().activeURL()];
510 }
511
512 - (BOOL)isLoading
513 {
514     return _page->pageLoadState().isLoading();
515 }
516
517 - (double)estimatedProgress
518 {
519     return _page->pageLoadState().estimatedProgress();
520 }
521
522 - (BOOL)hasOnlySecureContent
523 {
524     return _page->pageLoadState().hasOnlySecureContent();
525 }
526
527 - (NSArray *)certificateChain
528 {
529     auto certificateInfo = _page->pageLoadState().certificateInfo();
530     if (!certificateInfo)
531         return @[ ];
532
533     return (NSArray *)certificateInfo->certificateInfo().certificateChain() ?: @[ ];
534 }
535
536 - (BOOL)canGoBack
537 {
538     return _page->pageLoadState().canGoBack();
539 }
540
541 - (BOOL)canGoForward
542 {
543     return _page->pageLoadState().canGoForward();
544 }
545
546 - (WKNavigation *)goBack
547 {
548     auto navigation = _page->goBack();
549     if (!navigation)
550         return nil;
551  
552     return [wrapper(*navigation.release().leakRef()) autorelease];
553 }
554
555 - (WKNavigation *)goForward
556 {
557     auto navigation = _page->goForward();
558     if (!navigation)
559         return nil;
560
561     return [wrapper(*navigation.release().leakRef()) autorelease];
562 }
563
564 - (WKNavigation *)reload
565 {
566     const bool reloadFromOrigin = false;
567     const bool contentBlockersEnabled = true;
568     auto navigation = _page->reload(reloadFromOrigin, contentBlockersEnabled);
569     if (!navigation)
570         return nil;
571
572     return [wrapper(*navigation.release().leakRef()) autorelease];
573 }
574
575 - (WKNavigation *)reloadFromOrigin
576 {
577     const bool reloadFromOrigin = true;
578     const bool contentBlockersEnabled = true;
579     auto navigation = _page->reload(reloadFromOrigin, contentBlockersEnabled);
580     if (!navigation)
581         return nil;
582
583     return [wrapper(*navigation.release().leakRef()) autorelease];
584 }
585
586 - (void)stopLoading
587 {
588     _page->stopLoading();
589 }
590
591 static WKErrorCode callbackErrorCode(WebKit::CallbackBase::Error error)
592 {
593     switch (error) {
594     case WebKit::CallbackBase::Error::None:
595         ASSERT_NOT_REACHED();
596         return WKErrorUnknown;
597
598     case WebKit::CallbackBase::Error::Unknown:
599         return WKErrorUnknown;
600
601     case WebKit::CallbackBase::Error::ProcessExited:
602         return WKErrorWebContentProcessTerminated;
603
604     case WebKit::CallbackBase::Error::OwnerWasInvalidated:
605         return WKErrorWebViewInvalidated;
606     }
607 }
608
609 - (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^)(id, NSError *))completionHandler
610 {
611     auto handler = adoptNS([completionHandler copy]);
612
613     _page->runJavaScriptInMainFrame(javaScriptString, [handler](API::SerializedScriptValue* serializedScriptValue, bool hadException, WebKit::ScriptValueCallback::Error errorCode) {
614         if (!handler)
615             return;
616
617         if (errorCode != WebKit::ScriptValueCallback::Error::None) {
618             auto error = createNSError(callbackErrorCode(errorCode));
619             if (errorCode == WebKit::ScriptValueCallback::Error::OwnerWasInvalidated) {
620                 // The OwnerWasInvalidated callback is synchronous. We don't want to call the block from within it
621                 // because that can trigger re-entrancy bugs in WebKit.
622                 // FIXME: It would be even better if GenericCallback did this for us.
623                 dispatch_async(dispatch_get_main_queue(), [handler, error] {
624                     auto rawHandler = (void (^)(id, NSError *))handler.get();
625                     rawHandler(nil, error.get());
626                 });
627                 return;
628             }
629
630             auto rawHandler = (void (^)(id, NSError *))handler.get();
631             rawHandler(nil, error.get());
632             return;
633         }
634
635         auto rawHandler = (void (^)(id, NSError *))handler.get();
636         if (hadException) {
637             ASSERT(!serializedScriptValue);
638             rawHandler(nil, createNSError(WKErrorJavaScriptExceptionOccurred).get());
639             return;
640         }
641
642         if (!serializedScriptValue) {
643             rawHandler(nil, createNSError(WKErrorJavaScriptResultTypeIsUnsupported).get());
644             return;
645         }
646
647         id body = API::SerializedScriptValue::deserialize(*serializedScriptValue->internalRepresentation(), 0);
648         rawHandler(body, nil);
649     });
650 }
651
652 - (NSString *)customUserAgent
653 {
654     return _page->customUserAgent();
655 }
656
657 - (void)setCustomUserAgent:(NSString *)customUserAgent
658 {
659     _page->setCustomUserAgent(customUserAgent);
660 }
661
662 - (WKPageRef)_pageForTesting
663 {
664     return toAPI(_page.get());
665 }
666
667 - (BOOL)allowsLinkPreview
668 {
669 #if PLATFORM(MAC)
670     return [_wkView allowsLinkPreview];
671 #elif PLATFORM(IOS)
672     return _allowsLinkPreview;
673 #endif
674 }
675
676 - (void)setAllowsLinkPreview:(BOOL)allowsLinkPreview
677 {
678 #if PLATFORM(MAC)
679     [_wkView setAllowsLinkPreview:allowsLinkPreview];
680     return;
681 #elif PLATFORM(IOS)
682     if (_allowsLinkPreview == allowsLinkPreview)
683         return;
684
685     _allowsLinkPreview = allowsLinkPreview;
686
687 #if HAVE(LINK_PREVIEW)
688     if (_allowsLinkPreview)
689         [_contentView _registerPreview];
690     else
691         [_contentView _unregisterPreview];
692 #endif // HAVE(LINK_PREVIEW)
693 #endif // PLATFORM(IOS)
694 }
695
696 #pragma mark iOS-specific methods
697
698 #if PLATFORM(IOS)
699 - (void)setFrame:(CGRect)frame
700 {
701     CGRect oldFrame = self.frame;
702     [super setFrame:frame];
703
704     if (!CGSizeEqualToSize(oldFrame.size, frame.size))
705         [self _frameOrBoundsChanged];
706 }
707
708 - (void)setBounds:(CGRect)bounds
709 {
710     CGRect oldBounds = self.bounds;
711     [super setBounds:bounds];
712     [_customContentFixedOverlayView setFrame:self.bounds];
713
714     if (!CGSizeEqualToSize(oldBounds.size, bounds.size))
715         [self _frameOrBoundsChanged];
716 }
717
718 - (UIScrollView *)scrollView
719 {
720     return _scrollView.get();
721 }
722
723 - (BOOL)canAssistOnProgrammaticFocus
724 {
725     return _canAssistOnProgrammaticFocus;
726 }
727
728 - (void)setCanAssistOnProgrammaticFocus:(BOOL)canAssistOnProgrammaticFocus
729 {
730     _canAssistOnProgrammaticFocus = canAssistOnProgrammaticFocus;
731 }
732
733 - (WKBrowsingContextController *)browsingContextController
734 {
735     return [_contentView browsingContextController];
736 }
737
738 - (BOOL)becomeFirstResponder
739 {
740     return [_contentView becomeFirstResponder] || [super becomeFirstResponder];
741 }
742
743 static inline CGFloat floorToDevicePixel(CGFloat input, float deviceScaleFactor)
744 {
745     return CGFloor(input * deviceScaleFactor) / deviceScaleFactor;
746 }
747
748 static inline bool pointsEqualInDevicePixels(CGPoint a, CGPoint b, float deviceScaleFactor)
749 {
750     return fabs(a.x * deviceScaleFactor - b.x * deviceScaleFactor) < std::numeric_limits<float>::epsilon()
751         && fabs(a.y * deviceScaleFactor - b.y * deviceScaleFactor) < std::numeric_limits<float>::epsilon();
752 }
753
754 static CGSize roundScrollViewContentSize(const WebKit::WebPageProxy& page, CGSize contentSize)
755 {
756     float deviceScaleFactor = page.deviceScaleFactor();
757     return CGSizeMake(floorToDevicePixel(contentSize.width, deviceScaleFactor), floorToDevicePixel(contentSize.height, deviceScaleFactor));
758 }
759
760 - (UIView *)_currentContentView
761 {
762     return _customContentView ? _customContentView.get() : _contentView.get();
763 }
764
765 - (void)_setHasCustomContentView:(BOOL)pageHasCustomContentView loadedMIMEType:(const WTF::String&)mimeType
766 {
767     if (pageHasCustomContentView) {
768         [_customContentView removeFromSuperview];
769         [_customContentFixedOverlayView removeFromSuperview];
770
771         Class representationClass = [[_configuration _contentProviderRegistry] providerForMIMEType:mimeType];
772         ASSERT(representationClass);
773         _customContentView = adoptNS([[representationClass alloc] web_initWithFrame:self.bounds webView:self]);
774         _customContentFixedOverlayView = adoptNS([[UIView alloc] initWithFrame:self.bounds]);
775         [_customContentFixedOverlayView setUserInteractionEnabled:NO];
776
777         [[_contentView unscaledView] removeFromSuperview];
778         [_contentView removeFromSuperview];
779         [_scrollView addSubview:_customContentView.get()];
780         [self addSubview:_customContentFixedOverlayView.get()];
781
782         [_customContentView web_setMinimumSize:self.bounds.size];
783         [_customContentView web_setFixedOverlayView:_customContentFixedOverlayView.get()];
784     } else if (_customContentView) {
785         [_customContentView removeFromSuperview];
786         _customContentView = nullptr;
787
788         [_customContentFixedOverlayView removeFromSuperview];
789         _customContentFixedOverlayView = nullptr;
790
791         [_scrollView addSubview:_contentView.get()];
792         [_scrollView addSubview:[_contentView unscaledView]];
793         [_scrollView setContentSize:roundScrollViewContentSize(*_page, [_contentView frame].size)];
794
795         [_customContentFixedOverlayView setFrame:self.bounds];
796         [self addSubview:_customContentFixedOverlayView.get()];
797     }
798 }
799
800 - (void)_didFinishLoadingDataForCustomContentProviderWithSuggestedFilename:(const String&)suggestedFilename data:(NSData *)data
801 {
802     ASSERT(_customContentView);
803     [_customContentView web_setContentProviderData:data suggestedFilename:suggestedFilename];
804
805     // FIXME: It may make more sense for custom content providers to invoke this when they're ready,
806     // because there's no guarantee that all custom content providers will lay out synchronously.
807     _page->didLayoutForCustomContentProvider();
808 }
809
810 - (void)_setViewportMetaTagWidth:(float)newWidth
811 {
812     _viewportMetaTagWidth = newWidth;
813 }
814
815 - (void)_willInvokeUIScrollViewDelegateCallback
816 {
817     _delayUpdateVisibleContentRects = YES;
818 }
819
820 - (void)_didInvokeUIScrollViewDelegateCallback
821 {
822     _delayUpdateVisibleContentRects = NO;
823     if (_hadDelayedUpdateVisibleContentRects) {
824         _hadDelayedUpdateVisibleContentRects = NO;
825         [self _updateVisibleContentRects];
826     }
827 }
828
829 static CGFloat contentZoomScale(WKWebView *webView)
830 {
831     CGFloat scale = webView._currentContentView.layer.affineTransform.a;
832     ASSERT(scale == [webView->_scrollView zoomScale]);
833     return scale;
834 }
835
836 static WebCore::Color baseScrollViewBackgroundColor(WKWebView *webView)
837 {
838     if (webView->_customContentView)
839         return [webView->_customContentView backgroundColor].CGColor;
840
841     if (webView->_gestureController) {
842         WebCore::Color color = webView->_gestureController->backgroundColorForCurrentSnapshot();
843         if (color.isValid())
844             return color;
845     }
846
847     return webView->_page->pageExtendedBackgroundColor();
848 }
849
850 static WebCore::Color scrollViewBackgroundColor(WKWebView *webView)
851 {
852     if (!webView.opaque)
853         return WebCore::Color::transparent;
854
855     WebCore::Color color = baseScrollViewBackgroundColor(webView);
856
857     if (!color.isValid())
858         color = WebCore::Color::white;
859
860     CGFloat zoomScale = contentZoomScale(webView);
861     CGFloat minimumZoomScale = [webView->_scrollView minimumZoomScale];
862     if (zoomScale < minimumZoomScale) {
863         CGFloat slope = 12;
864         CGFloat opacity = std::max<CGFloat>(1 - slope * (minimumZoomScale - zoomScale), 0);
865         color = WebCore::colorWithOverrideAlpha(color.rgb(), opacity);
866     }
867
868     return color;
869 }
870
871 - (void)_updateScrollViewBackground
872 {
873     WebCore::Color color = scrollViewBackgroundColor(self);
874
875     if (_scrollViewBackgroundColor == color)
876         return;
877
878     _scrollViewBackgroundColor = color;
879
880     auto uiBackgroundColor = adoptNS([[UIColor alloc] initWithCGColor:cachedCGColor(color, WebCore::ColorSpaceDeviceRGB)]);
881     [_scrollView setBackgroundColor:uiBackgroundColor.get()];
882
883     // Update the indicator style based on the lightness/darkness of the background color.
884     double hue, saturation, lightness;
885     color.getHSL(hue, saturation, lightness);
886     if (lightness <= .5 && color.alpha() > 0)
887         [_scrollView setIndicatorStyle:UIScrollViewIndicatorStyleWhite];
888     else
889         [_scrollView setIndicatorStyle:UIScrollViewIndicatorStyleDefault];
890 }
891
892 - (CGPoint)_adjustedContentOffset:(CGPoint)point
893 {
894     CGPoint result = point;
895     UIEdgeInsets contentInset = [self _computedContentInset];
896
897     result.x -= contentInset.left;
898     result.y -= contentInset.top;
899
900     return result;
901 }
902
903 - (UIEdgeInsets)_computedContentInset
904 {
905     if (_haveSetObscuredInsets)
906         return _obscuredInsets;
907
908     return [_scrollView contentInset];
909 }
910
911 - (void)_processDidExit
912 {
913     if (!_customContentView && _dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing) {
914         NSUInteger indexOfResizeAnimationView = [[_scrollView subviews] indexOfObject:_resizeAnimationView.get()];
915         [_scrollView insertSubview:_contentView.get() atIndex:indexOfResizeAnimationView];
916         [_scrollView insertSubview:[_contentView unscaledView] atIndex:indexOfResizeAnimationView + 1];
917         [_resizeAnimationView removeFromSuperview];
918         _resizeAnimationView = nil;
919
920         _resizeAnimationTransformAdjustments = CATransform3DIdentity;
921     }
922     [_contentView setFrame:self.bounds];
923     [_scrollView setBackgroundColor:[UIColor whiteColor]];
924     [_scrollView setContentOffset:[self _adjustedContentOffset:CGPointZero]];
925     [_scrollView setZoomScale:1];
926
927     _viewportMetaTagWidth = -1;
928     _hasCommittedLoadForMainFrame = NO;
929     _needsResetViewStateAfterCommitLoadForMainFrame = NO;
930     _dynamicViewportUpdateMode = DynamicViewportUpdateMode::NotResizing;
931     [_contentView setHidden:NO];
932     _needsToRestoreExposedRect = NO;
933     _needsToRestoreUnobscuredCenter = NO;
934     _scrollViewBackgroundColor = WebCore::Color();
935     _delayUpdateVisibleContentRects = NO;
936     _hadDelayedUpdateVisibleContentRects = NO;
937 }
938
939 - (void)_didCommitLoadForMainFrame
940 {
941     _firstPaintAfterCommitLoadTransactionID = downcast<WebKit::RemoteLayerTreeDrawingAreaProxy>(*_page->drawingArea()).nextLayerTreeTransactionID();
942
943     _hasCommittedLoadForMainFrame = YES;
944     _needsResetViewStateAfterCommitLoadForMainFrame = YES;
945 }
946
947 static CGPoint contentOffsetBoundedInValidRange(UIScrollView *scrollView, CGPoint contentOffset)
948 {
949     UIEdgeInsets contentInsets = scrollView.contentInset;
950     CGSize contentSize = scrollView.contentSize;
951     CGSize scrollViewSize = scrollView.bounds.size;
952
953     CGFloat maxHorizontalOffset = contentSize.width + contentInsets.right - scrollViewSize.width;
954     contentOffset.x = std::min(maxHorizontalOffset, contentOffset.x);
955     contentOffset.x = std::max(-contentInsets.left, contentOffset.x);
956
957     CGFloat maxVerticalOffset = contentSize.height + contentInsets.bottom - scrollViewSize.height;
958     contentOffset.y = std::min(maxVerticalOffset, contentOffset.y);
959     contentOffset.y = std::max(-contentInsets.top, contentOffset.y);
960     return contentOffset;
961 }
962
963 static void changeContentOffsetBoundedInValidRange(UIScrollView *scrollView, WebCore::FloatPoint contentOffset)
964 {
965     scrollView.contentOffset = contentOffsetBoundedInValidRange(scrollView, contentOffset);
966 }
967
968 - (WebCore::FloatRect)visibleRectInViewCoordinates
969 {
970     WebCore::FloatRect bounds = self.bounds;
971     bounds.moveBy([_scrollView contentOffset]);
972     WebCore::FloatRect contentViewBounds = [_contentView bounds];
973     bounds.intersect(contentViewBounds);
974     return bounds;
975 }
976
977 // WebCore stores the page scale factor as float instead of double. When we get a scale from WebCore,
978 // we need to ignore differences that are within a small rounding error on floats.
979 static inline bool areEssentiallyEqualAsFloat(float a, float b)
980 {
981     return WTF::areEssentiallyEqual(a, b);
982 }
983
984 - (void)_didCommitLayerTree:(const WebKit::RemoteLayerTreeTransaction&)layerTreeTransaction
985 {
986     if (_customContentView)
987         return;
988
989     if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing) {
990         if (layerTreeTransaction.transactionID() >= _resizeAnimationTransformTransactionID) {
991             [_resizeAnimationView layer].sublayerTransform = _resizeAnimationTransformAdjustments;
992             if (_dynamicViewportUpdateMode == DynamicViewportUpdateMode::ResizingWithDocumentHidden) {
993                 [_contentView setHidden:NO];
994                 [self _endAnimatedResize];
995             }
996         }
997         return;
998     }
999
1000     CGSize newContentSize = roundScrollViewContentSize(*_page, [_contentView frame].size);
1001     [_scrollView _setContentSizePreservingContentOffsetDuringRubberband:newContentSize];
1002
1003     [_scrollView setMinimumZoomScale:layerTreeTransaction.minimumScaleFactor()];
1004     [_scrollView setMaximumZoomScale:layerTreeTransaction.maximumScaleFactor()];
1005     [_scrollView setZoomEnabled:layerTreeTransaction.allowsUserScaling()];
1006     if (!layerTreeTransaction.scaleWasSetByUIProcess() && ![_scrollView isZooming] && ![_scrollView isZoomBouncing] && ![_scrollView _isAnimatingZoom])
1007         [_scrollView setZoomScale:layerTreeTransaction.pageScaleFactor()];
1008
1009     [self _updateScrollViewBackground];
1010
1011     if (_gestureController)
1012         _gestureController->setRenderTreeSize(layerTreeTransaction.renderTreeSize());
1013
1014     if (_needsResetViewStateAfterCommitLoadForMainFrame && layerTreeTransaction.transactionID() >= _firstPaintAfterCommitLoadTransactionID) {
1015         _needsResetViewStateAfterCommitLoadForMainFrame = NO;
1016         [_scrollView setContentOffset:[self _adjustedContentOffset:CGPointZero]];
1017         [self _updateVisibleContentRects];
1018         if (_observedRenderingProgressEvents & _WKRenderingProgressEventFirstPaint)
1019             _navigationState->didFirstPaint();
1020     }
1021
1022     bool isTransactionAfterPageRestore = layerTreeTransaction.transactionID() >= _firstTransactionIDAfterPageRestore;
1023
1024     if (_needsToRestoreExposedRect && isTransactionAfterPageRestore) {
1025         _needsToRestoreExposedRect = NO;
1026
1027         if (areEssentiallyEqualAsFloat(contentZoomScale(self), _scaleToRestore)) {
1028             WebCore::FloatPoint exposedPosition = _exposedRectToRestore.location();
1029             exposedPosition.scale(_scaleToRestore, _scaleToRestore);
1030
1031             changeContentOffsetBoundedInValidRange(_scrollView.get(), exposedPosition);
1032             if (_gestureController)
1033                 _gestureController->didRestoreScrollPosition();
1034         }
1035         [self _updateVisibleContentRects];
1036     }
1037
1038     if (_needsToRestoreUnobscuredCenter && isTransactionAfterPageRestore) {
1039         _needsToRestoreUnobscuredCenter = NO;
1040
1041         if (areEssentiallyEqualAsFloat(contentZoomScale(self), _scaleToRestore)) {
1042             CGRect unobscuredRect = UIEdgeInsetsInsetRect(self.bounds, _obscuredInsets);
1043             WebCore::FloatSize unobscuredContentSizeAtNewScale(unobscuredRect.size.width / _scaleToRestore, unobscuredRect.size.height / _scaleToRestore);
1044             WebCore::FloatPoint topLeftInDocumentCoordinates(_unobscuredCenterToRestore.x() - unobscuredContentSizeAtNewScale.width() / 2, _unobscuredCenterToRestore.y() - unobscuredContentSizeAtNewScale.height() / 2);
1045
1046             topLeftInDocumentCoordinates.scale(_scaleToRestore, _scaleToRestore);
1047             topLeftInDocumentCoordinates.moveBy(WebCore::FloatPoint(-_obscuredInsets.left, -_obscuredInsets.top));
1048
1049             changeContentOffsetBoundedInValidRange(_scrollView.get(), topLeftInDocumentCoordinates);
1050             if (_gestureController)
1051                 _gestureController->didRestoreScrollPosition();
1052         }
1053         [self _updateVisibleContentRects];
1054     }
1055     
1056     if (WebKit::RemoteLayerTreeScrollingPerformanceData* scrollPerfData = _page->scrollingPerformanceData())
1057         scrollPerfData->didCommitLayerTree([self visibleRectInViewCoordinates]);
1058 }
1059
1060 - (void)_dynamicViewportUpdateChangedTargetToScale:(double)newScale position:(CGPoint)newScrollPosition nextValidLayerTreeTransactionID:(uint64_t)nextValidLayerTreeTransactionID
1061 {
1062     if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing) {
1063         CGFloat animatingScaleTarget = [[_resizeAnimationView layer] transform].m11;
1064         double currentTargetScale = animatingScaleTarget * [[_contentView layer] transform].m11;
1065         double scale = newScale / currentTargetScale;
1066         _resizeAnimationTransformAdjustments = CATransform3DMakeScale(scale, scale, 1);
1067
1068         CGPoint newContentOffset = [self _adjustedContentOffset:CGPointMake(newScrollPosition.x * newScale, newScrollPosition.y * newScale)];
1069         CGPoint currentContentOffset = [_scrollView contentOffset];
1070
1071         _resizeAnimationTransformAdjustments.m41 = (currentContentOffset.x - newContentOffset.x) / animatingScaleTarget;
1072         _resizeAnimationTransformAdjustments.m42 = (currentContentOffset.y - newContentOffset.y) / animatingScaleTarget;
1073         _resizeAnimationTransformTransactionID = nextValidLayerTreeTransactionID;
1074     }
1075 }
1076
1077 - (void)_couldNotRestorePageState
1078 {
1079     // The gestureController may be waiting for the scroll position to be restored
1080     // in order to remove the swipe snapshot. Since the scroll position could not be
1081     // restored, tell the gestureController it was restored so that it no longer waits
1082     // for it.
1083     if (_gestureController)
1084         _gestureController->didRestoreScrollPosition();
1085 }
1086
1087 - (void)_restorePageStateToExposedRect:(WebCore::FloatRect)exposedRect scale:(double)scale
1088 {
1089     if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing)
1090         return;
1091
1092     if (_customContentView)
1093         return;
1094
1095     _needsToRestoreUnobscuredCenter = NO;
1096     _needsToRestoreExposedRect = YES;
1097     _firstTransactionIDAfterPageRestore = downcast<WebKit::RemoteLayerTreeDrawingAreaProxy>(*_page->drawingArea()).nextLayerTreeTransactionID();
1098     _exposedRectToRestore = exposedRect;
1099     _scaleToRestore = scale;
1100 }
1101
1102 - (void)_restorePageStateToUnobscuredCenter:(WebCore::FloatPoint)center scale:(double)scale
1103 {
1104     if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing)
1105         return;
1106
1107     if (_customContentView)
1108         return;
1109
1110     _needsToRestoreExposedRect = NO;
1111     _needsToRestoreUnobscuredCenter = YES;
1112     _firstTransactionIDAfterPageRestore = downcast<WebKit::RemoteLayerTreeDrawingAreaProxy>(*_page->drawingArea()).nextLayerTreeTransactionID();
1113     _unobscuredCenterToRestore = center;
1114     _scaleToRestore = scale;
1115 }
1116
1117 - (PassRefPtr<WebKit::ViewSnapshot>)_takeViewSnapshot
1118 {
1119     float deviceScale = WKGetScreenScaleFactor();
1120     WebCore::FloatSize snapshotSize(self.bounds.size);
1121     snapshotSize.scale(deviceScale, deviceScale);
1122
1123     CATransform3D transform = CATransform3DMakeScale(deviceScale, deviceScale, 1);
1124
1125 #if USE(IOSURFACE)
1126     auto surface = WebCore::IOSurface::create(WebCore::expandedIntSize(snapshotSize), WebCore::ColorSpaceDeviceRGB);
1127     CARenderServerRenderLayerWithTransform(MACH_PORT_NULL, self.layer.context.contextId, reinterpret_cast<uint64_t>(self.layer), surface->surface(), 0, 0, &transform);
1128
1129     RefPtr<WebKit::ViewSnapshot> viewSnapshot = WebKit::ViewSnapshot::create(nullptr);
1130     WebCore::IOSurface::convertToFormat(WTF::move(surface), WebCore::IOSurface::Format::YUV422, [viewSnapshot](std::unique_ptr<WebCore::IOSurface> convertedSurface) {
1131         viewSnapshot->setSurface(WTF::move(convertedSurface));
1132     });
1133
1134     return viewSnapshot;
1135 #else
1136     uint32_t slotID = [WebKit::ViewSnapshotStore::snapshottingContext() createImageSlot:snapshotSize hasAlpha:YES];
1137
1138     if (!slotID)
1139         return nullptr;
1140
1141     CARenderServerCaptureLayerWithTransform(MACH_PORT_NULL, self.layer.context.contextId, (uint64_t)self.layer, slotID, 0, 0, &transform);
1142     WebCore::IntSize imageSize = WebCore::expandedIntSize(WebCore::FloatSize(snapshotSize));
1143     return WebKit::ViewSnapshot::create(slotID, imageSize, imageSize.width() * imageSize.height() * 4);
1144 #endif
1145 }
1146
1147 - (void)_zoomToPoint:(WebCore::FloatPoint)point atScale:(double)scale animated:(BOOL)animated
1148 {
1149     CFTimeInterval duration = 0;
1150     CGFloat zoomScale = contentZoomScale(self);
1151
1152     if (animated) {
1153         const double maximumZoomDuration = 0.4;
1154         const double minimumZoomDuration = 0.1;
1155         const double zoomDurationFactor = 0.3;
1156
1157         duration = std::min(fabs(log(zoomScale) - log(scale)) * zoomDurationFactor + minimumZoomDuration, maximumZoomDuration);
1158     }
1159
1160     if (scale != zoomScale)
1161         _page->willStartUserTriggeredZooming();
1162
1163     [_scrollView _zoomToCenter:point scale:scale duration:duration];
1164 }
1165
1166 - (void)_zoomToRect:(WebCore::FloatRect)targetRect atScale:(double)scale origin:(WebCore::FloatPoint)origin animated:(BOOL)animated
1167 {
1168     // FIXME: Some of this could be shared with _scrollToRect.
1169     const double visibleRectScaleChange = contentZoomScale(self) / scale;
1170     const WebCore::FloatRect visibleRect([self convertRect:self.bounds toView:self._currentContentView]);
1171     const WebCore::FloatRect unobscuredRect([self _contentRectForUserInteraction]);
1172
1173     const WebCore::FloatSize topLeftObscuredInsetAfterZoom((unobscuredRect.minXMinYCorner() - visibleRect.minXMinYCorner()) * visibleRectScaleChange);
1174     const WebCore::FloatSize bottomRightObscuredInsetAfterZoom((visibleRect.maxXMaxYCorner() - unobscuredRect.maxXMaxYCorner()) * visibleRectScaleChange);
1175
1176     const WebCore::FloatSize unobscuredRectSizeAfterZoom(unobscuredRect.size() * visibleRectScaleChange);
1177
1178     // Center to the target rect.
1179     WebCore::FloatPoint unobscuredRectLocationAfterZoom = targetRect.location() - (unobscuredRectSizeAfterZoom - targetRect.size()) * 0.5;
1180
1181     // Center to the tap point instead in case the target rect won't fit in a direction.
1182     if (targetRect.width() > unobscuredRectSizeAfterZoom.width())
1183         unobscuredRectLocationAfterZoom.setX(origin.x() - unobscuredRectSizeAfterZoom.width() / 2);
1184     if (targetRect.height() > unobscuredRectSizeAfterZoom.height())
1185         unobscuredRectLocationAfterZoom.setY(origin.y() - unobscuredRectSizeAfterZoom.height() / 2);
1186
1187     // We have computed where we want the unobscured rect to be. Now adjust for the obscuring insets.
1188     WebCore::FloatRect visibleRectAfterZoom(unobscuredRectLocationAfterZoom, unobscuredRectSizeAfterZoom);
1189     visibleRectAfterZoom.move(-topLeftObscuredInsetAfterZoom);
1190     visibleRectAfterZoom.expand(topLeftObscuredInsetAfterZoom + bottomRightObscuredInsetAfterZoom);
1191
1192     [self _zoomToPoint:visibleRectAfterZoom.center() atScale:scale animated:animated];
1193 }
1194
1195 static WebCore::FloatPoint constrainContentOffset(WebCore::FloatPoint contentOffset, WebCore::FloatSize contentSize, WebCore::FloatSize unobscuredContentSize)
1196 {
1197     WebCore::FloatSize maximumContentOffset = contentSize - unobscuredContentSize;
1198     contentOffset = contentOffset.shrunkTo(WebCore::FloatPoint(maximumContentOffset.width(), maximumContentOffset.height()));
1199     contentOffset = contentOffset.expandedTo(WebCore::FloatPoint());
1200     return contentOffset;
1201 }
1202
1203 - (void)_scrollToContentOffset:(WebCore::FloatPoint)contentOffsetInPageCoordinates scrollOrigin:(WebCore::IntPoint)scrollOrigin
1204 {
1205     if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing)
1206         return;
1207
1208     WebCore::FloatPoint contentOffsetRespectingOrigin = scrollOrigin + toFloatSize(contentOffsetInPageCoordinates);
1209
1210     WebCore::FloatPoint scaledOffset = contentOffsetRespectingOrigin;
1211     CGFloat zoomScale = contentZoomScale(self);
1212     scaledOffset.scale(zoomScale, zoomScale);
1213
1214     CGPoint contentOffsetInScrollViewCoordinates = [self _adjustedContentOffset:scaledOffset];
1215     contentOffsetInScrollViewCoordinates = contentOffsetBoundedInValidRange(_scrollView.get(), contentOffsetInScrollViewCoordinates);
1216
1217     [_scrollView _stopScrollingAndZoomingAnimations];
1218
1219     if (!CGPointEqualToPoint(contentOffsetInScrollViewCoordinates, [_scrollView contentOffset]))
1220         [_scrollView setContentOffset:contentOffsetInScrollViewCoordinates];
1221     else {
1222         // If we haven't changed anything, there would not be any VisibleContentRect update sent to the content.
1223         // The WebProcess would keep the invalid contentOffset as its scroll position.
1224         // To synchronize the WebProcess with what is on screen, we send the VisibleContentRect again.
1225         _page->resendLastVisibleContentRects();
1226     }
1227 }
1228
1229 - (BOOL)_scrollToRect:(WebCore::FloatRect)targetRect origin:(WebCore::FloatPoint)origin minimumScrollDistance:(float)minimumScrollDistance
1230 {
1231     WebCore::FloatRect unobscuredContentRect([self _contentRectForUserInteraction]);
1232     WebCore::FloatPoint unobscuredContentOffset = unobscuredContentRect.location();
1233     WebCore::FloatSize contentSize([self._currentContentView bounds].size);
1234
1235     // Center the target rect in the scroll view.
1236     // If the target doesn't fit in the scroll view, center on the gesture location instead.
1237     WebCore::FloatPoint newUnobscuredContentOffset;
1238     if (targetRect.width() <= unobscuredContentRect.width())
1239         newUnobscuredContentOffset.setX(targetRect.x() - (unobscuredContentRect.width() - targetRect.width()) / 2);
1240     else
1241         newUnobscuredContentOffset.setX(origin.x() - unobscuredContentRect.width() / 2);
1242     if (targetRect.height() <= unobscuredContentRect.height())
1243         newUnobscuredContentOffset.setY(targetRect.y() - (unobscuredContentRect.height() - targetRect.height()) / 2);
1244     else
1245         newUnobscuredContentOffset.setY(origin.y() - unobscuredContentRect.height() / 2);
1246     newUnobscuredContentOffset = constrainContentOffset(newUnobscuredContentOffset, contentSize, unobscuredContentRect.size());
1247
1248     if (unobscuredContentOffset == newUnobscuredContentOffset) {
1249         if (targetRect.width() > unobscuredContentRect.width())
1250             newUnobscuredContentOffset.setX(origin.x() - unobscuredContentRect.width() / 2);
1251         if (targetRect.height() > unobscuredContentRect.height())
1252             newUnobscuredContentOffset.setY(origin.y() - unobscuredContentRect.height() / 2);
1253         newUnobscuredContentOffset = constrainContentOffset(newUnobscuredContentOffset, contentSize, unobscuredContentRect.size());
1254     }
1255
1256     WebCore::FloatSize scrollViewOffsetDelta = newUnobscuredContentOffset - unobscuredContentOffset;
1257     scrollViewOffsetDelta.scale(contentZoomScale(self));
1258
1259     float scrollDistance = scrollViewOffsetDelta.diagonalLength();
1260     if (scrollDistance < minimumScrollDistance)
1261         return false;
1262
1263     [_contentView willStartZoomOrScroll];
1264
1265     [_scrollView setContentOffset:([_scrollView contentOffset] + scrollViewOffsetDelta) animated:YES];
1266     return true;
1267 }
1268
1269 - (void)_scrollByOffset:(WebCore::FloatPoint)offset
1270 {
1271     CGPoint currentOffset = ([_scrollView _isAnimatingScroll]) ? [_scrollView _animatedTargetOffset] : [_scrollView contentOffset];
1272
1273     CGPoint boundedOffset = contentOffsetBoundedInValidRange(_scrollView.get(), currentOffset + offset);
1274     
1275     if (CGPointEqualToPoint(boundedOffset, currentOffset))
1276         return;
1277     [_contentView willStartZoomOrScroll];
1278     [_scrollView setContentOffset:boundedOffset animated:YES];
1279 }
1280
1281 - (void)_zoomOutWithOrigin:(WebCore::FloatPoint)origin animated:(BOOL)animated
1282 {
1283     [self _zoomToPoint:origin atScale:[_scrollView minimumZoomScale] animated:animated];
1284 }
1285
1286 // focusedElementRect and selectionRect are both in document coordinates.
1287 - (void)_zoomToFocusRect:(WebCore::FloatRect)focusedElementRectInDocumentCoordinates selectionRect:(WebCore::FloatRect)selectionRectInDocumentCoordinates fontSize:(float)fontSize minimumScale:(double)minimumScale maximumScale:(double)maximumScale allowScaling:(BOOL)allowScaling forceScroll:(BOOL)forceScroll
1288 {
1289     const double WKWebViewStandardFontSize = 16;
1290     const double kMinimumHeightToShowContentAboveKeyboard = 106;
1291     const CFTimeInterval UIWebFormAnimationDuration = 0.25;
1292     const double CaretOffsetFromWindowEdge = 20;
1293
1294     // Zoom around the element's bounding frame. We use a "standard" size to determine the proper frame.
1295     double scale = allowScaling ? std::min(std::max(WKWebViewStandardFontSize / fontSize, minimumScale), maximumScale) : contentZoomScale(self);
1296     CGFloat documentWidth = [_contentView bounds].size.width;
1297     scale = CGRound(documentWidth * scale) / documentWidth;
1298
1299     UIWindow *window = [_scrollView window];
1300
1301     WebCore::FloatRect focusedElementRectInNewScale = focusedElementRectInDocumentCoordinates;
1302     focusedElementRectInNewScale.scale(scale);
1303     focusedElementRectInNewScale.moveBy([_contentView frame].origin);
1304
1305     // Find the portion of the view that is visible on the screen.
1306     UIViewController *topViewController = [[[_scrollView _viewControllerForAncestor] _rootAncestorViewController] _viewControllerForSupportedInterfaceOrientations];
1307     UIView *fullScreenView = topViewController.view;
1308     if (!fullScreenView)
1309         fullScreenView = window;
1310
1311     CGRect unobscuredScrollViewRectInWebViewCoordinates = UIEdgeInsetsInsetRect([self bounds], _obscuredInsets);
1312     CGRect visibleScrollViewBoundsInWebViewCoordinates = CGRectIntersection(unobscuredScrollViewRectInWebViewCoordinates, [fullScreenView convertRect:[fullScreenView bounds] toView:self]);
1313     CGRect formAssistantFrameInWebViewCoordinates = [window convertRect:_inputViewBounds toView:self];
1314     CGRect intersectionBetweenScrollViewAndFormAssistant = CGRectIntersection(visibleScrollViewBoundsInWebViewCoordinates, formAssistantFrameInWebViewCoordinates);
1315     CGSize visibleSize = visibleScrollViewBoundsInWebViewCoordinates.size;
1316
1317     CGFloat visibleOffsetFromTop = 0;
1318     if (!CGRectIsEmpty(intersectionBetweenScrollViewAndFormAssistant)) {
1319         CGFloat heightVisibleAboveFormAssistant = CGRectGetMinY(intersectionBetweenScrollViewAndFormAssistant) - CGRectGetMinY(visibleScrollViewBoundsInWebViewCoordinates);
1320         CGFloat heightVisibleBelowFormAssistant = CGRectGetMaxY(visibleScrollViewBoundsInWebViewCoordinates) - CGRectGetMaxY(intersectionBetweenScrollViewAndFormAssistant);
1321
1322         if (heightVisibleAboveFormAssistant >= kMinimumHeightToShowContentAboveKeyboard || heightVisibleBelowFormAssistant < heightVisibleAboveFormAssistant)
1323             visibleSize.height = heightVisibleAboveFormAssistant;
1324         else {
1325             visibleSize.height = heightVisibleBelowFormAssistant;
1326             visibleOffsetFromTop = CGRectGetMaxY(intersectionBetweenScrollViewAndFormAssistant) - CGRectGetMinY(visibleScrollViewBoundsInWebViewCoordinates);
1327         }
1328     }
1329
1330     BOOL selectionRectIsNotNull = !selectionRectInDocumentCoordinates.isZero();
1331     if (!forceScroll) {
1332         CGRect currentlyVisibleRegionInWebViewCoordinates;
1333         currentlyVisibleRegionInWebViewCoordinates.origin = unobscuredScrollViewRectInWebViewCoordinates.origin;
1334         currentlyVisibleRegionInWebViewCoordinates.origin.y += visibleOffsetFromTop;
1335         currentlyVisibleRegionInWebViewCoordinates.size = visibleSize;
1336
1337         // Don't bother scrolling if the entire node is already visible, whether or not we got a selectionRect.
1338         if (CGRectContainsRect(currentlyVisibleRegionInWebViewCoordinates, [self convertRect:focusedElementRectInDocumentCoordinates fromView:_contentView.get()]))
1339             return;
1340
1341         // Don't bother scrolling if we have a valid selectionRect and it is already visible.
1342         if (selectionRectIsNotNull && CGRectContainsRect(currentlyVisibleRegionInWebViewCoordinates, [self convertRect:selectionRectInDocumentCoordinates fromView:_contentView.get()]))
1343             return;
1344     }
1345
1346     // We want to zoom to the left/top corner of the DOM node, with as much spacing on all sides as we
1347     // can get based on the visible area after zooming (workingFrame).  The spacing in either dimension is half the
1348     // difference between the size of the DOM node and the size of the visible frame.
1349     CGFloat horizontalSpaceInWebViewCoordinates = std::max((visibleSize.width - focusedElementRectInNewScale.width()) / 2.0, 0.0);
1350     CGFloat verticalSpaceInWebViewCoordinates = std::max((visibleSize.height - focusedElementRectInNewScale.height()) / 2.0, 0.0);
1351
1352     CGPoint topLeft;
1353     topLeft.x = focusedElementRectInNewScale.x() - horizontalSpaceInWebViewCoordinates;
1354     topLeft.y = focusedElementRectInNewScale.y() - verticalSpaceInWebViewCoordinates - visibleOffsetFromTop;
1355
1356     CGFloat minimumAllowableHorizontalOffsetInWebViewCoordinates = -INFINITY;
1357     CGFloat minimumAllowableVerticalOffsetInWebViewCoordinates = -INFINITY;
1358     if (selectionRectIsNotNull) {
1359         WebCore::FloatRect selectionRectInNewScale = selectionRectInDocumentCoordinates;
1360         selectionRectInNewScale.scale(scale);
1361         selectionRectInNewScale.moveBy([_contentView frame].origin);
1362         minimumAllowableHorizontalOffsetInWebViewCoordinates = CGRectGetMaxX(selectionRectInNewScale) + CaretOffsetFromWindowEdge - visibleSize.width;
1363         minimumAllowableVerticalOffsetInWebViewCoordinates = CGRectGetMaxY(selectionRectInNewScale) + CaretOffsetFromWindowEdge - visibleSize.height - visibleOffsetFromTop;
1364     }
1365
1366     WebCore::FloatRect documentBoundsInNewScale = [_contentView bounds];
1367     documentBoundsInNewScale.scale(scale);
1368     documentBoundsInNewScale.moveBy([_contentView frame].origin);
1369
1370     // Constrain the left edge in document coordinates so that:
1371     //  - it isn't so small that the scrollVisibleRect isn't visible on the screen
1372     //  - it isn't so great that the document's right edge is less than the right edge of the screen
1373     if (selectionRectIsNotNull && topLeft.x < minimumAllowableHorizontalOffsetInWebViewCoordinates)
1374         topLeft.x = minimumAllowableHorizontalOffsetInWebViewCoordinates;
1375     else {
1376         CGFloat maximumAllowableHorizontalOffset = CGRectGetMaxX(documentBoundsInNewScale) - visibleSize.width;
1377         if (topLeft.x > maximumAllowableHorizontalOffset)
1378             topLeft.x = maximumAllowableHorizontalOffset;
1379     }
1380
1381     // Constrain the top edge in document coordinates so that:
1382     //  - it isn't so small that the scrollVisibleRect isn't visible on the screen
1383     //  - it isn't so great that the document's bottom edge is higher than the top of the form assistant
1384     if (selectionRectIsNotNull && topLeft.y < minimumAllowableVerticalOffsetInWebViewCoordinates)
1385         topLeft.y = minimumAllowableVerticalOffsetInWebViewCoordinates;
1386     else {
1387         CGFloat maximumAllowableVerticalOffset = CGRectGetMaxY(documentBoundsInNewScale) - visibleSize.height;
1388         if (topLeft.y > maximumAllowableVerticalOffset)
1389             topLeft.y = maximumAllowableVerticalOffset;
1390     }
1391
1392     WebCore::FloatPoint newCenter = CGPointMake(topLeft.x + unobscuredScrollViewRectInWebViewCoordinates.size.width / 2.0, topLeft.y + unobscuredScrollViewRectInWebViewCoordinates.size.height / 2.0);
1393
1394     if (scale != contentZoomScale(self))
1395         _page->willStartUserTriggeredZooming();
1396
1397     // The newCenter has been computed in the new scale, but _zoomToCenter expected the center to be in the original scale.
1398     newCenter.scale(1 / scale, 1 / scale);
1399     [_scrollView _zoomToCenter:newCenter
1400                         scale:scale
1401                      duration:UIWebFormAnimationDuration
1402                         force:YES];
1403 }
1404
1405 - (BOOL)_zoomToRect:(WebCore::FloatRect)targetRect withOrigin:(WebCore::FloatPoint)origin fitEntireRect:(BOOL)fitEntireRect minimumScale:(double)minimumScale maximumScale:(double)maximumScale minimumScrollDistance:(float)minimumScrollDistance
1406 {
1407     const float maximumScaleFactorDeltaForPanScroll = 0.02;
1408
1409     double currentScale = contentZoomScale(self);
1410
1411     WebCore::FloatSize unobscuredContentSize([self _contentRectForUserInteraction].size);
1412     double horizontalScale = unobscuredContentSize.width() * currentScale / targetRect.width();
1413     double verticalScale = unobscuredContentSize.height() * currentScale / targetRect.height();
1414
1415     horizontalScale = std::min(std::max(horizontalScale, minimumScale), maximumScale);
1416     verticalScale = std::min(std::max(verticalScale, minimumScale), maximumScale);
1417
1418     double targetScale = fitEntireRect ? std::min(horizontalScale, verticalScale) : horizontalScale;
1419     if (fabs(targetScale - currentScale) < maximumScaleFactorDeltaForPanScroll) {
1420         if ([self _scrollToRect:targetRect origin:origin minimumScrollDistance:minimumScrollDistance])
1421             return true;
1422     } else if (targetScale != currentScale) {
1423         [self _zoomToRect:targetRect atScale:targetScale origin:origin animated:YES];
1424         return true;
1425     }
1426     
1427     return false;
1428 }
1429
1430 - (void)didMoveToWindow
1431 {
1432     _page->viewStateDidChange(WebCore::ViewState::AllFlags);
1433 }
1434
1435 - (void)setOpaque:(BOOL)opaque
1436 {
1437     BOOL oldOpaque = self.opaque;
1438
1439     [super setOpaque:opaque];
1440     [_contentView setOpaque:opaque];
1441
1442     if (oldOpaque == opaque)
1443         return;
1444
1445     _page->setDrawsBackground(opaque);
1446     [self _updateScrollViewBackground];
1447 }
1448
1449 - (void)setBackgroundColor:(UIColor *)backgroundColor
1450 {
1451     [super setBackgroundColor:backgroundColor];
1452     [_contentView setBackgroundColor:backgroundColor];
1453 }
1454
1455 #pragma mark - UIScrollViewDelegate
1456
1457 - (BOOL)usesStandardContentView
1458 {
1459     return !_customContentView;
1460 }
1461
1462 - (CGSize)scrollView:(UIScrollView*)scrollView contentSizeForZoomScale:(CGFloat)scale withProposedSize:(CGSize)proposedSize
1463 {
1464     return roundScrollViewContentSize(*_page, proposedSize);
1465 }
1466
1467 - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
1468 {
1469     ASSERT(_scrollView == scrollView);
1470
1471     if (_customContentView)
1472         return _customContentView.get();
1473
1474     return _contentView.get();
1475 }
1476
1477 - (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view
1478 {
1479     if (![self usesStandardContentView])
1480         return;
1481
1482     if (scrollView.pinchGestureRecognizer.state == UIGestureRecognizerStateBegan) {
1483         _page->willStartUserTriggeredZooming();
1484         [_contentView scrollViewWillStartPanOrPinchGesture];
1485     }
1486     [_contentView willStartZoomOrScroll];
1487 }
1488
1489 - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
1490 {
1491     if (![self usesStandardContentView])
1492         return;
1493
1494     if (scrollView.panGestureRecognizer.state == UIGestureRecognizerStateBegan)
1495         [_contentView scrollViewWillStartPanOrPinchGesture];
1496
1497     [_contentView willStartZoomOrScroll];
1498 #if ENABLE(CSS_SCROLL_SNAP) && ENABLE(ASYNC_SCROLLING)
1499     // FIXME: We will want to detect whether snapping will occur before beginning to drag. See WebPageProxy::didCommitLayerTree.
1500     WebKit::RemoteScrollingCoordinatorProxy* coordinator = _page->scrollingCoordinatorProxy();
1501     ASSERT(scrollView == _scrollView.get());
1502     CGFloat scrollDecelerationFactor = (coordinator && coordinator->shouldSetScrollViewDecelerationRateFast()) ? UIScrollViewDecelerationRateFast : [_scrollView preferredScrollDecelerationFactor];
1503     scrollView.horizontalScrollDecelerationFactor = scrollDecelerationFactor;
1504     scrollView.verticalScrollDecelerationFactor = scrollDecelerationFactor;
1505 #endif
1506 }
1507
1508 - (void)_didFinishScrolling
1509 {
1510     if (![self usesStandardContentView])
1511         return;
1512
1513     [self _updateVisibleContentRects];
1514     [_contentView didFinishScrolling];
1515 }
1516
1517 - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
1518 {
1519     // Work around <rdar://problem/16374753> by avoiding deceleration while
1520     // zooming. We'll animate to the right place once the zoom finishes.
1521     if ([scrollView isZooming])
1522         *targetContentOffset = [scrollView contentOffset];
1523 #if ENABLE(CSS_SCROLL_SNAP) && ENABLE(ASYNC_SCROLLING)
1524     if (WebKit::RemoteScrollingCoordinatorProxy* coordinator = _page->scrollingCoordinatorProxy()) {
1525         // FIXME: Here, I'm finding the maximum horizontal/vertical scroll offsets. There's probably a better way to do this.
1526         CGSize maxScrollOffsets = CGSizeMake(scrollView.contentSize.width - scrollView.bounds.size.width, scrollView.contentSize.height - scrollView.bounds.size.height);
1527         
1528         CGRect fullViewRect = self.bounds;
1529
1530         UIEdgeInsets contentInset;
1531
1532         id<WKUIDelegatePrivate> uiDelegatePrivate = static_cast<id <WKUIDelegatePrivate>>([self UIDelegate]);
1533         if ([uiDelegatePrivate respondsToSelector:@selector(_webView:finalObscuredInsetsForScrollView:withVelocity:targetContentOffset:)])
1534             contentInset = [uiDelegatePrivate _webView:self finalObscuredInsetsForScrollView:scrollView withVelocity:velocity targetContentOffset:targetContentOffset];
1535         else
1536             contentInset = [self _computedContentInset];
1537
1538         CGRect unobscuredRect = UIEdgeInsetsInsetRect(fullViewRect, contentInset);
1539         
1540         coordinator->adjustTargetContentOffsetForSnapping(maxScrollOffsets, velocity, unobscuredRect.origin.y, targetContentOffset);
1541     }
1542 #endif
1543 }
1544
1545 - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
1546 {
1547     // If we're decelerating, scroll offset will be updated when scrollViewDidFinishDecelerating: is called.
1548     if (!decelerate)
1549         [self _didFinishScrolling];
1550 }
1551
1552 - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
1553 {
1554     [self _didFinishScrolling];
1555 }
1556
1557 - (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView
1558 {
1559     [self _didFinishScrolling];
1560 }
1561
1562 - (void)scrollViewDidScroll:(UIScrollView *)scrollView
1563 {
1564     if (![self usesStandardContentView])
1565         [_customContentView scrollViewDidScroll:(UIScrollView *)scrollView];
1566
1567     [self _updateVisibleContentRects];
1568     
1569     if (WebKit::RemoteLayerTreeScrollingPerformanceData* scrollPerfData = _page->scrollingPerformanceData())
1570         scrollPerfData->didScroll([self visibleRectInViewCoordinates]);
1571 }
1572
1573 - (void)scrollViewDidZoom:(UIScrollView *)scrollView
1574 {
1575     [self _updateScrollViewBackground];
1576     [self _updateVisibleContentRects];
1577 }
1578
1579 - (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale
1580 {
1581     ASSERT(scrollView == _scrollView);
1582     [self _updateVisibleContentRects];
1583     [_contentView didZoomToScale:scale];
1584 }
1585
1586 - (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
1587 {
1588     [self _didFinishScrolling];
1589 }
1590
1591 - (void)_scrollViewDidInterruptDecelerating:(UIScrollView *)scrollView
1592 {
1593     if (![self usesStandardContentView])
1594         return;
1595
1596     [_contentView didInterruptScrolling];
1597     [self _updateVisibleContentRects];
1598 }
1599
1600 - (void)_frameOrBoundsChanged
1601 {
1602     CGRect bounds = self.bounds;
1603     [_scrollView setFrame:bounds];
1604
1605     if (_dynamicViewportUpdateMode == DynamicViewportUpdateMode::NotResizing) {
1606         if (!_overridesMinimumLayoutSize)
1607             _page->setViewportConfigurationMinimumLayoutSize(WebCore::FloatSize(bounds.size));
1608         if (!_overridesMaximumUnobscuredSize)
1609             _page->setMaximumUnobscuredSize(WebCore::FloatSize(bounds.size));
1610         if (WebKit::DrawingAreaProxy* drawingArea = _page->drawingArea())
1611             drawingArea->setSize(WebCore::IntSize(bounds.size), WebCore::IntSize(), WebCore::IntSize());
1612     }
1613
1614     [_customContentView web_setMinimumSize:bounds.size];
1615     [self _updateVisibleContentRects];
1616 }
1617
1618 // Unobscured content rect where the user can interact. When the keyboard is up, this should be the area above or below the keyboard, wherever there is enough space.
1619 - (CGRect)_contentRectForUserInteraction
1620 {
1621     // FIXME: handle split keyboard.
1622     UIEdgeInsets obscuredInsets = _obscuredInsets;
1623     obscuredInsets.bottom = std::max(_obscuredInsets.bottom, _inputViewBounds.size.height);
1624     CGRect unobscuredRect = UIEdgeInsetsInsetRect(self.bounds, obscuredInsets);
1625     return [self convertRect:unobscuredRect toView:self._currentContentView];
1626 }
1627
1628 // Ideally UIScrollView would expose this for us: <rdar://problem/21394567>.
1629 - (BOOL)_scrollViewIsRubberBanding
1630 {
1631     float deviceScaleFactor = _page->deviceScaleFactor();
1632
1633     CGPoint contentOffset = [_scrollView contentOffset];
1634     CGPoint boundedOffset = contentOffsetBoundedInValidRange(_scrollView.get(), contentOffset);
1635     return !pointsEqualInDevicePixels(contentOffset, boundedOffset, deviceScaleFactor);
1636 }
1637
1638 - (void)_updateVisibleContentRects
1639 {
1640     if (![self usesStandardContentView]) {
1641         [_customContentView web_computedContentInsetDidChange];
1642         return;
1643     }
1644
1645     if (_delayUpdateVisibleContentRects) {
1646         _hadDelayedUpdateVisibleContentRects = YES;
1647         return;
1648     }
1649
1650     if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing)
1651         return;
1652
1653     if (_needsResetViewStateAfterCommitLoadForMainFrame)
1654         return;
1655
1656     if ([_scrollView isZoomBouncing])
1657         return;
1658
1659     CGRect fullViewRect = self.bounds;
1660     CGRect visibleRectInContentCoordinates = _frozenVisibleContentRect ? _frozenVisibleContentRect.value() : [self convertRect:fullViewRect toView:_contentView.get()];
1661
1662     CGRect unobscuredRect = UIEdgeInsetsInsetRect(fullViewRect, [self _computedContentInset]);
1663     CGRect unobscuredRectInContentCoordinates = _frozenUnobscuredContentRect ? _frozenUnobscuredContentRect.value() : [self convertRect:unobscuredRect toView:_contentView.get()];
1664
1665     CGFloat scaleFactor = contentZoomScale(self);
1666
1667     BOOL isStableState = !(_isChangingObscuredInsetsInteractively || [_scrollView isDragging] || [_scrollView isDecelerating] || [_scrollView isZooming] || [_scrollView _isAnimatingZoom] || [_scrollView _isScrollingToTop] || [self _scrollViewIsRubberBanding]);
1668
1669     // FIXME: this can be made static after we stop supporting iOS 8.x.
1670     if (isStableState && [_scrollView respondsToSelector:@selector(_isInterruptingDeceleration)])
1671         isStableState = ![_scrollView performSelector:@selector(_isInterruptingDeceleration)];
1672
1673 #if ENABLE(CSS_SCROLL_SNAP) && ENABLE(ASYNC_SCROLLING)
1674     if (isStableState) {
1675         WebKit::RemoteScrollingCoordinatorProxy* coordinator = _page->scrollingCoordinatorProxy();
1676         if (coordinator && coordinator->hasActiveSnapPoint()) {
1677             CGRect fullViewRect = self.bounds;
1678             CGRect unobscuredRect = UIEdgeInsetsInsetRect(fullViewRect, [self _computedContentInset]);
1679             
1680             CGPoint currentPoint = [_scrollView contentOffset];
1681             CGPoint activePoint = coordinator->nearestActiveContentInsetAdjustedSnapPoint(unobscuredRect.origin.y, currentPoint);
1682
1683             if (!CGPointEqualToPoint(activePoint, currentPoint)) {
1684                 RetainPtr<WKScrollView> strongScrollView = _scrollView;
1685                 dispatch_async(dispatch_get_main_queue(), [strongScrollView, activePoint] {
1686                     [strongScrollView setContentOffset:activePoint animated:NO];
1687                 });
1688             }
1689         }
1690     }
1691 #endif
1692     
1693     [_contentView didUpdateVisibleRect:visibleRectInContentCoordinates
1694         unobscuredRect:unobscuredRectInContentCoordinates
1695         unobscuredRectInScrollViewCoordinates:unobscuredRect
1696         scale:scaleFactor minimumScale:[_scrollView minimumZoomScale]
1697         inStableState:isStableState isChangingObscuredInsetsInteractively:_isChangingObscuredInsetsInteractively];
1698 }
1699
1700 - (void)_didFinishLoadForMainFrame
1701 {
1702     if (_gestureController)
1703         _gestureController->didFinishLoadForMainFrame();
1704 }
1705
1706 - (void)_didFailLoadForMainFrame
1707 {
1708     if (_gestureController)
1709         _gestureController->didFailLoadForMainFrame();
1710 }
1711
1712 - (void)_didSameDocumentNavigationForMainFrame:(WebKit::SameDocumentNavigationType)navigationType
1713 {
1714     [_customContentView web_didSameDocumentNavigation:toAPI(navigationType)];
1715
1716     if (_gestureController)
1717         _gestureController->didSameDocumentNavigationForMainFrame(navigationType);
1718 }
1719
1720 - (void)_keyboardChangedWithInfo:(NSDictionary *)keyboardInfo adjustScrollView:(BOOL)adjustScrollView
1721 {
1722     NSValue *endFrameValue = [keyboardInfo objectForKey:UIKeyboardFrameEndUserInfoKey];
1723     if (!endFrameValue)
1724         return;
1725
1726     // The keyboard rect is always in screen coordinates. In the view services case the window does not
1727     // have the interface orientation rotation transformation; its host does. So, it makes no sense to
1728     // clip the keyboard rect against its screen.
1729     if ([[self window] _isHostedInAnotherProcess])
1730         _inputViewBounds = [self.window convertRect:[endFrameValue CGRectValue] fromWindow:nil];
1731     else
1732         _inputViewBounds = [self.window convertRect:CGRectIntersection([endFrameValue CGRectValue], self.window.screen.bounds) fromWindow:nil];
1733
1734     [self _updateVisibleContentRects];
1735
1736     if (adjustScrollView)
1737         [_scrollView _adjustForAutomaticKeyboardInfo:keyboardInfo animated:YES lastAdjustment:&_lastAdjustmentForScroller];
1738 }
1739
1740 - (BOOL)_shouldUpdateKeyboardWithInfo:(NSDictionary *)keyboardInfo
1741 {
1742     if ([_contentView isAssistingNode])
1743         return YES;
1744
1745     NSNumber *isLocalKeyboard = [keyboardInfo valueForKey:UIKeyboardIsLocalUserInfoKey];
1746     return isLocalKeyboard && !isLocalKeyboard.boolValue;
1747 }
1748
1749 - (void)_keyboardWillChangeFrame:(NSNotification *)notification
1750 {
1751     if ([self _shouldUpdateKeyboardWithInfo:notification.userInfo])
1752         [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES];
1753 }
1754
1755 - (void)_keyboardDidChangeFrame:(NSNotification *)notification
1756 {
1757     [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:NO];
1758 }
1759
1760 - (void)_keyboardWillShow:(NSNotification *)notification
1761 {
1762     if ([self _shouldUpdateKeyboardWithInfo:notification.userInfo])
1763         [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES];
1764 }
1765
1766 - (void)_keyboardWillHide:(NSNotification *)notification
1767 {
1768     // Ignore keyboard will hide notifications sent during rotation. They're just there for
1769     // backwards compatibility reasons and processing the will hide notification would
1770     // temporarily screw up the the unobscured view area.
1771     if ([[UIPeripheralHost sharedInstance] rotationState])
1772         return;
1773
1774     [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES];
1775 }
1776
1777 - (void)_windowDidRotate:(NSNotification *)notification
1778 {
1779     if (!_overridesInterfaceOrientation)
1780         _page->setDeviceOrientation(deviceOrientation());
1781 }
1782
1783 - (void)_contentSizeCategoryDidChange:(NSNotification *)notification
1784 {
1785     _page->contentSizeCategoryDidChange([self _contentSizeCategory]);
1786 }
1787
1788 - (NSString *)_contentSizeCategory
1789 {
1790     return [[UIApplication sharedApplication] preferredContentSizeCategory];
1791 }
1792
1793 - (void)setAllowsBackForwardNavigationGestures:(BOOL)allowsBackForwardNavigationGestures
1794 {
1795     if (_allowsBackForwardNavigationGestures == allowsBackForwardNavigationGestures)
1796         return;
1797
1798     _allowsBackForwardNavigationGestures = allowsBackForwardNavigationGestures;
1799
1800     if (allowsBackForwardNavigationGestures) {
1801         if (!_gestureController) {
1802             _gestureController = std::make_unique<WebKit::ViewGestureController>(*_page);
1803             _gestureController->installSwipeHandler(self, [self scrollView]);
1804             _gestureController->setAlternateBackForwardListSourceView([_configuration _alternateWebViewForNavigationGestures]);
1805         }
1806     } else
1807         _gestureController = nullptr;
1808
1809     _page->setShouldRecordNavigationSnapshots(allowsBackForwardNavigationGestures);
1810 }
1811
1812 - (BOOL)allowsBackForwardNavigationGestures
1813 {
1814     return _allowsBackForwardNavigationGestures;
1815 }
1816
1817 - (void)_navigationGestureDidBegin
1818 {
1819     // During a back/forward swipe, there's a view interposed between this view and the content view that has
1820     // an offset and animation on it, which results in computing incorrect rectangles. Work around by using
1821     // frozen rects during swipes.
1822     CGRect fullViewRect = self.bounds;
1823     CGRect unobscuredRect = UIEdgeInsetsInsetRect(fullViewRect, [self _computedContentInset]);
1824
1825     _frozenVisibleContentRect = [self convertRect:fullViewRect toView:_contentView.get()];
1826     _frozenUnobscuredContentRect = [self convertRect:unobscuredRect toView:_contentView.get()];
1827 }
1828
1829 - (void)_navigationGestureDidEnd
1830 {
1831     _frozenVisibleContentRect = Nullopt;
1832     _frozenUnobscuredContentRect = Nullopt;
1833 }
1834
1835 #endif // PLATFORM(IOS)
1836
1837 #pragma mark OS X-specific methods
1838
1839 #if PLATFORM(MAC)
1840
1841 - (BOOL)becomeFirstResponder
1842 {
1843     return [[self window] makeFirstResponder: _wkView.get()];
1844 }
1845
1846 - (BOOL)acceptsFirstResponder
1847 {
1848     return [_wkView acceptsFirstResponder];
1849 }
1850
1851 - (void)resizeSubviewsWithOldSize:(NSSize)oldSize
1852 {
1853     [_wkView setFrame:self.bounds];
1854 }
1855
1856 - (void)setAllowsBackForwardNavigationGestures:(BOOL)allowsBackForwardNavigationGestures
1857 {
1858     [_wkView setAllowsBackForwardNavigationGestures:allowsBackForwardNavigationGestures];
1859 }
1860
1861 - (BOOL)allowsBackForwardNavigationGestures
1862 {
1863     return [_wkView allowsBackForwardNavigationGestures];
1864 }
1865
1866 - (void)setAllowsMagnification:(BOOL)allowsMagnification
1867 {
1868     [_wkView setAllowsMagnification:allowsMagnification];
1869 }
1870
1871 - (BOOL)allowsMagnification
1872 {
1873     return [_wkView allowsMagnification];
1874 }
1875
1876 - (void)setMagnification:(CGFloat)magnification
1877 {
1878     [_wkView setMagnification:magnification];
1879 }
1880
1881 - (CGFloat)magnification
1882 {
1883     return [_wkView magnification];
1884 }
1885
1886 - (void)setMagnification:(CGFloat)magnification centeredAtPoint:(CGPoint)point
1887 {
1888     [_wkView setMagnification:magnification centeredAtPoint:NSPointFromCGPoint(point)];
1889 }
1890
1891 - (BOOL)_ignoresNonWheelEvents
1892 {
1893     return [_wkView _ignoresNonWheelEvents];
1894 }
1895
1896 - (void)_setIgnoresNonWheelEvents:(BOOL)ignoresNonWheelEvents
1897 {
1898     [_wkView _setIgnoresNonWheelEvents:ignoresNonWheelEvents];
1899 }
1900
1901 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
1902 {
1903     return [_wkView draggingEntered:sender];
1904 }
1905
1906 - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
1907 {
1908     return [_wkView draggingUpdated:sender];
1909 }
1910
1911 - (void)draggingExited:(id <NSDraggingInfo>)sender
1912 {
1913     [_wkView draggingExited:sender];
1914 }
1915
1916 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
1917 {
1918     return [_wkView prepareForDragOperation:sender];
1919 }
1920
1921 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
1922 {
1923     return [_wkView performDragOperation:sender];
1924 }
1925 #endif // PLATFORM(MAC)
1926
1927 @end
1928
1929 @implementation WKWebView (WKPrivate)
1930
1931 - (BOOL)_isEditable
1932 {
1933     return _page->isEditable();
1934 }
1935
1936 - (void)_setEditable:(BOOL)editable
1937 {
1938     _page->setEditable(editable);
1939 #if !PLATFORM(IOS)
1940     [_wkView _addFontPanelObserver];
1941 #endif
1942 }
1943
1944 - (_WKRemoteObjectRegistry *)_remoteObjectRegistry
1945 {
1946     if (!_remoteObjectRegistry) {
1947         _remoteObjectRegistry = adoptNS([[_WKRemoteObjectRegistry alloc] _initWithMessageSender:*_page]);
1948         _page->process().processPool().addMessageReceiver(Messages::RemoteObjectRegistry::messageReceiverName(), _page->pageID(), [_remoteObjectRegistry remoteObjectRegistry]);
1949     }
1950
1951     return _remoteObjectRegistry.get();
1952 }
1953
1954 - (WKBrowsingContextHandle *)_handle
1955 {
1956     return [[[WKBrowsingContextHandle alloc] _initWithPageID:_page->pageID()] autorelease];
1957 }
1958
1959 - (_WKRenderingProgressEvents)_observedRenderingProgressEvents
1960 {
1961     return _observedRenderingProgressEvents;
1962 }
1963
1964 - (id <WKHistoryDelegatePrivate>)_historyDelegate
1965 {
1966     return _navigationState->historyDelegate().autorelease();
1967 }
1968
1969 - (void)_setHistoryDelegate:(id <WKHistoryDelegatePrivate>)historyDelegate
1970 {
1971     _page->setHistoryClient(_navigationState->createHistoryClient());
1972     _navigationState->setHistoryDelegate(historyDelegate);
1973 }
1974
1975 - (NSURL *)_unreachableURL
1976 {
1977     return [NSURL _web_URLWithWTFString:_page->pageLoadState().unreachableURL()];
1978 }
1979
1980 - (void)_loadAlternateHTMLString:(NSString *)string baseURL:(NSURL *)baseURL forUnreachableURL:(NSURL *)unreachableURL
1981 {
1982     _page->loadAlternateHTMLString(string, [baseURL _web_originalDataAsWTFString], [unreachableURL _web_originalDataAsWTFString]);
1983 }
1984
1985 - (NSArray *)_certificateChain
1986 {
1987     if (WebKit::WebFrameProxy* mainFrame = _page->mainFrame())
1988         return mainFrame->certificateInfo() ? (NSArray *)mainFrame->certificateInfo()->certificateInfo().certificateChain() : nil;
1989
1990     return nil;
1991 }
1992
1993 - (NSURL *)_committedURL
1994 {
1995     return [NSURL _web_URLWithWTFString:_page->pageLoadState().url()];
1996 }
1997
1998 - (NSString *)_MIMEType
1999 {
2000     if (_page->mainFrame())
2001         return _page->mainFrame()->mimeType();
2002
2003     return nil;
2004 }
2005
2006 - (NSString *)_userAgent
2007 {
2008     return _page->userAgent();
2009 }
2010
2011 - (NSString *)_applicationNameForUserAgent
2012 {
2013     return _page->applicationNameForUserAgent();
2014 }
2015
2016 - (void)_setApplicationNameForUserAgent:(NSString *)applicationNameForUserAgent
2017 {
2018     _page->setApplicationNameForUserAgent(applicationNameForUserAgent);
2019 }
2020
2021 - (NSString *)_customUserAgent
2022 {
2023     return self.customUserAgent;
2024 }
2025
2026 - (void)_setCustomUserAgent:(NSString *)customUserAgent
2027 {
2028     self.customUserAgent = customUserAgent;
2029 }
2030
2031 - (void)_setUserContentExtensionsEnabled:(BOOL)userContentExtensionsEnabled
2032 {
2033     // This is kept for binary compatibility with iOS 9.
2034 }
2035
2036 - (BOOL)_userContentExtensionsEnabled
2037 {
2038     // This is kept for binary compatibility with iOS 9.
2039     return true;
2040 }
2041
2042 - (pid_t)_webProcessIdentifier
2043 {
2044     return _page->isValid() ? _page->processIdentifier() : 0;
2045 }
2046
2047 - (void)_killWebContentProcess
2048 {
2049     if (!_page->isValid())
2050         return;
2051
2052     _page->process().terminate();
2053 }
2054
2055 - (WKNavigation *)_reloadWithoutContentBlockers
2056 {
2057     const bool reloadFromOrigin = false;
2058     const bool contentBlockersEnabled = false;
2059     auto navigation = _page->reload(reloadFromOrigin, contentBlockersEnabled);
2060     if (!navigation)
2061         return nil;
2062     
2063     return [wrapper(*navigation.release().leakRef()) autorelease];
2064 }
2065
2066 - (void)_killWebContentProcessAndResetState
2067 {
2068     _page->terminateProcess();
2069 }
2070
2071 #if PLATFORM(IOS)
2072 static WebCore::FloatSize activeMinimumLayoutSize(WKWebView *webView, const CGRect& bounds)
2073 {
2074     return WebCore::FloatSize(webView->_overridesMinimumLayoutSize ? webView->_minimumLayoutSizeOverride : bounds.size);
2075 }
2076
2077 static WebCore::FloatSize activeMaximumUnobscuredSize(WKWebView *webView, const CGRect& bounds)
2078 {
2079     return WebCore::FloatSize(webView->_overridesMaximumUnobscuredSize ? webView->_maximumUnobscuredSizeOverride : bounds.size);
2080 }
2081
2082 static int32_t activeOrientation(WKWebView *webView)
2083 {
2084     return webView->_overridesInterfaceOrientation ? deviceOrientationForUIInterfaceOrientation(webView->_interfaceOrientationOverride) : webView->_page->deviceOrientation();
2085 }
2086
2087 - (void (^)(void))_retainActiveFocusedState
2088 {
2089     ++_activeFocusedStateRetainCount;
2090
2091     // FIXME: Use something like CompletionHandlerCallChecker to ensure that the returned block is called before it's released.
2092     return [[[self] {
2093         --_activeFocusedStateRetainCount;
2094     } copy] autorelease];
2095 }
2096
2097 - (void)_becomeFirstResponderWithSelectionMovingForward:(BOOL)selectingForward completionHandler:(void (^)(BOOL didBecomeFirstResponder))completionHandler
2098 {
2099     typeof(completionHandler) completionHandlerCopy = nil;
2100     if (completionHandler)
2101         completionHandlerCopy = Block_copy(completionHandler);
2102
2103     [_contentView _becomeFirstResponderWithSelectionMovingForward:selectingForward completionHandler:[completionHandlerCopy](BOOL didBecomeFirstResponder) {
2104         if (!completionHandlerCopy)
2105             return;
2106
2107         completionHandlerCopy(didBecomeFirstResponder);
2108         Block_release(completionHandlerCopy);
2109     }];
2110 }
2111
2112 - (id)_snapshotLayerContentsForBackForwardListItem:(WKBackForwardListItem *)item
2113 {
2114     if (_page->backForwardList().currentItem() == &item._item)
2115         _page->recordNavigationSnapshot(*_page->backForwardList().currentItem());
2116
2117     if (auto* viewSnapshot = item._item.snapshot())
2118         return viewSnapshot->asLayerContents();
2119
2120     return nil;
2121 }
2122
2123 #endif
2124
2125 - (void)_didRelaunchProcess
2126 {
2127 #if PLATFORM(IOS)
2128     CGRect bounds = self.bounds;
2129     WebCore::FloatSize minimalLayoutSize = activeMinimumLayoutSize(self, bounds);
2130     _page->setViewportConfigurationMinimumLayoutSize(minimalLayoutSize);
2131     _page->setMaximumUnobscuredSize(activeMaximumUnobscuredSize(self, bounds));
2132 #endif
2133 }
2134
2135 - (NSData *)_sessionStateData
2136 {
2137     WebKit::SessionState sessionState = _page->sessionState();
2138
2139     // FIXME: This should not use the legacy session state encoder.
2140     return [wrapper(*WebKit::encodeLegacySessionState(sessionState).release().leakRef()) autorelease];
2141 }
2142
2143 - (_WKSessionState *)_sessionState
2144 {
2145     return adoptNS([[_WKSessionState alloc] _initWithSessionState:_page->sessionState()]).autorelease();
2146 }
2147
2148 - (void)_restoreFromSessionStateData:(NSData *)sessionStateData
2149 {
2150     // FIXME: This should not use the legacy session state decoder.
2151     WebKit::SessionState sessionState;
2152     if (!WebKit::decodeLegacySessionState(static_cast<const uint8_t*>(sessionStateData.bytes), sessionStateData.length, sessionState))
2153         return;
2154
2155     _page->restoreFromSessionState(WTF::move(sessionState), true);
2156 }
2157
2158 - (WKNavigation *)_restoreSessionState:(_WKSessionState *)sessionState andNavigate:(BOOL)navigate
2159 {
2160     auto navigation = _page->restoreFromSessionState(sessionState->_sessionState, navigate);
2161     if (!navigation)
2162         return nil;
2163
2164     return [wrapper(*navigation.release().leakRef()) autorelease];
2165 }
2166
2167 - (void)_close
2168 {
2169     _page->close();
2170 }
2171
2172 - (BOOL)_allowsRemoteInspection
2173 {
2174 #if ENABLE(REMOTE_INSPECTOR)
2175     return _page->allowsRemoteInspection();
2176 #else
2177     return NO;
2178 #endif
2179 }
2180
2181 - (void)_setAllowsRemoteInspection:(BOOL)allow
2182 {
2183 #if ENABLE(REMOTE_INSPECTOR)
2184     _page->setAllowsRemoteInspection(allow);
2185 #endif
2186 }
2187
2188 - (BOOL)_addsVisitedLinks
2189 {
2190     return _page->addsVisitedLinks();
2191 }
2192
2193 - (void)_setAddsVisitedLinks:(BOOL)addsVisitedLinks
2194 {
2195     _page->setAddsVisitedLinks(addsVisitedLinks);
2196 }
2197
2198 - (BOOL)_networkRequestsInProgress
2199 {
2200     return _page->pageLoadState().networkRequestsInProgress();
2201 }
2202
2203 static inline WebCore::LayoutMilestones layoutMilestones(_WKRenderingProgressEvents events)
2204 {
2205     WebCore::LayoutMilestones milestones = 0;
2206
2207     if (events & _WKRenderingProgressEventFirstLayout)
2208         milestones |= WebCore::DidFirstLayout;
2209
2210     if (events & _WKRenderingProgressEventFirstVisuallyNonEmptyLayout)
2211         milestones |= WebCore::DidFirstVisuallyNonEmptyLayout;
2212
2213     if (events & _WKRenderingProgressEventFirstPaintWithSignificantArea)
2214         milestones |= WebCore::DidHitRelevantRepaintedObjectsAreaThreshold;
2215
2216     if (events & _WKRenderingProgressEventReachedSessionRestorationRenderTreeSizeThreshold)
2217         milestones |= WebCore::ReachedSessionRestorationRenderTreeSizeThreshold;
2218
2219     if (events & _WKRenderingProgressEventFirstLayoutAfterSuppressedIncrementalRendering)
2220         milestones |= WebCore::DidFirstLayoutAfterSuppressedIncrementalRendering;
2221
2222     if (events & _WKRenderingProgressEventFirstPaintAfterSuppressedIncrementalRendering)
2223         milestones |= WebCore::DidFirstPaintAfterSuppressedIncrementalRendering;
2224
2225     return milestones;
2226 }
2227
2228 - (void)_setObservedRenderingProgressEvents:(_WKRenderingProgressEvents)observedRenderingProgressEvents
2229 {
2230     _observedRenderingProgressEvents = observedRenderingProgressEvents;
2231     _page->listenForLayoutMilestones(layoutMilestones(observedRenderingProgressEvents));
2232 }
2233
2234 - (void)_getMainResourceDataWithCompletionHandler:(void (^)(NSData *, NSError *))completionHandler
2235 {
2236     auto handler = adoptNS([completionHandler copy]);
2237
2238     _page->getMainResourceDataOfFrame(_page->mainFrame(), [handler](API::Data* data, WebKit::CallbackBase::Error error) {
2239         void (^completionHandlerBlock)(NSData *, NSError *) = (void (^)(NSData *, NSError *))handler.get();
2240         if (error != WebKit::CallbackBase::Error::None) {
2241             // FIXME: Pipe a proper error in from the WebPageProxy.
2242             RetainPtr<NSError> error = adoptNS([[NSError alloc] init]);
2243             completionHandlerBlock(nil, error.get());
2244         } else
2245             completionHandlerBlock(wrapper(*data), nil);
2246     });
2247 }
2248
2249 - (void)_getWebArchiveDataWithCompletionHandler:(void (^)(NSData *, NSError *))completionHandler
2250 {
2251     auto handler = adoptNS([completionHandler copy]);
2252
2253     _page->getWebArchiveOfFrame(_page->mainFrame(), [handler](API::Data* data, WebKit::CallbackBase::Error error) {
2254         void (^completionHandlerBlock)(NSData *, NSError *) = (void (^)(NSData *, NSError *))handler.get();
2255         if (error != WebKit::CallbackBase::Error::None) {
2256             // FIXME: Pipe a proper error in from the WebPageProxy.
2257             RetainPtr<NSError> error = adoptNS([[NSError alloc] init]);
2258             completionHandlerBlock(nil, error.get());
2259         } else
2260             completionHandlerBlock(wrapper(*data), nil);
2261     });
2262 }
2263
2264 - (_WKPaginationMode)_paginationMode
2265 {
2266     switch (_page->paginationMode()) {
2267     case WebCore::Pagination::Unpaginated:
2268         return _WKPaginationModeUnpaginated;
2269     case WebCore::Pagination::LeftToRightPaginated:
2270         return _WKPaginationModeLeftToRight;
2271     case WebCore::Pagination::RightToLeftPaginated:
2272         return _WKPaginationModeRightToLeft;
2273     case WebCore::Pagination::TopToBottomPaginated:
2274         return _WKPaginationModeTopToBottom;
2275     case WebCore::Pagination::BottomToTopPaginated:
2276         return _WKPaginationModeBottomToTop;
2277     }
2278
2279     ASSERT_NOT_REACHED();
2280     return _WKPaginationModeUnpaginated;
2281 }
2282
2283 - (void)_setPaginationMode:(_WKPaginationMode)paginationMode
2284 {
2285     WebCore::Pagination::Mode mode;
2286     switch (paginationMode) {
2287     case _WKPaginationModeUnpaginated:
2288         mode = WebCore::Pagination::Unpaginated;
2289         break;
2290     case _WKPaginationModeLeftToRight:
2291         mode = WebCore::Pagination::LeftToRightPaginated;
2292         break;
2293     case _WKPaginationModeRightToLeft:
2294         mode = WebCore::Pagination::RightToLeftPaginated;
2295         break;
2296     case _WKPaginationModeTopToBottom:
2297         mode = WebCore::Pagination::TopToBottomPaginated;
2298         break;
2299     case _WKPaginationModeBottomToTop:
2300         mode = WebCore::Pagination::BottomToTopPaginated;
2301         break;
2302     default:
2303         return;
2304     }
2305
2306     _page->setPaginationMode(mode);
2307 }
2308
2309 - (BOOL)_paginationBehavesLikeColumns
2310 {
2311     return _page->paginationBehavesLikeColumns();
2312 }
2313
2314 - (void)_setPaginationBehavesLikeColumns:(BOOL)behavesLikeColumns
2315 {
2316     _page->setPaginationBehavesLikeColumns(behavesLikeColumns);
2317 }
2318
2319 - (CGFloat)_pageLength
2320 {
2321     return _page->pageLength();
2322 }
2323
2324 - (void)_setPageLength:(CGFloat)pageLength
2325 {
2326     _page->setPageLength(pageLength);
2327 }
2328
2329 - (CGFloat)_gapBetweenPages
2330 {
2331     return _page->gapBetweenPages();
2332 }
2333
2334 - (void)_setGapBetweenPages:(CGFloat)gapBetweenPages
2335 {
2336     _page->setGapBetweenPages(gapBetweenPages);
2337 }
2338
2339 - (NSUInteger)_pageCount
2340 {
2341     return _page->pageCount();
2342 }
2343
2344 - (BOOL)_supportsTextZoom
2345 {
2346     return _page->supportsTextZoom();
2347 }
2348
2349 - (double)_textZoomFactor
2350 {
2351     return _page->textZoomFactor();
2352 }
2353
2354 - (void)_setTextZoomFactor:(double)zoomFactor
2355 {
2356     _page->setTextZoomFactor(zoomFactor);
2357 }
2358
2359 - (double)_pageZoomFactor
2360 {
2361     return _page->pageZoomFactor();
2362 }
2363
2364 - (void)_setPageZoomFactor:(double)zoomFactor
2365 {
2366     _page->setPageZoomFactor(zoomFactor);
2367 }
2368
2369 - (id <_WKDiagnosticLoggingDelegate>)_diagnosticLoggingDelegate
2370 {
2371     return [static_cast<WebKit::DiagnosticLoggingClient&>(_page->diagnosticLoggingClient()).delegate().leakRef() autorelease];
2372 }
2373
2374 - (void)_setDiagnosticLoggingDelegate:(id<_WKDiagnosticLoggingDelegate>)diagnosticLoggingDelegate
2375 {
2376     static_cast<WebKit::DiagnosticLoggingClient&>(_page->diagnosticLoggingClient()).setDelegate(diagnosticLoggingDelegate);
2377 }
2378
2379 - (id <_WKFindDelegate>)_findDelegate
2380 {
2381     return [static_cast<WebKit::FindClient&>(_page->findClient()).delegate().leakRef() autorelease];
2382 }
2383
2384 - (void)_setFindDelegate:(id<_WKFindDelegate>)findDelegate
2385 {
2386     static_cast<WebKit::FindClient&>(_page->findClient()).setDelegate(findDelegate);
2387 }
2388
2389 static inline WebKit::FindOptions toFindOptions(_WKFindOptions wkFindOptions)
2390 {
2391     unsigned findOptions = 0;
2392
2393     if (wkFindOptions & _WKFindOptionsCaseInsensitive)
2394         findOptions |= WebKit::FindOptionsCaseInsensitive;
2395     if (wkFindOptions & _WKFindOptionsAtWordStarts)
2396         findOptions |= WebKit::FindOptionsAtWordStarts;
2397     if (wkFindOptions & _WKFindOptionsTreatMedialCapitalAsWordStart)
2398         findOptions |= WebKit::FindOptionsTreatMedialCapitalAsWordStart;
2399     if (wkFindOptions & _WKFindOptionsBackwards)
2400         findOptions |= WebKit::FindOptionsBackwards;
2401     if (wkFindOptions & _WKFindOptionsWrapAround)
2402         findOptions |= WebKit::FindOptionsWrapAround;
2403     if (wkFindOptions & _WKFindOptionsShowOverlay)
2404         findOptions |= WebKit::FindOptionsShowOverlay;
2405     if (wkFindOptions & _WKFindOptionsShowFindIndicator)
2406         findOptions |= WebKit::FindOptionsShowFindIndicator;
2407     if (wkFindOptions & _WKFindOptionsShowHighlight)
2408         findOptions |= WebKit::FindOptionsShowHighlight;
2409     if (wkFindOptions & _WKFindOptionsDetermineMatchIndex)
2410         findOptions |= WebKit::FindOptionsDetermineMatchIndex;
2411
2412     return static_cast<WebKit::FindOptions>(findOptions);
2413 }
2414
2415 - (void)_countStringMatches:(NSString *)string options:(_WKFindOptions)options maxCount:(NSUInteger)maxCount
2416 {
2417 #if PLATFORM(IOS)
2418     if (_customContentView) {
2419         [_customContentView web_countStringMatches:string options:options maxCount:maxCount];
2420         return;
2421     }
2422 #endif
2423     _page->countStringMatches(string, toFindOptions(options), maxCount);
2424 }
2425
2426 - (void)_findString:(NSString *)string options:(_WKFindOptions)options maxCount:(NSUInteger)maxCount
2427 {
2428 #if PLATFORM(IOS)
2429     if (_customContentView) {
2430         [_customContentView web_findString:string options:options maxCount:maxCount];
2431         return;
2432     }
2433 #endif
2434     _page->findString(string, toFindOptions(options), maxCount);
2435 }
2436
2437 - (void)_hideFindUI
2438 {
2439 #if PLATFORM(IOS)
2440     if (_customContentView) {
2441         [_customContentView web_hideFindUI];
2442         return;
2443     }
2444 #endif
2445     _page->hideFindUI();
2446 }
2447
2448 - (void)_saveBackForwardSnapshotForItem:(WKBackForwardListItem *)item
2449 {
2450     _page->recordNavigationSnapshot(item._item);
2451 }
2452
2453 - (id <_WKFormDelegate>)_formDelegate
2454 {
2455     return _formDelegate.getAutoreleased();
2456 }
2457
2458 - (void)_setFormDelegate:(id <_WKFormDelegate>)formDelegate
2459 {
2460     _formDelegate = formDelegate;
2461
2462     class FormClient : public API::FormClient {
2463     public:
2464         explicit FormClient(WKWebView *webView)
2465             : m_webView(webView)
2466         {
2467         }
2468
2469         virtual ~FormClient() { }
2470
2471         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
2472         {
2473             if (userData && userData->type() != API::Object::Type::Data) {
2474                 ASSERT(!userData || userData->type() == API::Object::Type::Data);
2475                 m_webView->_page->process().connection()->markCurrentlyDispatchedMessageAsInvalid();
2476                 listener->continueSubmission();
2477                 return;
2478             }
2479
2480             auto formDelegate = m_webView->_formDelegate.get();
2481
2482             if (![formDelegate respondsToSelector:@selector(_webView:willSubmitFormValues:userObject:submissionHandler:)]) {
2483                 listener->continueSubmission();
2484                 return;
2485             }
2486
2487             auto valueMap = adoptNS([[NSMutableDictionary alloc] initWithCapacity:textFieldValues.size()]);
2488             for (const auto& pair : textFieldValues)
2489                 [valueMap setObject:pair.second forKey:pair.first];
2490
2491             NSObject <NSSecureCoding> *userObject = nil;
2492             if (API::Data* data = static_cast<API::Data*>(userData)) {
2493                 auto nsData = adoptNS([[NSData alloc] initWithBytesNoCopy:const_cast<void*>(static_cast<const void*>(data->bytes())) length:data->size() freeWhenDone:NO]);
2494                 auto unarchiver = adoptNS([[NSKeyedUnarchiver alloc] initForReadingWithData:nsData.get()]);
2495                 [unarchiver setRequiresSecureCoding:YES];
2496                 @try {
2497                     userObject = [unarchiver decodeObjectOfClass:[NSObject class] forKey:@"userObject"];
2498                 } @catch (NSException *exception) {
2499                     LOG_ERROR("Failed to decode user data: %@", exception);
2500                 }
2501             }
2502
2503             RefPtr<WebKit::WebFormSubmissionListenerProxy> localListener = WTF::move(listener);
2504             RefPtr<WebKit::CompletionHandlerCallChecker> checker = WebKit::CompletionHandlerCallChecker::create(formDelegate.get(), @selector(_webView:willSubmitFormValues:userObject:submissionHandler:));
2505             [formDelegate _webView:m_webView willSubmitFormValues:valueMap.get() userObject:userObject submissionHandler:[localListener, checker] {
2506                 checker->didCallCompletionHandler();
2507                 localListener->continueSubmission();
2508             }];
2509         }
2510
2511     private:
2512         WKWebView *m_webView;
2513     };
2514
2515     if (formDelegate)
2516         _page->setFormClient(std::make_unique<FormClient>(self));
2517     else
2518         _page->setFormClient(nullptr);
2519 }
2520
2521 - (BOOL)_isDisplayingStandaloneImageDocument
2522 {
2523     if (auto* mainFrame = _page->mainFrame())
2524         return mainFrame->isDisplayingStandaloneImageDocument();
2525     return NO;
2526 }
2527
2528 - (BOOL)_isDisplayingStandaloneMediaDocument
2529 {
2530     if (auto* mainFrame = _page->mainFrame())
2531         return mainFrame->isDisplayingStandaloneMediaDocument();
2532     return NO;
2533 }
2534
2535 - (BOOL)_isShowingNavigationGestureSnapshot
2536 {
2537     return _page->isShowingNavigationGestureSnapshot();
2538 }
2539
2540 - (_WKLayoutMode)_layoutMode
2541 {
2542 #if PLATFORM(MAC)
2543     switch ([_wkView _layoutMode]) {
2544     case kWKLayoutModeFixedSize:
2545         return _WKLayoutModeFixedSize;
2546     case kWKLayoutModeDynamicSizeComputedFromViewScale:
2547         return _WKLayoutModeDynamicSizeComputedFromViewScale;
2548     case kWKLayoutModeDynamicSizeComputedFromMinimumDocumentSize:
2549         return _WKLayoutModeDynamicSizeComputedFromMinimumDocumentSize;
2550     case kWKLayoutModeViewSize:
2551     default:
2552         return _WKLayoutModeViewSize;
2553     }
2554 #else
2555     return _page->useFixedLayout() ? _WKLayoutModeFixedSize : _WKLayoutModeViewSize;
2556 #endif
2557 }
2558
2559 - (void)_setLayoutMode:(_WKLayoutMode)layoutMode
2560 {
2561 #if PLATFORM(MAC)
2562     WKLayoutMode wkViewLayoutMode;
2563     switch (layoutMode) {
2564     case _WKLayoutModeFixedSize:
2565         wkViewLayoutMode = kWKLayoutModeFixedSize;
2566         break;
2567     case _WKLayoutModeDynamicSizeComputedFromViewScale:
2568         wkViewLayoutMode = kWKLayoutModeDynamicSizeComputedFromViewScale;
2569         break;
2570     case _WKLayoutModeDynamicSizeComputedFromMinimumDocumentSize:
2571         wkViewLayoutMode = kWKLayoutModeDynamicSizeComputedFromMinimumDocumentSize;
2572         break;
2573     case _WKLayoutModeViewSize:
2574     default:
2575         wkViewLayoutMode = kWKLayoutModeViewSize;
2576         break;
2577     }
2578     [_wkView _setLayoutMode:wkViewLayoutMode];
2579 #else
2580     _page->setUseFixedLayout(layoutMode == _WKLayoutModeFixedSize || layoutMode == _WKLayoutModeDynamicSizeComputedFromViewScale);
2581 #endif
2582 }
2583
2584 - (CGSize)_fixedLayoutSize
2585 {
2586     return _page->fixedLayoutSize();
2587 }
2588
2589 - (void)_setFixedLayoutSize:(CGSize)fixedLayoutSize
2590 {
2591     _page->setFixedLayoutSize(WebCore::expandedIntSize(WebCore::FloatSize(fixedLayoutSize)));
2592 }
2593
2594 - (CGFloat)_viewScale
2595 {
2596     return _page->viewScaleFactor();
2597 }
2598
2599 - (void)_setViewScale:(CGFloat)viewScale
2600 {
2601 #if PLATFORM(MAC)
2602     [_wkView _setViewScale:viewScale];
2603 #else
2604     if (viewScale <= 0 || isnan(viewScale) || isinf(viewScale))
2605         [NSException raise:NSInvalidArgumentException format:@"View scale should be a positive number"];
2606
2607     _page->scaleView(viewScale);
2608 #endif
2609 }
2610
2611 #pragma mark scrollperf methods
2612
2613 - (void)_setScrollPerformanceDataCollectionEnabled:(BOOL)enabled
2614 {
2615     _page->setScrollPerformanceDataCollectionEnabled(enabled);
2616 }
2617
2618 - (BOOL)_scrollPerformanceDataCollectionEnabled
2619 {
2620     return _page->scrollPerformanceDataCollectionEnabled();
2621 }
2622
2623 - (NSArray *)_scrollPerformanceData
2624 {
2625 #if PLATFORM(IOS)
2626     if (WebKit::RemoteLayerTreeScrollingPerformanceData* scrollPerfData = _page->scrollingPerformanceData())
2627         return scrollPerfData->data();
2628 #endif
2629     return nil;
2630 }
2631
2632 #pragma mark media playback restrictions
2633
2634 - (BOOL)_allowsMediaDocumentInlinePlayback
2635 {
2636 #if PLATFORM(IOS)
2637     return _page->allowsMediaDocumentInlinePlayback();
2638 #else
2639     return NO;
2640 #endif
2641 }
2642
2643 - (void)_setAllowsMediaDocumentInlinePlayback:(BOOL)flag
2644 {
2645 #if PLATFORM(IOS)
2646     _page->setAllowsMediaDocumentInlinePlayback(flag);
2647 #endif
2648 }
2649
2650 #pragma mark iOS-specific methods
2651
2652 #if PLATFORM(IOS)
2653
2654 - (CGSize)_minimumLayoutSizeOverride
2655 {
2656     ASSERT(_overridesMinimumLayoutSize);
2657     return _minimumLayoutSizeOverride;
2658 }
2659
2660 - (void)_setMinimumLayoutSizeOverride:(CGSize)minimumLayoutSizeOverride
2661 {
2662     _overridesMinimumLayoutSize = YES;
2663     if (CGSizeEqualToSize(_minimumLayoutSizeOverride, minimumLayoutSizeOverride))
2664         return;
2665
2666     _minimumLayoutSizeOverride = minimumLayoutSizeOverride;
2667     if (_dynamicViewportUpdateMode == DynamicViewportUpdateMode::NotResizing)
2668         _page->setViewportConfigurationMinimumLayoutSize(WebCore::FloatSize(minimumLayoutSizeOverride));
2669 }
2670
2671 - (UIEdgeInsets)_obscuredInsets
2672 {
2673     return _obscuredInsets;
2674 }
2675
2676 - (void)_setObscuredInsets:(UIEdgeInsets)obscuredInsets
2677 {
2678     ASSERT(obscuredInsets.top >= 0);
2679     ASSERT(obscuredInsets.left >= 0);
2680     ASSERT(obscuredInsets.bottom >= 0);
2681     ASSERT(obscuredInsets.right >= 0);
2682     
2683     _haveSetObscuredInsets = YES;
2684
2685     if (UIEdgeInsetsEqualToEdgeInsets(_obscuredInsets, obscuredInsets))
2686         return;
2687
2688     _obscuredInsets = obscuredInsets;
2689
2690     [self _updateVisibleContentRects];
2691 }
2692
2693 - (void)_setInterfaceOrientationOverride:(UIInterfaceOrientation)interfaceOrientation
2694 {
2695     if (!_overridesInterfaceOrientation)
2696         [[NSNotificationCenter defaultCenter] removeObserver:self name:UIWindowDidRotateNotification object:nil];
2697
2698     _overridesInterfaceOrientation = YES;
2699
2700     if (interfaceOrientation == _interfaceOrientationOverride)
2701         return;
2702
2703     _interfaceOrientationOverride = interfaceOrientation;
2704
2705     if (_dynamicViewportUpdateMode == DynamicViewportUpdateMode::NotResizing)
2706         _page->setDeviceOrientation(deviceOrientationForUIInterfaceOrientation(_interfaceOrientationOverride));
2707 }
2708
2709 - (UIInterfaceOrientation)_interfaceOrientationOverride
2710 {
2711     ASSERT(_overridesInterfaceOrientation);
2712     return _interfaceOrientationOverride;
2713 }
2714
2715 - (CGSize)_maximumUnobscuredSizeOverride
2716 {
2717     ASSERT(_overridesMaximumUnobscuredSize);
2718     return _maximumUnobscuredSizeOverride;
2719 }
2720
2721 - (void)_setMaximumUnobscuredSizeOverride:(CGSize)size
2722 {
2723     ASSERT(size.width <= self.bounds.size.width && size.height <= self.bounds.size.height);
2724     _overridesMaximumUnobscuredSize = YES;
2725     if (CGSizeEqualToSize(_maximumUnobscuredSizeOverride, size))
2726         return;
2727
2728     _maximumUnobscuredSizeOverride = size;
2729     if (_dynamicViewportUpdateMode == DynamicViewportUpdateMode::NotResizing)
2730         _page->setMaximumUnobscuredSize(WebCore::FloatSize(size));
2731 }
2732
2733 - (void)_setBackgroundExtendsBeyondPage:(BOOL)backgroundExtends
2734 {
2735     _page->setBackgroundExtendsBeyondPage(backgroundExtends);
2736 }
2737
2738 - (BOOL)_backgroundExtendsBeyondPage
2739 {
2740     return _page->backgroundExtendsBeyondPage();
2741 }
2742
2743 - (void)_beginInteractiveObscuredInsetsChange
2744 {
2745     ASSERT(!_isChangingObscuredInsetsInteractively);
2746     _isChangingObscuredInsetsInteractively = YES;
2747 }
2748
2749 - (void)_endInteractiveObscuredInsetsChange
2750 {
2751     ASSERT(_isChangingObscuredInsetsInteractively);
2752     _isChangingObscuredInsetsInteractively = NO;
2753     [self _updateVisibleContentRects];
2754 }
2755
2756 - (void)_hideContentUntilNextUpdate
2757 {
2758     if (auto* area = _page->drawingArea())
2759         area->hideContentUntilAnyUpdate();
2760 }
2761
2762 - (void)_beginAnimatedResizeWithUpdates:(void (^)(void))updateBlock
2763 {
2764     CGRect oldBounds = self.bounds;
2765     WebCore::FloatRect oldUnobscuredContentRect = _page->unobscuredContentRect();
2766
2767     if (_customContentView || !_hasCommittedLoadForMainFrame || CGRectIsEmpty(oldBounds) || oldUnobscuredContentRect.isEmpty()) {
2768         updateBlock();
2769         return;
2770     }
2771
2772     _dynamicViewportUpdateMode = DynamicViewportUpdateMode::ResizingWithAnimation;
2773
2774     WebCore::FloatSize oldMinimumLayoutSize = activeMinimumLayoutSize(self, oldBounds);
2775     WebCore::FloatSize oldMaximumUnobscuredSize = activeMaximumUnobscuredSize(self, oldBounds);
2776     int32_t oldOrientation = activeOrientation(self);
2777     UIEdgeInsets oldObscuredInsets = _obscuredInsets;
2778
2779     updateBlock();
2780
2781     CGRect newBounds = self.bounds;
2782     WebCore::FloatSize newMinimumLayoutSize = activeMinimumLayoutSize(self, newBounds);
2783     WebCore::FloatSize newMaximumUnobscuredSize = activeMaximumUnobscuredSize(self, newBounds);
2784     int32_t newOrientation = activeOrientation(self);
2785     UIEdgeInsets newObscuredInsets = _obscuredInsets;
2786     CGRect futureUnobscuredRectInSelfCoordinates = UIEdgeInsetsInsetRect(newBounds, _obscuredInsets);
2787     CGRect contentViewBounds = [_contentView bounds];
2788
2789     ASSERT_WITH_MESSAGE(!(_overridesMinimumLayoutSize && newMinimumLayoutSize.isEmpty()), "Clients controlling the layout size should maintain a valid layout size to minimize layouts.");
2790     if (CGRectIsEmpty(newBounds) || newMinimumLayoutSize.isEmpty() || CGRectIsEmpty(futureUnobscuredRectInSelfCoordinates) || CGRectIsEmpty(contentViewBounds)) {
2791         _dynamicViewportUpdateMode = DynamicViewportUpdateMode::NotResizing;
2792         [self _frameOrBoundsChanged];
2793         if (_overridesMinimumLayoutSize)
2794             _page->setViewportConfigurationMinimumLayoutSize(WebCore::FloatSize(newMinimumLayoutSize));
2795         if (_overridesMaximumUnobscuredSize)
2796             _page->setMaximumUnobscuredSize(WebCore::FloatSize(newMaximumUnobscuredSize));
2797         if (_overridesInterfaceOrientation)
2798             _page->setDeviceOrientation(newOrientation);
2799         [self _updateVisibleContentRects];
2800         return;
2801     }
2802
2803     if (CGRectEqualToRect(oldBounds, newBounds)
2804         && oldMinimumLayoutSize == newMinimumLayoutSize
2805         && oldMaximumUnobscuredSize == newMaximumUnobscuredSize
2806         && oldOrientation == newOrientation
2807         && UIEdgeInsetsEqualToEdgeInsets(oldObscuredInsets, newObscuredInsets)) {
2808         _dynamicViewportUpdateMode = DynamicViewportUpdateMode::NotResizing;
2809         [self _updateVisibleContentRects];
2810         return;
2811     }
2812
2813     _resizeAnimationTransformAdjustments = CATransform3DIdentity;
2814
2815     NSUInteger indexOfContentView = [[_scrollView subviews] indexOfObject:_contentView.get()];
2816     _resizeAnimationView = adoptNS([[UIView alloc] init]);
2817     [_scrollView insertSubview:_resizeAnimationView.get() atIndex:indexOfContentView];
2818     [_resizeAnimationView addSubview:_contentView.get()];
2819     [_resizeAnimationView addSubview:[_contentView unscaledView]];
2820
2821     CGSize contentSizeInContentViewCoordinates = contentViewBounds.size;
2822     [_scrollView setMinimumZoomScale:std::min(newMinimumLayoutSize.width() / contentSizeInContentViewCoordinates.width, [_scrollView minimumZoomScale])];
2823     [_scrollView setMaximumZoomScale:std::max(newMinimumLayoutSize.width() / contentSizeInContentViewCoordinates.width, [_scrollView maximumZoomScale])];
2824
2825     // Compute the new scale to keep the current content width in the scrollview.
2826     CGFloat oldWebViewWidthInContentViewCoordinates = oldUnobscuredContentRect.width();
2827     CGFloat visibleContentViewWidthInContentCoordinates = std::min(contentSizeInContentViewCoordinates.width, oldWebViewWidthInContentViewCoordinates);
2828     CGFloat targetScale = newMinimumLayoutSize.width() / visibleContentViewWidthInContentCoordinates;
2829     CGFloat resizeAnimationViewAnimationScale = targetScale / contentZoomScale(self);
2830     [_resizeAnimationView setTransform:CGAffineTransformMakeScale(resizeAnimationViewAnimationScale, resizeAnimationViewAnimationScale)];
2831
2832     // Compute a new position to keep the content centered.
2833     CGPoint originalContentCenter = oldUnobscuredContentRect.center();
2834     CGPoint originalContentCenterInSelfCoordinates = [self convertPoint:originalContentCenter fromView:_contentView.get()];
2835     CGPoint futureUnobscuredRectCenterInSelfCoordinates = CGPointMake(futureUnobscuredRectInSelfCoordinates.origin.x + futureUnobscuredRectInSelfCoordinates.size.width / 2, futureUnobscuredRectInSelfCoordinates.origin.y + futureUnobscuredRectInSelfCoordinates.size.height / 2);
2836
2837     CGPoint originalContentOffset = [_scrollView contentOffset];
2838     CGPoint contentOffset = originalContentOffset;
2839     contentOffset.x += (originalContentCenterInSelfCoordinates.x - futureUnobscuredRectCenterInSelfCoordinates.x);
2840     contentOffset.y += (originalContentCenterInSelfCoordinates.y - futureUnobscuredRectCenterInSelfCoordinates.y);
2841
2842     // Limit the new offset within the scrollview, we do not want to rubber band programmatically.
2843     CGSize futureContentSizeInSelfCoordinates = CGSizeMake(contentSizeInContentViewCoordinates.width * targetScale, contentSizeInContentViewCoordinates.height * targetScale);
2844     CGFloat maxHorizontalOffset = futureContentSizeInSelfCoordinates.width - newBounds.size.width + _obscuredInsets.right;
2845     contentOffset.x = std::min(contentOffset.x, maxHorizontalOffset);
2846     CGFloat maxVerticalOffset = futureContentSizeInSelfCoordinates.height - newBounds.size.height + _obscuredInsets.bottom;
2847     contentOffset.y = std::min(contentOffset.y, maxVerticalOffset);
2848
2849     contentOffset.x = std::max(contentOffset.x, -_obscuredInsets.left);
2850     contentOffset.y = std::max(contentOffset.y, -_obscuredInsets.top);
2851
2852     // Make the top/bottom edges "sticky" within 1 pixel.
2853     if (oldUnobscuredContentRect.maxY() > contentSizeInContentViewCoordinates.height - 1)
2854         contentOffset.y = maxVerticalOffset;
2855     if (oldUnobscuredContentRect.y() < 1)
2856         contentOffset.y = -_obscuredInsets.top;
2857
2858     // FIXME: if we have content centered after double tap to zoom, we should also try to keep that rect in view.
2859     [_scrollView setContentSize:roundScrollViewContentSize(*_page, futureContentSizeInSelfCoordinates)];
2860     [_scrollView setContentOffset:contentOffset];
2861
2862     CGRect visibleRectInContentCoordinates = [self convertRect:newBounds toView:_contentView.get()];
2863     CGRect unobscuredRectInContentCoordinates = [self convertRect:futureUnobscuredRectInSelfCoordinates toView:_contentView.get()];
2864
2865     _page->dynamicViewportSizeUpdate(newMinimumLayoutSize, newMaximumUnobscuredSize, visibleRectInContentCoordinates, unobscuredRectInContentCoordinates, futureUnobscuredRectInSelfCoordinates, targetScale, newOrientation);
2866     if (WebKit::DrawingAreaProxy* drawingArea = _page->drawingArea())
2867         drawingArea->setSize(WebCore::IntSize(newBounds.size), WebCore::IntSize(), WebCore::IntSize());
2868 }
2869
2870 - (void)_endAnimatedResize
2871 {
2872     if (_dynamicViewportUpdateMode == DynamicViewportUpdateMode::NotResizing)
2873         return;
2874
2875     _page->synchronizeDynamicViewportUpdate();
2876
2877     NSUInteger indexOfResizeAnimationView = [[_scrollView subviews] indexOfObject:_resizeAnimationView.get()];
2878     [_scrollView insertSubview:_contentView.get() atIndex:indexOfResizeAnimationView];
2879     [_scrollView insertSubview:[_contentView unscaledView] atIndex:indexOfResizeAnimationView + 1];
2880
2881     CALayer *contentViewLayer = [_contentView layer];
2882     CGFloat adjustmentScale = _resizeAnimationTransformAdjustments.m11;
2883     contentViewLayer.sublayerTransform = CATransform3DIdentity;
2884
2885     CGFloat animatingScaleTarget = [[_resizeAnimationView layer] transform].m11;
2886     CALayer *contentLayer = [_contentView layer];
2887     CATransform3D contentLayerTransform = contentLayer.transform;
2888     CGFloat currentScale = [[_resizeAnimationView layer] transform].m11 * contentLayerTransform.m11;
2889
2890     // We cannot use [UIScrollView setZoomScale:] directly because the UIScrollView delegate would get a callback with
2891     // an invalid contentOffset. The real content offset is only set below.
2892     // Since there is no public API for setting both the zoomScale and the contentOffset, we set the zoomScale manually
2893     // on the zoom layer and then only change the contentOffset.
2894     CGFloat adjustedScale = adjustmentScale * currentScale;
2895     contentLayerTransform.m11 = adjustedScale;
2896     contentLayerTransform.m22 = adjustedScale;
2897     contentLayer.transform = contentLayerTransform;
2898
2899     CGPoint currentScrollOffset = [_scrollView contentOffset];
2900     double horizontalScrollAdjustement = _resizeAnimationTransformAdjustments.m41 * animatingScaleTarget;
2901     double verticalScrollAdjustment = _resizeAnimationTransformAdjustments.m42 * animatingScaleTarget;
2902
2903     [_scrollView setContentSize:roundScrollViewContentSize(*_page, [_contentView frame].size)];
2904     [_scrollView setContentOffset:CGPointMake(currentScrollOffset.x - horizontalScrollAdjustement, currentScrollOffset.y - verticalScrollAdjustment)];
2905
2906     [_resizeAnimationView removeFromSuperview];
2907     _resizeAnimationView = nil;
2908     _resizeAnimationTransformAdjustments = CATransform3DIdentity;
2909
2910     _dynamicViewportUpdateMode = DynamicViewportUpdateMode::NotResizing;
2911     [_contentView setHidden:NO];
2912     [self _updateVisibleContentRects];
2913
2914     while (!_snapshotsDeferredDuringResize.isEmpty())
2915         _snapshotsDeferredDuringResize.takeLast()();
2916 }
2917
2918 - (void)_resizeWhileHidingContentWithUpdates:(void (^)(void))updateBlock
2919 {
2920     [self _beginAnimatedResizeWithUpdates:updateBlock];
2921     if (_dynamicViewportUpdateMode == DynamicViewportUpdateMode::ResizingWithAnimation) {
2922         [_contentView setHidden:YES];
2923         _dynamicViewportUpdateMode = DynamicViewportUpdateMode::ResizingWithDocumentHidden;
2924     }
2925 }
2926
2927 - (void)_setOverlaidAccessoryViewsInset:(CGSize)inset
2928 {
2929     [_customContentView web_setOverlaidAccessoryViewsInset:inset];
2930 }
2931
2932 - (void)_snapshotRect:(CGRect)rectInViewCoordinates intoImageOfWidth:(CGFloat)imageWidth completionHandler:(void(^)(CGImageRef))completionHandler
2933 {
2934     if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing) {
2935         // Defer snapshotting until after the current resize completes.
2936         void (^copiedCompletionHandler)(CGImageRef) = [completionHandler copy];
2937         RetainPtr<WKWebView> retainedSelf = self;
2938         _snapshotsDeferredDuringResize.append([retainedSelf, rectInViewCoordinates, imageWidth, copiedCompletionHandler] {
2939             [retainedSelf _snapshotRect:rectInViewCoordinates intoImageOfWidth:imageWidth completionHandler:copiedCompletionHandler];
2940             [copiedCompletionHandler release];
2941         });
2942         return;
2943     }
2944
2945     CGRect snapshotRectInContentCoordinates = [self convertRect:rectInViewCoordinates toView:self._currentContentView];
2946     CGFloat imageScale = imageWidth / snapshotRectInContentCoordinates.size.width;
2947     CGFloat imageHeight = imageScale * snapshotRectInContentCoordinates.size.height;
2948     CGSize imageSize = CGSizeMake(imageWidth, imageHeight);
2949
2950 #if USE(IOSURFACE)
2951     // If we are parented and thus won't incur a significant penalty from paging in tiles, snapshot the view hierarchy directly.
2952     if (CADisplay *display = self.window.screen._display) {
2953         auto surface = WebCore::IOSurface::create(WebCore::expandedIntSize(WebCore::FloatSize(imageSize)), WebCore::ColorSpaceDeviceRGB);
2954         CGFloat imageScaleInViewCoordinates = imageWidth / rectInViewCoordinates.size.width;
2955         CATransform3D transform = CATransform3DMakeScale(imageScaleInViewCoordinates, imageScaleInViewCoordinates, 1);
2956         transform = CATransform3DTranslate(transform, -rectInViewCoordinates.origin.x, -rectInViewCoordinates.origin.y, 0);
2957         CARenderServerRenderDisplayLayerWithTransformAndTimeOffset(MACH_PORT_NULL, (CFStringRef)display.name, self.layer.context.contextId, reinterpret_cast<uint64_t>(self.layer), surface->surface(), 0, 0, &transform, 0);
2958         completionHandler(surface->createImage().get());
2959         return;
2960     }
2961 #endif
2962     
2963     if (_customContentView) {
2964         UIGraphicsBeginImageContextWithOptions(imageSize, YES, 1);
2965
2966         UIView *customContentView = _customContentView.get();
2967         [customContentView.backgroundColor set];
2968         UIRectFill(CGRectMake(0, 0, imageWidth, imageHeight));
2969
2970         CGContextRef context = UIGraphicsGetCurrentContext();
2971         CGContextTranslateCTM(context, -snapshotRectInContentCoordinates.origin.x * imageScale, -snapshotRectInContentCoordinates.origin.y * imageScale);
2972         CGContextScaleCTM(context, imageScale, imageScale);
2973         [customContentView.layer renderInContext:context];
2974
2975         completionHandler([UIGraphicsGetImageFromCurrentImageContext() CGImage]);
2976
2977         UIGraphicsEndImageContext();
2978         return;
2979     }
2980
2981     void(^copiedCompletionHandler)(CGImageRef) = [completionHandler copy];
2982     _page->takeSnapshot(WebCore::enclosingIntRect(snapshotRectInContentCoordinates), WebCore::expandedIntSize(WebCore::FloatSize(imageSize)), WebKit::SnapshotOptionsExcludeDeviceScaleFactor, [=](const WebKit::ShareableBitmap::Handle& imageHandle, WebKit::CallbackBase::Error) {
2983         if (imageHandle.isNull()) {
2984             copiedCompletionHandler(nullptr);
2985             [copiedCompletionHandler release];
2986             return;
2987         }
2988
2989         RefPtr<WebKit::ShareableBitmap> bitmap = WebKit::ShareableBitmap::create(imageHandle, WebKit::SharedMemory::Protection::ReadOnly);
2990
2991         if (!bitmap) {
2992             copiedCompletionHandler(nullptr);
2993             [copiedCompletionHandler release];
2994             return;
2995         }
2996
2997         RetainPtr<CGImageRef> cgImage;
2998         cgImage = bitmap->makeCGImage();
2999         copiedCompletionHandler(cgImage.get());
3000         [copiedCompletionHandler release];
3001     });
3002 }
3003
3004 - (void)_overrideLayoutParametersWithMinimumLayoutSize:(CGSize)minimumLayoutSize minimumLayoutSizeForMinimalUI:(CGSize)minimumLayoutSizeForMinimalUI maximumUnobscuredSizeOverride:(CGSize)maximumUnobscuredSizeOverride
3005 {
3006     UNUSED_PARAM(minimumLayoutSizeForMinimalUI);
3007     [self _overrideLayoutParametersWithMinimumLayoutSize:minimumLayoutSize maximumUnobscuredSizeOverride:maximumUnobscuredSizeOverride];
3008 }
3009
3010 - (void)_overrideLayoutParametersWithMinimumLayoutSize:(CGSize)minimumLayoutSize maximumUnobscuredSizeOverride:(CGSize)maximumUnobscuredSizeOverride
3011 {
3012     [self _setMinimumLayoutSizeOverride:minimumLayoutSize];
3013     [self _setMaximumUnobscuredSizeOverride:maximumUnobscuredSizeOverride];
3014 }
3015
3016 - (UIView *)_viewForFindUI
3017 {
3018     return [self viewForZoomingInScrollView:[self scrollView]];
3019 }
3020
3021 - (BOOL)_isDisplayingPDF
3022 {
3023     return [_customContentView isKindOfClass:[WKPDFView class]];
3024 }
3025
3026 - (NSData *)_dataForDisplayedPDF
3027 {
3028     if (![self _isDisplayingPDF])
3029         return nil;
3030     CGPDFDocumentRef pdfDocument = [(WKPDFView *)_customContentView pdfDocument];
3031     return [(NSData *)CGDataProviderCopyData(CGPDFDocumentGetDataProvider(pdfDocument)) autorelease];
3032 }
3033
3034 - (NSString *)_suggestedFilenameForDisplayedPDF
3035 {
3036     if (![self _isDisplayingPDF])
3037         return nil;
3038     return [(WKPDFView *)_customContentView.get() suggestedFilename];
3039 }
3040
3041 - (CGFloat)_viewportMetaTagWidth
3042 {
3043     return _viewportMetaTagWidth;
3044 }
3045
3046 - (_WKWebViewPrintFormatter *)_webViewPrintFormatter
3047 {
3048     UIViewPrintFormatter *viewPrintFormatter = self.viewPrintFormatter;
3049     ASSERT([viewPrintFormatter isKindOfClass:[_WKWebViewPrintFormatter class]]);
3050     return (_WKWebViewPrintFormatter *)viewPrintFormatter;
3051 }
3052
3053 #else // #if PLATFORM(IOS)
3054
3055 #pragma mark - OS X-specific methods
3056
3057 - (NSColor *)_pageExtendedBackgroundColor
3058 {
3059     WebCore::Color color = _page->pageExtendedBackgroundColor();
3060     if (!color.isValid())
3061         return nil;
3062
3063     return nsColor(color);
3064 }
3065
3066 - (BOOL)_drawsTransparentBackground
3067 {
3068     return _page->drawsTransparentBackground();
3069 }
3070
3071 - (void)_setDrawsTransparentBackground:(BOOL)drawsTransparentBackground
3072 {
3073     _page->setDrawsTransparentBackground(drawsTransparentBackground);
3074 }
3075
3076 - (void)_setOverrideDeviceScaleFactor:(CGFloat)deviceScaleFactor
3077 {
3078     [_wkView _setOverrideDeviceScaleFactor:deviceScaleFactor];
3079 }
3080
3081 - (CGFloat)_overrideDeviceScaleFactor
3082 {
3083     return [_wkView _overrideDeviceScaleFactor];
3084 }
3085
3086 - (id)_immediateActionAnimationControllerForHitTestResult:(_WKHitTestResult *)hitTestResult withType:(_WKImmediateActionType)type userData:(id<NSSecureCoding>)userData
3087 {
3088     return nil;
3089 }
3090
3091 - (void)_setTopContentInset:(CGFloat)contentInset
3092 {
3093     [_wkView _setTopContentInset:contentInset];
3094 }
3095
3096 - (CGFloat)_topContentInset
3097 {
3098     return [_wkView _topContentInset];
3099 }
3100
3101 - (BOOL)_windowOcclusionDetectionEnabled
3102 {
3103     return [_wkView windowOcclusionDetectionEnabled];
3104 }
3105
3106 - (void)_setWindowOcclusionDetectionEnabled:(BOOL)flag
3107 {
3108     [_wkView setWindowOcclusionDetectionEnabled:flag];
3109 }
3110
3111 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
3112
3113 - (void)_setAutomaticallyAdjustsContentInsets:(BOOL)automaticallyAdjustsContentInsets
3114 {
3115     [_wkView _setAutomaticallyAdjustsContentInsets:automaticallyAdjustsContentInsets];
3116 }
3117
3118 - (BOOL)_automaticallyAdjustsContentInsets
3119 {
3120     return [_wkView _automaticallyAdjustsContentInsets];
3121 }
3122
3123 #endif // __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
3124
3125 #endif
3126
3127 @end
3128
3129
3130 @implementation WKWebView (WKTesting)
3131
3132 #if PLATFORM(IOS)
3133
3134 - (CGRect)_contentVisibleRect
3135 {
3136     return [self convertRect:[self bounds] toView:self._currentContentView];
3137 }
3138
3139 - (CGPoint)_convertPointFromContentsToView:(CGPoint)point
3140 {
3141     return [self convertPoint:point fromView:self._currentContentView];
3142 }
3143
3144 - (CGPoint)_convertPointFromViewToContents:(CGPoint)point
3145 {
3146     return [self convertPoint:point toView:self._currentContentView];
3147 }
3148
3149 #endif // PLATFORM(IOS)
3150
3151 // Execute the supplied block after the next transaction from the WebProcess.
3152 - (void)_doAfterNextPresentationUpdate:(void (^)(void))updateBlock
3153 {
3154     typeof(updateBlock) updateBlockCopy = nil;
3155     if (updateBlock)
3156         updateBlockCopy = Block_copy(updateBlock);
3157
3158     _page->callAfterNextPresentationUpdate([updateBlockCopy](WebKit::CallbackBase::Error error) {
3159         updateBlockCopy();
3160         Block_release(updateBlockCopy);
3161     });
3162 }
3163
3164 @end
3165
3166
3167 #if PLATFORM(MAC)
3168
3169 @implementation WKWebView (WKIBActions)
3170
3171 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
3172 {
3173     SEL action = item.action;
3174
3175     if (action == @selector(goBack:))
3176         return !!_page->backForwardList().backItem();
3177
3178     if (action == @selector(goForward:))
3179         return !!_page->backForwardList().forwardItem();
3180
3181     if (action == @selector(stopLoading:)) {
3182         // FIXME: Return no if we're stopped.
3183         return YES;
3184     }
3185
3186     if (action == @selector(reload:) || action == @selector(reloadFromOrigin:)) {
3187         // FIXME: Return no if we're loading.
3188         return YES;
3189     }
3190
3191     return NO;
3192 }
3193
3194 - (IBAction)goBack:(id)sender
3195 {
3196     [self goBack];
3197 }
3198
3199 - (IBAction)goForward:(id)sender
3200 {
3201     [self goForward];
3202 }
3203
3204 - (IBAction)reload:(id)sender
3205 {
3206     [self reload];
3207 }
3208
3209 - (IBAction)reloadFromOrigin:(id)sender
3210 {
3211     [self reloadFromOrigin];
3212 }
3213
3214 - (IBAction)stopLoading:(id)sender
3215 {
3216     _page->stopLoading();
3217 }
3218
3219 @end
3220
3221 #endif // PLATFORM(MAC)
3222
3223 #if PLATFORM(IOS)
3224 @implementation WKWebView (_WKWebViewPrintFormatter)
3225
3226 - (Class)_printFormatterClass
3227 {
3228     return [_WKWebViewPrintFormatter class];
3229 }
3230
3231 - (NSInteger)_computePageCountAndStartDrawingToPDFForFrame:(_WKFrameHandle *)frame printInfo:(const WebKit::PrintInfo&)printInfo firstPage:(uint32_t)firstPage computedTotalScaleFactor:(double&)totalScaleFactor
3232 {
3233     if ([self _isDisplayingPDF])
3234         return CGPDFDocumentGetNumberOfPages([(WKPDFView *)_customContentView pdfDocument]);
3235
3236     _pageIsPrintingToPDF = YES;
3237     Vector<WebCore::IntRect> pageRects;
3238     uint64_t frameID = frame ? frame._frameID : _page->mainFrame()->frameID();
3239     if (!_page->sendSync(Messages::WebPage::ComputePagesForPrintingAndStartDrawingToPDF(frameID, printInfo, firstPage), Messages::WebPage::ComputePagesForPrintingAndStartDrawingToPDF::Reply(pageRects, totalScaleFactor)))
3240         return 0;
3241     return pageRects.size();
3242 }
3243
3244 - (void)_endPrinting
3245 {
3246     _pageIsPrintingToPDF = NO;
3247     _printedDocument = nullptr;
3248     _page->send(Messages::WebPage::EndPrinting());
3249 }
3250
3251 // FIXME: milliseconds::max() overflows when converted to nanoseconds, causing condition_variable::wait_for() to believe
3252 // a timeout occurred on any spurious wakeup. Use nanoseconds::max() (converted to ms) to avoid this. We should perhaps
3253 // change waitForAndDispatchImmediately() to take nanoseconds to avoid this issue.
3254 static constexpr std::chrono::milliseconds didFinishLoadingTimeout = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::nanoseconds::max());
3255
3256 - (CGPDFDocumentRef)_printedDocument
3257 {
3258     if ([self _isDisplayingPDF]) {
3259         ASSERT(!_pageIsPrintingToPDF);
3260         return [(WKPDFView *)_customContentView pdfDocument];
3261     }
3262
3263     if (_pageIsPrintingToPDF) {
3264         if (!_page->process().connection()->waitForAndDispatchImmediately<Messages::WebPageProxy::DidFinishDrawingPagesToPDF>(_page->pageID(), didFinishLoadingTimeout)) {
3265             ASSERT_NOT_REACHED();
3266             return nullptr;
3267         }
3268         ASSERT(!_pageIsPrintingToPDF);
3269     }
3270     return _printedDocument.get();
3271 }
3272
3273 - (void)_setPrintedDocument:(CGPDFDocumentRef)printedDocument
3274 {
3275     if (!_pageIsPrintingToPDF)
3276         return;
3277     ASSERT(![self _isDisplayingPDF]);
3278     _printedDocument = printedDocument;
3279     _pageIsPrintingToPDF = NO;
3280 }
3281
3282 @end
3283 #endif
3284
3285 #endif // WK_API_ENABLED