c655b0819fcebfb10b0c533f90ebfe15ea64a4ca
[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 "NavigationState.h"
32 #import "RemoteLayerTreeTransaction.h"
33 #import "RemoteObjectRegistry.h"
34 #import "RemoteObjectRegistryMessages.h"
35 #import "UIClient.h"
36 #import "ViewGestureController.h"
37 #import "WKBackForwardListInternal.h"
38 #import "WKBackForwardListItemInternal.h"
39 #import "WKBrowsingContextHandleInternal.h"
40 #import "WKHistoryDelegatePrivate.h"
41 #import "WKNSData.h"
42 #import "WKNSURLExtras.h"
43 #import "WKNavigationDelegate.h"
44 #import "WKNavigationInternal.h"
45 #import "WKPreferencesInternal.h"
46 #import "WKProcessPoolInternal.h"
47 #import "WKRemoteObjectRegistryInternal.h"
48 #import "WKUIDelegate.h"
49 #import "WKWebViewConfigurationInternal.h"
50 #import "WKWebViewContentProvider.h"
51 #import "WebBackForwardList.h"
52 #import "WebCertificateInfo.h"
53 #import "WebContext.h"
54 #import "WebPageGroup.h"
55 #import "WebPageProxy.h"
56 #import "WebProcessProxy.h"
57 #import "_WKVisitedLinkProviderInternal.h"
58 #import <wtf/RetainPtr.h>
59
60 #if PLATFORM(IOS)
61 #import "WKScrollView.h"
62 #import "WKWebViewContentProviderRegistry.h"
63 #import <UIKit/UIPeripheralHost_Private.h>
64
65 @interface UIScrollView (UIScrollViewInternal)
66 - (void)_adjustForAutomaticKeyboardInfo:(NSDictionary*)info animated:(BOOL)animated lastAdjustment:(CGFloat*)lastAdjustment;
67 @end
68
69 @interface UIPeripheralHost(UIKitInternal)
70 - (CGFloat)getVerticalOverlapForView:(UIView *)view usingKeyboardInfo:(NSDictionary *)info;
71 @end
72
73 #endif
74
75 #if PLATFORM(MAC)
76 #import "WKViewInternal.h"
77 #import <WebCore/ColorMac.h>
78 #endif
79
80 @implementation WKWebView {
81     RetainPtr<WKWebViewConfiguration> _configuration;
82     std::unique_ptr<WebKit::NavigationState> _navigationState;
83
84     RetainPtr<WKRemoteObjectRegistry> _remoteObjectRegistry;
85     _WKRenderingProgressEvents _observedRenderingProgressEvents;
86
87 #if PLATFORM(IOS)
88     RetainPtr<WKScrollView> _scrollView;
89     RetainPtr<WKContentView> _contentView;
90
91     BOOL _isWaitingForNewLayerTreeAfterDidCommitLoad;
92     BOOL _hasStaticMinimumLayoutSize;
93     CGSize _minimumLayoutSizeOverride;
94
95     UIEdgeInsets _obscuredInsets;
96     bool _isChangingObscuredInsetsInteractively;
97     CGFloat _lastAdjustmentForScroller;
98     CGFloat _keyboardVerticalOverlap;
99
100     std::unique_ptr<WebKit::ViewGestureController> _gestureController;
101     BOOL _allowsBackForwardNavigationGestures;
102
103     RetainPtr<UIView <WKWebViewContentProvider>> _customContentView;
104 #endif
105 #if PLATFORM(MAC)
106     RetainPtr<WKView> _wkView;
107 #endif
108 }
109
110 - (instancetype)initWithFrame:(CGRect)frame
111 {
112     return [self initWithFrame:frame configuration:adoptNS([[WKWebViewConfiguration alloc] init]).get()];
113 }
114
115 - (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration
116 {
117     if (!(self = [super initWithFrame:frame]))
118         return nil;
119
120     _configuration = adoptNS([configuration copy]);
121
122     if (WKWebView *relatedWebView = [_configuration _relatedWebView]) {
123         WKProcessPool *processPool = [_configuration processPool];
124         WKProcessPool *relatedWebViewProcessPool = [relatedWebView->_configuration processPool];
125         if (processPool && processPool != relatedWebViewProcessPool)
126             [NSException raise:NSInvalidArgumentException format:@"Related web view %@ has process pool %@ but configuration specifies a different process pool %@", relatedWebView, relatedWebViewProcessPool, configuration.processPool];
127
128         [_configuration setProcessPool:relatedWebViewProcessPool];
129     }
130
131     if (![_configuration processPool])
132         [_configuration setProcessPool:adoptNS([[WKProcessPool alloc] init]).get()];
133
134     if (![_configuration preferences])
135         [_configuration setPreferences:adoptNS([[WKPreferences alloc] init]).get()];
136
137     if (![_configuration _visitedLinkProvider])
138         [_configuration _setVisitedLinkProvider:adoptNS([[_WKVisitedLinkProvider alloc] init]).get()];
139
140 #if PLATFORM(IOS)
141     if (![_configuration _contentProviderRegistry])
142         [_configuration _setContentProviderRegistry:adoptNS([[WKWebViewContentProviderRegistry alloc] init]).get()];
143 #endif
144
145     CGRect bounds = self.bounds;
146
147     WebKit::WebContext& context = *[_configuration processPool]->_context;
148
149     WebKit::WebPageConfiguration webPageConfiguration;
150     webPageConfiguration.preferences = [_configuration preferences]->_preferences.get();
151     if (WKWebView *relatedWebView = [_configuration _relatedWebView])
152         webPageConfiguration.relatedPage = relatedWebView->_page.get();
153
154     webPageConfiguration.visitedLinkProvider = [_configuration _visitedLinkProvider]->_visitedLinkProvider.get();
155
156     RefPtr<WebKit::WebPageGroup> pageGroup;
157     NSString *groupIdentifier = configuration._groupIdentifier;
158     if (groupIdentifier.length) {
159         pageGroup = WebKit::WebPageGroup::create(configuration._groupIdentifier);
160         webPageConfiguration.pageGroup = pageGroup.get();
161     }
162
163 #if PLATFORM(IOS)
164     _scrollView = adoptNS([[WKScrollView alloc] initWithFrame:bounds]);
165     [_scrollView setInternalDelegate:self];
166     [_scrollView setBouncesZoom:YES];
167
168     [self addSubview:_scrollView.get()];
169
170     _contentView = adoptNS([[WKContentView alloc] initWithFrame:bounds context:context configuration:std::move(webPageConfiguration) webView:self]);
171     _page = [_contentView page];
172     [_contentView layer].anchorPoint = CGPointZero;
173     [_contentView setFrame:bounds];
174     [_scrollView addSubview:_contentView.get()];
175
176     [self _frameOrBoundsChanged];
177
178     NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
179     [center addObserver:self selector:@selector(_keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];
180     [center addObserver:self selector:@selector(_keyboardDidChangeFrame:) name:UIKeyboardDidChangeFrameNotification object:nil];
181     [center addObserver:self selector:@selector(_keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
182     [center addObserver:self selector:@selector(_keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
183
184     [[_configuration _contentProviderRegistry] addPage:*_page];
185 #endif
186
187 #if PLATFORM(MAC)
188     _wkView = [[WKView alloc] initWithFrame:bounds context:context configuration:std::move(webPageConfiguration)];
189     [self addSubview:_wkView.get()];
190     _page = WebKit::toImpl([_wkView pageRef]);
191 #endif
192
193     _navigationState = std::make_unique<WebKit::NavigationState>(self);
194     _page->setPolicyClient(_navigationState->createPolicyClient());
195     _page->setLoaderClient(_navigationState->createLoaderClient());
196
197     _page->setUIClient(std::make_unique<WebKit::UIClient>(self));
198
199     return self;
200 }
201
202 - (void)dealloc
203 {
204     [_remoteObjectRegistry _invalidate];
205 #if PLATFORM(IOS)
206     [[_configuration _contentProviderRegistry] removePage:*_page];
207     [[NSNotificationCenter defaultCenter] removeObserver:self];
208 #endif
209
210     [super dealloc];
211 }
212
213 - (WKWebViewConfiguration *)configuration
214 {
215     return [[_configuration copy] autorelease];
216 }
217
218 - (WKBackForwardList *)backForwardList
219 {
220     return wrapper(_page->backForwardList());
221 }
222
223 - (id <WKNavigationDelegate>)navigationDelegate
224 {
225     return [_navigationState->navigationDelegate().leakRef() autorelease];
226 }
227
228 - (void)setNavigationDelegate:(id <WKNavigationDelegate>)navigationDelegate
229 {
230     _navigationState->setNavigationDelegate(navigationDelegate);
231 }
232
233 - (id <WKUIDelegate>)UIDelegate
234 {
235     return [static_cast<WebKit::UIClient&>(_page->uiClient()).delegate().leakRef() autorelease];
236 }
237
238 - (void)setUIDelegate:(id<WKUIDelegate>)UIDelegate
239 {
240     static_cast<WebKit::UIClient&>(_page->uiClient()).setDelegate(UIDelegate);
241 }
242
243 - (WKNavigation *)loadRequest:(NSURLRequest *)request
244 {
245     uint64_t navigationID = _page->loadRequest(request);
246     auto navigation = _navigationState->createLoadRequestNavigation(navigationID, request);
247
248     return [navigation.leakRef() autorelease];
249 }
250
251 - (WKNavigation *)goToBackForwardListItem:(WKBackForwardListItem *)item
252 {
253     _page->goToBackForwardItem(&item._item);
254
255     // FIXME: return a WKNavigation object.
256     return nil;
257 }
258
259 - (IBAction)stopLoading:(id)sender
260 {
261     _page->stopLoading();
262 }
263
264 - (NSString *)title
265 {
266     return _page->pageLoadState().title();
267 }
268
269 - (NSURL *)activeURL
270 {
271     return [NSURL _web_URLWithWTFString:_page->pageLoadState().activeURL()];
272 }
273
274 - (BOOL)isLoading
275 {
276     return _page->pageLoadState().isLoading();
277 }
278
279 - (double)estimatedProgress
280 {
281     return _page->pageLoadState().estimatedProgress();
282 }
283
284 - (BOOL)hasOnlySecureContent
285 {
286     return _page->pageLoadState().hasOnlySecureContent();
287 }
288
289 // FIXME: This should be KVO compliant.
290 - (BOOL)canGoBack
291 {
292     return !!_page->backForwardList().backItem();
293 }
294
295 // FIXME: This should be KVO compliant.
296 - (BOOL)canGoForward
297 {
298     return !!_page->backForwardList().forwardItem();
299 }
300
301 // FIXME: This should return a WKNavigation object.
302 - (void)goBack
303 {
304     _page->goBack();
305 }
306
307 // FIXME: This should return a WKNavigation object.
308 - (void)goForward
309 {
310     _page->goForward();
311 }
312
313 #pragma mark iOS-specific methods
314
315 #if PLATFORM(IOS)
316 - (void)setFrame:(CGRect)frame
317 {
318     CGRect oldFrame = self.frame;
319     [super setFrame:frame];
320
321     if (!CGSizeEqualToSize(oldFrame.size, frame.size))
322         [self _frameOrBoundsChanged];
323 }
324
325 - (void)setBounds:(CGRect)bounds
326 {
327     CGRect oldBounds = self.bounds;
328     [super setBounds:bounds];
329
330     if (!CGSizeEqualToSize(oldBounds.size, bounds.size))
331         [self _frameOrBoundsChanged];
332 }
333
334 - (UIScrollView *)scrollView
335 {
336     return _scrollView.get();
337 }
338
339 - (WKBrowsingContextController *)browsingContextController
340 {
341     return [_contentView browsingContextController];
342 }
343
344 - (void)_setHasCustomContentView:(BOOL)pageHasCustomContentView loadedMIMEType:(const WTF::String&)mimeType
345 {
346     if (pageHasCustomContentView) {
347         [_customContentView removeFromSuperview];
348
349         Class representationClass = [[_configuration _contentProviderRegistry] providerForMIMEType:mimeType];
350         ASSERT(representationClass);
351         _customContentView = adoptNS([[representationClass alloc] init]);
352
353         [_contentView removeFromSuperview];
354         [_scrollView addSubview:_customContentView.get()];
355
356         [_customContentView web_setMinimumSize:self.bounds.size];
357         [_customContentView web_setScrollView:_scrollView.get()];
358     } else if (_customContentView) {
359         [_customContentView removeFromSuperview];
360         _customContentView = nullptr;
361
362         [_scrollView addSubview:_contentView.get()];
363         [_scrollView setContentSize:[_contentView frame].size];
364     }
365 }
366
367 - (void)_didFinishLoadingDataForCustomContentProviderWithSuggestedFilename:(const String&)suggestedFilename data:(NSData *)data
368 {
369     ASSERT(_customContentView);
370     [_customContentView web_setContentProviderData:data];
371 }
372
373 - (void)_didCommitLoadForMainFrame
374 {
375     _isWaitingForNewLayerTreeAfterDidCommitLoad = YES;
376 }
377
378 // This is a convenience method that will convert _page->pageExtendedBackgroundColor() from a WebCore::Color to a UIColor *.
379 - (UIColor *)pageExtendedBackgroundColor
380 {
381     WebCore::Color color = _page->pageExtendedBackgroundColor();
382     if (!color.isValid())
383         return nil;
384
385     return [UIColor colorWithRed:(color.red() / 255.0) green:(color.green() / 255.0) blue:(color.blue() / 255.0) alpha:(color.alpha() / 255.0)];
386 }
387
388 - (void)_updateScrollViewBackground
389 {
390     UIColor *pageExtendedBackgroundColor = [self pageExtendedBackgroundColor];
391
392     if ([_scrollView zoomScale] < [_scrollView minimumZoomScale]) {
393         CGFloat slope = 12;
394         CGFloat opacity = std::max(1 - slope * ([_scrollView minimumZoomScale] - [_scrollView zoomScale]), static_cast<CGFloat>(0));
395         pageExtendedBackgroundColor = [pageExtendedBackgroundColor colorWithAlphaComponent:opacity];
396     }
397
398     [_scrollView setBackgroundColor:pageExtendedBackgroundColor];
399 }
400
401 - (void)_didCommitLayerTree:(const WebKit::RemoteLayerTreeTransaction&)layerTreeTransaction
402 {
403     ASSERT(!_customContentView);
404
405     [_scrollView setContentSize:[_contentView frame].size];
406     [_scrollView setMinimumZoomScale:layerTreeTransaction.minimumScaleFactor()];
407     [_scrollView setMaximumZoomScale:layerTreeTransaction.maximumScaleFactor()];
408     [_scrollView setZoomEnabled:layerTreeTransaction.allowsUserScaling()];
409     if (!layerTreeTransaction.scaleWasSetByUIProcess() && ![_scrollView isZooming] && ![_scrollView isZoomBouncing] && ![_scrollView _isAnimatingZoom])
410         [_scrollView setZoomScale:layerTreeTransaction.pageScaleFactor()];
411     
412     [self _updateScrollViewBackground];
413
414     if (_gestureController)
415         _gestureController->setRenderTreeSize(layerTreeTransaction.renderTreeSize());
416
417     if (_isWaitingForNewLayerTreeAfterDidCommitLoad) {
418         UIEdgeInsets inset = [_scrollView contentInset];
419         [_scrollView setContentOffset:CGPointMake(-inset.left, -inset.top)];
420         _isWaitingForNewLayerTreeAfterDidCommitLoad = NO;
421     }
422
423 }
424
425 - (RetainPtr<CGImageRef>)_takeViewSnapshot
426 {
427     // FIXME: We should be able to use acquire an IOSurface directly, instead of going to CGImage here and back in ViewSnapshotStore.
428     UIGraphicsBeginImageContextWithOptions(self.bounds.size, YES, self.window.screen.scale);
429     [self drawViewHierarchyInRect:[self bounds] afterScreenUpdates:NO];
430     UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
431     UIGraphicsEndImageContext();
432     return image.CGImage;
433 }
434
435 - (void)_zoomToPoint:(WebCore::FloatPoint)point atScale:(double)scale
436 {
437     double maximumZoomDuration = 0.4;
438     double minimumZoomDuration = 0.1;
439     double zoomDurationFactor = 0.3;
440
441     CFTimeInterval duration = std::min(fabs(log([_scrollView zoomScale]) - log(scale)) * zoomDurationFactor + minimumZoomDuration, maximumZoomDuration);
442
443     if (scale != [_scrollView zoomScale])
444         [_contentView willStartUserTriggeredZoom];
445
446     [_scrollView _zoomToCenter:point scale:scale duration:duration];
447 }
448
449 - (void)_zoomToRect:(WebCore::FloatRect)targetRect atScale:(double)scale origin:(WebCore::FloatPoint)origin
450 {
451     WebCore::FloatSize unobscuredContentSize([self _contentRectForUserInteraction].size);
452     WebCore::FloatSize targetRectSizeAfterZoom = targetRect.size();
453     targetRectSizeAfterZoom.scale(scale);
454
455     // Center the target rect in the scroll view.
456     // If the target doesn't fit in the scroll view, center on the gesture location instead.
457     WebCore::FloatPoint zoomCenter = targetRect.center();
458
459     if (targetRectSizeAfterZoom.width() > unobscuredContentSize.width())
460         zoomCenter.setX(origin.x());
461     if (targetRectSizeAfterZoom.height() > unobscuredContentSize.height())
462         zoomCenter.setY(origin.y());
463
464     [self _zoomToPoint:zoomCenter atScale:scale];
465 }
466
467 static WebCore::FloatPoint constrainContentOffset(WebCore::FloatPoint contentOffset, WebCore::FloatSize contentSize, WebCore::FloatSize unobscuredContentSize)
468 {
469     WebCore::FloatSize maximumContentOffset = contentSize - unobscuredContentSize;
470     contentOffset = contentOffset.shrunkTo(WebCore::FloatPoint(maximumContentOffset.width(), maximumContentOffset.height()));
471     contentOffset = contentOffset.expandedTo(WebCore::FloatPoint());
472     return contentOffset;
473 }
474
475 - (BOOL)_scrollToRect:(WebCore::FloatRect)targetRect origin:(WebCore::FloatPoint)origin minimumScrollDistance:(float)minimumScrollDistance
476 {
477     WebCore::FloatRect unobscuredContentRect([self _contentRectForUserInteraction]);
478     WebCore::FloatPoint unobscuredContentOffset = unobscuredContentRect.location();
479     WebCore::FloatSize contentSize([_contentView bounds].size);
480
481     // Center the target rect in the scroll view.
482     // If the target doesn't fit in the scroll view, center on the gesture location instead.
483     WebCore::FloatPoint newUnobscuredContentOffset;
484     if (targetRect.width() <= unobscuredContentRect.width())
485         newUnobscuredContentOffset.setX(targetRect.x() - (unobscuredContentRect.width() - targetRect.width()) / 2);
486     else
487         newUnobscuredContentOffset.setX(origin.x() - unobscuredContentRect.width() / 2);
488     if (targetRect.height() <= unobscuredContentRect.height())
489         newUnobscuredContentOffset.setY(targetRect.y() - (unobscuredContentRect.height() - targetRect.height()) / 2);
490     else
491         newUnobscuredContentOffset.setY(origin.y() - unobscuredContentRect.height() / 2);
492     newUnobscuredContentOffset = constrainContentOffset(newUnobscuredContentOffset, contentSize, unobscuredContentRect.size());
493
494     if (unobscuredContentOffset == newUnobscuredContentOffset) {
495         if (targetRect.width() > unobscuredContentRect.width())
496             newUnobscuredContentOffset.setX(origin.x() - unobscuredContentRect.width() / 2);
497         if (targetRect.height() > unobscuredContentRect.height())
498             newUnobscuredContentOffset.setY(origin.y() - unobscuredContentRect.height() / 2);
499         newUnobscuredContentOffset = constrainContentOffset(newUnobscuredContentOffset, contentSize, unobscuredContentRect.size());
500     }
501
502     WebCore::FloatSize scrollViewOffsetDelta = newUnobscuredContentOffset - unobscuredContentOffset;
503     scrollViewOffsetDelta.scale([_scrollView zoomScale]);
504
505     float scrollDistance = scrollViewOffsetDelta.diagonalLength();
506     if (scrollDistance < minimumScrollDistance)
507         return false;
508
509     [_scrollView setContentOffset:([_scrollView contentOffset] + scrollViewOffsetDelta) animated:YES];
510     return true;
511 }
512
513 - (void)_zoomOutWithOrigin:(WebCore::FloatPoint)origin
514 {
515     [self _zoomToPoint:origin atScale:[_scrollView minimumZoomScale]];
516 }
517
518 - (BOOL)_zoomToRect:(WebCore::FloatRect)targetRect withOrigin:(WebCore::FloatPoint)origin fitEntireRect:(BOOL)fitEntireRect minimumScale:(double)minimumScale maximumScale:(double)maximumScale minimumScrollDistance:(float)minimumScrollDistance
519 {
520     const float maximumScaleFactorDeltaForPanScroll = 0.02;
521
522     double currentScale = [_scrollView zoomScale];
523
524     WebCore::FloatSize unobscuredContentSize([self _contentRectForUserInteraction].size);
525     double horizontalScale = unobscuredContentSize.width() * currentScale / targetRect.width();
526     double verticalScale = unobscuredContentSize.height() * currentScale / targetRect.height();
527
528     horizontalScale = std::min(std::max(horizontalScale, minimumScale), maximumScale);
529     verticalScale = std::min(std::max(verticalScale, minimumScale), maximumScale);
530
531     double targetScale = fitEntireRect ? std::min(horizontalScale, verticalScale) : horizontalScale;
532     if (fabs(targetScale - currentScale) < maximumScaleFactorDeltaForPanScroll) {
533         if ([self _scrollToRect:targetRect origin:origin minimumScrollDistance:minimumScrollDistance])
534             return true;
535     } else if (targetScale != currentScale) {
536         [self _zoomToRect:targetRect atScale:targetScale origin:origin];
537         return true;
538     }
539     
540     return false;
541 }
542
543 #pragma mark - UIScrollViewDelegate
544
545 - (BOOL)usesStandardContentView
546 {
547     return !_customContentView;
548 }
549
550 - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
551 {
552     ASSERT(_scrollView == scrollView);
553
554     if (_customContentView)
555         return _customContentView.get();
556
557     return _contentView.get();
558 }
559
560 - (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view
561 {
562     if (![self usesStandardContentView])
563         return;
564
565     if (scrollView.pinchGestureRecognizer.state == UIGestureRecognizerStateBegan)
566         [_contentView willStartUserTriggeredZoom];
567     [_contentView willStartZoomOrScroll];
568 }
569
570 - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
571 {
572     if (![self usesStandardContentView])
573         return;
574
575     if (scrollView.panGestureRecognizer.state == UIGestureRecognizerStateBegan)
576         [_contentView willStartUserTriggeredScroll];
577     [_contentView willStartZoomOrScroll];
578 }
579
580 - (void)_didFinishScrolling
581 {
582     if (![self usesStandardContentView])
583         return;
584
585     [self _updateVisibleContentRects];
586     [_contentView didFinishScrolling];
587 }
588
589 - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
590 {
591     // If we're decelerating, scroll offset will be updated when scrollViewDidFinishDecelerating: is called.
592     if (!decelerate)
593         [self _didFinishScrolling];
594 }
595
596 - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
597 {
598     [self _didFinishScrolling];
599 }
600
601 - (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView
602 {
603     [self _didFinishScrolling];
604 }
605
606 - (void)scrollViewDidScroll:(UIScrollView *)scrollView
607 {
608     [self _updateVisibleContentRects];
609 }
610
611 - (void)scrollViewDidZoom:(UIScrollView *)scrollView
612 {
613     [self _updateScrollViewBackground];
614     [self _updateVisibleContentRects];
615 }
616
617 - (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale
618 {
619     ASSERT(scrollView == _scrollView);
620     [self _updateVisibleContentRects];
621     [_contentView didZoomToScale:scale];
622 }
623
624 - (void)_frameOrBoundsChanged
625 {
626     CGRect bounds = self.bounds;
627
628     if (!_hasStaticMinimumLayoutSize)
629         [_contentView setMinimumLayoutSize:bounds.size];
630     [_scrollView setFrame:bounds];
631     [_contentView setMinimumSize:bounds.size];
632     [_customContentView web_setMinimumSize:bounds.size];
633     [self _updateVisibleContentRects];
634 }
635
636 // 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.
637 - (CGRect)_contentRectForUserInteraction
638 {
639     // FIXME: handle split keyboard.
640     UIEdgeInsets obscuredInsets = _obscuredInsets;
641     obscuredInsets.bottom = std::max(_obscuredInsets.bottom, _keyboardVerticalOverlap);
642     CGRect unobscuredRect = UIEdgeInsetsInsetRect(self.bounds, obscuredInsets);
643     return [self convertRect:unobscuredRect toView:_contentView.get()];
644 }
645
646 - (void)_updateVisibleContentRects
647 {
648     if (![self usesStandardContentView])
649         return;
650
651     CGRect fullViewRect = self.bounds;
652     CGRect visibleRectInContentCoordinates = [self convertRect:fullViewRect toView:_contentView.get()];
653
654     CGRect unobscuredRect = UIEdgeInsetsInsetRect(fullViewRect, _obscuredInsets);
655     CGRect unobscuredRectInContentCoordinates = [self convertRect:unobscuredRect toView:_contentView.get()];
656
657     CGFloat scaleFactor = [_scrollView zoomScale];
658
659     BOOL isStableState = !(_isChangingObscuredInsetsInteractively || [_scrollView isDragging] || [_scrollView isDecelerating] || [_scrollView isZooming] || [_scrollView isZoomBouncing] || [_scrollView _isAnimatingZoom]);
660     [_contentView didUpdateVisibleRect:visibleRectInContentCoordinates unobscuredRect:unobscuredRectInContentCoordinates scale:scaleFactor inStableState:isStableState];
661 }
662
663 - (void)_keyboardChangedWithInfo:(NSDictionary *)keyboardInfo adjustScrollView:(BOOL)adjustScrollView
664 {
665     _keyboardVerticalOverlap = [[UIPeripheralHost sharedInstance] getVerticalOverlapForView:self usingKeyboardInfo:keyboardInfo];
666     [self _updateVisibleContentRects];
667
668     if (adjustScrollView)
669         [_scrollView _adjustForAutomaticKeyboardInfo:keyboardInfo animated:YES lastAdjustment:&_lastAdjustmentForScroller];
670 }
671
672 - (void)_keyboardWillChangeFrame:(NSNotification *)notification
673 {
674     if ([_contentView isAssistingNode])
675         [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES];
676 }
677
678 - (void)_keyboardDidChangeFrame:(NSNotification *)notification
679 {
680     [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:NO];
681 }
682
683 - (void)_keyboardWillShow:(NSNotification *)notification
684 {
685     if ([_contentView isAssistingNode])
686         [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES];
687 }
688
689 - (void)_keyboardWillHide:(NSNotification *)notification
690 {
691     // Ignore keyboard will hide notifications sent during rotation. They're just there for
692     // backwards compatibility reasons and processing the will hide notification would
693     // temporarily screw up the the unobscured view area.
694     if ([[UIPeripheralHost sharedInstance] rotationState])
695         return;
696
697     [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES];
698 }
699
700 - (void)setAllowsBackForwardNavigationGestures:(BOOL)allowsBackForwardNavigationGestures
701 {
702     if (_allowsBackForwardNavigationGestures == allowsBackForwardNavigationGestures)
703         return;
704
705     _allowsBackForwardNavigationGestures = allowsBackForwardNavigationGestures;
706
707     if (allowsBackForwardNavigationGestures) {
708         if (!_gestureController) {
709             _gestureController = std::make_unique<WebKit::ViewGestureController>(*_page);
710             _gestureController->installSwipeHandler(self, [self scrollView]);
711         }
712     } else
713         _gestureController = nullptr;
714
715     _page->setShouldRecordNavigationSnapshots(allowsBackForwardNavigationGestures);
716 }
717
718 - (BOOL)allowsBackForwardNavigationGestures
719 {
720     return _allowsBackForwardNavigationGestures;
721 }
722
723 #endif
724
725 #pragma mark OS X-specific methods
726
727 #if PLATFORM(MAC)
728
729 - (void)resizeSubviewsWithOldSize:(NSSize)oldSize
730 {
731     [_wkView setFrame:self.bounds];
732 }
733
734 - (void)setAllowsBackForwardNavigationGestures:(BOOL)allowsBackForwardNavigationGestures
735 {
736     [_wkView setAllowsBackForwardNavigationGestures:allowsBackForwardNavigationGestures];
737 }
738
739 - (BOOL)allowsBackForwardNavigationGestures
740 {
741     return [_wkView allowsBackForwardNavigationGestures];
742 }
743
744 - (void)setAllowsMagnification:(BOOL)allowsMagnification
745 {
746     [_wkView setAllowsMagnification:allowsMagnification];
747 }
748
749 - (BOOL)allowsMagnification
750 {
751     return [_wkView allowsMagnification];
752 }
753
754 - (void)setMagnification:(CGFloat)magnification
755 {
756     [_wkView setMagnification:magnification];
757 }
758
759 - (CGFloat)magnification
760 {
761     return [_wkView magnification];
762 }
763
764 - (void)setMagnification:(CGFloat)magnification centeredAtPoint:(CGPoint)point
765 {
766     [_wkView setMagnification:magnification centeredAtPoint:NSPointFromCGPoint(point)];
767 }
768
769 #endif
770
771 @end
772
773 @implementation WKWebView (WKPrivate)
774
775 - (WKRemoteObjectRegistry *)_remoteObjectRegistry
776 {
777     if (!_remoteObjectRegistry) {
778         _remoteObjectRegistry = adoptNS([[WKRemoteObjectRegistry alloc] _initWithMessageSender:*_page]);
779         _page->process().context().addMessageReceiver(Messages::RemoteObjectRegistry::messageReceiverName(), _page->pageID(), [_remoteObjectRegistry remoteObjectRegistry]);
780     }
781
782     return _remoteObjectRegistry.get();
783 }
784
785 - (WKBrowsingContextHandle *)_handle
786 {
787     return [[[WKBrowsingContextHandle alloc] _initWithPageID:_page->pageID()] autorelease];
788 }
789
790 - (_WKRenderingProgressEvents)_observedRenderingProgressEvents
791 {
792     return _observedRenderingProgressEvents;
793 }
794
795 - (id <WKHistoryDelegatePrivate>)_historyDelegate
796 {
797     return [_navigationState->historyDelegate().leakRef() autorelease];
798 }
799
800 - (void)_setHistoryDelegate:(id <WKHistoryDelegatePrivate>)historyDelegate
801 {
802     _navigationState->setHistoryDelegate(historyDelegate);
803 }
804
805 - (NSURL *)_unreachableURL
806 {
807     return [NSURL _web_URLWithWTFString:_page->pageLoadState().unreachableURL()];
808 }
809
810 - (void)_loadAlternateHTMLString:(NSString *)string baseURL:(NSURL *)baseURL forUnreachableURL:(NSURL *)unreachableURL
811 {
812     _page->loadAlternateHTMLString(string, [baseURL _web_originalDataAsWTFString], [unreachableURL _web_originalDataAsWTFString]);
813 }
814
815 - (WKNavigation *)_reload
816 {
817     _page->reload(false);
818
819     // FIXME: return a WKNavigation object.
820     return nil;
821 }
822
823 - (NSArray *)_certificateChain
824 {
825     if (WebKit::WebFrameProxy* mainFrame = _page->mainFrame())
826         return mainFrame->certificateInfo() ? (NSArray *)mainFrame->certificateInfo()->certificateInfo().certificateChain() : nil;
827
828     return nil;
829 }
830
831 - (NSURL *)_committedURL
832 {
833     return [NSURL _web_URLWithWTFString:_page->pageLoadState().url()];
834 }
835
836 - (NSString *)_applicationNameForUserAgent
837 {
838     return _page->applicationNameForUserAgent();
839 }
840
841 - (void)_setApplicationNameForUserAgent:(NSString *)applicationNameForUserAgent
842 {
843     _page->setApplicationNameForUserAgent(applicationNameForUserAgent);
844 }
845
846 - (NSString *)_customUserAgent
847 {
848     return _page->customUserAgent();
849 }
850
851 - (void)_setCustomUserAgent:(NSString *)_customUserAgent
852 {
853     _page->setCustomUserAgent(_customUserAgent);
854 }
855
856 - (pid_t)_webProcessIdentifier
857 {
858     return _page->processIdentifier();
859 }
860
861 - (NSData *)_sessionState
862 {
863     return [wrapper(*_page->sessionStateData(nullptr, nullptr).leakRef()) autorelease];
864 }
865
866 static void releaseNSData(unsigned char*, const void* data)
867 {
868     [(NSData *)data release];
869 }
870
871 - (void)_restoreFromSessionState:(NSData *)sessionState
872 {
873     [sessionState retain];
874     _page->restoreFromSessionStateData(API::Data::createWithoutCopying((const unsigned char*)sessionState.bytes, sessionState.length, releaseNSData, sessionState).get());
875 }
876
877 - (void)_close
878 {
879     _page->close();
880 }
881
882 - (BOOL)_privateBrowsingEnabled
883 {
884     return [_configuration preferences]->_preferences->privateBrowsingEnabled();
885 }
886
887 - (void)_setPrivateBrowsingEnabled:(BOOL)privateBrowsingEnabled
888 {
889     [_configuration preferences]->_preferences->setPrivateBrowsingEnabled(privateBrowsingEnabled);
890 }
891
892 - (BOOL)_allowsRemoteInspection
893 {
894 #if ENABLE(REMOTE_INSPECTOR)
895     return _page->allowsRemoteInspection();
896 #else
897     return NO;
898 #endif
899 }
900
901 - (void)_setAllowsRemoteInspection:(BOOL)allow
902 {
903 #if ENABLE(REMOTE_INSPECTOR)
904     _page->setAllowsRemoteInspection(allow);
905 #endif
906 }
907
908 static inline WebCore::LayoutMilestones layoutMilestones(_WKRenderingProgressEvents events)
909 {
910     WebCore::LayoutMilestones milestones = 0;
911
912     if (events & _WKRenderingProgressEventFirstLayout)
913         milestones |= WebCore::DidFirstLayout;
914
915     if (events & _WKRenderingProgressEventFirstPaintWithSignificantArea)
916         milestones |= WebCore::DidHitRelevantRepaintedObjectsAreaThreshold;
917
918     return milestones;
919 }
920
921 - (void)_setObservedRenderingProgressEvents:(_WKRenderingProgressEvents)observedRenderingProgressEvents
922 {
923     _observedRenderingProgressEvents = observedRenderingProgressEvents;
924     _page->listenForLayoutMilestones(layoutMilestones(observedRenderingProgressEvents));
925 }
926
927 - (void)_runJavaScriptInMainFrame:(NSString *)scriptString
928 {
929     _page->runJavaScriptInMainFrame(scriptString, WebKit::ScriptValueCallback::create([](bool, WebKit::WebSerializedScriptValue*){}));
930 }
931
932 - (_WKPaginationMode)_paginationMode
933 {
934     switch (_page->paginationMode()) {
935     case WebCore::Pagination::Unpaginated:
936         return _WKPaginationModeUnpaginated;
937     case WebCore::Pagination::LeftToRightPaginated:
938         return _WKPaginationModeLeftToRight;
939     case WebCore::Pagination::RightToLeftPaginated:
940         return _WKPaginationModeRightToLeft;
941     case WebCore::Pagination::TopToBottomPaginated:
942         return _WKPaginationModeTopToBottom;
943     case WebCore::Pagination::BottomToTopPaginated:
944         return _WKPaginationModeBottomToTop;
945     }
946
947     ASSERT_NOT_REACHED();
948     return _WKPaginationModeUnpaginated;
949 }
950
951 - (void)_setPaginationMode:(_WKPaginationMode)paginationMode
952 {
953     WebCore::Pagination::Mode mode;
954     switch (paginationMode) {
955     case _WKPaginationModeUnpaginated:
956         mode = WebCore::Pagination::Unpaginated;
957         break;
958     case _WKPaginationModeLeftToRight:
959         mode = WebCore::Pagination::LeftToRightPaginated;
960         break;
961     case _WKPaginationModeRightToLeft:
962         mode = WebCore::Pagination::RightToLeftPaginated;
963         break;
964     case _WKPaginationModeTopToBottom:
965         mode = WebCore::Pagination::TopToBottomPaginated;
966         break;
967     case _WKPaginationModeBottomToTop:
968         mode = WebCore::Pagination::BottomToTopPaginated;
969         break;
970     default:
971         return;
972     }
973
974     _page->setPaginationMode(mode);
975 }
976
977 - (BOOL)_paginationBehavesLikeColumns
978 {
979     return _page->paginationBehavesLikeColumns();
980 }
981
982 - (void)_setPaginationBehavesLikeColumns:(BOOL)behavesLikeColumns
983 {
984     _page->setPaginationBehavesLikeColumns(behavesLikeColumns);
985 }
986
987 - (CGFloat)_pageLength
988 {
989     return _page->pageLength();
990 }
991
992 - (void)_setPageLength:(CGFloat)pageLength
993 {
994     _page->setPageLength(pageLength);
995 }
996
997 - (CGFloat)_gapBetweenPages
998 {
999     return _page->gapBetweenPages();
1000 }
1001
1002 - (void)_setGapBetweenPages:(CGFloat)gapBetweenPages
1003 {
1004     _page->setGapBetweenPages(gapBetweenPages);
1005 }
1006
1007 - (NSUInteger)_pageCount
1008 {
1009     return _page->pageCount();
1010 }
1011
1012 - (BOOL)_supportsTextZoom
1013 {
1014     return _page->supportsTextZoom();
1015 }
1016
1017 - (double)_textZoomFactor
1018 {
1019     return _page->textZoomFactor();
1020 }
1021
1022 - (void)_setTextZoomFactor:(double)zoomFactor
1023 {
1024     _page->setTextZoomFactor(zoomFactor);
1025 }
1026
1027 - (double)_pageZoomFactor
1028 {
1029     return _page->pageZoomFactor();
1030 }
1031
1032 - (void)_setPageZoomFactor:(double)zoomFactor
1033 {
1034     _page->setPageZoomFactor(zoomFactor);
1035 }
1036
1037 #pragma mark iOS-specific methods
1038
1039 #if PLATFORM(IOS)
1040
1041 - (CGSize)_minimumLayoutSizeOverride
1042 {
1043     ASSERT(_hasStaticMinimumLayoutSize);
1044     return _minimumLayoutSizeOverride;
1045 }
1046
1047 - (void)_setMinimumLayoutSizeOverride:(CGSize)minimumLayoutSizeOverride
1048 {
1049     _hasStaticMinimumLayoutSize = YES;
1050     _minimumLayoutSizeOverride = minimumLayoutSizeOverride;
1051     [_contentView setMinimumLayoutSize:minimumLayoutSizeOverride];
1052 }
1053
1054 - (UIEdgeInsets)_obscuredInsets
1055 {
1056     return _obscuredInsets;
1057 }
1058
1059 - (void)_setObscuredInsets:(UIEdgeInsets)obscuredInsets
1060 {
1061     ASSERT(obscuredInsets.top >= 0);
1062     ASSERT(obscuredInsets.left >= 0);
1063     ASSERT(obscuredInsets.bottom >= 0);
1064     ASSERT(obscuredInsets.right >= 0);
1065     _obscuredInsets = obscuredInsets;
1066     [self _updateVisibleContentRects];
1067 }
1068
1069 - (UIColor *)_pageExtendedBackgroundColor
1070 {
1071     // This is deprecated.
1072     return nil;
1073 }
1074
1075 - (void)_setBackgroundExtendsBeyondPage:(BOOL)backgroundExtends
1076 {
1077     _page->setBackgroundExtendsBeyondPage(backgroundExtends);
1078 }
1079
1080 - (BOOL)_backgroundExtendsBeyondPage
1081 {
1082     return _page->backgroundExtendsBeyondPage();
1083 }
1084
1085 - (void)_beginInteractiveObscuredInsetsChange
1086 {
1087     ASSERT(!_isChangingObscuredInsetsInteractively);
1088     _isChangingObscuredInsetsInteractively = YES;
1089 }
1090
1091 - (void)_endInteractiveObscuredInsetsChange
1092 {
1093     ASSERT(_isChangingObscuredInsetsInteractively);
1094     _isChangingObscuredInsetsInteractively = NO;
1095     [self _updateVisibleContentRects];
1096 }
1097
1098 - (void)_snapshotRect:(CGRect)rectInViewCoordinates intoImageOfWidth:(CGFloat)imageWidth completionHandler:(void(^)(CGImageRef))completionHandler
1099 {
1100     CGRect snapshotRectInContentCoordinates = [self convertRect:rectInViewCoordinates toView:_contentView.get()];
1101     CGFloat imageHeight = imageWidth / snapshotRectInContentCoordinates.size.width * snapshotRectInContentCoordinates.size.height;
1102     CGSize imageSize = CGSizeMake(imageWidth, imageHeight);
1103
1104     void(^copiedCompletionHandler)(CGImageRef) = [completionHandler copy];
1105     _page->takeSnapshot(WebCore::enclosingIntRect(snapshotRectInContentCoordinates), WebCore::expandedIntSize(WebCore::FloatSize(imageSize)), WebKit::SnapshotOptionsExcludeDeviceScaleFactor, [copiedCompletionHandler](bool, const WebKit::ShareableBitmap::Handle& imageHandle) {
1106         if (imageHandle.isNull()) {
1107             copiedCompletionHandler(nullptr);
1108             [copiedCompletionHandler release];
1109             return;
1110         }
1111
1112         RefPtr<WebKit::ShareableBitmap> bitmap = WebKit::ShareableBitmap::create(imageHandle, WebKit::SharedMemory::ReadOnly);
1113
1114         if (!bitmap) {
1115             copiedCompletionHandler(nullptr);
1116             [copiedCompletionHandler release];
1117             return;
1118         }
1119
1120         RetainPtr<CGImageRef> cgImage;
1121         cgImage = bitmap->makeCGImage();
1122         copiedCompletionHandler(cgImage.get());
1123         [copiedCompletionHandler release];
1124     });
1125 }
1126
1127 #else
1128
1129 #pragma mark - OS X-specific methods
1130
1131 - (NSColor *)_pageExtendedBackgroundColor
1132 {
1133     WebCore::Color color = _page->pageExtendedBackgroundColor();
1134     if (!color.isValid())
1135         return nil;
1136
1137     return nsColor(color);
1138 }
1139
1140 - (BOOL)_drawsTransparentBackground
1141 {
1142     return _page->drawsTransparentBackground();
1143 }
1144
1145 - (void)_setDrawsTransparentBackground:(BOOL)drawsTransparentBackground
1146 {
1147     _page->setDrawsTransparentBackground(drawsTransparentBackground);
1148 }
1149
1150 #endif
1151
1152 @end
1153
1154 #endif // WK_API_ENABLED