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