REGRESSION (r170935): WKWebView is always transparent until the first layer tree...
[WebKit-https.git] / Source / WebKit2 / UIProcess / API / Cocoa / WKWebView.mm
1 /*
2  * Copyright (C) 2014 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 "FindClient.h"
33 #import "LegacySessionStateCoding.h"
34 #import "NavigationState.h"
35 #import "RemoteLayerTreeTransaction.h"
36 #import "RemoteObjectRegistry.h"
37 #import "RemoteObjectRegistryMessages.h"
38 #import "UIDelegate.h"
39 #import "ViewGestureController.h"
40 #import "ViewSnapshotStore.h"
41 #import "WKBackForwardListInternal.h"
42 #import "WKBackForwardListItemInternal.h"
43 #import "WKBrowsingContextHandleInternal.h"
44 #import "WKErrorInternal.h"
45 #import "WKHistoryDelegatePrivate.h"
46 #import "WKNSData.h"
47 #import "WKNSURLExtras.h"
48 #import "WKNavigationDelegate.h"
49 #import "WKNavigationInternal.h"
50 #import "WKPreferencesInternal.h"
51 #import "WKProcessPoolInternal.h"
52 #import "WKUIDelegate.h"
53 #import "WKUIDelegatePrivate.h"
54 #import "WKUserContentControllerInternal.h"
55 #import "WKWebViewConfigurationInternal.h"
56 #import "WKWebViewContentProvider.h"
57 #import "WebBackForwardList.h"
58 #import "WebCertificateInfo.h"
59 #import "WebContext.h"
60 #import "WebFormSubmissionListenerProxy.h"
61 #import "WebKitSystemInterface.h"
62 #import "WebPageGroup.h"
63 #import "WebPageProxy.h"
64 #import "WebPreferencesKeys.h"
65 #import "WebProcessProxy.h"
66 #import "WebSerializedScriptValue.h"
67 #import "_WKFindDelegate.h"
68 #import "_WKFormDelegate.h"
69 #import "_WKRemoteObjectRegistryInternal.h"
70 #import "_WKSessionStateInternal.h"
71 #import "_WKVisitedLinkProviderInternal.h"
72 #import "_WKWebsiteDataStoreInternal.h"
73 #import <JavaScriptCore/JSContext.h>
74 #import <JavaScriptCore/JSValue.h>
75 #import <wtf/HashMap.h>
76 #import <wtf/NeverDestroyed.h>
77 #import <wtf/RetainPtr.h>
78
79 #if PLATFORM(IOS)
80 #import "_WKFrameHandleInternal.h"
81 #import "_WKWebViewPrintFormatter.h"
82 #import "PrintInfo.h"
83 #import "ProcessThrottler.h"
84 #import "RemoteLayerTreeDrawingAreaProxy.h"
85 #import "WKPDFView.h"
86 #import "WKScrollView.h"
87 #import "WKWebViewContentProviderRegistry.h"
88 #import "WebPageMessages.h"
89 #import <CoreGraphics/CGFloat.h>
90 #import <CoreGraphics/CGPDFDocumentPrivate.h>
91 #import <UIKit/UIApplication.h>
92 #import <UIKit/UIDevice_Private.h>
93 #import <UIKit/UIPeripheralHost_Private.h>
94 #import <UIKit/UIWindow_Private.h>
95 #import <QuartzCore/CARenderServer.h>
96 #import <QuartzCore/QuartzCorePrivate.h>
97 #import <WebCore/InspectorOverlay.h>
98
99 @interface UIScrollView (UIScrollViewInternal)
100 - (void)_adjustForAutomaticKeyboardInfo:(NSDictionary*)info animated:(BOOL)animated lastAdjustment:(CGFloat*)lastAdjustment;
101 - (BOOL)_isScrollingToTop;
102 @end
103
104 @interface UIPeripheralHost(UIKitInternal)
105 - (CGFloat)getVerticalOverlapForView:(UIView *)view usingKeyboardInfo:(NSDictionary *)info;
106 @end
107
108 @interface UIView (UIViewInternal)
109 - (UIViewController *)_viewControllerForAncestor;
110 @end
111
112 @interface UIWindow (UIWindowInternal)
113 - (BOOL)_isHostedInAnotherProcess;
114 @end
115
116 @interface UIViewController (UIViewControllerInternal)
117 - (UIViewController *)_rootAncestorViewController;
118 - (UIViewController *)_viewControllerForSupportedInterfaceOrientations;
119 @end
120 #endif
121
122 #if PLATFORM(MAC)
123 #import "WKViewInternal.h"
124 #import <WebCore/ColorMac.h>
125 #endif
126
127
128 static HashMap<WebKit::WebPageProxy*, WKWebView *>& pageToViewMap()
129 {
130     static NeverDestroyed<HashMap<WebKit::WebPageProxy*, WKWebView *>> map;
131     return map;
132 }
133
134 WKWebView* fromWebPageProxy(WebKit::WebPageProxy& page)
135 {
136     return pageToViewMap().get(&page);
137 }
138
139 @implementation WKWebView {
140     std::unique_ptr<WebKit::NavigationState> _navigationState;
141     std::unique_ptr<WebKit::UIDelegate> _uiDelegate;
142
143     RetainPtr<_WKRemoteObjectRegistry> _remoteObjectRegistry;
144     _WKRenderingProgressEvents _observedRenderingProgressEvents;
145
146     WebKit::WeakObjCPtr<id <_WKFormDelegate>> _formDelegate;
147 #if PLATFORM(IOS)
148     RetainPtr<WKScrollView> _scrollView;
149     RetainPtr<WKContentView> _contentView;
150
151     BOOL _overridesMinimumLayoutSize;
152     CGSize _minimumLayoutSizeOverride;
153     BOOL _overridesMinimumLayoutSizeForMinimalUI;
154     CGSize _minimumLayoutSizeOverrideForMinimalUI;
155     BOOL _overridesMaximumUnobscuredSize;
156     CGSize _maximumUnobscuredSizeOverride;
157     BOOL _usesMinimalUI;
158     BOOL _needsToNotifyDelegateAboutMinimalUI;
159     CGRect _inputViewBounds;
160     CGFloat _viewportMetaTagWidth;
161
162     UIEdgeInsets _obscuredInsets;
163     bool _isChangingObscuredInsetsInteractively;
164
165     UIInterfaceOrientation _interfaceOrientationOverride;
166     BOOL _overridesInterfaceOrientation;
167
168     BOOL _needsResetViewStateAfterCommitLoadForMainFrame;
169     uint64_t _firstPaintAfterCommitLoadTransactionID;
170     BOOL _isAnimatingResize;
171     CATransform3D _resizeAnimationTransformAdjustments;
172     RetainPtr<UIView> _resizeAnimationView;
173     CGFloat _lastAdjustmentForScroller;
174
175     BOOL _needsToRestoreExposedRect;
176     WebCore::FloatRect _exposedRectToRestore;
177     BOOL _needsToRestoreUnobscuredCenter;
178     WebCore::FloatPoint _unobscuredCenterToRestore;
179     uint64_t _firstTransactionIDAfterPageRestore;
180     double _scaleToRestore;
181
182     std::unique_ptr<WebKit::ViewGestureController> _gestureController;
183     BOOL _allowsBackForwardNavigationGestures;
184
185     RetainPtr<UIView <WKWebViewContentProvider>> _customContentView;
186     RetainPtr<UIView> _customContentFixedOverlayView;
187
188     WebCore::Color _scrollViewBackgroundColor;
189
190     BOOL _delayUpdateVisibleContentRects;
191     BOOL _hadDelayedUpdateVisibleContentRects;
192
193     BOOL _pageIsPrintingToPDF;
194     RetainPtr<CGPDFDocumentRef> _printedDocument;
195 #endif
196 #if PLATFORM(MAC)
197     RetainPtr<WKView> _wkView;
198 #endif
199 }
200
201 - (instancetype)initWithFrame:(CGRect)frame
202 {
203     return [self initWithFrame:frame configuration:adoptNS([[WKWebViewConfiguration alloc] init]).get()];
204 }
205
206 #if PLATFORM(IOS)
207 static int32_t deviceOrientationForUIInterfaceOrientation(UIInterfaceOrientation orientation)
208 {
209     switch (orientation) {
210     case UIInterfaceOrientationUnknown:
211     case UIInterfaceOrientationPortrait:
212         return 0;
213     case UIInterfaceOrientationPortraitUpsideDown:
214         return 180;
215     case UIInterfaceOrientationLandscapeLeft:
216         return -90;
217     case UIInterfaceOrientationLandscapeRight:
218         return 90;
219     }
220 }
221
222 static int32_t deviceOrientation()
223 {
224     return deviceOrientationForUIInterfaceOrientation([[UIApplication sharedApplication] statusBarOrientation]);
225 }
226 #endif
227
228 - (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration
229 {
230     if (!(self = [super initWithFrame:frame]))
231         return nil;
232
233     _configuration = adoptNS([configuration copy]);
234
235     if (WKWebView *relatedWebView = [_configuration _relatedWebView]) {
236         WKProcessPool *processPool = [_configuration processPool];
237         WKProcessPool *relatedWebViewProcessPool = [relatedWebView->_configuration processPool];
238         if (processPool && processPool != relatedWebViewProcessPool)
239             [NSException raise:NSInvalidArgumentException format:@"Related web view %@ has process pool %@ but configuration specifies a different process pool %@", relatedWebView, relatedWebViewProcessPool, configuration.processPool];
240
241         [_configuration setProcessPool:relatedWebViewProcessPool];
242     }
243
244     [_configuration _validate];
245
246     CGRect bounds = self.bounds;
247
248     WebKit::WebContext& context = *[_configuration processPool]->_context;
249
250     WebKit::WebPageConfiguration webPageConfiguration;
251     webPageConfiguration.preferences = [_configuration preferences]->_preferences.get();
252     if (WKWebView *relatedWebView = [_configuration _relatedWebView])
253         webPageConfiguration.relatedPage = relatedWebView->_page.get();
254
255     webPageConfiguration.userContentController = [_configuration userContentController]->_userContentControllerProxy.get();
256     webPageConfiguration.visitedLinkProvider = [_configuration _visitedLinkProvider]->_visitedLinkProvider.get();
257     webPageConfiguration.session = [_configuration _websiteDataStore]->_session.get();
258     
259     RefPtr<WebKit::WebPageGroup> pageGroup;
260     NSString *groupIdentifier = configuration._groupIdentifier;
261     if (groupIdentifier.length) {
262         pageGroup = WebKit::WebPageGroup::create(configuration._groupIdentifier);
263         webPageConfiguration.pageGroup = pageGroup.get();
264     }
265
266     webPageConfiguration.preferenceValues.set(WebKit::WebPreferencesKey::suppressesIncrementalRenderingKey(), WebKit::WebPreferencesStore::Value(!![_configuration suppressesIncrementalRendering]));
267
268 #if PLATFORM(IOS)
269     webPageConfiguration.preferenceValues.set(WebKit::WebPreferencesKey::mediaPlaybackAllowsInlineKey(), WebKit::WebPreferencesStore::Value(!![_configuration allowsInlineMediaPlayback]));
270     webPageConfiguration.preferenceValues.set(WebKit::WebPreferencesKey::mediaPlaybackRequiresUserGestureKey(), WebKit::WebPreferencesStore::Value(!![_configuration mediaPlaybackRequiresUserAction]));
271     webPageConfiguration.preferenceValues.set(WebKit::WebPreferencesKey::mediaPlaybackAllowsAirPlayKey(), WebKit::WebPreferencesStore::Value(!![_configuration mediaPlaybackAllowsAirPlay]));
272 #endif
273
274 #if PLATFORM(IOS)
275     _scrollView = adoptNS([[WKScrollView alloc] initWithFrame:bounds]);
276     [_scrollView setInternalDelegate:self];
277     [_scrollView setBouncesZoom:YES];
278
279     [self addSubview:_scrollView.get()];
280
281     _contentView = adoptNS([[WKContentView alloc] initWithFrame:bounds context:context configuration:WTF::move(webPageConfiguration) webView:self]);
282
283     _page = [_contentView page];
284     _page->setApplicationNameForUserAgent([@"Mobile/" stringByAppendingString:[UIDevice currentDevice].buildVersion]);
285     _page->setDeviceOrientation(deviceOrientation());
286
287     [_contentView layer].anchorPoint = CGPointZero;
288     [_contentView setFrame:bounds];
289     [_scrollView addSubview:_contentView.get()];
290     [_scrollView addSubview:[_contentView unscaledView]];
291     [self _updateScrollViewBackground];
292
293     _viewportMetaTagWidth = -1;
294
295     [self _frameOrBoundsChanged];
296
297     NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
298     [center addObserver:self selector:@selector(_keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];
299     [center addObserver:self selector:@selector(_keyboardDidChangeFrame:) name:UIKeyboardDidChangeFrameNotification object:nil];
300     [center addObserver:self selector:@selector(_keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
301     [center addObserver:self selector:@selector(_keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
302     [center addObserver:self selector:@selector(_windowDidRotate:) name:UIWindowDidRotateNotification object:nil];
303     [center addObserver:self selector:@selector(_contentSizeCategoryDidChange:) name:UIContentSizeCategoryDidChangeNotification object:nil];
304     _page->contentSizeCategoryDidChange([self _contentSizeCategory]);
305
306     [[_configuration _contentProviderRegistry] addPage:*_page];
307 #endif
308
309 #if PLATFORM(MAC)
310     _wkView = adoptNS([[WKView alloc] initWithFrame:bounds context:context configuration:WTF::move(webPageConfiguration) webView:self]);
311     [self addSubview:_wkView.get()];
312     _page = WebKit::toImpl([_wkView pageRef]);
313
314 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
315     [_wkView _setAutomaticallyAdjustsContentInsets:YES];
316 #endif
317 #endif
318
319     _page->setBackgroundExtendsBeyondPage(true);
320
321     _navigationState = std::make_unique<WebKit::NavigationState>(self);
322     _page->setPolicyClient(_navigationState->createPolicyClient());
323     _page->setLoaderClient(_navigationState->createLoaderClient());
324
325     _uiDelegate = std::make_unique<WebKit::UIDelegate>(self);
326     _page->setUIClient(_uiDelegate->createUIClient());
327
328     _page->setFindClient(std::make_unique<WebKit::FindClient>(self));
329
330     pageToViewMap().add(_page.get(), self);
331
332     return self;
333 }
334
335 - (instancetype)initWithCoder:(NSCoder *)coder
336 {
337     [self release];
338     return nil;
339 }
340
341 - (void)dealloc
342 {
343     _page->close();
344
345     [_remoteObjectRegistry _invalidate];
346 #if PLATFORM(IOS)
347     [[_configuration _contentProviderRegistry] removePage:*_page];
348     [[NSNotificationCenter defaultCenter] removeObserver:self];
349 #endif
350
351     pageToViewMap().remove(_page.get());
352
353     [super dealloc];
354 }
355
356 - (WKWebViewConfiguration *)configuration
357 {
358     return [[_configuration copy] autorelease];
359 }
360
361 - (WKBackForwardList *)backForwardList
362 {
363     return wrapper(_page->backForwardList());
364 }
365
366 - (id <WKNavigationDelegate>)navigationDelegate
367 {
368     return _navigationState->navigationDelegate().autorelease();
369 }
370
371 - (void)setNavigationDelegate:(id <WKNavigationDelegate>)navigationDelegate
372 {
373     _navigationState->setNavigationDelegate(navigationDelegate);
374 }
375
376 - (id <WKUIDelegate>)UIDelegate
377 {
378     return _uiDelegate->delegate().autorelease();
379 }
380
381 - (void)setUIDelegate:(id<WKUIDelegate>)UIDelegate
382 {
383     _uiDelegate->setDelegate(UIDelegate);
384 }
385
386 - (WKNavigation *)loadRequest:(NSURLRequest *)request
387 {
388     uint64_t navigationID = _page->loadRequest(request);
389     auto navigation = _navigationState->createLoadRequestNavigation(navigationID, request);
390
391     return navigation.autorelease();
392 }
393
394 - (WKNavigation *)loadHTMLString:(NSString *)string baseURL:(NSURL *)baseURL
395 {
396     uint64_t navigationID = _page->loadHTMLString(string, baseURL.absoluteString);
397     if (!navigationID)
398         return nil;
399
400     auto navigation = _navigationState->createLoadDataNavigation(navigationID);
401
402     return navigation.autorelease();
403 }
404
405 - (WKNavigation *)goToBackForwardListItem:(WKBackForwardListItem *)item
406 {
407     uint64_t navigationID = _page->goToBackForwardItem(&item._item);
408
409     auto navigation = _navigationState->createBackForwardNavigation(navigationID, item._item);
410
411     return navigation.autorelease();
412 }
413
414 - (NSString *)title
415 {
416     return _page->pageLoadState().title();
417 }
418
419 - (NSURL *)URL
420 {
421     return [NSURL _web_URLWithWTFString:_page->pageLoadState().activeURL()];
422 }
423
424 - (BOOL)isLoading
425 {
426     return _page->pageLoadState().isLoading();
427 }
428
429 - (double)estimatedProgress
430 {
431     return _page->pageLoadState().estimatedProgress();
432 }
433
434 - (BOOL)hasOnlySecureContent
435 {
436     return _page->pageLoadState().hasOnlySecureContent();
437 }
438
439 - (BOOL)canGoBack
440 {
441     return _page->pageLoadState().canGoBack();
442 }
443
444 - (BOOL)canGoForward
445 {
446     return _page->pageLoadState().canGoForward();
447 }
448
449 - (WKNavigation *)goBack
450 {
451     uint64_t navigationID = _page->goBack();
452     if (!navigationID)
453         return nil;
454
455     ASSERT(_page->backForwardList().currentItem());
456     auto navigation = _navigationState->createBackForwardNavigation(navigationID, *_page->backForwardList().currentItem());
457  
458     return navigation.autorelease();
459 }
460
461 - (WKNavigation *)goForward
462 {
463     uint64_t navigationID = _page->goForward();
464     if (!navigationID)
465         return nil;
466
467     ASSERT(_page->backForwardList().currentItem());
468     auto navigation = _navigationState->createBackForwardNavigation(navigationID, *_page->backForwardList().currentItem());
469
470     return navigation.autorelease();
471 }
472
473 - (WKNavigation *)reload
474 {
475     uint64_t navigationID = _page->reload(false);
476     if (!navigationID)
477         return nil;
478
479     auto navigation = _navigationState->createReloadNavigation(navigationID);
480     return navigation.autorelease();
481 }
482
483 - (WKNavigation *)reloadFromOrigin
484 {
485     uint64_t navigationID = _page->reload(true);
486     if (!navigationID)
487         return nil;
488
489     auto navigation = _navigationState->createReloadNavigation(navigationID);
490     return navigation.autorelease();
491 }
492
493 - (void)stopLoading
494 {
495     _page->stopLoading();
496 }
497
498 static WKErrorCode callbackErrorCode(WebKit::CallbackBase::Error error)
499 {
500     switch (error) {
501     case WebKit::CallbackBase::Error::None:
502         ASSERT_NOT_REACHED();
503         return WKErrorUnknown;
504
505     case WebKit::CallbackBase::Error::Unknown:
506         return WKErrorUnknown;
507
508     case WebKit::CallbackBase::Error::ProcessExited:
509         return WKErrorWebContentProcessTerminated;
510
511     case WebKit::CallbackBase::Error::OwnerWasInvalidated:
512         return WKErrorWebViewInvalidated;
513     }
514 }
515
516 - (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^)(id, NSError *))completionHandler
517 {
518     auto handler = adoptNS([completionHandler copy]);
519
520     _page->runJavaScriptInMainFrame(javaScriptString, [handler](WebKit::WebSerializedScriptValue* serializedScriptValue, WebKit::ScriptValueCallback::Error errorCode) {
521         if (!handler)
522             return;
523
524         auto completionHandler = (void (^)(id, NSError *))handler.get();
525
526         if (errorCode != WebKit::ScriptValueCallback::Error::None) {
527             auto error = createNSError(callbackErrorCode(errorCode));
528             if (errorCode == WebKit::ScriptValueCallback::Error::OwnerWasInvalidated) {
529                 // The OwnerWasInvalidated callback is synchronous. We don't want to call the block from within it
530                 // because that can trigger re-entrancy bugs in WebKit.
531                 // FIXME: It would be even better if GenericCallback did this for us.
532                 dispatch_async(dispatch_get_main_queue(), [completionHandler, error] {
533                     completionHandler(nil, error.get());
534                 });
535                 return;
536             }
537
538             completionHandler(nil, error.get());
539             return;
540         }
541
542         if (!serializedScriptValue) {
543             completionHandler(nil, createNSError(WKErrorJavaScriptExceptionOccurred).get());
544             return;
545         }
546
547         auto context = adoptNS([[JSContext alloc] init]);
548         JSValueRef valueRef = serializedScriptValue->deserialize([context JSGlobalContextRef], 0);
549         JSValue *value = [JSValue valueWithJSValueRef:valueRef inContext:context.get()];
550
551         completionHandler([value toObject], nil);
552     });
553 }
554
555 #pragma mark iOS-specific methods
556
557 #if PLATFORM(IOS)
558 - (void)setFrame:(CGRect)frame
559 {
560     CGRect oldFrame = self.frame;
561     [super setFrame:frame];
562
563     if (!CGSizeEqualToSize(oldFrame.size, frame.size))
564         [self _frameOrBoundsChanged];
565 }
566
567 - (void)setBounds:(CGRect)bounds
568 {
569     CGRect oldBounds = self.bounds;
570     [super setBounds:bounds];
571     [_customContentFixedOverlayView setFrame:self.bounds];
572
573     if (!CGSizeEqualToSize(oldBounds.size, bounds.size))
574         [self _frameOrBoundsChanged];
575 }
576
577 - (UIScrollView *)scrollView
578 {
579     return _scrollView.get();
580 }
581
582 - (WKBrowsingContextController *)browsingContextController
583 {
584     return [_contentView browsingContextController];
585 }
586
587 static inline CGFloat floorToDevicePixel(CGFloat input, float deviceScaleFactor)
588 {
589     return CGFloor(input * deviceScaleFactor) / deviceScaleFactor;
590 }
591
592 static CGSize roundScrollViewContentSize(const WebKit::WebPageProxy& page, CGSize contentSize)
593 {
594     float deviceScaleFactor = page.deviceScaleFactor();
595     return CGSizeMake(floorToDevicePixel(contentSize.width, deviceScaleFactor), floorToDevicePixel(contentSize.height, deviceScaleFactor));
596 }
597
598 - (UIView *)_currentContentView
599 {
600     return _customContentView ? _customContentView.get() : _contentView.get();
601 }
602
603 - (void)_setHasCustomContentView:(BOOL)pageHasCustomContentView loadedMIMEType:(const WTF::String&)mimeType
604 {
605     if (pageHasCustomContentView) {
606         [_customContentView removeFromSuperview];
607         [_customContentFixedOverlayView removeFromSuperview];
608
609         Class representationClass = [[_configuration _contentProviderRegistry] providerForMIMEType:mimeType];
610         ASSERT(representationClass);
611         _customContentView = adoptNS([[representationClass alloc] web_initWithFrame:self.bounds webView:self]);
612         _customContentFixedOverlayView = adoptNS([[UIView alloc] initWithFrame:self.bounds]);
613         [_customContentFixedOverlayView setUserInteractionEnabled:NO];
614
615         [[_contentView unscaledView] removeFromSuperview];
616         [_contentView removeFromSuperview];
617         [_scrollView addSubview:_customContentView.get()];
618         [self addSubview:_customContentFixedOverlayView.get()];
619
620         [_customContentView web_setMinimumSize:self.bounds.size];
621         [_customContentView web_setFixedOverlayView:_customContentFixedOverlayView.get()];
622     } else if (_customContentView) {
623         [_customContentView removeFromSuperview];
624         _customContentView = nullptr;
625
626         [_customContentFixedOverlayView removeFromSuperview];
627         _customContentFixedOverlayView = nullptr;
628
629         [_scrollView addSubview:_contentView.get()];
630         [_scrollView addSubview:[_contentView unscaledView]];
631         [_scrollView setContentSize:roundScrollViewContentSize(*_page, [_contentView frame].size)];
632
633         [_customContentFixedOverlayView setFrame:self.bounds];
634         [self addSubview:_customContentFixedOverlayView.get()];
635     }
636 }
637
638 - (void)_didFinishLoadingDataForCustomContentProviderWithSuggestedFilename:(const String&)suggestedFilename data:(NSData *)data
639 {
640     ASSERT(_customContentView);
641     [_customContentView web_setContentProviderData:data suggestedFilename:suggestedFilename];
642 }
643
644 - (void)_setViewportMetaTagWidth:(float)newWidth
645 {
646     _viewportMetaTagWidth = newWidth;
647 }
648
649 - (void)_willInvokeUIScrollViewDelegateCallback
650 {
651     _delayUpdateVisibleContentRects = YES;
652 }
653
654 - (void)_didInvokeUIScrollViewDelegateCallback
655 {
656     _delayUpdateVisibleContentRects = NO;
657     if (_hadDelayedUpdateVisibleContentRects) {
658         _hadDelayedUpdateVisibleContentRects = NO;
659         [self _updateVisibleContentRects];
660     }
661 }
662
663 static CGFloat contentZoomScale(WKWebView *webView)
664 {
665     CGFloat scale = webView._currentContentView.layer.affineTransform.a;
666     ASSERT(scale == [webView->_scrollView zoomScale]);
667     return scale;
668 }
669
670 static WebCore::Color scrollViewBackgroundColor(WKWebView *webView)
671 {
672     if (!webView.opaque)
673         return WebCore::Color::transparent;
674
675     WebCore::Color color;
676
677     if (webView->_customContentView)
678         color = [webView->_customContentView backgroundColor].CGColor;
679     else
680         color = webView->_page->pageExtendedBackgroundColor();
681
682     if (!color.isValid())
683         color = WebCore::Color::white;
684
685     CGFloat zoomScale = contentZoomScale(webView);
686     CGFloat minimumZoomScale = [webView->_scrollView minimumZoomScale];
687     if (zoomScale < minimumZoomScale) {
688         CGFloat slope = 12;
689         CGFloat opacity = std::max<CGFloat>(1 - slope * (minimumZoomScale - zoomScale), 0);
690         color = WebCore::colorWithOverrideAlpha(color.rgb(), opacity);
691     }
692
693     return color;
694 }
695
696 - (void)_updateScrollViewBackground
697 {
698     WebCore::Color color = scrollViewBackgroundColor(self);
699
700     if (_scrollViewBackgroundColor == color)
701         return;
702
703     _scrollViewBackgroundColor = color;
704
705     auto uiBackgroundColor = adoptNS([[UIColor alloc] initWithCGColor:cachedCGColor(color, WebCore::ColorSpaceDeviceRGB)]);
706     [_scrollView setBackgroundColor:uiBackgroundColor.get()];
707 }
708
709 - (void)_setUsesMinimalUI:(BOOL)usesMinimalUI
710 {
711     _usesMinimalUI = usesMinimalUI;
712     _needsToNotifyDelegateAboutMinimalUI = YES;
713 }
714
715 - (BOOL)_usesMinimalUI
716 {
717     return _usesMinimalUI;
718 }
719
720 - (CGPoint)_adjustedContentOffset:(CGPoint)point
721 {
722     CGPoint result = point;
723     UIEdgeInsets contentInset = [self _computedContentInset];
724
725     result.x -= contentInset.left;
726     result.y -= contentInset.top;
727
728     return result;
729 }
730
731 - (UIEdgeInsets)_computedContentInset
732 {
733     if (!UIEdgeInsetsEqualToEdgeInsets(_obscuredInsets, UIEdgeInsetsZero))
734         return _obscuredInsets;
735
736     return [_scrollView contentInset];
737 }
738
739 - (void)_processDidExit
740 {
741     if (!_customContentView && _isAnimatingResize) {
742         NSUInteger indexOfResizeAnimationView = [[_scrollView subviews] indexOfObject:_resizeAnimationView.get()];
743         [_scrollView insertSubview:_contentView.get() atIndex:indexOfResizeAnimationView];
744         [_scrollView insertSubview:[_contentView unscaledView] atIndex:indexOfResizeAnimationView + 1];
745         [_resizeAnimationView removeFromSuperview];
746         _resizeAnimationView = nil;
747
748         _isAnimatingResize = NO;
749         _resizeAnimationTransformAdjustments = CATransform3DIdentity;
750     }
751     [_contentView setFrame:self.bounds];
752     [_scrollView setBackgroundColor:[UIColor whiteColor]];
753     [_scrollView setContentOffset:[self _adjustedContentOffset:CGPointZero]];
754     [_scrollView setZoomScale:1];
755
756     _viewportMetaTagWidth = -1;
757     _needsResetViewStateAfterCommitLoadForMainFrame = NO;
758     _needsToRestoreExposedRect = NO;
759     _needsToRestoreUnobscuredCenter = NO;
760     _scrollViewBackgroundColor = WebCore::Color();
761     _delayUpdateVisibleContentRects = NO;
762     _hadDelayedUpdateVisibleContentRects = NO;
763 }
764
765 - (void)_didCommitLoadForMainFrame
766 {
767     _firstPaintAfterCommitLoadTransactionID = toRemoteLayerTreeDrawingAreaProxy(_page->drawingArea())->nextLayerTreeTransactionID();
768
769     _needsResetViewStateAfterCommitLoadForMainFrame = YES;
770     _usesMinimalUI = NO;
771 }
772
773 static void changeContentOffsetBoundedInValidRange(UIScrollView *scrollView, WebCore::FloatPoint contentOffset)
774 {
775     UIEdgeInsets contentInsets = scrollView.contentInset;
776     CGSize contentSize = scrollView.contentSize;
777     CGSize scrollViewSize = scrollView.bounds.size;
778
779     float maxHorizontalOffset = contentSize.width + contentInsets.right - scrollViewSize.width;
780     if (contentOffset.x() > maxHorizontalOffset)
781         contentOffset.setX(maxHorizontalOffset);
782     float maxVerticalOffset = contentSize.height + contentInsets.bottom - scrollViewSize.height;
783     if (contentOffset.y() > maxVerticalOffset)
784         contentOffset.setY(maxVerticalOffset);
785     if (contentOffset.x() < -contentInsets.left)
786         contentOffset.setX(-contentInsets.left);
787     if (contentOffset.y() < -contentInsets.top)
788         contentOffset.setY(-contentInsets.top);
789     scrollView.contentOffset = contentOffset;
790 }
791
792 - (void)_didCommitLayerTree:(const WebKit::RemoteLayerTreeTransaction&)layerTreeTransaction
793 {
794     if (_customContentView)
795         return;
796
797     if (_isAnimatingResize) {
798         [_resizeAnimationView layer].sublayerTransform = _resizeAnimationTransformAdjustments;
799         return;
800     }
801
802     CGSize newContentSize = roundScrollViewContentSize(*_page, [_contentView frame].size);
803     [_scrollView _setContentSizePreservingContentOffsetDuringRubberband:newContentSize];
804
805     [_scrollView setMinimumZoomScale:layerTreeTransaction.minimumScaleFactor()];
806     [_scrollView setMaximumZoomScale:layerTreeTransaction.maximumScaleFactor()];
807     [_scrollView setZoomEnabled:layerTreeTransaction.allowsUserScaling()];
808     if (!layerTreeTransaction.scaleWasSetByUIProcess() && ![_scrollView isZooming] && ![_scrollView isZoomBouncing] && ![_scrollView _isAnimatingZoom])
809         [_scrollView setZoomScale:layerTreeTransaction.pageScaleFactor()];
810
811     [self _updateScrollViewBackground];
812
813     if (_gestureController)
814         _gestureController->setRenderTreeSize(layerTreeTransaction.renderTreeSize());
815
816     if (_needsToNotifyDelegateAboutMinimalUI || _needsResetViewStateAfterCommitLoadForMainFrame) {
817         _needsToNotifyDelegateAboutMinimalUI = NO;
818
819         auto delegate = _uiDelegate->delegate();
820         if ([delegate respondsToSelector:@selector(_webView:usesMinimalUI:)])
821             [static_cast<id <WKUIDelegatePrivate>>(delegate.get()) _webView:self usesMinimalUI:_usesMinimalUI];
822     }
823
824     if (_needsResetViewStateAfterCommitLoadForMainFrame && layerTreeTransaction.transactionID() >= _firstPaintAfterCommitLoadTransactionID) {
825         _needsResetViewStateAfterCommitLoadForMainFrame = NO;
826         [_scrollView setContentOffset:[self _adjustedContentOffset:CGPointZero]];
827         [self _updateVisibleContentRects];
828     }
829
830     if (_needsToRestoreExposedRect && layerTreeTransaction.transactionID() >= _firstTransactionIDAfterPageRestore) {
831         _needsToRestoreExposedRect = NO;
832
833         if (withinEpsilon(contentZoomScale(self), _scaleToRestore)) {
834             WebCore::FloatPoint exposedPosition = _exposedRectToRestore.location();
835             exposedPosition.scale(_scaleToRestore, _scaleToRestore);
836
837             changeContentOffsetBoundedInValidRange(_scrollView.get(), exposedPosition);
838         }
839         [self _updateVisibleContentRects];
840     }
841
842     if (_needsToRestoreUnobscuredCenter && layerTreeTransaction.transactionID() >= _firstTransactionIDAfterPageRestore) {
843         _needsToRestoreUnobscuredCenter = NO;
844
845         if (withinEpsilon(contentZoomScale(self), _scaleToRestore)) {
846             CGRect unobscuredRect = UIEdgeInsetsInsetRect(self.bounds, _obscuredInsets);
847             WebCore::FloatSize unobscuredContentSizeAtNewScale(unobscuredRect.size.width / _scaleToRestore, unobscuredRect.size.height / _scaleToRestore);
848             WebCore::FloatPoint topLeftInDocumentCoordinate(_unobscuredCenterToRestore.x() - unobscuredContentSizeAtNewScale.width() / 2, _unobscuredCenterToRestore.y() - unobscuredContentSizeAtNewScale.height() / 2);
849
850             topLeftInDocumentCoordinate.scale(_scaleToRestore, _scaleToRestore);
851             topLeftInDocumentCoordinate.moveBy(WebCore::FloatPoint(-_obscuredInsets.left, -_obscuredInsets.top));
852
853             changeContentOffsetBoundedInValidRange(_scrollView.get(), topLeftInDocumentCoordinate);
854         }
855         [self _updateVisibleContentRects];
856     }
857 }
858
859 - (void)_dynamicViewportUpdateChangedTargetToScale:(double)newScale position:(CGPoint)newScrollPosition
860 {
861     if (_isAnimatingResize) {
862         CGFloat animatingScaleTarget = [[_resizeAnimationView layer] transform].m11;
863         double currentTargetScale = animatingScaleTarget * [[_contentView layer] transform].m11;
864         double scale = newScale / currentTargetScale;
865         _resizeAnimationTransformAdjustments = CATransform3DMakeScale(scale, scale, 1);
866
867         CGPoint newContentOffset = [self _adjustedContentOffset:CGPointMake(newScrollPosition.x * newScale, newScrollPosition.y * newScale)];
868         CGPoint currentContentOffset = [_scrollView contentOffset];
869
870         _resizeAnimationTransformAdjustments.m41 = (currentContentOffset.x - newContentOffset.x) / animatingScaleTarget;
871         _resizeAnimationTransformAdjustments.m42 = (currentContentOffset.y - newContentOffset.y) / animatingScaleTarget;
872     }
873 }
874
875 - (void)_restorePageStateToExposedRect:(WebCore::FloatRect)exposedRect scale:(double)scale
876 {
877     if (_isAnimatingResize)
878         return;
879
880     if (_customContentView)
881         return;
882
883     _needsToRestoreUnobscuredCenter = NO;
884     _needsToRestoreExposedRect = YES;
885     _firstTransactionIDAfterPageRestore = toRemoteLayerTreeDrawingAreaProxy(_page->drawingArea())->nextLayerTreeTransactionID();
886     _exposedRectToRestore = exposedRect;
887     _scaleToRestore = scale;
888 }
889
890 - (void)_restorePageStateToUnobscuredCenter:(WebCore::FloatPoint)center scale:(double)scale
891 {
892     if (_isAnimatingResize)
893         return;
894
895     if (_customContentView)
896         return;
897
898     _needsToRestoreExposedRect = NO;
899     _needsToRestoreUnobscuredCenter = YES;
900     _firstTransactionIDAfterPageRestore = toRemoteLayerTreeDrawingAreaProxy(_page->drawingArea())->nextLayerTreeTransactionID();
901     _unobscuredCenterToRestore = center;
902     _scaleToRestore = scale;
903 }
904
905 - (PassRefPtr<WebKit::ViewSnapshot>)_takeViewSnapshot
906 {
907     float deviceScale = WKGetScreenScaleFactor();
908     CGSize snapshotSize = self.bounds.size;
909     snapshotSize.width *= deviceScale;
910     snapshotSize.height *= deviceScale;
911
912     uint32_t slotID = [WebKit::ViewSnapshotStore::snapshottingContext() createImageSlot:snapshotSize hasAlpha:YES];
913
914     if (!slotID)
915         return nullptr;
916
917     CATransform3D transform = CATransform3DMakeScale(deviceScale, deviceScale, 1);
918     CARenderServerCaptureLayerWithTransform(MACH_PORT_NULL, self.layer.context.contextId, (uint64_t)self.layer, slotID, 0, 0, &transform);
919
920     WebCore::IntSize imageSize = WebCore::expandedIntSize(WebCore::FloatSize(snapshotSize));
921     return WebKit::ViewSnapshot::create(slotID, imageSize, imageSize.width() * imageSize.height() * 4);
922 }
923
924 - (void)_zoomToPoint:(WebCore::FloatPoint)point atScale:(double)scale
925 {
926     double maximumZoomDuration = 0.4;
927     double minimumZoomDuration = 0.1;
928     double zoomDurationFactor = 0.3;
929
930     CGFloat zoomScale = contentZoomScale(self);
931     CFTimeInterval duration = std::min(fabs(log(zoomScale) - log(scale)) * zoomDurationFactor + minimumZoomDuration, maximumZoomDuration);
932
933     if (scale != zoomScale)
934         _page->willStartUserTriggeredZooming();
935
936     [_scrollView _zoomToCenter:point scale:scale duration:duration];
937 }
938
939 - (void)_zoomToRect:(WebCore::FloatRect)targetRect atScale:(double)scale origin:(WebCore::FloatPoint)origin
940 {
941     // FIMXE: Some of this could be shared with _scrollToRect.
942     const double visibleRectScaleChange = contentZoomScale(self) / scale;
943     const WebCore::FloatRect visibleRect([self convertRect:self.bounds toView:self._currentContentView]);
944     const WebCore::FloatRect unobscuredRect([self _contentRectForUserInteraction]);
945
946     const WebCore::FloatSize topLeftObscuredInsetAfterZoom((unobscuredRect.minXMinYCorner() - visibleRect.minXMinYCorner()) * visibleRectScaleChange);
947     const WebCore::FloatSize bottomRightObscuredInsetAfterZoom((visibleRect.maxXMaxYCorner() - unobscuredRect.maxXMaxYCorner()) * visibleRectScaleChange);
948
949     const WebCore::FloatSize unobscuredRectSizeAfterZoom(unobscuredRect.size() * visibleRectScaleChange);
950
951     // Center to the target rect.
952     WebCore::FloatPoint unobscuredRectLocationAfterZoom = targetRect.location() - (unobscuredRectSizeAfterZoom - targetRect.size()) * 0.5;
953
954     // Center to the tap point instead in case the target rect won't fit in a direction.
955     if (targetRect.width() > unobscuredRectSizeAfterZoom.width())
956         unobscuredRectLocationAfterZoom.setX(origin.x() - unobscuredRectSizeAfterZoom.width() / 2);
957     if (targetRect.height() > unobscuredRectSizeAfterZoom.height())
958         unobscuredRectLocationAfterZoom.setY(origin.y() - unobscuredRectSizeAfterZoom.height() / 2);
959
960     // We have computed where we want the unobscured rect to be. Now adjust for the obscuring insets.
961     WebCore::FloatRect visibleRectAfterZoom(unobscuredRectLocationAfterZoom, unobscuredRectSizeAfterZoom);
962     visibleRectAfterZoom.move(-topLeftObscuredInsetAfterZoom);
963     visibleRectAfterZoom.expand(topLeftObscuredInsetAfterZoom + bottomRightObscuredInsetAfterZoom);
964
965     [self _zoomToPoint:visibleRectAfterZoom.center() atScale:scale];
966 }
967
968 static WebCore::FloatPoint constrainContentOffset(WebCore::FloatPoint contentOffset, WebCore::FloatSize contentSize, WebCore::FloatSize unobscuredContentSize)
969 {
970     WebCore::FloatSize maximumContentOffset = contentSize - unobscuredContentSize;
971     contentOffset = contentOffset.shrunkTo(WebCore::FloatPoint(maximumContentOffset.width(), maximumContentOffset.height()));
972     contentOffset = contentOffset.expandedTo(WebCore::FloatPoint());
973     return contentOffset;
974 }
975
976 - (void)_scrollToContentOffset:(WebCore::FloatPoint)contentOffset
977 {
978     if (_isAnimatingResize)
979         return;
980
981     [_scrollView _stopScrollingAndZoomingAnimations];
982
983     WebCore::FloatPoint scaledOffset = contentOffset;
984     CGFloat zoomScale = contentZoomScale(self);
985     scaledOffset.scale(zoomScale, zoomScale);
986
987     [_scrollView setContentOffset:[self _adjustedContentOffset:scaledOffset]];
988 }
989
990 - (BOOL)_scrollToRect:(WebCore::FloatRect)targetRect origin:(WebCore::FloatPoint)origin minimumScrollDistance:(float)minimumScrollDistance
991 {
992     WebCore::FloatRect unobscuredContentRect([self _contentRectForUserInteraction]);
993     WebCore::FloatPoint unobscuredContentOffset = unobscuredContentRect.location();
994     WebCore::FloatSize contentSize([self._currentContentView bounds].size);
995
996     // Center the target rect in the scroll view.
997     // If the target doesn't fit in the scroll view, center on the gesture location instead.
998     WebCore::FloatPoint newUnobscuredContentOffset;
999     if (targetRect.width() <= unobscuredContentRect.width())
1000         newUnobscuredContentOffset.setX(targetRect.x() - (unobscuredContentRect.width() - targetRect.width()) / 2);
1001     else
1002         newUnobscuredContentOffset.setX(origin.x() - unobscuredContentRect.width() / 2);
1003     if (targetRect.height() <= unobscuredContentRect.height())
1004         newUnobscuredContentOffset.setY(targetRect.y() - (unobscuredContentRect.height() - targetRect.height()) / 2);
1005     else
1006         newUnobscuredContentOffset.setY(origin.y() - unobscuredContentRect.height() / 2);
1007     newUnobscuredContentOffset = constrainContentOffset(newUnobscuredContentOffset, contentSize, unobscuredContentRect.size());
1008
1009     if (unobscuredContentOffset == newUnobscuredContentOffset) {
1010         if (targetRect.width() > unobscuredContentRect.width())
1011             newUnobscuredContentOffset.setX(origin.x() - unobscuredContentRect.width() / 2);
1012         if (targetRect.height() > unobscuredContentRect.height())
1013             newUnobscuredContentOffset.setY(origin.y() - unobscuredContentRect.height() / 2);
1014         newUnobscuredContentOffset = constrainContentOffset(newUnobscuredContentOffset, contentSize, unobscuredContentRect.size());
1015     }
1016
1017     WebCore::FloatSize scrollViewOffsetDelta = newUnobscuredContentOffset - unobscuredContentOffset;
1018     scrollViewOffsetDelta.scale(contentZoomScale(self));
1019
1020     float scrollDistance = scrollViewOffsetDelta.diagonalLength();
1021     if (scrollDistance < minimumScrollDistance)
1022         return false;
1023
1024     [_scrollView setContentOffset:([_scrollView contentOffset] + scrollViewOffsetDelta) animated:YES];
1025     return true;
1026 }
1027
1028 - (void)_zoomOutWithOrigin:(WebCore::FloatPoint)origin
1029 {
1030     [self _zoomToPoint:origin atScale:[_scrollView minimumZoomScale]];
1031 }
1032
1033 // focusedElementRect and selectionRect are both in document coordinates.
1034 - (void)_zoomToFocusRect:(WebCore::FloatRect)focusedElementRectInDocumentCoordinates selectionRect:(WebCore::FloatRect)selectionRectInDocumentCoordinates fontSize:(float)fontSize minimumScale:(double)minimumScale maximumScale:(double)maximumScale allowScaling:(BOOL)allowScaling forceScroll:(BOOL)forceScroll
1035 {
1036     const double WKWebViewStandardFontSize = 16;
1037     const double kMinimumHeightToShowContentAboveKeyboard = 106;
1038     const CFTimeInterval UIWebFormAnimationDuration = 0.25;
1039     const double CaretOffsetFromWindowEdge = 20;
1040
1041     // Zoom around the element's bounding frame. We use a "standard" size to determine the proper frame.
1042     double scale = allowScaling ? std::min(std::max(WKWebViewStandardFontSize / fontSize, minimumScale), maximumScale) : contentZoomScale(self);
1043     CGFloat documentWidth = [_contentView bounds].size.width;
1044     scale = CGRound(documentWidth * scale) / documentWidth;
1045
1046     UIWindow *window = [_scrollView window];
1047
1048     WebCore::FloatRect focusedElementRectInNewScale = focusedElementRectInDocumentCoordinates;
1049     focusedElementRectInNewScale.scale(scale);
1050     focusedElementRectInNewScale.moveBy([_contentView frame].origin);
1051
1052     // Find the portion of the view that is visible on the screen.
1053     UIViewController *topViewController = [[[_scrollView _viewControllerForAncestor] _rootAncestorViewController] _viewControllerForSupportedInterfaceOrientations];
1054     UIView *fullScreenView = topViewController.view;
1055     if (!fullScreenView)
1056         fullScreenView = window;
1057
1058     CGRect unobscuredScrollViewRectInWebViewCoordinates = UIEdgeInsetsInsetRect([self bounds], _obscuredInsets);
1059     CGRect visibleScrollViewBoundsInWebViewCoordinates = CGRectIntersection(unobscuredScrollViewRectInWebViewCoordinates, [fullScreenView convertRect:[fullScreenView bounds] toView:self]);
1060     CGRect formAssistantFrameInWebViewCoordinates = [window convertRect:_inputViewBounds toView:self];
1061     CGRect intersectionBetweenScrollViewAndFormAssistant = CGRectIntersection(visibleScrollViewBoundsInWebViewCoordinates, formAssistantFrameInWebViewCoordinates);
1062     CGSize visibleSize = visibleScrollViewBoundsInWebViewCoordinates.size;
1063
1064     CGFloat visibleOffsetFromTop = 0;
1065     if (!CGRectIsEmpty(intersectionBetweenScrollViewAndFormAssistant)) {
1066         CGFloat heightVisibleAboveFormAssistant = CGRectGetMinY(intersectionBetweenScrollViewAndFormAssistant) - CGRectGetMinY(visibleScrollViewBoundsInWebViewCoordinates);
1067         CGFloat heightVisibleBelowFormAssistant = CGRectGetMaxY(visibleScrollViewBoundsInWebViewCoordinates) - CGRectGetMaxY(intersectionBetweenScrollViewAndFormAssistant);
1068
1069         if (heightVisibleAboveFormAssistant >= kMinimumHeightToShowContentAboveKeyboard || heightVisibleBelowFormAssistant < heightVisibleAboveFormAssistant)
1070             visibleSize.height = heightVisibleAboveFormAssistant;
1071         else {
1072             visibleSize.height = heightVisibleBelowFormAssistant;
1073             visibleOffsetFromTop = CGRectGetMaxY(intersectionBetweenScrollViewAndFormAssistant) - CGRectGetMinY(visibleScrollViewBoundsInWebViewCoordinates);
1074         }
1075     }
1076
1077     BOOL selectionRectIsNotNull = !selectionRectInDocumentCoordinates.isZero();
1078     if (!forceScroll) {
1079         CGRect currentlyVisibleRegionInWebViewCoordinates;
1080         currentlyVisibleRegionInWebViewCoordinates.origin = unobscuredScrollViewRectInWebViewCoordinates.origin;
1081         currentlyVisibleRegionInWebViewCoordinates.origin.y += visibleOffsetFromTop;
1082         currentlyVisibleRegionInWebViewCoordinates.size = visibleSize;
1083
1084         // Don't bother scrolling if the entire node is already visible, whether or not we got a selectionRect.
1085         if (CGRectContainsRect(currentlyVisibleRegionInWebViewCoordinates, [self convertRect:focusedElementRectInDocumentCoordinates fromView:_contentView.get()]))
1086             return;
1087
1088         // Don't bother scrolling if we have a valid selectionRect and it is already visible.
1089         if (selectionRectIsNotNull && CGRectContainsRect(currentlyVisibleRegionInWebViewCoordinates, [self convertRect:selectionRectInDocumentCoordinates fromView:_contentView.get()]))
1090             return;
1091     }
1092
1093     // We want to zoom to the left/top corner of the DOM node, with as much spacing on all sides as we
1094     // can get based on the visible area after zooming (workingFrame).  The spacing in either dimension is half the
1095     // difference between the size of the DOM node and the size of the visible frame.
1096     CGFloat horizontalSpaceInWebViewCoordinates = std::max((visibleSize.width - focusedElementRectInNewScale.width()) / 2.0, 0.0);
1097     CGFloat verticalSpaceInWebViewCoordinates = std::max((visibleSize.height - focusedElementRectInNewScale.height()) / 2.0, 0.0);
1098
1099     CGPoint topLeft;
1100     topLeft.x = focusedElementRectInNewScale.x() - horizontalSpaceInWebViewCoordinates;
1101     topLeft.y = focusedElementRectInNewScale.y() - verticalSpaceInWebViewCoordinates - visibleOffsetFromTop;
1102
1103     CGFloat minimumAllowableHorizontalOffsetInWebViewCoordinates = -INFINITY;
1104     CGFloat minimumAllowableVerticalOffsetInWebViewCoordinates = -INFINITY;
1105     if (selectionRectIsNotNull) {
1106         WebCore::FloatRect selectionRectInNewScale = selectionRectInDocumentCoordinates;
1107         selectionRectInNewScale.scale(scale);
1108         selectionRectInNewScale.moveBy([_contentView frame].origin);
1109         minimumAllowableHorizontalOffsetInWebViewCoordinates = CGRectGetMaxX(selectionRectInNewScale) + CaretOffsetFromWindowEdge - visibleSize.width;
1110         minimumAllowableVerticalOffsetInWebViewCoordinates = CGRectGetMaxY(selectionRectInNewScale) + CaretOffsetFromWindowEdge - visibleSize.height - visibleOffsetFromTop;
1111     }
1112
1113     WebCore::FloatRect documentBoundsInNewScale = [_contentView bounds];
1114     documentBoundsInNewScale.scale(scale);
1115     documentBoundsInNewScale.moveBy([_contentView frame].origin);
1116
1117     // Constrain the left edge in document coordinates so that:
1118     //  - it isn't so small that the scrollVisibleRect isn't visible on the screen
1119     //  - it isn't so great that the document's right edge is less than the right edge of the screen
1120     if (selectionRectIsNotNull && topLeft.x < minimumAllowableHorizontalOffsetInWebViewCoordinates)
1121         topLeft.x = minimumAllowableHorizontalOffsetInWebViewCoordinates;
1122     else {
1123         CGFloat maximumAllowableHorizontalOffset = CGRectGetMaxX(documentBoundsInNewScale) - visibleSize.width;
1124         if (topLeft.x > maximumAllowableHorizontalOffset)
1125             topLeft.x = maximumAllowableHorizontalOffset;
1126     }
1127
1128     // Constrain the top edge in document coordinates so that:
1129     //  - it isn't so small that the scrollVisibleRect isn't visible on the screen
1130     //  - it isn't so great that the document's bottom edge is higher than the top of the form assistant
1131     if (selectionRectIsNotNull && topLeft.y < minimumAllowableVerticalOffsetInWebViewCoordinates)
1132         topLeft.y = minimumAllowableVerticalOffsetInWebViewCoordinates;
1133     else {
1134         CGFloat maximumAllowableVerticalOffset = CGRectGetMaxY(documentBoundsInNewScale) - visibleSize.height;
1135         if (topLeft.y > maximumAllowableVerticalOffset)
1136             topLeft.y = maximumAllowableVerticalOffset;
1137     }
1138
1139     WebCore::FloatPoint newCenter = CGPointMake(topLeft.x + unobscuredScrollViewRectInWebViewCoordinates.size.width / 2.0, topLeft.y + unobscuredScrollViewRectInWebViewCoordinates.size.height / 2.0);
1140
1141     if (scale != contentZoomScale(self))
1142         _page->willStartUserTriggeredZooming();
1143
1144     // The newCenter has been computed in the new scale, but _zoomToCenter expected the center to be in the original scale.
1145     newCenter.scale(1 / scale, 1 / scale);
1146     [_scrollView _zoomToCenter:newCenter
1147                         scale:scale
1148                      duration:UIWebFormAnimationDuration
1149                         force:YES];
1150 }
1151
1152 - (BOOL)_zoomToRect:(WebCore::FloatRect)targetRect withOrigin:(WebCore::FloatPoint)origin fitEntireRect:(BOOL)fitEntireRect minimumScale:(double)minimumScale maximumScale:(double)maximumScale minimumScrollDistance:(float)minimumScrollDistance
1153 {
1154     const float maximumScaleFactorDeltaForPanScroll = 0.02;
1155
1156     double currentScale = contentZoomScale(self);
1157
1158     WebCore::FloatSize unobscuredContentSize([self _contentRectForUserInteraction].size);
1159     double horizontalScale = unobscuredContentSize.width() * currentScale / targetRect.width();
1160     double verticalScale = unobscuredContentSize.height() * currentScale / targetRect.height();
1161
1162     horizontalScale = std::min(std::max(horizontalScale, minimumScale), maximumScale);
1163     verticalScale = std::min(std::max(verticalScale, minimumScale), maximumScale);
1164
1165     double targetScale = fitEntireRect ? std::min(horizontalScale, verticalScale) : horizontalScale;
1166     if (fabs(targetScale - currentScale) < maximumScaleFactorDeltaForPanScroll) {
1167         if ([self _scrollToRect:targetRect origin:origin minimumScrollDistance:minimumScrollDistance])
1168             return true;
1169     } else if (targetScale != currentScale) {
1170         [self _zoomToRect:targetRect atScale:targetScale origin:origin];
1171         return true;
1172     }
1173     
1174     return false;
1175 }
1176
1177 - (void)didMoveToWindow
1178 {
1179     _page->viewStateDidChange(WebCore::ViewState::IsInWindow);
1180 }
1181
1182 - (void)setOpaque:(BOOL)opaque
1183 {
1184     BOOL oldOpaque = self.opaque;
1185
1186     [super setOpaque:opaque];
1187     [_contentView setOpaque:opaque];
1188
1189     if (oldOpaque == opaque)
1190         return;
1191
1192     _page->setDrawsBackground(opaque);
1193     [self _updateScrollViewBackground];
1194 }
1195
1196 - (void)setBackgroundColor:(UIColor *)backgroundColor
1197 {
1198     [super setBackgroundColor:backgroundColor];
1199     [_contentView setBackgroundColor:backgroundColor];
1200 }
1201
1202 #pragma mark - UIScrollViewDelegate
1203
1204 - (BOOL)usesStandardContentView
1205 {
1206     return !_customContentView;
1207 }
1208
1209 - (CGSize)scrollView:(UIScrollView*)scrollView contentSizeForZoomScale:(CGFloat)scale withProposedSize:(CGSize)proposedSize
1210 {
1211     return roundScrollViewContentSize(*_page, proposedSize);
1212 }
1213
1214 - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
1215 {
1216     ASSERT(_scrollView == scrollView);
1217
1218     if (_customContentView)
1219         return _customContentView.get();
1220
1221     return _contentView.get();
1222 }
1223
1224 - (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view
1225 {
1226     if (![self usesStandardContentView])
1227         return;
1228
1229     if (scrollView.pinchGestureRecognizer.state == UIGestureRecognizerStateBegan) {
1230         _page->willStartUserTriggeredZooming();
1231         [_contentView scrollViewWillStartPanOrPinchGesture];
1232     }
1233     [_contentView willStartZoomOrScroll];
1234 }
1235
1236 - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
1237 {
1238     if (![self usesStandardContentView])
1239         return;
1240
1241     if (scrollView.panGestureRecognizer.state == UIGestureRecognizerStateBegan)
1242         [_contentView scrollViewWillStartPanOrPinchGesture];
1243     [_contentView willStartZoomOrScroll];
1244 }
1245
1246 - (void)_didFinishScrolling
1247 {
1248     if (![self usesStandardContentView])
1249         return;
1250
1251     [self _updateVisibleContentRects];
1252     [_contentView didFinishScrolling];
1253 }
1254
1255 - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
1256 {
1257     // Work around <rdar://problem/16374753> by avoiding deceleration while
1258     // zooming. We'll animate to the right place once the zoom finishes.
1259     if ([scrollView isZooming])
1260         *targetContentOffset = [scrollView contentOffset];
1261 }
1262
1263 - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
1264 {
1265     // If we're decelerating, scroll offset will be updated when scrollViewDidFinishDecelerating: is called.
1266     if (!decelerate)
1267         [self _didFinishScrolling];
1268 }
1269
1270 - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
1271 {
1272     [self _didFinishScrolling];
1273 }
1274
1275 - (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView
1276 {
1277     [self _didFinishScrolling];
1278 }
1279
1280 - (void)scrollViewDidScroll:(UIScrollView *)scrollView
1281 {
1282     if (![self usesStandardContentView])
1283         [_customContentView scrollViewDidScroll:(UIScrollView *)scrollView];
1284
1285     [self _updateVisibleContentRects];
1286 }
1287
1288 - (void)scrollViewDidZoom:(UIScrollView *)scrollView
1289 {
1290     [self _updateScrollViewBackground];
1291     [self _updateVisibleContentRects];
1292 }
1293
1294 - (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale
1295 {
1296     ASSERT(scrollView == _scrollView);
1297     [self _updateVisibleContentRects];
1298     [_contentView didZoomToScale:scale];
1299 }
1300
1301 - (void)_frameOrBoundsChanged
1302 {
1303     CGRect bounds = self.bounds;
1304     if (!_isAnimatingResize) {
1305         if (!_overridesMinimumLayoutSize)
1306             _page->setViewportConfigurationMinimumLayoutSize(WebCore::FloatSize(bounds.size));
1307         if (!_overridesMinimumLayoutSizeForMinimalUI)
1308             _page->setViewportConfigurationMinimumLayoutSizeForMinimalUI(WebCore::FloatSize(bounds.size));
1309         if (!_overridesMaximumUnobscuredSize)
1310             _page->setMaximumUnobscuredSize(WebCore::FloatSize(bounds.size));
1311     }
1312
1313     [_scrollView setFrame:bounds];
1314     [_contentView setMinimumSize:bounds.size];
1315     [_customContentView web_setMinimumSize:bounds.size];
1316     [self _updateVisibleContentRects];
1317 }
1318
1319 // 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.
1320 - (CGRect)_contentRectForUserInteraction
1321 {
1322     // FIXME: handle split keyboard.
1323     UIEdgeInsets obscuredInsets = _obscuredInsets;
1324     obscuredInsets.bottom = std::max(_obscuredInsets.bottom, _inputViewBounds.size.height);
1325     CGRect unobscuredRect = UIEdgeInsetsInsetRect(self.bounds, obscuredInsets);
1326     return [self convertRect:unobscuredRect toView:self._currentContentView];
1327 }
1328
1329 - (void)_updateVisibleContentRects
1330 {
1331     if (![self usesStandardContentView]) {
1332         [_customContentView web_computedContentInsetDidChange];
1333         return;
1334     }
1335
1336     if (_delayUpdateVisibleContentRects) {
1337         _hadDelayedUpdateVisibleContentRects = YES;
1338         return;
1339     }
1340
1341     if (_isAnimatingResize)
1342         return;
1343
1344     if (_needsResetViewStateAfterCommitLoadForMainFrame)
1345         return;
1346
1347     CGRect fullViewRect = self.bounds;
1348     CGRect visibleRectInContentCoordinates = [self convertRect:fullViewRect toView:_contentView.get()];
1349
1350     CGRect unobscuredRect = UIEdgeInsetsInsetRect(fullViewRect, [self _computedContentInset]);
1351     CGRect unobscuredRectInContentCoordinates = [self convertRect:unobscuredRect toView:_contentView.get()];
1352
1353     CGFloat scaleFactor = contentZoomScale(self);
1354
1355     BOOL isStableState = !(_isChangingObscuredInsetsInteractively || [_scrollView isDragging] || [_scrollView isDecelerating] || [_scrollView isZooming] || [_scrollView isZoomBouncing] || [_scrollView _isAnimatingZoom] || [_scrollView _isScrollingToTop]);
1356     [_contentView didUpdateVisibleRect:visibleRectInContentCoordinates
1357         unobscuredRect:unobscuredRectInContentCoordinates
1358         unobscuredRectInScrollViewCoordinates:unobscuredRect
1359         scale:scaleFactor minimumScale:[_scrollView minimumZoomScale]
1360         inStableState:isStableState isChangingObscuredInsetsInteractively:_isChangingObscuredInsetsInteractively];
1361 }
1362
1363 - (void)_keyboardChangedWithInfo:(NSDictionary *)keyboardInfo adjustScrollView:(BOOL)adjustScrollView
1364 {
1365     NSValue *endFrameValue = [keyboardInfo objectForKey:UIKeyboardFrameEndUserInfoKey];
1366     if (!endFrameValue)
1367         return;
1368
1369     // The keyboard rect is always in screen coordinates. In the view services case the window does not
1370     // have the interface orientation rotation transformation; its host does. So, it makes no sense to
1371     // clip the keyboard rect against its screen.
1372     if ([[self window] _isHostedInAnotherProcess])
1373         _inputViewBounds = [self.window convertRect:[endFrameValue CGRectValue] fromWindow:nil];
1374     else
1375         _inputViewBounds = [self.window convertRect:CGRectIntersection([endFrameValue CGRectValue], self.window.screen.bounds) fromWindow:nil];
1376
1377     [self _updateVisibleContentRects];
1378
1379     if (adjustScrollView)
1380         [_scrollView _adjustForAutomaticKeyboardInfo:keyboardInfo animated:YES lastAdjustment:&_lastAdjustmentForScroller];
1381 }
1382
1383 - (void)_keyboardWillChangeFrame:(NSNotification *)notification
1384 {
1385     if ([_contentView isAssistingNode])
1386         [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES];
1387 }
1388
1389 - (void)_keyboardDidChangeFrame:(NSNotification *)notification
1390 {
1391     [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:NO];
1392 }
1393
1394 - (void)_keyboardWillShow:(NSNotification *)notification
1395 {
1396     if ([_contentView isAssistingNode])
1397         [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES];
1398 }
1399
1400 - (void)_keyboardWillHide:(NSNotification *)notification
1401 {
1402     // Ignore keyboard will hide notifications sent during rotation. They're just there for
1403     // backwards compatibility reasons and processing the will hide notification would
1404     // temporarily screw up the the unobscured view area.
1405     if ([[UIPeripheralHost sharedInstance] rotationState])
1406         return;
1407
1408     [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES];
1409 }
1410
1411 - (void)_windowDidRotate:(NSNotification *)notification
1412 {
1413     if (!_overridesInterfaceOrientation)
1414         _page->setDeviceOrientation(deviceOrientation());
1415 }
1416
1417 - (void)_contentSizeCategoryDidChange:(NSNotification *)notification
1418 {
1419     _page->contentSizeCategoryDidChange([self _contentSizeCategory]);
1420 }
1421
1422 - (NSString *)_contentSizeCategory
1423 {
1424     return [[UIApplication sharedApplication] preferredContentSizeCategory];
1425 }
1426
1427 - (void)setAllowsBackForwardNavigationGestures:(BOOL)allowsBackForwardNavigationGestures
1428 {
1429     if (_allowsBackForwardNavigationGestures == allowsBackForwardNavigationGestures)
1430         return;
1431
1432     _allowsBackForwardNavigationGestures = allowsBackForwardNavigationGestures;
1433
1434     if (allowsBackForwardNavigationGestures) {
1435         if (!_gestureController) {
1436             _gestureController = std::make_unique<WebKit::ViewGestureController>(*_page);
1437             _gestureController->installSwipeHandler(self, [self scrollView]);
1438         }
1439     } else
1440         _gestureController = nullptr;
1441
1442     _page->setShouldRecordNavigationSnapshots(allowsBackForwardNavigationGestures);
1443 }
1444
1445 - (BOOL)allowsBackForwardNavigationGestures
1446 {
1447     return _allowsBackForwardNavigationGestures;
1448 }
1449
1450 #endif
1451
1452 #pragma mark OS X-specific methods
1453
1454 #if PLATFORM(MAC)
1455
1456 - (void)resizeSubviewsWithOldSize:(NSSize)oldSize
1457 {
1458     [_wkView setFrame:self.bounds];
1459 }
1460
1461 - (void)setAllowsBackForwardNavigationGestures:(BOOL)allowsBackForwardNavigationGestures
1462 {
1463     [_wkView setAllowsBackForwardNavigationGestures:allowsBackForwardNavigationGestures];
1464 }
1465
1466 - (BOOL)allowsBackForwardNavigationGestures
1467 {
1468     return [_wkView allowsBackForwardNavigationGestures];
1469 }
1470
1471 - (void)setAllowsMagnification:(BOOL)allowsMagnification
1472 {
1473     [_wkView setAllowsMagnification:allowsMagnification];
1474 }
1475
1476 - (BOOL)allowsMagnification
1477 {
1478     return [_wkView allowsMagnification];
1479 }
1480
1481 - (void)setMagnification:(CGFloat)magnification
1482 {
1483     [_wkView setMagnification:magnification];
1484 }
1485
1486 - (CGFloat)magnification
1487 {
1488     return [_wkView magnification];
1489 }
1490
1491 - (void)setMagnification:(CGFloat)magnification centeredAtPoint:(CGPoint)point
1492 {
1493     [_wkView setMagnification:magnification centeredAtPoint:NSPointFromCGPoint(point)];
1494 }
1495
1496 #endif
1497
1498 @end
1499
1500 @implementation WKWebView (WKPrivate)
1501
1502 - (_WKRemoteObjectRegistry *)_remoteObjectRegistry
1503 {
1504     if (!_remoteObjectRegistry) {
1505         _remoteObjectRegistry = adoptNS([[_WKRemoteObjectRegistry alloc] _initWithMessageSender:*_page]);
1506         _page->process().context().addMessageReceiver(Messages::RemoteObjectRegistry::messageReceiverName(), _page->pageID(), [_remoteObjectRegistry remoteObjectRegistry]);
1507     }
1508
1509     return _remoteObjectRegistry.get();
1510 }
1511
1512 - (WKBrowsingContextHandle *)_handle
1513 {
1514     return [[[WKBrowsingContextHandle alloc] _initWithPageID:_page->pageID()] autorelease];
1515 }
1516
1517 - (_WKRenderingProgressEvents)_observedRenderingProgressEvents
1518 {
1519     return _observedRenderingProgressEvents;
1520 }
1521
1522 - (id <WKHistoryDelegatePrivate>)_historyDelegate
1523 {
1524     return _navigationState->historyDelegate().autorelease();
1525 }
1526
1527 - (void)_setHistoryDelegate:(id <WKHistoryDelegatePrivate>)historyDelegate
1528 {
1529     _navigationState->setHistoryDelegate(historyDelegate);
1530 }
1531
1532 - (NSURL *)_unreachableURL
1533 {
1534     return [NSURL _web_URLWithWTFString:_page->pageLoadState().unreachableURL()];
1535 }
1536
1537 - (void)_loadAlternateHTMLString:(NSString *)string baseURL:(NSURL *)baseURL forUnreachableURL:(NSURL *)unreachableURL
1538 {
1539     _page->loadAlternateHTMLString(string, [baseURL _web_originalDataAsWTFString], [unreachableURL _web_originalDataAsWTFString]);
1540 }
1541
1542 - (NSArray *)_certificateChain
1543 {
1544     if (WebKit::WebFrameProxy* mainFrame = _page->mainFrame())
1545         return mainFrame->certificateInfo() ? (NSArray *)mainFrame->certificateInfo()->certificateInfo().certificateChain() : nil;
1546
1547     return nil;
1548 }
1549
1550 - (NSURL *)_committedURL
1551 {
1552     return [NSURL _web_URLWithWTFString:_page->pageLoadState().url()];
1553 }
1554
1555 - (NSString *)_MIMEType
1556 {
1557     if (_page->mainFrame())
1558         return _page->mainFrame()->mimeType();
1559
1560     return nil;
1561 }
1562
1563 - (NSString *)_applicationNameForUserAgent
1564 {
1565     return _page->applicationNameForUserAgent();
1566 }
1567
1568 - (void)_setApplicationNameForUserAgent:(NSString *)applicationNameForUserAgent
1569 {
1570     _page->setApplicationNameForUserAgent(applicationNameForUserAgent);
1571 }
1572
1573 - (NSString *)_customUserAgent
1574 {
1575     return _page->customUserAgent();
1576 }
1577
1578 - (void)_setCustomUserAgent:(NSString *)_customUserAgent
1579 {
1580     _page->setCustomUserAgent(_customUserAgent);
1581 }
1582
1583 - (pid_t)_webProcessIdentifier
1584 {
1585     return _page->isValid() ? _page->processIdentifier() : 0;
1586 }
1587
1588 - (void)_killWebContentProcess
1589 {
1590     if (!_page->isValid())
1591         return;
1592
1593     _page->process().terminate();
1594 }
1595
1596 #if PLATFORM(IOS)
1597 static WebCore::FloatSize activeMinimumLayoutSize(WKWebView *webView, const CGRect& bounds)
1598 {
1599     return WebCore::FloatSize(webView->_overridesMinimumLayoutSize ? webView->_minimumLayoutSizeOverride : bounds.size);
1600 }
1601
1602 static WebCore::FloatSize activeMinimumLayoutSizeForMinimalUI(WKWebView *webView, WebCore::FloatSize minimumLayoutSize)
1603 {
1604     return webView->_overridesMinimumLayoutSizeForMinimalUI ? WebCore::FloatSize(webView->_minimumLayoutSizeOverrideForMinimalUI) : minimumLayoutSize;
1605 }
1606
1607 static WebCore::FloatSize activeMaximumUnobscuredSize(WKWebView *webView, const CGRect& bounds)
1608 {
1609     return WebCore::FloatSize(webView->_overridesMaximumUnobscuredSize ? webView->_maximumUnobscuredSizeOverride : bounds.size);
1610 }
1611
1612 static int32_t activeOrientation(WKWebView *webView)
1613 {
1614     return webView->_overridesInterfaceOrientation ? deviceOrientationForUIInterfaceOrientation(webView->_interfaceOrientationOverride) : webView->_page->deviceOrientation();
1615 }
1616 #endif
1617
1618 - (void)_didRelaunchProcess
1619 {
1620 #if PLATFORM(IOS)
1621     CGRect bounds = self.bounds;
1622     WebCore::FloatSize minimalLayoutSize = activeMinimumLayoutSize(self, bounds);
1623     _page->setViewportConfigurationMinimumLayoutSize(minimalLayoutSize);
1624     _page->setViewportConfigurationMinimumLayoutSizeForMinimalUI(activeMinimumLayoutSizeForMinimalUI(self, minimalLayoutSize));
1625     _page->setMaximumUnobscuredSize(activeMaximumUnobscuredSize(self, bounds));
1626 #endif
1627 }
1628
1629 - (NSData *)_sessionStateData
1630 {
1631     WebKit::SessionState sessionState = _page->sessionState();
1632
1633     // FIXME: This should not use the legacy session state encoder.
1634     return [wrapper(*WebKit::encodeLegacySessionState(sessionState).release().leakRef()) autorelease];
1635 }
1636
1637 // FIXME: This should return a _WKSessionState object.
1638 - (id)_sessionState
1639 {
1640     return adoptNS([[_WKSessionState alloc] _initWithSessionState:_page->sessionState()]).autorelease();
1641 }
1642
1643 - (void)_restoreFromSessionStateData:(NSData *)sessionStateData
1644 {
1645     // FIXME: This should not use the legacy session state decoder.
1646     WebKit::SessionState sessionState;
1647     if (!WebKit::decodeLegacySessionState(static_cast<const uint8_t*>(sessionStateData.bytes), sessionStateData.length, sessionState))
1648         return;
1649
1650     if (uint64_t navigationID = _page->restoreFromSessionState(WTF::move(sessionState), true)) {
1651         // FIXME: This is not necessarily always a reload navigation.
1652         _navigationState->createReloadNavigation(navigationID);
1653     }
1654 }
1655
1656 // FIXME: Remove this once nobody is using it.
1657 - (void)_restoreFromSessionState:(id)sessionState
1658 {
1659     [self _restoreSessionState:sessionState andNavigate:YES];
1660 }
1661
1662 - (WKNavigation *)_restoreSessionState:(_WKSessionState *)sessionState andNavigate:(BOOL)navigate
1663 {
1664     if (uint64_t navigationID = _page->restoreFromSessionState(sessionState->_sessionState, navigate)) {
1665         // FIXME: This is not necessarily always a reload navigation.
1666         return _navigationState->createReloadNavigation(navigationID).autorelease();
1667     }
1668
1669     return nil;
1670 }
1671
1672 - (void)_close
1673 {
1674     _page->close();
1675 }
1676
1677 - (BOOL)_allowsRemoteInspection
1678 {
1679 #if ENABLE(REMOTE_INSPECTOR)
1680     return _page->allowsRemoteInspection();
1681 #else
1682     return NO;
1683 #endif
1684 }
1685
1686 - (void)_setAllowsRemoteInspection:(BOOL)allow
1687 {
1688 #if ENABLE(REMOTE_INSPECTOR)
1689     _page->setAllowsRemoteInspection(allow);
1690 #endif
1691 }
1692
1693 - (BOOL)_addsVisitedLinks
1694 {
1695     return _page->addsVisitedLinks();
1696 }
1697
1698 - (void)_setAddsVisitedLinks:(BOOL)addsVisitedLinks
1699 {
1700     _page->setAddsVisitedLinks(addsVisitedLinks);
1701 }
1702
1703 - (BOOL)_networkRequestsInProgress
1704 {
1705     return _page->pageLoadState().networkRequestsInProgress();
1706 }
1707
1708 static inline WebCore::LayoutMilestones layoutMilestones(_WKRenderingProgressEvents events)
1709 {
1710     WebCore::LayoutMilestones milestones = 0;
1711
1712     if (events & _WKRenderingProgressEventFirstLayout)
1713         milestones |= WebCore::DidFirstLayout;
1714
1715     if (events & _WKRenderingProgressEventFirstPaintWithSignificantArea)
1716         milestones |= WebCore::DidHitRelevantRepaintedObjectsAreaThreshold;
1717
1718     return milestones;
1719 }
1720
1721 - (void)_setObservedRenderingProgressEvents:(_WKRenderingProgressEvents)observedRenderingProgressEvents
1722 {
1723     _observedRenderingProgressEvents = observedRenderingProgressEvents;
1724     _page->listenForLayoutMilestones(layoutMilestones(observedRenderingProgressEvents));
1725 }
1726
1727 - (void)_getMainResourceDataWithCompletionHandler:(void (^)(NSData *, NSError *))completionHandler
1728 {
1729     auto handler = adoptNS([completionHandler copy]);
1730
1731     _page->getMainResourceDataOfFrame(_page->mainFrame(), [handler](API::Data* data, WebKit::CallbackBase::Error error) {
1732         void (^completionHandlerBlock)(NSData *, NSError *) = (void (^)(NSData *, NSError *))handler.get();
1733         if (error != WebKit::CallbackBase::Error::None) {
1734             // FIXME: Pipe a proper error in from the WebPageProxy.
1735             RetainPtr<NSError> error = adoptNS([[NSError alloc] init]);
1736             completionHandlerBlock(nil, error.get());
1737         } else
1738             completionHandlerBlock(wrapper(*data), nil);
1739     });
1740 }
1741
1742 - (void)_getWebArchiveDataWithCompletionHandler:(void (^)(NSData *, NSError *))completionHandler
1743 {
1744     auto handler = adoptNS([completionHandler copy]);
1745
1746     _page->getWebArchiveOfFrame(_page->mainFrame(), [handler](API::Data* data, WebKit::CallbackBase::Error error) {
1747         void (^completionHandlerBlock)(NSData *, NSError *) = (void (^)(NSData *, NSError *))handler.get();
1748         if (error != WebKit::CallbackBase::Error::None) {
1749             // FIXME: Pipe a proper error in from the WebPageProxy.
1750             RetainPtr<NSError> error = adoptNS([[NSError alloc] init]);
1751             completionHandlerBlock(nil, error.get());
1752         } else
1753             completionHandlerBlock(wrapper(*data), nil);
1754     });
1755 }
1756
1757 - (_WKPaginationMode)_paginationMode
1758 {
1759     switch (_page->paginationMode()) {
1760     case WebCore::Pagination::Unpaginated:
1761         return _WKPaginationModeUnpaginated;
1762     case WebCore::Pagination::LeftToRightPaginated:
1763         return _WKPaginationModeLeftToRight;
1764     case WebCore::Pagination::RightToLeftPaginated:
1765         return _WKPaginationModeRightToLeft;
1766     case WebCore::Pagination::TopToBottomPaginated:
1767         return _WKPaginationModeTopToBottom;
1768     case WebCore::Pagination::BottomToTopPaginated:
1769         return _WKPaginationModeBottomToTop;
1770     }
1771
1772     ASSERT_NOT_REACHED();
1773     return _WKPaginationModeUnpaginated;
1774 }
1775
1776 - (void)_setPaginationMode:(_WKPaginationMode)paginationMode
1777 {
1778     WebCore::Pagination::Mode mode;
1779     switch (paginationMode) {
1780     case _WKPaginationModeUnpaginated:
1781         mode = WebCore::Pagination::Unpaginated;
1782         break;
1783     case _WKPaginationModeLeftToRight:
1784         mode = WebCore::Pagination::LeftToRightPaginated;
1785         break;
1786     case _WKPaginationModeRightToLeft:
1787         mode = WebCore::Pagination::RightToLeftPaginated;
1788         break;
1789     case _WKPaginationModeTopToBottom:
1790         mode = WebCore::Pagination::TopToBottomPaginated;
1791         break;
1792     case _WKPaginationModeBottomToTop:
1793         mode = WebCore::Pagination::BottomToTopPaginated;
1794         break;
1795     default:
1796         return;
1797     }
1798
1799     _page->setPaginationMode(mode);
1800 }
1801
1802 - (BOOL)_paginationBehavesLikeColumns
1803 {
1804     return _page->paginationBehavesLikeColumns();
1805 }
1806
1807 - (void)_setPaginationBehavesLikeColumns:(BOOL)behavesLikeColumns
1808 {
1809     _page->setPaginationBehavesLikeColumns(behavesLikeColumns);
1810 }
1811
1812 - (CGFloat)_pageLength
1813 {
1814     return _page->pageLength();
1815 }
1816
1817 - (void)_setPageLength:(CGFloat)pageLength
1818 {
1819     _page->setPageLength(pageLength);
1820 }
1821
1822 - (CGFloat)_gapBetweenPages
1823 {
1824     return _page->gapBetweenPages();
1825 }
1826
1827 - (void)_setGapBetweenPages:(CGFloat)gapBetweenPages
1828 {
1829     _page->setGapBetweenPages(gapBetweenPages);
1830 }
1831
1832 - (NSUInteger)_pageCount
1833 {
1834     return _page->pageCount();
1835 }
1836
1837 - (BOOL)_supportsTextZoom
1838 {
1839     return _page->supportsTextZoom();
1840 }
1841
1842 - (double)_textZoomFactor
1843 {
1844     return _page->textZoomFactor();
1845 }
1846
1847 - (void)_setTextZoomFactor:(double)zoomFactor
1848 {
1849     _page->setTextZoomFactor(zoomFactor);
1850 }
1851
1852 - (double)_pageZoomFactor
1853 {
1854     return _page->pageZoomFactor();
1855 }
1856
1857 - (void)_setPageZoomFactor:(double)zoomFactor
1858 {
1859     _page->setPageZoomFactor(zoomFactor);
1860 }
1861
1862 - (id <_WKFindDelegate>)_findDelegate
1863 {
1864     return [static_cast<WebKit::FindClient&>(_page->findClient()).delegate().leakRef() autorelease];
1865 }
1866
1867 - (void)_setFindDelegate:(id<_WKFindDelegate>)findDelegate
1868 {
1869     static_cast<WebKit::FindClient&>(_page->findClient()).setDelegate(findDelegate);
1870 }
1871
1872 static inline WebKit::FindOptions toFindOptions(_WKFindOptions wkFindOptions)
1873 {
1874     unsigned findOptions = 0;
1875
1876     if (wkFindOptions & _WKFindOptionsCaseInsensitive)
1877         findOptions |= WebKit::FindOptionsCaseInsensitive;
1878     if (wkFindOptions & _WKFindOptionsAtWordStarts)
1879         findOptions |= WebKit::FindOptionsAtWordStarts;
1880     if (wkFindOptions & _WKFindOptionsTreatMedialCapitalAsWordStart)
1881         findOptions |= WebKit::FindOptionsTreatMedialCapitalAsWordStart;
1882     if (wkFindOptions & _WKFindOptionsBackwards)
1883         findOptions |= WebKit::FindOptionsBackwards;
1884     if (wkFindOptions & _WKFindOptionsWrapAround)
1885         findOptions |= WebKit::FindOptionsWrapAround;
1886     if (wkFindOptions & _WKFindOptionsShowOverlay)
1887         findOptions |= WebKit::FindOptionsShowOverlay;
1888     if (wkFindOptions & _WKFindOptionsShowFindIndicator)
1889         findOptions |= WebKit::FindOptionsShowFindIndicator;
1890     if (wkFindOptions & _WKFindOptionsShowHighlight)
1891         findOptions |= WebKit::FindOptionsShowHighlight;
1892     if (wkFindOptions & _WKFindOptionsDetermineMatchIndex)
1893         findOptions |= WebKit::FindOptionsDetermineMatchIndex;
1894
1895     return static_cast<WebKit::FindOptions>(findOptions);
1896 }
1897
1898 - (void)_countStringMatches:(NSString *)string options:(_WKFindOptions)options maxCount:(NSUInteger)maxCount
1899 {
1900     _page->countStringMatches(string, toFindOptions(options), maxCount);
1901 }
1902
1903 - (void)_findString:(NSString *)string options:(_WKFindOptions)options maxCount:(NSUInteger)maxCount
1904 {
1905     _page->findString(string, toFindOptions(options), maxCount);
1906 }
1907
1908 - (void)_hideFindUI
1909 {
1910     _page->hideFindUI();
1911 }
1912
1913 - (id <_WKFormDelegate>)_formDelegate
1914 {
1915     return _formDelegate.getAutoreleased();
1916 }
1917
1918 - (void)_setFormDelegate:(id <_WKFormDelegate>)formDelegate
1919 {
1920     _formDelegate = formDelegate;
1921
1922     class FormClient : public API::FormClient {
1923     public:
1924         explicit FormClient(WKWebView *webView)
1925             : m_webView(webView)
1926         {
1927         }
1928
1929         virtual ~FormClient() { }
1930
1931         virtual bool willSubmitForm(WebKit::WebPageProxy*, WebKit::WebFrameProxy*, WebKit::WebFrameProxy* sourceFrame, const Vector<std::pair<WTF::String, WTF::String>>& textFieldValues, API::Object* userData, WebKit::WebFormSubmissionListenerProxy* listener) override
1932         {
1933             if (userData && userData->type() != API::Object::Type::Data) {
1934                 ASSERT(!userData || userData->type() == API::Object::Type::Data);
1935                 m_webView->_page->process().connection()->markCurrentlyDispatchedMessageAsInvalid();
1936                 return false;
1937             }
1938
1939             auto formDelegate = m_webView->_formDelegate.get();
1940
1941             if (![formDelegate respondsToSelector:@selector(_webView:willSubmitFormValues:userObject:submissionHandler:)])
1942                 return false;
1943
1944             auto valueMap = adoptNS([[NSMutableDictionary alloc] initWithCapacity:textFieldValues.size()]);
1945             for (const auto& pair : textFieldValues)
1946                 [valueMap setObject:pair.second forKey:pair.first];
1947
1948             NSObject <NSSecureCoding> *userObject = nil;
1949             if (API::Data* data = static_cast<API::Data*>(userData)) {
1950                 auto nsData = adoptNS([[NSData alloc] initWithBytesNoCopy:const_cast<void*>(static_cast<const void*>(data->bytes())) length:data->size() freeWhenDone:NO]);
1951                 auto unarchiver = adoptNS([[NSKeyedUnarchiver alloc] initForReadingWithData:nsData.get()]);
1952                 [unarchiver setRequiresSecureCoding:YES];
1953                 @try {
1954                     userObject = [unarchiver decodeObjectOfClass:[NSObject class] forKey:@"userObject"];
1955                 } @catch (NSException *exception) {
1956                     LOG_ERROR("Failed to decode user data: %@", exception);
1957                 }
1958             }
1959
1960             [formDelegate _webView:m_webView willSubmitFormValues:valueMap.get() userObject:userObject submissionHandler:^{
1961                 listener->continueSubmission();
1962             }];
1963             return true;
1964         }
1965
1966     private:
1967         WKWebView *m_webView;
1968     };
1969
1970     if (formDelegate)
1971         _page->setFormClient(std::make_unique<FormClient>(self));
1972     else
1973         _page->setFormClient(nullptr);
1974 }
1975
1976 - (BOOL)_isDisplayingStandaloneImageDocument
1977 {
1978     if (auto* mainFrame = _page->mainFrame())
1979         return mainFrame->isDisplayingStandaloneImageDocument();
1980     return NO;
1981 }
1982
1983 #pragma mark iOS-specific methods
1984
1985 #if PLATFORM(IOS)
1986
1987 - (CGSize)_minimumLayoutSizeOverride
1988 {
1989     ASSERT(_overridesMinimumLayoutSize);
1990     return _minimumLayoutSizeOverride;
1991 }
1992
1993 - (void)_setMinimumLayoutSizeOverride:(CGSize)minimumLayoutSizeOverride
1994 {
1995     _overridesMinimumLayoutSize = YES;
1996     if (CGSizeEqualToSize(_minimumLayoutSizeOverride, minimumLayoutSizeOverride))
1997         return;
1998
1999     _minimumLayoutSizeOverride = minimumLayoutSizeOverride;
2000     if (!_isAnimatingResize)
2001         _page->setViewportConfigurationMinimumLayoutSize(WebCore::FloatSize(minimumLayoutSizeOverride));
2002 }
2003
2004 - (CGSize)_minimumLayoutSizeOverrideForMinimalUI
2005 {
2006     ASSERT(_overridesMinimumLayoutSizeForMinimalUI);
2007     return _minimumLayoutSizeOverrideForMinimalUI;
2008 }
2009
2010 - (void)_setMinimumLayoutSizeOverrideForMinimalUI:(CGSize)size
2011 {
2012     _overridesMinimumLayoutSizeForMinimalUI = YES;
2013     if (CGSizeEqualToSize(_minimumLayoutSizeOverrideForMinimalUI, size))
2014         return;
2015
2016     _minimumLayoutSizeOverrideForMinimalUI = size;
2017     if (!_isAnimatingResize)
2018         _page->setViewportConfigurationMinimumLayoutSizeForMinimalUI(WebCore::FloatSize(size));
2019 }
2020
2021 - (UIEdgeInsets)_obscuredInsets
2022 {
2023     return _obscuredInsets;
2024 }
2025
2026 - (void)_setObscuredInsets:(UIEdgeInsets)obscuredInsets
2027 {
2028     ASSERT(obscuredInsets.top >= 0);
2029     ASSERT(obscuredInsets.left >= 0);
2030     ASSERT(obscuredInsets.bottom >= 0);
2031     ASSERT(obscuredInsets.right >= 0);
2032
2033     if (UIEdgeInsetsEqualToEdgeInsets(_obscuredInsets, obscuredInsets))
2034         return;
2035
2036     _obscuredInsets = obscuredInsets;
2037
2038     [self _updateVisibleContentRects];
2039 }
2040
2041 - (void)_setInterfaceOrientationOverride:(UIInterfaceOrientation)interfaceOrientation
2042 {
2043     if (!_overridesInterfaceOrientation)
2044         [[NSNotificationCenter defaultCenter] removeObserver:self name:UIWindowDidRotateNotification object:nil];
2045
2046     _overridesInterfaceOrientation = YES;
2047
2048     if (interfaceOrientation == _interfaceOrientationOverride)
2049         return;
2050
2051     _interfaceOrientationOverride = interfaceOrientation;
2052
2053     if (!_isAnimatingResize)
2054         _page->setDeviceOrientation(deviceOrientationForUIInterfaceOrientation(_interfaceOrientationOverride));
2055 }
2056
2057 - (UIInterfaceOrientation)_interfaceOrientationOverride
2058 {
2059     ASSERT(_overridesInterfaceOrientation);
2060     return _interfaceOrientationOverride;
2061 }
2062
2063 - (CGSize)_maximumUnobscuredSizeOverride
2064 {
2065     ASSERT(_overridesMaximumUnobscuredSize);
2066     return _maximumUnobscuredSizeOverride;
2067 }
2068
2069 - (void)_setMaximumUnobscuredSizeOverride:(CGSize)size
2070 {
2071     ASSERT(size.width <= self.bounds.size.width && size.height <= self.bounds.size.height);
2072     _overridesMaximumUnobscuredSize = YES;
2073     if (CGSizeEqualToSize(_maximumUnobscuredSizeOverride, size))
2074         return;
2075
2076     _maximumUnobscuredSizeOverride = size;
2077     if (!_isAnimatingResize)
2078         _page->setMaximumUnobscuredSize(WebCore::FloatSize(size));
2079 }
2080
2081 - (void)_setBackgroundExtendsBeyondPage:(BOOL)backgroundExtends
2082 {
2083     _page->setBackgroundExtendsBeyondPage(backgroundExtends);
2084 }
2085
2086 - (BOOL)_backgroundExtendsBeyondPage
2087 {
2088     return _page->backgroundExtendsBeyondPage();
2089 }
2090
2091 - (void)_beginInteractiveObscuredInsetsChange
2092 {
2093     ASSERT(!_isChangingObscuredInsetsInteractively);
2094     _isChangingObscuredInsetsInteractively = YES;
2095 }
2096
2097 - (void)_endInteractiveObscuredInsetsChange
2098 {
2099     ASSERT(_isChangingObscuredInsetsInteractively);
2100     _isChangingObscuredInsetsInteractively = NO;
2101     [self _updateVisibleContentRects];
2102 }
2103
2104 - (void)_beginAnimatedResizeWithUpdates:(void (^)(void))updateBlock
2105 {
2106     if (_customContentView) {
2107         updateBlock();
2108         return;
2109     }
2110
2111     _isAnimatingResize = YES;
2112
2113     CGRect oldBounds = self.bounds;
2114     WebCore::FloatSize oldMinimumLayoutSize = activeMinimumLayoutSize(self, oldBounds);
2115     WebCore::FloatSize oldMinimumLayoutSizeForMinimalUI = activeMinimumLayoutSizeForMinimalUI(self, oldMinimumLayoutSize);
2116     WebCore::FloatSize oldMaximumUnobscuredSize = activeMaximumUnobscuredSize(self, oldBounds);
2117     int32_t oldOrientation = activeOrientation(self);
2118     UIEdgeInsets oldObscuredInsets = _obscuredInsets;
2119     WebCore::FloatRect oldUnobscuredContentRect = _page->unobscuredContentRect();
2120
2121     updateBlock();
2122
2123     CGRect newBounds = self.bounds;
2124     WebCore::FloatSize newMinimumLayoutSize = activeMinimumLayoutSize(self, newBounds);
2125     WebCore::FloatSize newMinimumLayoutSizeForMinimalUI = activeMinimumLayoutSizeForMinimalUI(self, newMinimumLayoutSize);
2126     WebCore::FloatSize newMaximumUnobscuredSize = activeMaximumUnobscuredSize(self, newBounds);
2127     int32_t newOrientation = activeOrientation(self);
2128     UIEdgeInsets newObscuredInsets = _obscuredInsets;
2129
2130     if (CGRectEqualToRect(oldBounds, newBounds)
2131         && oldMinimumLayoutSize == newMinimumLayoutSize
2132         && oldMinimumLayoutSizeForMinimalUI == newMinimumLayoutSizeForMinimalUI
2133         && oldMaximumUnobscuredSize == newMaximumUnobscuredSize
2134         && oldOrientation == newOrientation
2135         && UIEdgeInsetsEqualToEdgeInsets(oldObscuredInsets, newObscuredInsets)) {
2136         _isAnimatingResize = NO;
2137         [self _updateVisibleContentRects];
2138         return;
2139     }
2140
2141     _resizeAnimationTransformAdjustments = CATransform3DIdentity;
2142
2143     NSUInteger indexOfContentView = [[_scrollView subviews] indexOfObject:_contentView.get()];
2144     _resizeAnimationView = adoptNS([[UIView alloc] init]);
2145     [_scrollView insertSubview:_resizeAnimationView.get() atIndex:indexOfContentView];
2146     [_resizeAnimationView addSubview:_contentView.get()];
2147     [_resizeAnimationView addSubview:[_contentView unscaledView]];
2148
2149     CGSize contentSizeInContentViewCoordinates = [_contentView bounds].size;
2150     [_scrollView setMinimumZoomScale:std::min(newMinimumLayoutSize.width() / contentSizeInContentViewCoordinates.width, [_scrollView minimumZoomScale])];
2151     [_scrollView setMaximumZoomScale:std::max(newMinimumLayoutSize.width() / contentSizeInContentViewCoordinates.width, [_scrollView maximumZoomScale])];
2152
2153     // Compute the new scale to keep the current content width in the scrollview.
2154     CGFloat oldWebViewWidthInContentViewCoordinates = oldUnobscuredContentRect.width();
2155     CGFloat visibleContentViewWidthInContentCoordinates = std::min(contentSizeInContentViewCoordinates.width, oldWebViewWidthInContentViewCoordinates);
2156     CGFloat targetScale = newMinimumLayoutSize.width() / visibleContentViewWidthInContentCoordinates;
2157     CGFloat resizeAnimationViewAnimationScale = targetScale / contentZoomScale(self);
2158     [_resizeAnimationView setTransform:CGAffineTransformMakeScale(resizeAnimationViewAnimationScale, resizeAnimationViewAnimationScale)];
2159
2160     // Compute a new position to keep the content centered.
2161     CGPoint originalContentCenter = oldUnobscuredContentRect.center();
2162     CGPoint originalContentCenterInSelfCoordinates = [self convertPoint:originalContentCenter fromView:_contentView.get()];
2163     CGRect futureUnobscuredRectInSelfCoordinates = UIEdgeInsetsInsetRect(newBounds, _obscuredInsets);
2164     CGPoint futureUnobscuredRectCenterInSelfCoordinates = CGPointMake(futureUnobscuredRectInSelfCoordinates.origin.x + futureUnobscuredRectInSelfCoordinates.size.width / 2, futureUnobscuredRectInSelfCoordinates.origin.y + futureUnobscuredRectInSelfCoordinates.size.height / 2);
2165
2166     CGPoint originalContentOffset = [_scrollView contentOffset];
2167     CGPoint contentOffset = originalContentOffset;
2168     contentOffset.x += (originalContentCenterInSelfCoordinates.x - futureUnobscuredRectCenterInSelfCoordinates.x);
2169     contentOffset.y += (originalContentCenterInSelfCoordinates.y - futureUnobscuredRectCenterInSelfCoordinates.y);
2170
2171     // Limit the new offset within the scrollview, we do not want to rubber band programmatically.
2172     CGSize futureContentSizeInSelfCoordinates = CGSizeMake(contentSizeInContentViewCoordinates.width * targetScale, contentSizeInContentViewCoordinates.height * targetScale);
2173     CGFloat maxHorizontalOffset = futureContentSizeInSelfCoordinates.width - newBounds.size.width + _obscuredInsets.right;
2174     contentOffset.x = std::min(contentOffset.x, maxHorizontalOffset);
2175     CGFloat maxVerticalOffset = futureContentSizeInSelfCoordinates.height - newBounds.size.height + _obscuredInsets.bottom;
2176     contentOffset.y = std::min(contentOffset.y, maxVerticalOffset);
2177
2178     contentOffset.x = std::max(contentOffset.x, -_obscuredInsets.left);
2179     contentOffset.y = std::max(contentOffset.y, -_obscuredInsets.top);
2180
2181     // Make the top/bottom edges "sticky" within 1 pixel.
2182     if (oldUnobscuredContentRect.maxY() > contentSizeInContentViewCoordinates.height - 1)
2183         contentOffset.y = maxVerticalOffset;
2184     if (oldUnobscuredContentRect.y() < 1)
2185         contentOffset.y = -_obscuredInsets.top;
2186
2187     // FIXME: if we have content centered after double tap to zoom, we should also try to keep that rect in view.
2188     [_scrollView setContentSize:roundScrollViewContentSize(*_page, futureContentSizeInSelfCoordinates)];
2189     [_scrollView setContentOffset:contentOffset];
2190
2191     CGRect visibleRectInContentCoordinates = [self convertRect:newBounds toView:_contentView.get()];
2192     CGRect unobscuredRectInContentCoordinates = [self convertRect:futureUnobscuredRectInSelfCoordinates toView:_contentView.get()];
2193
2194     _page->dynamicViewportSizeUpdate(newMinimumLayoutSize, newMinimumLayoutSizeForMinimalUI, newMaximumUnobscuredSize, visibleRectInContentCoordinates, unobscuredRectInContentCoordinates, futureUnobscuredRectInSelfCoordinates, targetScale, newOrientation);
2195 }
2196
2197 - (void)_endAnimatedResize
2198 {
2199     if (!_isAnimatingResize)
2200         return;
2201
2202     _page->synchronizeDynamicViewportUpdate();
2203
2204     NSUInteger indexOfResizeAnimationView = [[_scrollView subviews] indexOfObject:_resizeAnimationView.get()];
2205     [_scrollView insertSubview:_contentView.get() atIndex:indexOfResizeAnimationView];
2206     [_scrollView insertSubview:[_contentView unscaledView] atIndex:indexOfResizeAnimationView + 1];
2207
2208     CALayer *contentViewLayer = [_contentView layer];
2209     CGFloat adjustmentScale = _resizeAnimationTransformAdjustments.m11;
2210     contentViewLayer.sublayerTransform = CATransform3DIdentity;
2211
2212     CGFloat animatingScaleTarget = [[_resizeAnimationView layer] transform].m11;
2213     CALayer *contentLayer = [_contentView layer];
2214     CATransform3D contentLayerTransform = contentLayer.transform;
2215     CGFloat currentScale = [[_resizeAnimationView layer] transform].m11 * contentLayerTransform.m11;
2216
2217     // We cannot use [UIScrollView setZoomScale:] directly because the UIScrollView delegate would get a callback with
2218     // an invalid contentOffset. The real content offset is only set below.
2219     // Since there is no public API for setting both the zoomScale and the contentOffset, we set the zoomScale manually
2220     // on the zoom layer and then only change the contentOffset.
2221     CGFloat adjustedScale = adjustmentScale * currentScale;
2222     contentLayerTransform.m11 = adjustedScale;
2223     contentLayerTransform.m22 = adjustedScale;
2224     contentLayer.transform = contentLayerTransform;
2225
2226     CGPoint currentScrollOffset = [_scrollView contentOffset];
2227     double horizontalScrollAdjustement = _resizeAnimationTransformAdjustments.m41 * animatingScaleTarget;
2228     double verticalScrollAdjustment = _resizeAnimationTransformAdjustments.m42 * animatingScaleTarget;
2229
2230     [_scrollView setContentSize:roundScrollViewContentSize(*_page, [_contentView frame].size)];
2231     [_scrollView setContentOffset:CGPointMake(currentScrollOffset.x - horizontalScrollAdjustement, currentScrollOffset.y - verticalScrollAdjustment)];
2232
2233     [_resizeAnimationView removeFromSuperview];
2234     _resizeAnimationView = nil;
2235     _resizeAnimationTransformAdjustments = CATransform3DIdentity;
2236
2237     _isAnimatingResize = NO;
2238     [self _updateVisibleContentRects];
2239 }
2240
2241 - (void)_setOverlaidAccessoryViewsInset:(CGSize)inset
2242 {
2243     [_customContentView web_setOverlaidAccessoryViewsInset:inset];
2244 }
2245
2246 - (void)_snapshotRect:(CGRect)rectInViewCoordinates intoImageOfWidth:(CGFloat)imageWidth completionHandler:(void(^)(CGImageRef))completionHandler
2247 {
2248     CGRect snapshotRectInContentCoordinates = [self convertRect:rectInViewCoordinates toView:_contentView.get()];
2249     CGFloat imageHeight = imageWidth / snapshotRectInContentCoordinates.size.width * snapshotRectInContentCoordinates.size.height;
2250     CGSize imageSize = CGSizeMake(imageWidth, imageHeight);
2251     
2252     void(^copiedCompletionHandler)(CGImageRef) = [completionHandler copy];
2253     _page->takeSnapshot(WebCore::enclosingIntRect(snapshotRectInContentCoordinates), WebCore::expandedIntSize(WebCore::FloatSize(imageSize)), WebKit::SnapshotOptionsExcludeDeviceScaleFactor, [=](const WebKit::ShareableBitmap::Handle& imageHandle, WebKit::CallbackBase::Error) {
2254         if (imageHandle.isNull()) {
2255             copiedCompletionHandler(nullptr);
2256             [copiedCompletionHandler release];
2257             return;
2258         }
2259
2260         RefPtr<WebKit::ShareableBitmap> bitmap = WebKit::ShareableBitmap::create(imageHandle, WebKit::SharedMemory::ReadOnly);
2261
2262         if (!bitmap) {
2263             copiedCompletionHandler(nullptr);
2264             [copiedCompletionHandler release];
2265             return;
2266         }
2267
2268         RetainPtr<CGImageRef> cgImage;
2269         cgImage = bitmap->makeCGImage();
2270         copiedCompletionHandler(cgImage.get());
2271         [copiedCompletionHandler release];
2272     });
2273 }
2274
2275 - (void)_overrideLayoutParametersWithMinimumLayoutSize:(CGSize)minimumLayoutSize minimumLayoutSizeForMinimalUI:(CGSize)minimumLayoutSizeForMinimalUI maximumUnobscuredSizeOverride:(CGSize)maximumUnobscuredSizeOverride
2276 {
2277     // FIXME: After Safari is updated to use this function instead of setting the parameters separately, we should remove
2278     // the individual setters and send a single message to send everything at once to the WebProcess.
2279     self._minimumLayoutSizeOverride = minimumLayoutSize;
2280     self._minimumLayoutSizeOverrideForMinimalUI = minimumLayoutSizeForMinimalUI;
2281     self._maximumUnobscuredSizeOverride = maximumUnobscuredSizeOverride;
2282 }
2283
2284 - (UIView *)_viewForFindUI
2285 {
2286     return [self viewForZoomingInScrollView:[self scrollView]];
2287 }
2288
2289 - (BOOL)_isDisplayingPDF
2290 {
2291     return [_customContentView isKindOfClass:[WKPDFView class]];
2292 }
2293
2294 - (NSData *)_dataForDisplayedPDF
2295 {
2296     if (![self _isDisplayingPDF])
2297         return nil;
2298     CGPDFDocumentRef pdfDocument = [(WKPDFView *)_customContentView pdfDocument];
2299     return [(NSData *)CGDataProviderCopyData(CGPDFDocumentGetDataProvider(pdfDocument)) autorelease];
2300 }
2301
2302 - (NSString *)_suggestedFilenameForDisplayedPDF
2303 {
2304     if (![self _isDisplayingPDF])
2305         return nil;
2306     return [(WKPDFView *)_customContentView.get() suggestedFilename];
2307 }
2308
2309 - (CGFloat)_viewportMetaTagWidth
2310 {
2311     return _viewportMetaTagWidth;
2312 }
2313
2314 - (_WKWebViewPrintFormatter *)_webViewPrintFormatter
2315 {
2316     UIViewPrintFormatter *viewPrintFormatter = self.viewPrintFormatter;
2317     ASSERT([viewPrintFormatter isKindOfClass:[_WKWebViewPrintFormatter class]]);
2318     return (_WKWebViewPrintFormatter *)viewPrintFormatter;
2319 }
2320
2321 #else
2322
2323 #pragma mark - OS X-specific methods
2324
2325 - (NSColor *)_pageExtendedBackgroundColor
2326 {
2327     WebCore::Color color = _page->pageExtendedBackgroundColor();
2328     if (!color.isValid())
2329         return nil;
2330
2331     return nsColor(color);
2332 }
2333
2334 - (BOOL)_drawsTransparentBackground
2335 {
2336     return _page->drawsTransparentBackground();
2337 }
2338
2339 - (void)_setDrawsTransparentBackground:(BOOL)drawsTransparentBackground
2340 {
2341     _page->setDrawsTransparentBackground(drawsTransparentBackground);
2342 }
2343
2344 - (void)_setTopContentInset:(CGFloat)contentInset
2345 {
2346     [_wkView _setTopContentInset:contentInset];
2347 }
2348
2349 - (CGFloat)_topContentInset
2350 {
2351     return [_wkView _topContentInset];
2352 }
2353
2354 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
2355
2356 - (void)_setAutomaticallyAdjustsContentInsets:(BOOL)automaticallyAdjustsContentInsets
2357 {
2358     [_wkView _setAutomaticallyAdjustsContentInsets:automaticallyAdjustsContentInsets];
2359 }
2360
2361 - (BOOL)_automaticallyAdjustsContentInsets
2362 {
2363     return [_wkView _automaticallyAdjustsContentInsets];
2364 }
2365
2366 #endif
2367
2368 #endif
2369
2370 @end
2371
2372 #if !TARGET_OS_IPHONE
2373
2374 @implementation WKWebView (WKIBActions)
2375
2376 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
2377 {
2378     SEL action = item.action;
2379
2380     if (action == @selector(goBack:))
2381         return !!_page->backForwardList().backItem();
2382
2383     if (action == @selector(goForward:))
2384         return !!_page->backForwardList().forwardItem();
2385
2386     if (action == @selector(stopLoading:)) {
2387         // FIXME: Return no if we're stopped.
2388         return YES;
2389     }
2390
2391     if (action == @selector(reload:) || action == @selector(reloadFromOrigin:)) {
2392         // FIXME: Return no if we're loading.
2393         return YES;
2394     }
2395
2396     return NO;
2397 }
2398
2399 - (IBAction)goBack:(id)sender
2400 {
2401     [self goBack];
2402 }
2403
2404 - (IBAction)goForward:(id)sender
2405 {
2406     [self goForward];
2407 }
2408
2409 - (IBAction)reload:(id)sender
2410 {
2411     [self reload];
2412 }
2413
2414 - (IBAction)reloadFromOrigin:(id)sender
2415 {
2416     [self reloadFromOrigin];
2417 }
2418
2419 - (IBAction)stopLoading:(id)sender
2420 {
2421     _page->stopLoading();
2422 }
2423
2424 @end
2425
2426 #endif
2427
2428 #if PLATFORM(IOS)
2429 @implementation WKWebView (_WKWebViewPrintFormatter)
2430
2431 - (Class)_printFormatterClass
2432 {
2433     return [_WKWebViewPrintFormatter class];
2434 }
2435
2436 - (NSInteger)_computePageCountAndStartDrawingToPDFForFrame:(_WKFrameHandle *)frame printInfo:(const WebKit::PrintInfo&)printInfo firstPage:(uint32_t)firstPage computedTotalScaleFactor:(double&)totalScaleFactor
2437 {
2438     if ([self _isDisplayingPDF])
2439         return CGPDFDocumentGetNumberOfPages([(WKPDFView *)_customContentView pdfDocument]);
2440
2441     _pageIsPrintingToPDF = YES;
2442     Vector<WebCore::IntRect> pageRects;
2443     uint64_t frameID = frame ? frame._frameID : _page->mainFrame()->frameID();
2444     if (!_page->sendSync(Messages::WebPage::ComputePagesForPrintingAndStartDrawingToPDF(frameID, printInfo, firstPage), Messages::WebPage::ComputePagesForPrintingAndStartDrawingToPDF::Reply(pageRects, totalScaleFactor)))
2445         return 0;
2446     return pageRects.size();
2447 }
2448
2449 - (void)_endPrinting
2450 {
2451     _pageIsPrintingToPDF = NO;
2452     _printedDocument = nullptr;
2453     _page->send(Messages::WebPage::EndPrinting());
2454 }
2455
2456 // FIXME: milliseconds::max() overflows when converted to nanoseconds, causing condition_variable::wait_for() to believe
2457 // a timeout occurred on any spurious wakeup. Use nanoseconds::max() (converted to ms) to avoid this. We should perhaps
2458 // change waitForAndDispatchImmediately() to take nanoseconds to avoid this issue.
2459 static constexpr std::chrono::milliseconds didFinishLoadingTimeout = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::nanoseconds::max());
2460
2461 - (CGPDFDocumentRef)_printedDocument
2462 {
2463     if ([self _isDisplayingPDF]) {
2464         ASSERT(!_pageIsPrintingToPDF);
2465         return [(WKPDFView *)_customContentView pdfDocument];
2466     }
2467
2468     if (_pageIsPrintingToPDF) {
2469         if (!_page->process().connection()->waitForAndDispatchImmediately<Messages::WebPageProxy::DidFinishDrawingPagesToPDF>(_page->pageID(), didFinishLoadingTimeout)) {
2470             ASSERT_NOT_REACHED();
2471             return nullptr;
2472         }
2473         ASSERT(!_pageIsPrintingToPDF);
2474     }
2475     return _printedDocument.get();
2476 }
2477
2478 - (void)_setPrintedDocument:(CGPDFDocumentRef)printedDocument
2479 {
2480     if (!_pageIsPrintingToPDF)
2481         return;
2482     ASSERT(![self _isDisplayingPDF]);
2483     _printedDocument = printedDocument;
2484     _pageIsPrintingToPDF = NO;
2485 }
2486
2487 @end
2488 #endif
2489
2490 #endif // WK_API_ENABLED