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