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