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