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