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