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