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