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