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