[iOS][WK2] Add a SPI to exclude the extended background from some areas of WKWebView
[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 "WKBackForwardListInternal.h"
40 #import "WKBackForwardListItemInternal.h"
41 #import "WKBrowsingContextHandleInternal.h"
42 #import "WKHistoryDelegatePrivate.h"
43 #import "WKNSData.h"
44 #import "WKNSURLExtras.h"
45 #import "WKNavigationDelegate.h"
46 #import "WKNavigationInternal.h"
47 #import "WKPreferencesInternal.h"
48 #import "WKProcessPoolInternal.h"
49 #import "WKUIDelegate.h"
50 #import "WKUserContentController.h"
51 #import "WKWebViewConfigurationInternal.h"
52 #import "WKWebViewContentProvider.h"
53 #import "WebBackForwardList.h"
54 #import "WebCertificateInfo.h"
55 #import "WebContext.h"
56 #import "WebFormSubmissionListenerProxy.h"
57 #import "WebPageGroup.h"
58 #import "WebPageProxy.h"
59 #import "WebProcessProxy.h"
60 #import "_WKFindDelegate.h"
61 #import "_WKFormDelegate.h"
62 #import "_WKRemoteObjectRegistryInternal.h"
63 #import "_WKVisitedLinkProviderInternal.h"
64 #import <wtf/RetainPtr.h>
65
66 #if PLATFORM(IOS)
67 #import "WKPDFView.h"
68 #import "WKScrollView.h"
69 #import "WKWebViewContentProviderRegistry.h"
70 #import <CoreGraphics/CGFloat.h>
71 #import <UIKit/UIPeripheralHost_Private.h>
72
73 @interface UIScrollView (UIScrollViewInternal)
74 - (void)_adjustForAutomaticKeyboardInfo:(NSDictionary*)info animated:(BOOL)animated lastAdjustment:(CGFloat*)lastAdjustment;
75 @end
76
77 @interface UIPeripheralHost(UIKitInternal)
78 - (CGFloat)getVerticalOverlapForView:(UIView *)view usingKeyboardInfo:(NSDictionary *)info;
79 @end
80 #endif
81
82 #if PLATFORM(MAC)
83 #import "WKViewInternal.h"
84 #import <WebCore/ColorMac.h>
85 #endif
86
87 @implementation WKWebView {
88     std::unique_ptr<WebKit::NavigationState> _navigationState;
89     std::unique_ptr<WebKit::UIDelegate> _uiDelegate;
90
91     RetainPtr<_WKRemoteObjectRegistry> _remoteObjectRegistry;
92     _WKRenderingProgressEvents _observedRenderingProgressEvents;
93
94     WebKit::WeakObjCPtr<id <_WKFormDelegate>> _formDelegate;
95
96 #if PLATFORM(IOS)
97     RetainPtr<WKScrollView> _scrollView;
98     RetainPtr<WKContentView> _contentView;
99
100     BOOL _hasStaticMinimumLayoutSize;
101     CGSize _minimumLayoutSizeOverride;
102     CGSize _minimumLayoutSizeOverrideForMinimalUI;
103
104     UIEdgeInsets _obscuredInsets;
105     bool _isChangingObscuredInsetsInteractively;
106
107     UIEdgeInsets _extendedBackgroundExclusionInsets;
108     CALayer *_extendedBackgroundLayer;
109
110     BOOL _isAnimatingResize;
111     CATransform3D _resizeAnimationTransformAdjustments;
112     RetainPtr<UIView> _resizeAnimationView;
113     CGFloat _lastAdjustmentForScroller;
114     CGFloat _keyboardVerticalOverlap;
115
116     std::unique_ptr<WebKit::ViewGestureController> _gestureController;
117     BOOL _allowsBackForwardNavigationGestures;
118
119     RetainPtr<UIView <WKWebViewContentProvider>> _customContentView;
120 #endif
121 #if PLATFORM(MAC)
122     RetainPtr<WKView> _wkView;
123 #endif
124 }
125
126 - (instancetype)initWithFrame:(CGRect)frame
127 {
128     return [self initWithFrame:frame configuration:adoptNS([[WKWebViewConfiguration alloc] init]).get()];
129 }
130
131 - (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration
132 {
133     if (!(self = [super initWithFrame:frame]))
134         return nil;
135
136     _configuration = adoptNS([configuration copy]);
137
138     if (WKWebView *relatedWebView = [_configuration _relatedWebView]) {
139         WKProcessPool *processPool = [_configuration processPool];
140         WKProcessPool *relatedWebViewProcessPool = [relatedWebView->_configuration processPool];
141         if (processPool && processPool != relatedWebViewProcessPool)
142             [NSException raise:NSInvalidArgumentException format:@"Related web view %@ has process pool %@ but configuration specifies a different process pool %@", relatedWebView, relatedWebViewProcessPool, configuration.processPool];
143
144         [_configuration setProcessPool:relatedWebViewProcessPool];
145     }
146
147     if (![_configuration processPool])
148         [_configuration setProcessPool:adoptNS([[WKProcessPool alloc] init]).get()];
149
150     if (![_configuration preferences])
151         [_configuration setPreferences:adoptNS([[WKPreferences alloc] init]).get()];
152
153     if (![_configuration userContentController])
154         [_configuration setUserContentController:adoptNS([[WKUserContentController alloc] init]).get()];
155
156     if (![_configuration _visitedLinkProvider])
157         [_configuration _setVisitedLinkProvider:adoptNS([[_WKVisitedLinkProvider alloc] init]).get()];
158
159 #if PLATFORM(IOS)
160     if (![_configuration _contentProviderRegistry])
161         [_configuration _setContentProviderRegistry:adoptNS([[WKWebViewContentProviderRegistry alloc] init]).get()];
162 #endif
163
164     CGRect bounds = self.bounds;
165
166     WebKit::WebContext& context = *[_configuration processPool]->_context;
167
168     WebKit::WebPageConfiguration webPageConfiguration;
169     webPageConfiguration.preferences = [_configuration preferences]->_preferences.get();
170     if (WKWebView *relatedWebView = [_configuration _relatedWebView])
171         webPageConfiguration.relatedPage = relatedWebView->_page.get();
172
173     webPageConfiguration.visitedLinkProvider = [_configuration _visitedLinkProvider]->_visitedLinkProvider.get();
174
175     RefPtr<WebKit::WebPageGroup> pageGroup;
176     NSString *groupIdentifier = configuration._groupIdentifier;
177     if (groupIdentifier.length) {
178         pageGroup = WebKit::WebPageGroup::create(configuration._groupIdentifier);
179         webPageConfiguration.pageGroup = pageGroup.get();
180     }
181
182 #if PLATFORM(IOS)
183     _scrollView = adoptNS([[WKScrollView alloc] initWithFrame:bounds]);
184     [_scrollView setInternalDelegate:self];
185     [_scrollView setBouncesZoom:YES];
186
187     [self addSubview:_scrollView.get()];
188
189     {
190         RetainPtr<CALayer> backgroundLayer = [[CALayer alloc] init];
191         _extendedBackgroundLayer = backgroundLayer.get();
192         _extendedBackgroundLayer.frame = bounds;
193         _extendedBackgroundLayer.backgroundColor = cachedCGColor(WebCore::Color(WebCore::Color::white), WebCore::ColorSpaceDeviceRGB);
194         [[self layer] insertSublayer:_extendedBackgroundLayer below:[_scrollView layer]];
195     }
196
197     _contentView = adoptNS([[WKContentView alloc] initWithFrame:bounds context:context configuration:std::move(webPageConfiguration) webView:self]);
198     _page = [_contentView page];
199     [_contentView layer].anchorPoint = CGPointZero;
200     [_contentView setFrame:bounds];
201     [_scrollView addSubview:_contentView.get()];
202
203     [self _frameOrBoundsChanged];
204
205     NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
206     [center addObserver:self selector:@selector(_keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];
207     [center addObserver:self selector:@selector(_keyboardDidChangeFrame:) name:UIKeyboardDidChangeFrameNotification object:nil];
208     [center addObserver:self selector:@selector(_keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
209     [center addObserver:self selector:@selector(_keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
210
211     [[_configuration _contentProviderRegistry] addPage:*_page];
212 #endif
213
214 #if PLATFORM(MAC)
215     _wkView = [[WKView alloc] initWithFrame:bounds context:context configuration:std::move(webPageConfiguration) webView:self];
216     [self addSubview:_wkView.get()];
217     _page = WebKit::toImpl([_wkView pageRef]);
218 #endif
219
220     _navigationState = std::make_unique<WebKit::NavigationState>(self);
221     _page->setPolicyClient(_navigationState->createPolicyClient());
222     _page->setLoaderClient(_navigationState->createLoaderClient());
223
224     _uiDelegate = std::make_unique<WebKit::UIDelegate>(self);
225     _page->setUIClient(_uiDelegate->createUIClient());
226
227     _page->setFindClient(std::make_unique<WebKit::FindClient>(self));
228
229     return self;
230 }
231
232 - (void)dealloc
233 {
234     [_remoteObjectRegistry _invalidate];
235 #if PLATFORM(IOS)
236     [[_configuration _contentProviderRegistry] removePage:*_page];
237     [[NSNotificationCenter defaultCenter] removeObserver:self];
238 #endif
239
240     [super dealloc];
241 }
242
243 - (WKWebViewConfiguration *)configuration
244 {
245     return [[_configuration copy] autorelease];
246 }
247
248 - (WKBackForwardList *)backForwardList
249 {
250     return wrapper(_page->backForwardList());
251 }
252
253 - (id <WKNavigationDelegate>)navigationDelegate
254 {
255     return [_navigationState->navigationDelegate().leakRef() autorelease];
256 }
257
258 - (void)setNavigationDelegate:(id <WKNavigationDelegate>)navigationDelegate
259 {
260     _navigationState->setNavigationDelegate(navigationDelegate);
261 }
262
263 - (id <WKUIDelegate>)UIDelegate
264 {
265     return [_uiDelegate->delegate().leakRef() autorelease];
266 }
267
268 - (void)setUIDelegate:(id<WKUIDelegate>)UIDelegate
269 {
270     _uiDelegate->setDelegate(UIDelegate);
271 }
272
273 - (WKNavigation *)loadRequest:(NSURLRequest *)request
274 {
275     uint64_t navigationID = _page->loadRequest(request);
276     auto navigation = _navigationState->createLoadRequestNavigation(navigationID, request);
277
278     return [navigation.leakRef() autorelease];
279 }
280
281 - (WKNavigation *)goToBackForwardListItem:(WKBackForwardListItem *)item
282 {
283     _page->goToBackForwardItem(&item._item);
284
285     // FIXME: return a WKNavigation object.
286     return nil;
287 }
288
289 - (NSString *)title
290 {
291     return _page->pageLoadState().title();
292 }
293
294 - (NSURL *)URL
295 {
296     return [NSURL _web_URLWithWTFString:_page->pageLoadState().activeURL()];
297 }
298
299 - (BOOL)isLoading
300 {
301     return _page->pageLoadState().isLoading();
302 }
303
304 - (double)estimatedProgress
305 {
306     return _page->pageLoadState().estimatedProgress();
307 }
308
309 - (BOOL)hasOnlySecureContent
310 {
311     return _page->pageLoadState().hasOnlySecureContent();
312 }
313
314 // FIXME: This should be KVO compliant.
315 - (BOOL)canGoBack
316 {
317     return !!_page->backForwardList().backItem();
318 }
319
320 // FIXME: This should be KVO compliant.
321 - (BOOL)canGoForward
322 {
323     return !!_page->backForwardList().forwardItem();
324 }
325
326 - (WKNavigation *)goBack
327 {
328     _page->goBack();
329
330     // FIXME: Return a navigation object.
331     return nil;
332 }
333
334 - (WKNavigation *)goForward
335 {
336     _page->goForward();
337
338     // FIXME: Return a navigation object.
339     return nil;
340 }
341
342 - (WKNavigation *)reload
343 {
344     _page->reload(false);
345
346     // FIXME: Return a navigation object.
347     return nil;
348 }
349
350 - (WKNavigation *)reloadFromOrigin
351 {
352     _page->reload(true);
353
354     // FIXME: Return a navigation object.
355     return nil;
356 }
357
358 - (void)stopLoading
359 {
360     _page->stopLoading();
361 }
362
363 #pragma mark iOS-specific methods
364
365 #if PLATFORM(IOS)
366 - (void)setFrame:(CGRect)frame
367 {
368     CGRect oldFrame = self.frame;
369     [super setFrame:frame];
370
371     if (!CGSizeEqualToSize(oldFrame.size, frame.size))
372         [self _frameOrBoundsChanged];
373 }
374
375 - (void)setBounds:(CGRect)bounds
376 {
377     CGRect oldBounds = self.bounds;
378     [super setBounds:bounds];
379
380     if (!CGSizeEqualToSize(oldBounds.size, bounds.size))
381         [self _frameOrBoundsChanged];
382 }
383
384 - (UIScrollView *)scrollView
385 {
386     return _scrollView.get();
387 }
388
389 - (WKBrowsingContextController *)browsingContextController
390 {
391     return [_contentView browsingContextController];
392 }
393
394 - (void)_setHasCustomContentView:(BOOL)pageHasCustomContentView loadedMIMEType:(const WTF::String&)mimeType
395 {
396     if (pageHasCustomContentView) {
397         [_customContentView removeFromSuperview];
398
399         Class representationClass = [[_configuration _contentProviderRegistry] providerForMIMEType:mimeType];
400         ASSERT(representationClass);
401         _customContentView = adoptNS([[representationClass alloc] init]);
402
403         [_contentView removeFromSuperview];
404         [_scrollView addSubview:_customContentView.get()];
405
406         [_customContentView web_setMinimumSize:self.bounds.size];
407         [_customContentView web_setScrollView:_scrollView.get()];
408     } else if (_customContentView) {
409         [_customContentView removeFromSuperview];
410         _customContentView = nullptr;
411
412         [_scrollView addSubview:_contentView.get()];
413         [_scrollView setContentSize:[_contentView frame].size];
414     }
415 }
416
417 - (void)_didFinishLoadingDataForCustomContentProviderWithSuggestedFilename:(const String&)suggestedFilename data:(NSData *)data
418 {
419     ASSERT(_customContentView);
420     [_customContentView web_setContentProviderData:data suggestedFilename:suggestedFilename];
421 }
422
423 static CGFloat contentZoomScale(WKWebView* webView)
424 {
425     UIView *zoomView;
426     if (webView->_customContentView)
427         zoomView = webView->_customContentView.get();
428     else
429         zoomView = webView->_contentView.get();
430
431     CGFloat scale = [[zoomView layer] affineTransform].a;
432     ASSERT(scale == [webView->_scrollView zoomScale]);
433     return scale;
434 }
435
436 - (void)_updateScrollViewBackground
437 {
438     WebCore::Color color = _page->pageExtendedBackgroundColor();
439     RetainPtr<CGColorRef> cgColor = cachedCGColor(color, WebCore::ColorSpaceDeviceRGB);
440
441     CGFloat zoomScale = contentZoomScale(self);
442     CGFloat minimumZoomScale = [_scrollView minimumZoomScale];
443     if (zoomScale < minimumZoomScale) {
444         CGFloat slope = 12;
445         CGFloat opacity = std::max(1 - slope * (minimumZoomScale - zoomScale), static_cast<CGFloat>(0));
446         cgColor = adoptCF(CGColorCreateCopyWithAlpha(cgColor.get(), opacity));
447     }
448     _extendedBackgroundLayer.backgroundColor = cgColor.get();
449 }
450
451 - (void)_didCommitLayerTree:(const WebKit::RemoteLayerTreeTransaction&)layerTreeTransaction
452 {
453     if (_customContentView)
454         return;
455
456     if (_isAnimatingResize) {
457         [_resizeAnimationView layer].sublayerTransform = _resizeAnimationTransformAdjustments;
458         return;
459     }
460
461     [_scrollView setContentSize:[_contentView frame].size];
462     [_scrollView setMinimumZoomScale:layerTreeTransaction.minimumScaleFactor()];
463     [_scrollView setMaximumZoomScale:layerTreeTransaction.maximumScaleFactor()];
464     [_scrollView setZoomEnabled:layerTreeTransaction.allowsUserScaling()];
465     if (!layerTreeTransaction.scaleWasSetByUIProcess() && ![_scrollView isZooming] && ![_scrollView isZoomBouncing] && ![_scrollView _isAnimatingZoom])
466         [_scrollView setZoomScale:layerTreeTransaction.pageScaleFactor()];
467
468     [self _updateScrollViewBackground];
469
470     if (_gestureController)
471         _gestureController->setRenderTreeSize(layerTreeTransaction.renderTreeSize());
472 }
473
474 - (void)_dynamicViewportUpdateChangedTargetToScale:(double)newScale position:(CGPoint)newScrollPosition
475 {
476     if (_isAnimatingResize) {
477         CGFloat animatingScaleTarget = [[_resizeAnimationView layer] transform].m11;
478         double currentTargetScale = animatingScaleTarget * [[_contentView layer] transform].m11;
479         double scale = newScale / currentTargetScale;
480         _resizeAnimationTransformAdjustments = CATransform3DMakeScale(scale, scale, 0);
481
482         CGPoint newContentOffset = CGPointMake(newScrollPosition.x * newScale, newScrollPosition.y * newScale);
483         newContentOffset.x -= _obscuredInsets.left;
484         newContentOffset.y -= _obscuredInsets.top;
485         CGPoint currentContentOffset = [_scrollView contentOffset];
486
487         _resizeAnimationTransformAdjustments.m41 = (currentContentOffset.x - newContentOffset.x) / animatingScaleTarget;
488         _resizeAnimationTransformAdjustments.m42 = (currentContentOffset.y - newContentOffset.y) / animatingScaleTarget;
489     }
490 }
491
492 - (RetainPtr<CGImageRef>)_takeViewSnapshot
493 {
494     // FIXME: We should be able to use acquire an IOSurface directly, instead of going to CGImage here and back in ViewSnapshotStore.
495     UIGraphicsBeginImageContextWithOptions(self.bounds.size, YES, self.window.screen.scale);
496     [self drawViewHierarchyInRect:[self bounds] afterScreenUpdates:NO];
497     UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
498     UIGraphicsEndImageContext();
499     return image.CGImage;
500 }
501
502 - (void)_zoomToPoint:(WebCore::FloatPoint)point atScale:(double)scale
503 {
504     double maximumZoomDuration = 0.4;
505     double minimumZoomDuration = 0.1;
506     double zoomDurationFactor = 0.3;
507
508     CGFloat zoomScale = contentZoomScale(self);
509     CFTimeInterval duration = std::min(fabs(log(zoomScale) - log(scale)) * zoomDurationFactor + minimumZoomDuration, maximumZoomDuration);
510
511     if (scale != zoomScale)
512         [_contentView willStartUserTriggeredZoom];
513
514     [_scrollView _zoomToCenter:point scale:scale duration:duration];
515 }
516
517 - (void)_zoomToRect:(WebCore::FloatRect)targetRect atScale:(double)scale origin:(WebCore::FloatPoint)origin
518 {
519     WebCore::FloatSize unobscuredContentSize([self _contentRectForUserInteraction].size);
520     WebCore::FloatSize targetRectSizeAfterZoom = targetRect.size();
521     targetRectSizeAfterZoom.scale(scale);
522
523     // Center the target rect in the scroll view.
524     // If the target doesn't fit in the scroll view, center on the gesture location instead.
525     WebCore::FloatPoint zoomCenter = targetRect.center();
526
527     if (targetRectSizeAfterZoom.width() > unobscuredContentSize.width())
528         zoomCenter.setX(origin.x());
529     if (targetRectSizeAfterZoom.height() > unobscuredContentSize.height())
530         zoomCenter.setY(origin.y());
531
532     [self _zoomToPoint:zoomCenter atScale:scale];
533 }
534
535 static WebCore::FloatPoint constrainContentOffset(WebCore::FloatPoint contentOffset, WebCore::FloatSize contentSize, WebCore::FloatSize unobscuredContentSize)
536 {
537     WebCore::FloatSize maximumContentOffset = contentSize - unobscuredContentSize;
538     contentOffset = contentOffset.shrunkTo(WebCore::FloatPoint(maximumContentOffset.width(), maximumContentOffset.height()));
539     contentOffset = contentOffset.expandedTo(WebCore::FloatPoint());
540     return contentOffset;
541 }
542
543 - (void)_scrollToContentOffset:(WebCore::FloatPoint)contentOffset
544 {
545     if (_isAnimatingResize)
546         return;
547
548     WebCore::FloatPoint scaledOffset = contentOffset;
549     CGFloat zoomScale = contentZoomScale(self);
550     scaledOffset.scale(zoomScale, zoomScale);
551
552     scaledOffset -= WebCore::FloatSize(_obscuredInsets.left, _obscuredInsets.top);
553     [_scrollView setContentOffset:scaledOffset];
554 }
555
556 - (BOOL)_scrollToRect:(WebCore::FloatRect)targetRect origin:(WebCore::FloatPoint)origin minimumScrollDistance:(float)minimumScrollDistance
557 {
558     WebCore::FloatRect unobscuredContentRect([self _contentRectForUserInteraction]);
559     WebCore::FloatPoint unobscuredContentOffset = unobscuredContentRect.location();
560     WebCore::FloatSize contentSize([_contentView bounds].size);
561
562     // Center the target rect in the scroll view.
563     // If the target doesn't fit in the scroll view, center on the gesture location instead.
564     WebCore::FloatPoint newUnobscuredContentOffset;
565     if (targetRect.width() <= unobscuredContentRect.width())
566         newUnobscuredContentOffset.setX(targetRect.x() - (unobscuredContentRect.width() - targetRect.width()) / 2);
567     else
568         newUnobscuredContentOffset.setX(origin.x() - unobscuredContentRect.width() / 2);
569     if (targetRect.height() <= unobscuredContentRect.height())
570         newUnobscuredContentOffset.setY(targetRect.y() - (unobscuredContentRect.height() - targetRect.height()) / 2);
571     else
572         newUnobscuredContentOffset.setY(origin.y() - unobscuredContentRect.height() / 2);
573     newUnobscuredContentOffset = constrainContentOffset(newUnobscuredContentOffset, contentSize, unobscuredContentRect.size());
574
575     if (unobscuredContentOffset == newUnobscuredContentOffset) {
576         if (targetRect.width() > unobscuredContentRect.width())
577             newUnobscuredContentOffset.setX(origin.x() - unobscuredContentRect.width() / 2);
578         if (targetRect.height() > unobscuredContentRect.height())
579             newUnobscuredContentOffset.setY(origin.y() - unobscuredContentRect.height() / 2);
580         newUnobscuredContentOffset = constrainContentOffset(newUnobscuredContentOffset, contentSize, unobscuredContentRect.size());
581     }
582
583     WebCore::FloatSize scrollViewOffsetDelta = newUnobscuredContentOffset - unobscuredContentOffset;
584     scrollViewOffsetDelta.scale(contentZoomScale(self));
585
586     float scrollDistance = scrollViewOffsetDelta.diagonalLength();
587     if (scrollDistance < minimumScrollDistance)
588         return false;
589
590     [_scrollView setContentOffset:([_scrollView contentOffset] + scrollViewOffsetDelta) animated:YES];
591     return true;
592 }
593
594 - (void)_zoomOutWithOrigin:(WebCore::FloatPoint)origin
595 {
596     [self _zoomToPoint:origin atScale:[_scrollView minimumZoomScale]];
597 }
598
599 - (BOOL)_zoomToRect:(WebCore::FloatRect)targetRect withOrigin:(WebCore::FloatPoint)origin fitEntireRect:(BOOL)fitEntireRect minimumScale:(double)minimumScale maximumScale:(double)maximumScale minimumScrollDistance:(float)minimumScrollDistance
600 {
601     const float maximumScaleFactorDeltaForPanScroll = 0.02;
602
603     double currentScale = contentZoomScale(self);
604
605     WebCore::FloatSize unobscuredContentSize([self _contentRectForUserInteraction].size);
606     double horizontalScale = unobscuredContentSize.width() * currentScale / targetRect.width();
607     double verticalScale = unobscuredContentSize.height() * currentScale / targetRect.height();
608
609     horizontalScale = std::min(std::max(horizontalScale, minimumScale), maximumScale);
610     verticalScale = std::min(std::max(verticalScale, minimumScale), maximumScale);
611
612     double targetScale = fitEntireRect ? std::min(horizontalScale, verticalScale) : horizontalScale;
613     if (fabs(targetScale - currentScale) < maximumScaleFactorDeltaForPanScroll) {
614         if ([self _scrollToRect:targetRect origin:origin minimumScrollDistance:minimumScrollDistance])
615             return true;
616     } else if (targetScale != currentScale) {
617         [self _zoomToRect:targetRect atScale:targetScale origin:origin];
618         return true;
619     }
620     
621     return false;
622 }
623
624 - (void)didMoveToWindow
625 {
626     _page->viewStateDidChange(WebCore::ViewState::IsInWindow);
627 }
628
629 #pragma mark - UIScrollViewDelegate
630
631 - (BOOL)usesStandardContentView
632 {
633     return !_customContentView;
634 }
635
636 - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
637 {
638     ASSERT(_scrollView == scrollView);
639
640     if (_customContentView)
641         return _customContentView.get();
642
643     return _contentView.get();
644 }
645
646 - (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view
647 {
648     if (![self usesStandardContentView])
649         return;
650
651     if (scrollView.pinchGestureRecognizer.state == UIGestureRecognizerStateBegan)
652         [_contentView willStartUserTriggeredZoom];
653     [_contentView willStartZoomOrScroll];
654 }
655
656 - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
657 {
658     if (![self usesStandardContentView])
659         return;
660
661     if (scrollView.panGestureRecognizer.state == UIGestureRecognizerStateBegan)
662         [_contentView willStartUserTriggeredScroll];
663     [_contentView willStartZoomOrScroll];
664 }
665
666 - (void)_didFinishScrolling
667 {
668     if (![self usesStandardContentView])
669         return;
670
671     [self _updateVisibleContentRects];
672     [_contentView didFinishScrolling];
673 }
674
675 - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
676 {
677     // If we're decelerating, scroll offset will be updated when scrollViewDidFinishDecelerating: is called.
678     if (!decelerate)
679         [self _didFinishScrolling];
680 }
681
682 - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
683 {
684     [self _didFinishScrolling];
685 }
686
687 - (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView
688 {
689     [self _didFinishScrolling];
690 }
691
692 - (void)scrollViewDidScroll:(UIScrollView *)scrollView
693 {
694     if (![self usesStandardContentView])
695         [_customContentView scrollViewDidScroll:(UIScrollView *)scrollView];
696
697     [self _updateVisibleContentRects];
698 }
699
700 - (void)scrollViewDidZoom:(UIScrollView *)scrollView
701 {
702     [self _updateScrollViewBackground];
703     [self _updateVisibleContentRects];
704 }
705
706 - (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale
707 {
708     ASSERT(scrollView == _scrollView);
709     [self _updateVisibleContentRects];
710     [_contentView didZoomToScale:scale];
711 }
712
713 static inline void setViewportConfigurationMinimumLayoutSize(WebKit::WebPageProxy& page, const CGSize& size)
714 {
715     page.setViewportConfigurationMinimumLayoutSize(WebCore::FloatSize(size));
716 }
717
718 - (void)_frameOrBoundsChanged
719 {
720     CGRect bounds = self.bounds;
721
722     CGRect backgroundLayerRect = UIEdgeInsetsInsetRect(bounds, _extendedBackgroundExclusionInsets);
723     _extendedBackgroundLayer.frame = backgroundLayerRect;
724
725     if (!_hasStaticMinimumLayoutSize && !_isAnimatingResize)
726         setViewportConfigurationMinimumLayoutSize(*_page, bounds.size);
727     [_scrollView setFrame:bounds];
728     [_contentView setMinimumSize:bounds.size];
729     [_customContentView web_setMinimumSize:bounds.size];
730     [self _updateVisibleContentRects];
731 }
732
733 // 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.
734 - (CGRect)_contentRectForUserInteraction
735 {
736     // FIXME: handle split keyboard.
737     UIEdgeInsets obscuredInsets = _obscuredInsets;
738     obscuredInsets.bottom = std::max(_obscuredInsets.bottom, _keyboardVerticalOverlap);
739     CGRect unobscuredRect = UIEdgeInsetsInsetRect(self.bounds, obscuredInsets);
740     return [self convertRect:unobscuredRect toView:_contentView.get()];
741 }
742
743 - (void)_updateVisibleContentRects
744 {
745     if (![self usesStandardContentView])
746         return;
747
748     if (_isAnimatingResize)
749         return;
750
751     CGRect fullViewRect = self.bounds;
752     CGRect visibleRectInContentCoordinates = [self convertRect:fullViewRect toView:_contentView.get()];
753
754     CGRect unobscuredRect = UIEdgeInsetsInsetRect(fullViewRect, _obscuredInsets);
755     CGRect unobscuredRectInContentCoordinates = [self convertRect:unobscuredRect toView:_contentView.get()];
756
757     CGFloat scaleFactor = contentZoomScale(self);
758
759     BOOL isStableState = !(_isChangingObscuredInsetsInteractively || [_scrollView isDragging] || [_scrollView isDecelerating] || [_scrollView isZooming] || [_scrollView isZoomBouncing] || [_scrollView _isAnimatingZoom]);
760     [_contentView didUpdateVisibleRect:visibleRectInContentCoordinates unobscuredRect:unobscuredRectInContentCoordinates scale:scaleFactor inStableState:isStableState];
761 }
762
763 - (void)_keyboardChangedWithInfo:(NSDictionary *)keyboardInfo adjustScrollView:(BOOL)adjustScrollView
764 {
765     _keyboardVerticalOverlap = [[UIPeripheralHost sharedInstance] getVerticalOverlapForView:self usingKeyboardInfo:keyboardInfo];
766     [self _updateVisibleContentRects];
767
768     if (adjustScrollView)
769         [_scrollView _adjustForAutomaticKeyboardInfo:keyboardInfo animated:YES lastAdjustment:&_lastAdjustmentForScroller];
770 }
771
772 - (void)_keyboardWillChangeFrame:(NSNotification *)notification
773 {
774     if ([_contentView isAssistingNode])
775         [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES];
776 }
777
778 - (void)_keyboardDidChangeFrame:(NSNotification *)notification
779 {
780     [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:NO];
781 }
782
783 - (void)_keyboardWillShow:(NSNotification *)notification
784 {
785     if ([_contentView isAssistingNode])
786         [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES];
787 }
788
789 - (void)_keyboardWillHide:(NSNotification *)notification
790 {
791     // Ignore keyboard will hide notifications sent during rotation. They're just there for
792     // backwards compatibility reasons and processing the will hide notification would
793     // temporarily screw up the the unobscured view area.
794     if ([[UIPeripheralHost sharedInstance] rotationState])
795         return;
796
797     [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES];
798 }
799
800 - (void)setAllowsBackForwardNavigationGestures:(BOOL)allowsBackForwardNavigationGestures
801 {
802     if (_allowsBackForwardNavigationGestures == allowsBackForwardNavigationGestures)
803         return;
804
805     _allowsBackForwardNavigationGestures = allowsBackForwardNavigationGestures;
806
807     if (allowsBackForwardNavigationGestures) {
808         if (!_gestureController) {
809             _gestureController = std::make_unique<WebKit::ViewGestureController>(*_page);
810             _gestureController->installSwipeHandler(self, [self scrollView]);
811         }
812     } else
813         _gestureController = nullptr;
814
815     _page->setShouldRecordNavigationSnapshots(allowsBackForwardNavigationGestures);
816 }
817
818 - (BOOL)allowsBackForwardNavigationGestures
819 {
820     return _allowsBackForwardNavigationGestures;
821 }
822
823 #endif
824
825 #pragma mark OS X-specific methods
826
827 #if PLATFORM(MAC)
828
829 - (void)resizeSubviewsWithOldSize:(NSSize)oldSize
830 {
831     [_wkView setFrame:self.bounds];
832 }
833
834 - (void)setAllowsBackForwardNavigationGestures:(BOOL)allowsBackForwardNavigationGestures
835 {
836     [_wkView setAllowsBackForwardNavigationGestures:allowsBackForwardNavigationGestures];
837 }
838
839 - (BOOL)allowsBackForwardNavigationGestures
840 {
841     return [_wkView allowsBackForwardNavigationGestures];
842 }
843
844 - (void)setAllowsMagnification:(BOOL)allowsMagnification
845 {
846     [_wkView setAllowsMagnification:allowsMagnification];
847 }
848
849 - (BOOL)allowsMagnification
850 {
851     return [_wkView allowsMagnification];
852 }
853
854 - (void)setMagnification:(CGFloat)magnification
855 {
856     [_wkView setMagnification:magnification];
857 }
858
859 - (CGFloat)magnification
860 {
861     return [_wkView magnification];
862 }
863
864 - (void)setMagnification:(CGFloat)magnification centeredAtPoint:(CGPoint)point
865 {
866     [_wkView setMagnification:magnification centeredAtPoint:NSPointFromCGPoint(point)];
867 }
868
869 #endif
870
871 @end
872
873 @implementation WKWebView (WKPrivate)
874
875 - (_WKRemoteObjectRegistry *)_remoteObjectRegistry
876 {
877     if (!_remoteObjectRegistry) {
878         _remoteObjectRegistry = adoptNS([[_WKRemoteObjectRegistry alloc] _initWithMessageSender:*_page]);
879         _page->process().context().addMessageReceiver(Messages::RemoteObjectRegistry::messageReceiverName(), _page->pageID(), [_remoteObjectRegistry remoteObjectRegistry]);
880     }
881
882     return _remoteObjectRegistry.get();
883 }
884
885 - (WKBrowsingContextHandle *)_handle
886 {
887     return [[[WKBrowsingContextHandle alloc] _initWithPageID:_page->pageID()] autorelease];
888 }
889
890 - (_WKRenderingProgressEvents)_observedRenderingProgressEvents
891 {
892     return _observedRenderingProgressEvents;
893 }
894
895 - (id <WKHistoryDelegatePrivate>)_historyDelegate
896 {
897     return [_navigationState->historyDelegate().leakRef() autorelease];
898 }
899
900 - (void)_setHistoryDelegate:(id <WKHistoryDelegatePrivate>)historyDelegate
901 {
902     _navigationState->setHistoryDelegate(historyDelegate);
903 }
904
905 - (NSURL *)_unreachableURL
906 {
907     return [NSURL _web_URLWithWTFString:_page->pageLoadState().unreachableURL()];
908 }
909
910 - (void)_loadAlternateHTMLString:(NSString *)string baseURL:(NSURL *)baseURL forUnreachableURL:(NSURL *)unreachableURL
911 {
912     _page->loadAlternateHTMLString(string, [baseURL _web_originalDataAsWTFString], [unreachableURL _web_originalDataAsWTFString]);
913 }
914
915 - (WKNavigation *)_reload
916 {
917     _page->reload(false);
918
919     // FIXME: return a WKNavigation object.
920     return nil;
921 }
922
923 - (NSArray *)_certificateChain
924 {
925     if (WebKit::WebFrameProxy* mainFrame = _page->mainFrame())
926         return mainFrame->certificateInfo() ? (NSArray *)mainFrame->certificateInfo()->certificateInfo().certificateChain() : nil;
927
928     return nil;
929 }
930
931 - (NSURL *)_committedURL
932 {
933     return [NSURL _web_URLWithWTFString:_page->pageLoadState().url()];
934 }
935
936 - (NSString *)_applicationNameForUserAgent
937 {
938     return _page->applicationNameForUserAgent();
939 }
940
941 - (void)_setApplicationNameForUserAgent:(NSString *)applicationNameForUserAgent
942 {
943     _page->setApplicationNameForUserAgent(applicationNameForUserAgent);
944 }
945
946 - (NSString *)_customUserAgent
947 {
948     return _page->customUserAgent();
949 }
950
951 - (void)_setCustomUserAgent:(NSString *)_customUserAgent
952 {
953     _page->setCustomUserAgent(_customUserAgent);
954 }
955
956 - (pid_t)_webProcessIdentifier
957 {
958     return _page->isValid() ? _page->processIdentifier() : 0;
959 }
960
961 - (NSData *)_sessionState
962 {
963     return [wrapper(*_page->sessionStateData(nullptr, nullptr).leakRef()) autorelease];
964 }
965
966 static void releaseNSData(unsigned char*, const void* data)
967 {
968     [(NSData *)data release];
969 }
970
971 - (void)_restoreFromSessionState:(NSData *)sessionState
972 {
973     [sessionState retain];
974     _page->restoreFromSessionStateData(API::Data::createWithoutCopying((const unsigned char*)sessionState.bytes, sessionState.length, releaseNSData, sessionState).get());
975 }
976
977 - (void)_close
978 {
979     _page->close();
980 }
981
982 - (BOOL)_privateBrowsingEnabled
983 {
984     return [_configuration preferences]->_preferences->privateBrowsingEnabled();
985 }
986
987 - (void)_setPrivateBrowsingEnabled:(BOOL)privateBrowsingEnabled
988 {
989     [_configuration preferences]->_preferences->setPrivateBrowsingEnabled(privateBrowsingEnabled);
990 }
991
992 - (BOOL)_allowsRemoteInspection
993 {
994 #if ENABLE(REMOTE_INSPECTOR)
995     return _page->allowsRemoteInspection();
996 #else
997     return NO;
998 #endif
999 }
1000
1001 - (void)_setAllowsRemoteInspection:(BOOL)allow
1002 {
1003 #if ENABLE(REMOTE_INSPECTOR)
1004     _page->setAllowsRemoteInspection(allow);
1005 #endif
1006 }
1007
1008 static inline WebCore::LayoutMilestones layoutMilestones(_WKRenderingProgressEvents events)
1009 {
1010     WebCore::LayoutMilestones milestones = 0;
1011
1012     if (events & _WKRenderingProgressEventFirstLayout)
1013         milestones |= WebCore::DidFirstLayout;
1014
1015     if (events & _WKRenderingProgressEventFirstPaintWithSignificantArea)
1016         milestones |= WebCore::DidHitRelevantRepaintedObjectsAreaThreshold;
1017
1018     return milestones;
1019 }
1020
1021 - (void)_setObservedRenderingProgressEvents:(_WKRenderingProgressEvents)observedRenderingProgressEvents
1022 {
1023     _observedRenderingProgressEvents = observedRenderingProgressEvents;
1024     _page->listenForLayoutMilestones(layoutMilestones(observedRenderingProgressEvents));
1025 }
1026
1027 - (void)_runJavaScriptInMainFrame:(NSString *)scriptString
1028 {
1029     _page->runJavaScriptInMainFrame(scriptString, WebKit::ScriptValueCallback::create([](bool, WebKit::WebSerializedScriptValue*){}));
1030 }
1031
1032 - (_WKPaginationMode)_paginationMode
1033 {
1034     switch (_page->paginationMode()) {
1035     case WebCore::Pagination::Unpaginated:
1036         return _WKPaginationModeUnpaginated;
1037     case WebCore::Pagination::LeftToRightPaginated:
1038         return _WKPaginationModeLeftToRight;
1039     case WebCore::Pagination::RightToLeftPaginated:
1040         return _WKPaginationModeRightToLeft;
1041     case WebCore::Pagination::TopToBottomPaginated:
1042         return _WKPaginationModeTopToBottom;
1043     case WebCore::Pagination::BottomToTopPaginated:
1044         return _WKPaginationModeBottomToTop;
1045     }
1046
1047     ASSERT_NOT_REACHED();
1048     return _WKPaginationModeUnpaginated;
1049 }
1050
1051 - (void)_setPaginationMode:(_WKPaginationMode)paginationMode
1052 {
1053     WebCore::Pagination::Mode mode;
1054     switch (paginationMode) {
1055     case _WKPaginationModeUnpaginated:
1056         mode = WebCore::Pagination::Unpaginated;
1057         break;
1058     case _WKPaginationModeLeftToRight:
1059         mode = WebCore::Pagination::LeftToRightPaginated;
1060         break;
1061     case _WKPaginationModeRightToLeft:
1062         mode = WebCore::Pagination::RightToLeftPaginated;
1063         break;
1064     case _WKPaginationModeTopToBottom:
1065         mode = WebCore::Pagination::TopToBottomPaginated;
1066         break;
1067     case _WKPaginationModeBottomToTop:
1068         mode = WebCore::Pagination::BottomToTopPaginated;
1069         break;
1070     default:
1071         return;
1072     }
1073
1074     _page->setPaginationMode(mode);
1075 }
1076
1077 - (BOOL)_paginationBehavesLikeColumns
1078 {
1079     return _page->paginationBehavesLikeColumns();
1080 }
1081
1082 - (void)_setPaginationBehavesLikeColumns:(BOOL)behavesLikeColumns
1083 {
1084     _page->setPaginationBehavesLikeColumns(behavesLikeColumns);
1085 }
1086
1087 - (CGFloat)_pageLength
1088 {
1089     return _page->pageLength();
1090 }
1091
1092 - (void)_setPageLength:(CGFloat)pageLength
1093 {
1094     _page->setPageLength(pageLength);
1095 }
1096
1097 - (CGFloat)_gapBetweenPages
1098 {
1099     return _page->gapBetweenPages();
1100 }
1101
1102 - (void)_setGapBetweenPages:(CGFloat)gapBetweenPages
1103 {
1104     _page->setGapBetweenPages(gapBetweenPages);
1105 }
1106
1107 - (NSUInteger)_pageCount
1108 {
1109     return _page->pageCount();
1110 }
1111
1112 - (BOOL)_supportsTextZoom
1113 {
1114     return _page->supportsTextZoom();
1115 }
1116
1117 - (double)_textZoomFactor
1118 {
1119     return _page->textZoomFactor();
1120 }
1121
1122 - (void)_setTextZoomFactor:(double)zoomFactor
1123 {
1124     _page->setTextZoomFactor(zoomFactor);
1125 }
1126
1127 - (double)_pageZoomFactor
1128 {
1129     return _page->pageZoomFactor();
1130 }
1131
1132 - (void)_setPageZoomFactor:(double)zoomFactor
1133 {
1134     _page->setPageZoomFactor(zoomFactor);
1135 }
1136
1137 - (id <_WKFindDelegate>)_findDelegate
1138 {
1139     return [static_cast<WebKit::FindClient&>(_page->findClient()).delegate().leakRef() autorelease];
1140 }
1141
1142 - (void)_setFindDelegate:(id<_WKFindDelegate>)findDelegate
1143 {
1144     static_cast<WebKit::FindClient&>(_page->findClient()).setDelegate(findDelegate);
1145 }
1146
1147 static inline WebKit::FindOptions toFindOptions(_WKFindOptions wkFindOptions)
1148 {
1149     unsigned findOptions = 0;
1150
1151     if (wkFindOptions & _WKFindOptionsCaseInsensitive)
1152         findOptions |= WebKit::FindOptionsCaseInsensitive;
1153     if (wkFindOptions & _WKFindOptionsAtWordStarts)
1154         findOptions |= WebKit::FindOptionsAtWordStarts;
1155     if (wkFindOptions & _WKFindOptionsTreatMedialCapitalAsWordStart)
1156         findOptions |= WebKit::FindOptionsTreatMedialCapitalAsWordStart;
1157     if (wkFindOptions & _WKFindOptionsBackwards)
1158         findOptions |= WebKit::FindOptionsBackwards;
1159     if (wkFindOptions & _WKFindOptionsWrapAround)
1160         findOptions |= WebKit::FindOptionsWrapAround;
1161     if (wkFindOptions & _WKFindOptionsShowOverlay)
1162         findOptions |= WebKit::FindOptionsShowOverlay;
1163     if (wkFindOptions & _WKFindOptionsShowFindIndicator)
1164         findOptions |= WebKit::FindOptionsShowFindIndicator;
1165     if (wkFindOptions & _WKFindOptionsShowHighlight)
1166         findOptions |= WebKit::FindOptionsShowHighlight;
1167     if (wkFindOptions & _WKFindOptionsDetermineMatchIndex)
1168         findOptions |= WebKit::FindOptionsDetermineMatchIndex;
1169
1170     return static_cast<WebKit::FindOptions>(findOptions);
1171 }
1172
1173 - (void)_countStringMatches:(NSString *)string options:(_WKFindOptions)options maxCount:(NSUInteger)maxCount
1174 {
1175     _page->countStringMatches(string, toFindOptions(options), maxCount);
1176 }
1177
1178 - (void)_findString:(NSString *)string options:(_WKFindOptions)options maxCount:(NSUInteger)maxCount
1179 {
1180     _page->findString(string, toFindOptions(options), maxCount);
1181 }
1182
1183 - (void)_hideFindUI
1184 {
1185     _page->hideFindUI();
1186 }
1187
1188 - (id <_WKFormDelegate>)_formDelegate
1189 {
1190     return _formDelegate.getAutoreleased();
1191 }
1192
1193 - (void)_setFormDelegate:(id <_WKFormDelegate>)formDelegate
1194 {
1195     _formDelegate = formDelegate;
1196
1197     class FormClient : public API::FormClient {
1198     public:
1199         explicit FormClient(WKWebView *webView)
1200             : m_webView(webView)
1201         {
1202         }
1203
1204         virtual ~FormClient() { }
1205
1206         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
1207         {
1208             if (userData && userData->type() != API::Object::Type::Data) {
1209                 ASSERT(!userData || userData->type() == API::Object::Type::Data);
1210                 m_webView->_page->process().connection()->markCurrentlyDispatchedMessageAsInvalid();
1211                 return false;
1212             }
1213
1214             auto formDelegate = m_webView->_formDelegate.get();
1215
1216             if (![formDelegate respondsToSelector:@selector(_webView:willSubmitFormValues:userObject:submissionHandler:)])
1217                 return false;
1218
1219             auto valueMap = adoptNS([[NSMutableDictionary alloc] initWithCapacity:textFieldValues.size()]);
1220             for (const auto& pair : textFieldValues)
1221                 [valueMap setObject:pair.second forKey:pair.first];
1222
1223             NSObject <NSSecureCoding> *userObject = nil;
1224             if (API::Data* data = static_cast<API::Data*>(userData)) {
1225                 auto nsData = adoptNS([[NSData alloc] initWithBytesNoCopy:const_cast<void*>(static_cast<const void*>(data->bytes())) length:data->size() freeWhenDone:NO]);
1226                 auto unarchiver = adoptNS([[NSKeyedUnarchiver alloc] initForReadingWithData:nsData.get()]);
1227                 [unarchiver setRequiresSecureCoding:YES];
1228                 @try {
1229                     userObject = [unarchiver decodeObjectOfClass:[NSObject class] forKey:@"userObject"];
1230                 } @catch (NSException *exception) {
1231                     LOG_ERROR("Failed to decode user data: %@", exception);
1232                 }
1233             }
1234
1235             [formDelegate _webView:m_webView willSubmitFormValues:valueMap.get() userObject:userObject submissionHandler:^{
1236                 listener->continueSubmission();
1237             }];
1238             return true;
1239         }
1240
1241     private:
1242         WKWebView *m_webView;
1243     };
1244
1245     if (formDelegate)
1246         _page->setFormClient(std::make_unique<FormClient>(self));
1247     else
1248         _page->setFormClient(nullptr);
1249 }
1250
1251 #pragma mark iOS-specific methods
1252
1253 #if PLATFORM(IOS)
1254
1255 - (CGSize)_minimumLayoutSizeOverride
1256 {
1257     ASSERT(_hasStaticMinimumLayoutSize);
1258     return _minimumLayoutSizeOverride;
1259 }
1260
1261 - (void)_setMinimumLayoutSizeOverride:(CGSize)minimumLayoutSizeOverride
1262 {
1263     _hasStaticMinimumLayoutSize = YES;
1264     _minimumLayoutSizeOverride = minimumLayoutSizeOverride;
1265     if (!_isAnimatingResize)
1266         setViewportConfigurationMinimumLayoutSize(*_page, minimumLayoutSizeOverride);
1267 }
1268
1269 - (CGSize)_minimumLayoutSizeOverrideForMinimalUI
1270 {
1271     return _minimumLayoutSizeOverrideForMinimalUI;
1272 }
1273
1274 - (void)_setMinimumLayoutSizeOverrideForMinimalUI:(CGSize)size
1275 {
1276     _minimumLayoutSizeOverrideForMinimalUI = size;
1277     if (!_isAnimatingResize)
1278         _page->setMinimumLayoutSizeForMinimalUI(WebCore::FloatSize(size));
1279 }
1280
1281 - (UIEdgeInsets)_obscuredInsets
1282 {
1283     return _obscuredInsets;
1284 }
1285
1286 - (void)_setObscuredInsets:(UIEdgeInsets)obscuredInsets
1287 {
1288     ASSERT(obscuredInsets.top >= 0);
1289     ASSERT(obscuredInsets.left >= 0);
1290     ASSERT(obscuredInsets.bottom >= 0);
1291     ASSERT(obscuredInsets.right >= 0);
1292
1293     if (UIEdgeInsetsEqualToEdgeInsets(_obscuredInsets, obscuredInsets))
1294         return;
1295
1296     _obscuredInsets = obscuredInsets;
1297     [self _updateVisibleContentRects];
1298 }
1299
1300 - (void)_setBackgroundExtendsBeyondPage:(BOOL)backgroundExtends
1301 {
1302     _page->setBackgroundExtendsBeyondPage(backgroundExtends);
1303 }
1304
1305 - (BOOL)_backgroundExtendsBeyondPage
1306 {
1307     return _page->backgroundExtendsBeyondPage();
1308 }
1309
1310 - (void)_setExtendedBackgroundExclusionInsets:(UIEdgeInsets)extendedBackgroundExclusionInsets
1311 {
1312     if (UIEdgeInsetsEqualToEdgeInsets(extendedBackgroundExclusionInsets, _extendedBackgroundExclusionInsets))
1313         return;
1314
1315     _extendedBackgroundExclusionInsets = extendedBackgroundExclusionInsets;
1316     _extendedBackgroundLayer.frame = UIEdgeInsetsInsetRect(self.bounds, _extendedBackgroundExclusionInsets);
1317 }
1318
1319 - (UIEdgeInsets)_extendedBackgroundExclusionInsets
1320 {
1321     return _extendedBackgroundExclusionInsets;
1322 }
1323
1324 - (void)_beginInteractiveObscuredInsetsChange
1325 {
1326     ASSERT(!_isChangingObscuredInsetsInteractively);
1327     _isChangingObscuredInsetsInteractively = YES;
1328 }
1329
1330 - (void)_endInteractiveObscuredInsetsChange
1331 {
1332     ASSERT(_isChangingObscuredInsetsInteractively);
1333     _isChangingObscuredInsetsInteractively = NO;
1334     [self _updateVisibleContentRects];
1335 }
1336
1337 - (void)_beginAnimatedResizeWithUpdates:(void (^)(void))updateBlock
1338 {
1339     _isAnimatingResize = YES;
1340     _resizeAnimationTransformAdjustments = CATransform3DIdentity;
1341
1342     _resizeAnimationView = adoptNS([[UIView alloc] init]);
1343     [_scrollView addSubview:_resizeAnimationView.get()];
1344     [_resizeAnimationView addSubview:_contentView.get()];
1345
1346     CGRect oldBounds = self.bounds;
1347     CGSize oldMinimumLayoutSize = oldBounds.size;
1348     if (_hasStaticMinimumLayoutSize)
1349         oldMinimumLayoutSize = _minimumLayoutSizeOverride;
1350     WebCore::FloatRect oldUnobscuredContentRect = _page->unobscuredContentRect();
1351
1352     updateBlock();
1353
1354     if (_customContentView)
1355         return;
1356
1357     CGRect newBounds = self.bounds;
1358     CGSize newMinimumLayoutSize = newBounds.size;
1359     if (_hasStaticMinimumLayoutSize)
1360         newMinimumLayoutSize = _minimumLayoutSizeOverride;
1361
1362     if (CGSizeEqualToSize(newMinimumLayoutSize, oldMinimumLayoutSize) && CGRectEqualToRect(newBounds, oldBounds))
1363         return;
1364
1365     CGSize contentSizeInContentViewCoordinates = [_contentView bounds].size;
1366     [_scrollView setMinimumZoomScale:std::min(newBounds.size.width / contentSizeInContentViewCoordinates.width, [_scrollView minimumZoomScale])];
1367     [_scrollView setMaximumZoomScale:std::max(newBounds.size.width / contentSizeInContentViewCoordinates.width, [_scrollView maximumZoomScale])];
1368
1369     // Compute the new scale to keep the current content width in the scrollview.
1370     CGFloat currentScale = contentZoomScale(self);
1371     CGFloat oldWebViewWidthInContentViewCoordinates = oldBounds.size.width / currentScale;
1372     CGFloat visibleContentViewWidthInContentCoordinates = std::min(contentSizeInContentViewCoordinates.width, oldWebViewWidthInContentViewCoordinates);
1373     CGFloat targetScale = newBounds.size.width / visibleContentViewWidthInContentCoordinates;
1374     CGFloat resizeAnimationViewAnimationScale = targetScale / currentScale;
1375     [_resizeAnimationView setTransform:CGAffineTransformMakeScale(resizeAnimationViewAnimationScale, resizeAnimationViewAnimationScale)];
1376
1377     // Compute a new position to keep the content centered.
1378     CGPoint originalContentCenter = oldUnobscuredContentRect.center();
1379     CGPoint originalContentCenterInSelfCoordinates = [self convertPoint:originalContentCenter fromView:_contentView.get()];
1380     CGRect futureUnobscuredRectInSelfCoordinates = UIEdgeInsetsInsetRect(newBounds, _obscuredInsets);
1381     CGPoint futureUnobscuredRectCenterInSelfCoordinates = CGPointMake(futureUnobscuredRectInSelfCoordinates.origin.x + futureUnobscuredRectInSelfCoordinates.size.width / 2, futureUnobscuredRectInSelfCoordinates.origin.y + futureUnobscuredRectInSelfCoordinates.size.height / 2);
1382
1383     CGPoint originalContentOffset = [_scrollView contentOffset];
1384     CGPoint contentOffset = originalContentOffset;
1385     contentOffset.x += (originalContentCenterInSelfCoordinates.x - futureUnobscuredRectCenterInSelfCoordinates.x);
1386     contentOffset.y += (originalContentCenterInSelfCoordinates.y - futureUnobscuredRectCenterInSelfCoordinates.y);
1387
1388     // Limit the new offset within the scrollview, we do not want to rubber band programmatically.
1389     CGSize futureContentSizeInSelfCoordinates = CGSizeMake(contentSizeInContentViewCoordinates.width * targetScale, contentSizeInContentViewCoordinates.height * targetScale);
1390     CGFloat maxHorizontalOffset = futureContentSizeInSelfCoordinates.width - newBounds.size.width + _obscuredInsets.right;
1391     contentOffset.x = std::min(contentOffset.x, maxHorizontalOffset);
1392     CGFloat maxVerticalOffset = futureContentSizeInSelfCoordinates.height - newBounds.size.height + _obscuredInsets.bottom;
1393     contentOffset.y = std::min(contentOffset.y, maxVerticalOffset);
1394
1395     contentOffset.x = std::max(contentOffset.x, -_obscuredInsets.left);
1396     contentOffset.y = std::max(contentOffset.y, -_obscuredInsets.top);
1397
1398     // Make the top/bottom edges "sticky" within 1 pixel.
1399     if (oldUnobscuredContentRect.maxY() > contentSizeInContentViewCoordinates.height - 1)
1400         contentOffset.y = maxVerticalOffset;
1401     if (oldUnobscuredContentRect.y() < 1)
1402         contentOffset.y = -_obscuredInsets.top;
1403
1404     // FIXME: if we have content centered after double tap to zoom, we should also try to keep that rect in view.
1405     [_scrollView setContentOffset:contentOffset];
1406
1407     CGRect visibleRectInContentCoordinates = [self convertRect:newBounds toView:_contentView.get()];
1408
1409     CGRect unobscuredRect = UIEdgeInsetsInsetRect(newBounds, _obscuredInsets);
1410     CGRect unobscuredRectInContentCoordinates = [self convertRect:unobscuredRect toView:_contentView.get()];
1411
1412     _page->dynamicViewportSizeUpdate(WebCore::FloatSize(newMinimumLayoutSize.width, newMinimumLayoutSize.height), visibleRectInContentCoordinates, unobscuredRectInContentCoordinates, targetScale);
1413 }
1414
1415 - (void)_endAnimatedResize
1416 {
1417     if (!_customContentView) {
1418         [_scrollView addSubview:_contentView.get()];
1419
1420         CALayer *contentViewLayer = [_contentView layer];
1421         CATransform3D resizeAnimationTransformAdjustements = _resizeAnimationTransformAdjustments;
1422         CGFloat adjustmentScale = resizeAnimationTransformAdjustements.m11;
1423         contentViewLayer.sublayerTransform = CATransform3DIdentity;
1424
1425         CGFloat animatingScaleTarget = [[_resizeAnimationView layer] transform].m11;
1426         CGFloat currentScale = [[_resizeAnimationView layer] transform].m11 * [[_contentView layer] transform].m11;
1427         CGPoint currentScrollOffset = [_scrollView contentOffset];
1428         [_scrollView setZoomScale:adjustmentScale * currentScale];
1429
1430         double horizontalScrollAdjustement = _resizeAnimationTransformAdjustments.m41 * animatingScaleTarget;
1431         double verticalScrollAdjustment = _resizeAnimationTransformAdjustments.m42 * animatingScaleTarget;
1432
1433         [_scrollView setContentOffset:CGPointMake(currentScrollOffset.x - horizontalScrollAdjustement, currentScrollOffset.y - verticalScrollAdjustment)];
1434
1435         [_resizeAnimationView removeFromSuperview];
1436         _resizeAnimationView = nil;
1437     }
1438
1439     _resizeAnimationTransformAdjustments = CATransform3DIdentity;
1440     _isAnimatingResize = NO;
1441     [self _updateVisibleContentRects];
1442 }
1443
1444 - (void)_showInspectorIndication
1445 {
1446     [_contentView setShowingInspectorIndication:YES];
1447 }
1448
1449 - (void)_hideInspectorIndication
1450 {
1451     [_contentView setShowingInspectorIndication:NO];
1452 }
1453
1454 - (void)_snapshotRect:(CGRect)rectInViewCoordinates intoImageOfWidth:(CGFloat)imageWidth completionHandler:(void(^)(CGImageRef))completionHandler
1455 {
1456     CGRect snapshotRectInContentCoordinates = [self convertRect:rectInViewCoordinates toView:_contentView.get()];
1457     CGFloat imageHeight = imageWidth / snapshotRectInContentCoordinates.size.width * snapshotRectInContentCoordinates.size.height;
1458     CGSize imageSize = CGSizeMake(imageWidth, imageHeight);
1459
1460     void(^copiedCompletionHandler)(CGImageRef) = [completionHandler copy];
1461     _page->takeSnapshot(WebCore::enclosingIntRect(snapshotRectInContentCoordinates), WebCore::expandedIntSize(WebCore::FloatSize(imageSize)), WebKit::SnapshotOptionsExcludeDeviceScaleFactor, [copiedCompletionHandler](bool, const WebKit::ShareableBitmap::Handle& imageHandle) {
1462         if (imageHandle.isNull()) {
1463             copiedCompletionHandler(nullptr);
1464             [copiedCompletionHandler release];
1465             return;
1466         }
1467
1468         RefPtr<WebKit::ShareableBitmap> bitmap = WebKit::ShareableBitmap::create(imageHandle, WebKit::SharedMemory::ReadOnly);
1469
1470         if (!bitmap) {
1471             copiedCompletionHandler(nullptr);
1472             [copiedCompletionHandler release];
1473             return;
1474         }
1475
1476         RetainPtr<CGImageRef> cgImage;
1477         cgImage = bitmap->makeCGImage();
1478         copiedCompletionHandler(cgImage.get());
1479         [copiedCompletionHandler release];
1480     });
1481 }
1482
1483 - (UIView *)_viewForFindUI
1484 {
1485     return [self viewForZoomingInScrollView:[self scrollView]];
1486 }
1487
1488 - (BOOL)_isDisplayingPDF
1489 {
1490     return [_customContentView isKindOfClass:[WKPDFView class]];
1491 }
1492
1493 - (NSData *)_dataForDisplayedPDF
1494 {
1495     if (![self _isDisplayingPDF])
1496         return nil;
1497     return [(WKPDFView *)_customContentView.get() documentData];
1498 }
1499
1500 - (NSString *)_suggestedFilenameForDisplayedPDF
1501 {
1502     if (![self _isDisplayingPDF])
1503         return nil;
1504     return [(WKPDFView *)_customContentView.get() suggestedFilename];
1505 }
1506
1507 // FIXME: Remove this once nobody uses it.
1508 - (NSURL *)activeURL
1509 {
1510     return self.URL;
1511 }
1512
1513 #else
1514
1515 #pragma mark - OS X-specific methods
1516
1517 - (NSColor *)_pageExtendedBackgroundColor
1518 {
1519     WebCore::Color color = _page->pageExtendedBackgroundColor();
1520     if (!color.isValid())
1521         return nil;
1522
1523     return nsColor(color);
1524 }
1525
1526 - (BOOL)_drawsTransparentBackground
1527 {
1528     return _page->drawsTransparentBackground();
1529 }
1530
1531 - (void)_setDrawsTransparentBackground:(BOOL)drawsTransparentBackground
1532 {
1533     _page->setDrawsTransparentBackground(drawsTransparentBackground);
1534 }
1535
1536 - (void)_setTopContentInset:(CGFloat)contentInset
1537 {
1538     _page->setTopContentInset(contentInset);
1539 }
1540
1541 - (CGFloat)_topContentInset
1542 {
1543     return _page->topContentInset();
1544 }
1545
1546 #endif
1547
1548 @end
1549
1550 #if !TARGET_OS_IPHONE
1551
1552 @implementation WKWebView (WKIBActions)
1553
1554 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
1555 {
1556     SEL action = item.action;
1557
1558     if (action == @selector(goBack:))
1559         return !!_page->backForwardList().backItem();
1560
1561     if (action == @selector(goForward:))
1562         return !!_page->backForwardList().forwardItem();
1563
1564     if (action == @selector(stopLoading:)) {
1565         // FIXME: Return no if we're stopped.
1566         return YES;
1567     }
1568
1569     if (action == @selector(reload:) || action == @selector(reloadFromOrigin:)) {
1570         // FIXME: Return no if we're loading.
1571         return YES;
1572     }
1573
1574     return NO;
1575 }
1576
1577 - (IBAction)goBack:(id)sender
1578 {
1579     [self goBack];
1580 }
1581
1582 - (IBAction)goForward:(id)sender
1583 {
1584     [self goForward];
1585 }
1586
1587 - (IBAction)reload:(id)sender
1588 {
1589     [self reload];
1590 }
1591
1592 - (IBAction)reloadFromOrigin:(id)sender
1593 {
1594     [self reloadFromOrigin];
1595 }
1596
1597 - (IBAction)stopLoading:(id)sender
1598 {
1599     _page->stopLoading();
1600 }
1601
1602 @end
1603
1604 #endif
1605
1606 #endif // WK_API_ENABLED