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