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