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