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