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