Fix iOS build.
[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 - (NSString *)title
260 {
261     return _page->pageLoadState().title();
262 }
263
264 - (NSURL *)activeURL
265 {
266     return [NSURL _web_URLWithWTFString:_page->pageLoadState().activeURL()];
267 }
268
269 - (BOOL)isLoading
270 {
271     return _page->pageLoadState().isLoading();
272 }
273
274 - (double)estimatedProgress
275 {
276     return _page->pageLoadState().estimatedProgress();
277 }
278
279 - (BOOL)hasOnlySecureContent
280 {
281     return _page->pageLoadState().hasOnlySecureContent();
282 }
283
284 // FIXME: This should be KVO compliant.
285 - (BOOL)canGoBack
286 {
287     return !!_page->backForwardList().backItem();
288 }
289
290 // FIXME: This should be KVO compliant.
291 - (BOOL)canGoForward
292 {
293     return !!_page->backForwardList().forwardItem();
294 }
295
296 - (WKNavigation *)goBack
297 {
298     _page->goBack();
299
300     // FIXME: Return a navigation object.
301     return nil;
302 }
303
304 - (WKNavigation *)goForward
305 {
306     _page->goForward();
307
308     // FIXME: Return a navigation object.
309     return nil;
310 }
311
312 #pragma mark iOS-specific methods
313
314 #if PLATFORM(IOS)
315 - (void)setFrame:(CGRect)frame
316 {
317     CGRect oldFrame = self.frame;
318     [super setFrame:frame];
319
320     if (!CGSizeEqualToSize(oldFrame.size, frame.size))
321         [self _frameOrBoundsChanged];
322 }
323
324 - (void)setBounds:(CGRect)bounds
325 {
326     CGRect oldBounds = self.bounds;
327     [super setBounds:bounds];
328
329     if (!CGSizeEqualToSize(oldBounds.size, bounds.size))
330         [self _frameOrBoundsChanged];
331 }
332
333 - (UIScrollView *)scrollView
334 {
335     return _scrollView.get();
336 }
337
338 - (WKBrowsingContextController *)browsingContextController
339 {
340     return [_contentView browsingContextController];
341 }
342
343 - (void)_setHasCustomContentView:(BOOL)pageHasCustomContentView loadedMIMEType:(const WTF::String&)mimeType
344 {
345     if (pageHasCustomContentView) {
346         [_customContentView removeFromSuperview];
347
348         Class representationClass = [[_configuration _contentProviderRegistry] providerForMIMEType:mimeType];
349         ASSERT(representationClass);
350         _customContentView = adoptNS([[representationClass alloc] init]);
351
352         [_contentView removeFromSuperview];
353         [_scrollView addSubview:_customContentView.get()];
354
355         [_customContentView web_setMinimumSize:self.bounds.size];
356         [_customContentView web_setScrollView:_scrollView.get()];
357     } else if (_customContentView) {
358         [_customContentView removeFromSuperview];
359         _customContentView = nullptr;
360
361         [_scrollView addSubview:_contentView.get()];
362         [_scrollView setContentSize:[_contentView frame].size];
363     }
364 }
365
366 - (void)_didFinishLoadingDataForCustomContentProviderWithSuggestedFilename:(const String&)suggestedFilename data:(NSData *)data
367 {
368     ASSERT(_customContentView);
369     [_customContentView web_setContentProviderData:data];
370 }
371
372 - (void)_didCommitLoadForMainFrame
373 {
374     _isWaitingForNewLayerTreeAfterDidCommitLoad = YES;
375 }
376
377 // This is a convenience method that will convert _page->pageExtendedBackgroundColor() from a WebCore::Color to a UIColor *.
378 - (UIColor *)pageExtendedBackgroundColor
379 {
380     WebCore::Color color = _page->pageExtendedBackgroundColor();
381     if (!color.isValid())
382         return nil;
383
384     return [UIColor colorWithRed:(color.red() / 255.0) green:(color.green() / 255.0) blue:(color.blue() / 255.0) alpha:(color.alpha() / 255.0)];
385 }
386
387 static CGFloat contentZoomScale(WKWebView* webView)
388 {
389     UIView *zoomView;
390     if (webView->_customContentView)
391         zoomView = webView->_customContentView.get();
392     else
393         zoomView = webView->_contentView.get();
394
395     CGFloat scale = [[zoomView layer] affineTransform].a;
396     ASSERT(scale == [webView->_scrollView zoomScale]);
397     return scale;
398 }
399
400 - (void)_updateScrollViewBackground
401 {
402     UIColor *pageExtendedBackgroundColor = [self pageExtendedBackgroundColor];
403
404     CGFloat zoomScale = contentZoomScale(self);
405     CGFloat minimumZoomScale = [_scrollView minimumZoomScale];
406     if (zoomScale < minimumZoomScale) {
407         CGFloat slope = 12;
408         CGFloat opacity = std::max(1 - slope * (minimumZoomScale - zoomScale), static_cast<CGFloat>(0));
409         pageExtendedBackgroundColor = [pageExtendedBackgroundColor colorWithAlphaComponent:opacity];
410     }
411
412     [_scrollView setBackgroundColor:pageExtendedBackgroundColor];
413 }
414
415 - (void)_didCommitLayerTree:(const WebKit::RemoteLayerTreeTransaction&)layerTreeTransaction
416 {
417     ASSERT(!_customContentView);
418
419     [_scrollView setContentSize:[_contentView frame].size];
420     [_scrollView setMinimumZoomScale:layerTreeTransaction.minimumScaleFactor()];
421     [_scrollView setMaximumZoomScale:layerTreeTransaction.maximumScaleFactor()];
422     [_scrollView setZoomEnabled:layerTreeTransaction.allowsUserScaling()];
423     if (!layerTreeTransaction.scaleWasSetByUIProcess() && ![_scrollView isZooming] && ![_scrollView isZoomBouncing] && ![_scrollView _isAnimatingZoom])
424         [_scrollView setZoomScale:layerTreeTransaction.pageScaleFactor()];
425     
426     [self _updateScrollViewBackground];
427
428     if (_gestureController)
429         _gestureController->setRenderTreeSize(layerTreeTransaction.renderTreeSize());
430
431     if (_isWaitingForNewLayerTreeAfterDidCommitLoad) {
432         UIEdgeInsets inset = [_scrollView contentInset];
433         [_scrollView setContentOffset:CGPointMake(-inset.left, -inset.top)];
434         _isWaitingForNewLayerTreeAfterDidCommitLoad = NO;
435     }
436
437 }
438
439 - (RetainPtr<CGImageRef>)_takeViewSnapshot
440 {
441     // FIXME: We should be able to use acquire an IOSurface directly, instead of going to CGImage here and back in ViewSnapshotStore.
442     UIGraphicsBeginImageContextWithOptions(self.bounds.size, YES, self.window.screen.scale);
443     [self drawViewHierarchyInRect:[self bounds] afterScreenUpdates:NO];
444     UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
445     UIGraphicsEndImageContext();
446     return image.CGImage;
447 }
448
449 - (void)_zoomToPoint:(WebCore::FloatPoint)point atScale:(double)scale
450 {
451     double maximumZoomDuration = 0.4;
452     double minimumZoomDuration = 0.1;
453     double zoomDurationFactor = 0.3;
454
455     CGFloat zoomScale = contentZoomScale(self);
456     CFTimeInterval duration = std::min(fabs(log(zoomScale) - log(scale)) * zoomDurationFactor + minimumZoomDuration, maximumZoomDuration);
457
458     if (scale != zoomScale)
459         [_contentView willStartUserTriggeredZoom];
460
461     [_scrollView _zoomToCenter:point scale:scale duration:duration];
462 }
463
464 - (void)_zoomToRect:(WebCore::FloatRect)targetRect atScale:(double)scale origin:(WebCore::FloatPoint)origin
465 {
466     WebCore::FloatSize unobscuredContentSize([self _contentRectForUserInteraction].size);
467     WebCore::FloatSize targetRectSizeAfterZoom = targetRect.size();
468     targetRectSizeAfterZoom.scale(scale);
469
470     // Center the target rect in the scroll view.
471     // If the target doesn't fit in the scroll view, center on the gesture location instead.
472     WebCore::FloatPoint zoomCenter = targetRect.center();
473
474     if (targetRectSizeAfterZoom.width() > unobscuredContentSize.width())
475         zoomCenter.setX(origin.x());
476     if (targetRectSizeAfterZoom.height() > unobscuredContentSize.height())
477         zoomCenter.setY(origin.y());
478
479     [self _zoomToPoint:zoomCenter atScale:scale];
480 }
481
482 static WebCore::FloatPoint constrainContentOffset(WebCore::FloatPoint contentOffset, WebCore::FloatSize contentSize, WebCore::FloatSize unobscuredContentSize)
483 {
484     WebCore::FloatSize maximumContentOffset = contentSize - unobscuredContentSize;
485     contentOffset = contentOffset.shrunkTo(WebCore::FloatPoint(maximumContentOffset.width(), maximumContentOffset.height()));
486     contentOffset = contentOffset.expandedTo(WebCore::FloatPoint());
487     return contentOffset;
488 }
489
490 - (BOOL)_scrollToRect:(WebCore::FloatRect)targetRect origin:(WebCore::FloatPoint)origin minimumScrollDistance:(float)minimumScrollDistance
491 {
492     WebCore::FloatRect unobscuredContentRect([self _contentRectForUserInteraction]);
493     WebCore::FloatPoint unobscuredContentOffset = unobscuredContentRect.location();
494     WebCore::FloatSize contentSize([_contentView bounds].size);
495
496     // Center the target rect in the scroll view.
497     // If the target doesn't fit in the scroll view, center on the gesture location instead.
498     WebCore::FloatPoint newUnobscuredContentOffset;
499     if (targetRect.width() <= unobscuredContentRect.width())
500         newUnobscuredContentOffset.setX(targetRect.x() - (unobscuredContentRect.width() - targetRect.width()) / 2);
501     else
502         newUnobscuredContentOffset.setX(origin.x() - unobscuredContentRect.width() / 2);
503     if (targetRect.height() <= unobscuredContentRect.height())
504         newUnobscuredContentOffset.setY(targetRect.y() - (unobscuredContentRect.height() - targetRect.height()) / 2);
505     else
506         newUnobscuredContentOffset.setY(origin.y() - unobscuredContentRect.height() / 2);
507     newUnobscuredContentOffset = constrainContentOffset(newUnobscuredContentOffset, contentSize, unobscuredContentRect.size());
508
509     if (unobscuredContentOffset == newUnobscuredContentOffset) {
510         if (targetRect.width() > unobscuredContentRect.width())
511             newUnobscuredContentOffset.setX(origin.x() - unobscuredContentRect.width() / 2);
512         if (targetRect.height() > unobscuredContentRect.height())
513             newUnobscuredContentOffset.setY(origin.y() - unobscuredContentRect.height() / 2);
514         newUnobscuredContentOffset = constrainContentOffset(newUnobscuredContentOffset, contentSize, unobscuredContentRect.size());
515     }
516
517     WebCore::FloatSize scrollViewOffsetDelta = newUnobscuredContentOffset - unobscuredContentOffset;
518     scrollViewOffsetDelta.scale(contentZoomScale(self));
519
520     float scrollDistance = scrollViewOffsetDelta.diagonalLength();
521     if (scrollDistance < minimumScrollDistance)
522         return false;
523
524     [_scrollView setContentOffset:([_scrollView contentOffset] + scrollViewOffsetDelta) animated:YES];
525     return true;
526 }
527
528 - (void)_zoomOutWithOrigin:(WebCore::FloatPoint)origin
529 {
530     [self _zoomToPoint:origin atScale:[_scrollView minimumZoomScale]];
531 }
532
533 - (BOOL)_zoomToRect:(WebCore::FloatRect)targetRect withOrigin:(WebCore::FloatPoint)origin fitEntireRect:(BOOL)fitEntireRect minimumScale:(double)minimumScale maximumScale:(double)maximumScale minimumScrollDistance:(float)minimumScrollDistance
534 {
535     const float maximumScaleFactorDeltaForPanScroll = 0.02;
536
537     double currentScale = contentZoomScale(self);
538
539     WebCore::FloatSize unobscuredContentSize([self _contentRectForUserInteraction].size);
540     double horizontalScale = unobscuredContentSize.width() * currentScale / targetRect.width();
541     double verticalScale = unobscuredContentSize.height() * currentScale / targetRect.height();
542
543     horizontalScale = std::min(std::max(horizontalScale, minimumScale), maximumScale);
544     verticalScale = std::min(std::max(verticalScale, minimumScale), maximumScale);
545
546     double targetScale = fitEntireRect ? std::min(horizontalScale, verticalScale) : horizontalScale;
547     if (fabs(targetScale - currentScale) < maximumScaleFactorDeltaForPanScroll) {
548         if ([self _scrollToRect:targetRect origin:origin minimumScrollDistance:minimumScrollDistance])
549             return true;
550     } else if (targetScale != currentScale) {
551         [self _zoomToRect:targetRect atScale:targetScale origin:origin];
552         return true;
553     }
554     
555     return false;
556 }
557
558 #pragma mark - UIScrollViewDelegate
559
560 - (BOOL)usesStandardContentView
561 {
562     return !_customContentView;
563 }
564
565 - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
566 {
567     ASSERT(_scrollView == scrollView);
568
569     if (_customContentView)
570         return _customContentView.get();
571
572     return _contentView.get();
573 }
574
575 - (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view
576 {
577     if (![self usesStandardContentView])
578         return;
579
580     if (scrollView.pinchGestureRecognizer.state == UIGestureRecognizerStateBegan)
581         [_contentView willStartUserTriggeredZoom];
582     [_contentView willStartZoomOrScroll];
583 }
584
585 - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
586 {
587     if (![self usesStandardContentView])
588         return;
589
590     if (scrollView.panGestureRecognizer.state == UIGestureRecognizerStateBegan)
591         [_contentView willStartUserTriggeredScroll];
592     [_contentView willStartZoomOrScroll];
593 }
594
595 - (void)_didFinishScrolling
596 {
597     if (![self usesStandardContentView])
598         return;
599
600     [self _updateVisibleContentRects];
601     [_contentView didFinishScrolling];
602 }
603
604 - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
605 {
606     // If we're decelerating, scroll offset will be updated when scrollViewDidFinishDecelerating: is called.
607     if (!decelerate)
608         [self _didFinishScrolling];
609 }
610
611 - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
612 {
613     [self _didFinishScrolling];
614 }
615
616 - (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView
617 {
618     [self _didFinishScrolling];
619 }
620
621 - (void)scrollViewDidScroll:(UIScrollView *)scrollView
622 {
623     [self _updateVisibleContentRects];
624 }
625
626 - (void)scrollViewDidZoom:(UIScrollView *)scrollView
627 {
628     [self _updateScrollViewBackground];
629     [self _updateVisibleContentRects];
630 }
631
632 - (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale
633 {
634     ASSERT(scrollView == _scrollView);
635     [self _updateVisibleContentRects];
636     [_contentView didZoomToScale:scale];
637 }
638
639 - (void)_frameOrBoundsChanged
640 {
641     CGRect bounds = self.bounds;
642
643     if (!_hasStaticMinimumLayoutSize)
644         [_contentView setMinimumLayoutSize:bounds.size];
645     [_scrollView setFrame:bounds];
646     [_contentView setMinimumSize:bounds.size];
647     [_customContentView web_setMinimumSize:bounds.size];
648     [self _updateVisibleContentRects];
649 }
650
651 // 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.
652 - (CGRect)_contentRectForUserInteraction
653 {
654     // FIXME: handle split keyboard.
655     UIEdgeInsets obscuredInsets = _obscuredInsets;
656     obscuredInsets.bottom = std::max(_obscuredInsets.bottom, _keyboardVerticalOverlap);
657     CGRect unobscuredRect = UIEdgeInsetsInsetRect(self.bounds, obscuredInsets);
658     return [self convertRect:unobscuredRect toView:_contentView.get()];
659 }
660
661 - (void)_updateVisibleContentRects
662 {
663     if (![self usesStandardContentView])
664         return;
665
666     CGRect fullViewRect = self.bounds;
667     CGRect visibleRectInContentCoordinates = [self convertRect:fullViewRect toView:_contentView.get()];
668
669     CGRect unobscuredRect = UIEdgeInsetsInsetRect(fullViewRect, _obscuredInsets);
670     CGRect unobscuredRectInContentCoordinates = [self convertRect:unobscuredRect toView:_contentView.get()];
671
672     CGFloat scaleFactor = contentZoomScale(self);
673
674     BOOL isStableState = !(_isChangingObscuredInsetsInteractively || [_scrollView isDragging] || [_scrollView isDecelerating] || [_scrollView isZooming] || [_scrollView isZoomBouncing] || [_scrollView _isAnimatingZoom]);
675     [_contentView didUpdateVisibleRect:visibleRectInContentCoordinates unobscuredRect:unobscuredRectInContentCoordinates scale:scaleFactor inStableState:isStableState];
676 }
677
678 - (void)_keyboardChangedWithInfo:(NSDictionary *)keyboardInfo adjustScrollView:(BOOL)adjustScrollView
679 {
680     _keyboardVerticalOverlap = [[UIPeripheralHost sharedInstance] getVerticalOverlapForView:self usingKeyboardInfo:keyboardInfo];
681     [self _updateVisibleContentRects];
682
683     if (adjustScrollView)
684         [_scrollView _adjustForAutomaticKeyboardInfo:keyboardInfo animated:YES lastAdjustment:&_lastAdjustmentForScroller];
685 }
686
687 - (void)_keyboardWillChangeFrame:(NSNotification *)notification
688 {
689     if ([_contentView isAssistingNode])
690         [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES];
691 }
692
693 - (void)_keyboardDidChangeFrame:(NSNotification *)notification
694 {
695     [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:NO];
696 }
697
698 - (void)_keyboardWillShow:(NSNotification *)notification
699 {
700     if ([_contentView isAssistingNode])
701         [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES];
702 }
703
704 - (void)_keyboardWillHide:(NSNotification *)notification
705 {
706     // Ignore keyboard will hide notifications sent during rotation. They're just there for
707     // backwards compatibility reasons and processing the will hide notification would
708     // temporarily screw up the the unobscured view area.
709     if ([[UIPeripheralHost sharedInstance] rotationState])
710         return;
711
712     [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES];
713 }
714
715 - (void)setAllowsBackForwardNavigationGestures:(BOOL)allowsBackForwardNavigationGestures
716 {
717     if (_allowsBackForwardNavigationGestures == allowsBackForwardNavigationGestures)
718         return;
719
720     _allowsBackForwardNavigationGestures = allowsBackForwardNavigationGestures;
721
722     if (allowsBackForwardNavigationGestures) {
723         if (!_gestureController) {
724             _gestureController = std::make_unique<WebKit::ViewGestureController>(*_page);
725             _gestureController->installSwipeHandler(self, [self scrollView]);
726         }
727     } else
728         _gestureController = nullptr;
729
730     _page->setShouldRecordNavigationSnapshots(allowsBackForwardNavigationGestures);
731 }
732
733 - (BOOL)allowsBackForwardNavigationGestures
734 {
735     return _allowsBackForwardNavigationGestures;
736 }
737
738 #endif
739
740 #pragma mark OS X-specific methods
741
742 #if PLATFORM(MAC)
743
744 - (void)resizeSubviewsWithOldSize:(NSSize)oldSize
745 {
746     [_wkView setFrame:self.bounds];
747 }
748
749 - (void)setAllowsBackForwardNavigationGestures:(BOOL)allowsBackForwardNavigationGestures
750 {
751     [_wkView setAllowsBackForwardNavigationGestures:allowsBackForwardNavigationGestures];
752 }
753
754 - (BOOL)allowsBackForwardNavigationGestures
755 {
756     return [_wkView allowsBackForwardNavigationGestures];
757 }
758
759 - (void)setAllowsMagnification:(BOOL)allowsMagnification
760 {
761     [_wkView setAllowsMagnification:allowsMagnification];
762 }
763
764 - (BOOL)allowsMagnification
765 {
766     return [_wkView allowsMagnification];
767 }
768
769 - (void)setMagnification:(CGFloat)magnification
770 {
771     [_wkView setMagnification:magnification];
772 }
773
774 - (CGFloat)magnification
775 {
776     return [_wkView magnification];
777 }
778
779 - (void)setMagnification:(CGFloat)magnification centeredAtPoint:(CGPoint)point
780 {
781     [_wkView setMagnification:magnification centeredAtPoint:NSPointFromCGPoint(point)];
782 }
783
784 #endif
785
786 @end
787
788 @implementation WKWebView (WKPrivate)
789
790 - (WKRemoteObjectRegistry *)_remoteObjectRegistry
791 {
792     if (!_remoteObjectRegistry) {
793         _remoteObjectRegistry = adoptNS([[WKRemoteObjectRegistry alloc] _initWithMessageSender:*_page]);
794         _page->process().context().addMessageReceiver(Messages::RemoteObjectRegistry::messageReceiverName(), _page->pageID(), [_remoteObjectRegistry remoteObjectRegistry]);
795     }
796
797     return _remoteObjectRegistry.get();
798 }
799
800 - (WKBrowsingContextHandle *)_handle
801 {
802     return [[[WKBrowsingContextHandle alloc] _initWithPageID:_page->pageID()] autorelease];
803 }
804
805 - (_WKRenderingProgressEvents)_observedRenderingProgressEvents
806 {
807     return _observedRenderingProgressEvents;
808 }
809
810 - (id <WKHistoryDelegatePrivate>)_historyDelegate
811 {
812     return [_navigationState->historyDelegate().leakRef() autorelease];
813 }
814
815 - (void)_setHistoryDelegate:(id <WKHistoryDelegatePrivate>)historyDelegate
816 {
817     _navigationState->setHistoryDelegate(historyDelegate);
818 }
819
820 - (NSURL *)_unreachableURL
821 {
822     return [NSURL _web_URLWithWTFString:_page->pageLoadState().unreachableURL()];
823 }
824
825 - (void)_loadAlternateHTMLString:(NSString *)string baseURL:(NSURL *)baseURL forUnreachableURL:(NSURL *)unreachableURL
826 {
827     _page->loadAlternateHTMLString(string, [baseURL _web_originalDataAsWTFString], [unreachableURL _web_originalDataAsWTFString]);
828 }
829
830 - (WKNavigation *)_reload
831 {
832     _page->reload(false);
833
834     // FIXME: return a WKNavigation object.
835     return nil;
836 }
837
838 - (NSArray *)_certificateChain
839 {
840     if (WebKit::WebFrameProxy* mainFrame = _page->mainFrame())
841         return mainFrame->certificateInfo() ? (NSArray *)mainFrame->certificateInfo()->certificateInfo().certificateChain() : nil;
842
843     return nil;
844 }
845
846 - (NSURL *)_committedURL
847 {
848     return [NSURL _web_URLWithWTFString:_page->pageLoadState().url()];
849 }
850
851 - (NSString *)_applicationNameForUserAgent
852 {
853     return _page->applicationNameForUserAgent();
854 }
855
856 - (void)_setApplicationNameForUserAgent:(NSString *)applicationNameForUserAgent
857 {
858     _page->setApplicationNameForUserAgent(applicationNameForUserAgent);
859 }
860
861 - (NSString *)_customUserAgent
862 {
863     return _page->customUserAgent();
864 }
865
866 - (void)_setCustomUserAgent:(NSString *)_customUserAgent
867 {
868     _page->setCustomUserAgent(_customUserAgent);
869 }
870
871 - (pid_t)_webProcessIdentifier
872 {
873     return _page->processIdentifier();
874 }
875
876 - (NSData *)_sessionState
877 {
878     return [wrapper(*_page->sessionStateData(nullptr, nullptr).leakRef()) autorelease];
879 }
880
881 static void releaseNSData(unsigned char*, const void* data)
882 {
883     [(NSData *)data release];
884 }
885
886 - (void)_restoreFromSessionState:(NSData *)sessionState
887 {
888     [sessionState retain];
889     _page->restoreFromSessionStateData(API::Data::createWithoutCopying((const unsigned char*)sessionState.bytes, sessionState.length, releaseNSData, sessionState).get());
890 }
891
892 - (void)_close
893 {
894     _page->close();
895 }
896
897 - (BOOL)_privateBrowsingEnabled
898 {
899     return [_configuration preferences]->_preferences->privateBrowsingEnabled();
900 }
901
902 - (void)_setPrivateBrowsingEnabled:(BOOL)privateBrowsingEnabled
903 {
904     [_configuration preferences]->_preferences->setPrivateBrowsingEnabled(privateBrowsingEnabled);
905 }
906
907 - (BOOL)_allowsRemoteInspection
908 {
909 #if ENABLE(REMOTE_INSPECTOR)
910     return _page->allowsRemoteInspection();
911 #else
912     return NO;
913 #endif
914 }
915
916 - (void)_setAllowsRemoteInspection:(BOOL)allow
917 {
918 #if ENABLE(REMOTE_INSPECTOR)
919     _page->setAllowsRemoteInspection(allow);
920 #endif
921 }
922
923 static inline WebCore::LayoutMilestones layoutMilestones(_WKRenderingProgressEvents events)
924 {
925     WebCore::LayoutMilestones milestones = 0;
926
927     if (events & _WKRenderingProgressEventFirstLayout)
928         milestones |= WebCore::DidFirstLayout;
929
930     if (events & _WKRenderingProgressEventFirstPaintWithSignificantArea)
931         milestones |= WebCore::DidHitRelevantRepaintedObjectsAreaThreshold;
932
933     return milestones;
934 }
935
936 - (void)_setObservedRenderingProgressEvents:(_WKRenderingProgressEvents)observedRenderingProgressEvents
937 {
938     _observedRenderingProgressEvents = observedRenderingProgressEvents;
939     _page->listenForLayoutMilestones(layoutMilestones(observedRenderingProgressEvents));
940 }
941
942 - (void)_runJavaScriptInMainFrame:(NSString *)scriptString
943 {
944     _page->runJavaScriptInMainFrame(scriptString, WebKit::ScriptValueCallback::create([](bool, WebKit::WebSerializedScriptValue*){}));
945 }
946
947 - (_WKPaginationMode)_paginationMode
948 {
949     switch (_page->paginationMode()) {
950     case WebCore::Pagination::Unpaginated:
951         return _WKPaginationModeUnpaginated;
952     case WebCore::Pagination::LeftToRightPaginated:
953         return _WKPaginationModeLeftToRight;
954     case WebCore::Pagination::RightToLeftPaginated:
955         return _WKPaginationModeRightToLeft;
956     case WebCore::Pagination::TopToBottomPaginated:
957         return _WKPaginationModeTopToBottom;
958     case WebCore::Pagination::BottomToTopPaginated:
959         return _WKPaginationModeBottomToTop;
960     }
961
962     ASSERT_NOT_REACHED();
963     return _WKPaginationModeUnpaginated;
964 }
965
966 - (void)_setPaginationMode:(_WKPaginationMode)paginationMode
967 {
968     WebCore::Pagination::Mode mode;
969     switch (paginationMode) {
970     case _WKPaginationModeUnpaginated:
971         mode = WebCore::Pagination::Unpaginated;
972         break;
973     case _WKPaginationModeLeftToRight:
974         mode = WebCore::Pagination::LeftToRightPaginated;
975         break;
976     case _WKPaginationModeRightToLeft:
977         mode = WebCore::Pagination::RightToLeftPaginated;
978         break;
979     case _WKPaginationModeTopToBottom:
980         mode = WebCore::Pagination::TopToBottomPaginated;
981         break;
982     case _WKPaginationModeBottomToTop:
983         mode = WebCore::Pagination::BottomToTopPaginated;
984         break;
985     default:
986         return;
987     }
988
989     _page->setPaginationMode(mode);
990 }
991
992 - (BOOL)_paginationBehavesLikeColumns
993 {
994     return _page->paginationBehavesLikeColumns();
995 }
996
997 - (void)_setPaginationBehavesLikeColumns:(BOOL)behavesLikeColumns
998 {
999     _page->setPaginationBehavesLikeColumns(behavesLikeColumns);
1000 }
1001
1002 - (CGFloat)_pageLength
1003 {
1004     return _page->pageLength();
1005 }
1006
1007 - (void)_setPageLength:(CGFloat)pageLength
1008 {
1009     _page->setPageLength(pageLength);
1010 }
1011
1012 - (CGFloat)_gapBetweenPages
1013 {
1014     return _page->gapBetweenPages();
1015 }
1016
1017 - (void)_setGapBetweenPages:(CGFloat)gapBetweenPages
1018 {
1019     _page->setGapBetweenPages(gapBetweenPages);
1020 }
1021
1022 - (NSUInteger)_pageCount
1023 {
1024     return _page->pageCount();
1025 }
1026
1027 - (BOOL)_supportsTextZoom
1028 {
1029     return _page->supportsTextZoom();
1030 }
1031
1032 - (double)_textZoomFactor
1033 {
1034     return _page->textZoomFactor();
1035 }
1036
1037 - (void)_setTextZoomFactor:(double)zoomFactor
1038 {
1039     _page->setTextZoomFactor(zoomFactor);
1040 }
1041
1042 - (double)_pageZoomFactor
1043 {
1044     return _page->pageZoomFactor();
1045 }
1046
1047 - (void)_setPageZoomFactor:(double)zoomFactor
1048 {
1049     _page->setPageZoomFactor(zoomFactor);
1050 }
1051
1052 #pragma mark iOS-specific methods
1053
1054 #if PLATFORM(IOS)
1055
1056 - (CGSize)_minimumLayoutSizeOverride
1057 {
1058     ASSERT(_hasStaticMinimumLayoutSize);
1059     return _minimumLayoutSizeOverride;
1060 }
1061
1062 - (void)_setMinimumLayoutSizeOverride:(CGSize)minimumLayoutSizeOverride
1063 {
1064     _hasStaticMinimumLayoutSize = YES;
1065     _minimumLayoutSizeOverride = minimumLayoutSizeOverride;
1066     [_contentView setMinimumLayoutSize:minimumLayoutSizeOverride];
1067 }
1068
1069 - (UIEdgeInsets)_obscuredInsets
1070 {
1071     return _obscuredInsets;
1072 }
1073
1074 - (void)_setObscuredInsets:(UIEdgeInsets)obscuredInsets
1075 {
1076     ASSERT(obscuredInsets.top >= 0);
1077     ASSERT(obscuredInsets.left >= 0);
1078     ASSERT(obscuredInsets.bottom >= 0);
1079     ASSERT(obscuredInsets.right >= 0);
1080     _obscuredInsets = obscuredInsets;
1081     [self _updateVisibleContentRects];
1082 }
1083
1084 - (UIColor *)_pageExtendedBackgroundColor
1085 {
1086     // This is deprecated.
1087     return nil;
1088 }
1089
1090 - (void)_setBackgroundExtendsBeyondPage:(BOOL)backgroundExtends
1091 {
1092     _page->setBackgroundExtendsBeyondPage(backgroundExtends);
1093 }
1094
1095 - (BOOL)_backgroundExtendsBeyondPage
1096 {
1097     return _page->backgroundExtendsBeyondPage();
1098 }
1099
1100 - (void)_beginInteractiveObscuredInsetsChange
1101 {
1102     ASSERT(!_isChangingObscuredInsetsInteractively);
1103     _isChangingObscuredInsetsInteractively = YES;
1104 }
1105
1106 - (void)_endInteractiveObscuredInsetsChange
1107 {
1108     ASSERT(_isChangingObscuredInsetsInteractively);
1109     _isChangingObscuredInsetsInteractively = NO;
1110     [self _updateVisibleContentRects];
1111 }
1112
1113 - (void)_snapshotRect:(CGRect)rectInViewCoordinates intoImageOfWidth:(CGFloat)imageWidth completionHandler:(void(^)(CGImageRef))completionHandler
1114 {
1115     CGRect snapshotRectInContentCoordinates = [self convertRect:rectInViewCoordinates toView:_contentView.get()];
1116     CGFloat imageHeight = imageWidth / snapshotRectInContentCoordinates.size.width * snapshotRectInContentCoordinates.size.height;
1117     CGSize imageSize = CGSizeMake(imageWidth, imageHeight);
1118
1119     void(^copiedCompletionHandler)(CGImageRef) = [completionHandler copy];
1120     _page->takeSnapshot(WebCore::enclosingIntRect(snapshotRectInContentCoordinates), WebCore::expandedIntSize(WebCore::FloatSize(imageSize)), WebKit::SnapshotOptionsExcludeDeviceScaleFactor, [copiedCompletionHandler](bool, const WebKit::ShareableBitmap::Handle& imageHandle) {
1121         if (imageHandle.isNull()) {
1122             copiedCompletionHandler(nullptr);
1123             [copiedCompletionHandler release];
1124             return;
1125         }
1126
1127         RefPtr<WebKit::ShareableBitmap> bitmap = WebKit::ShareableBitmap::create(imageHandle, WebKit::SharedMemory::ReadOnly);
1128
1129         if (!bitmap) {
1130             copiedCompletionHandler(nullptr);
1131             [copiedCompletionHandler release];
1132             return;
1133         }
1134
1135         RetainPtr<CGImageRef> cgImage;
1136         cgImage = bitmap->makeCGImage();
1137         copiedCompletionHandler(cgImage.get());
1138         [copiedCompletionHandler release];
1139     });
1140 }
1141
1142 #else
1143
1144 #pragma mark - OS X-specific methods
1145
1146 - (NSColor *)_pageExtendedBackgroundColor
1147 {
1148     WebCore::Color color = _page->pageExtendedBackgroundColor();
1149     if (!color.isValid())
1150         return nil;
1151
1152     return nsColor(color);
1153 }
1154
1155 - (BOOL)_drawsTransparentBackground
1156 {
1157     return _page->drawsTransparentBackground();
1158 }
1159
1160 - (void)_setDrawsTransparentBackground:(BOOL)drawsTransparentBackground
1161 {
1162     _page->setDrawsTransparentBackground(drawsTransparentBackground);
1163 }
1164
1165 - (void)_setTopContentInset:(CGFloat)contentInset
1166 {
1167     _page->setTopContentInset(contentInset);
1168 }
1169
1170 - (CGFloat)_topContentInset
1171 {
1172     return _page->topContentInset();
1173 }
1174
1175 #endif
1176
1177 @end
1178
1179 #if !TARGET_OS_IPHONE
1180
1181 @implementation WKWebView (WKIBActions)
1182
1183 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
1184 {
1185     SEL action = item.action;
1186
1187     if (action == @selector(goBack:))
1188         return !!_page->backForwardList().backItem();
1189
1190     if (action == @selector(goForward:))
1191         return !!_page->backForwardList().forwardItem();
1192
1193     if (action == @selector(stopLoading:)) {
1194         // FIXME: Return no if we're stopped.
1195         return YES;
1196     }
1197
1198     return NO;
1199 }
1200
1201 - (IBAction)goBack:(id)sender
1202 {
1203     [self goBack];
1204 }
1205
1206 - (IBAction)goForward:(id)sender
1207 {
1208     [self goForward];
1209 }
1210
1211 - (IBAction)stopLoading:(id)sender
1212 {
1213     _page->stopLoading();
1214 }
1215
1216 @end
1217
1218 #endif
1219
1220 #endif // WK_API_ENABLED