2 * Copyright (C) 2014 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #import "WKWebViewInternal.h"
31 #import "NavigationState.h"
32 #import "RemoteLayerTreeTransaction.h"
33 #import "RemoteObjectRegistry.h"
34 #import "RemoteObjectRegistryMessages.h"
36 #import "ViewGestureController.h"
37 #import "WKBackForwardListInternal.h"
38 #import "WKBackForwardListItemInternal.h"
39 #import "WKBrowsingContextHandleInternal.h"
40 #import "WKHistoryDelegatePrivate.h"
42 #import "WKNavigationDelegate.h"
43 #import "WKNavigationInternal.h"
44 #import "WKPreferencesInternal.h"
45 #import "WKProcessPoolInternal.h"
46 #import "WKRemoteObjectRegistryInternal.h"
47 #import "WKUIDelegate.h"
48 #import "WKWebViewConfigurationPrivate.h"
49 #import "WebCertificateInfo.h"
50 #import "WebContext.h"
51 #import "WebBackForwardList.h"
52 #import "WebPageProxy.h"
53 #import "WebProcessProxy.h"
54 #import "WKNSURLExtras.h"
55 #import <wtf/RetainPtr.h>
58 #import "WKScrollView.h"
59 #import <UIKit/UIPeripheralHost_Private.h>
61 @interface UIScrollView (UIScrollViewInternal)
62 - (void)_adjustForAutomaticKeyboardInfo:(NSDictionary*)info animated:(BOOL)animated lastAdjustment:(CGFloat*)lastAdjustment;
68 #import "WKViewInternal.h"
71 @implementation WKWebView {
72 RetainPtr<WKWebViewConfiguration> _configuration;
73 std::unique_ptr<WebKit::NavigationState> _navigationState;
75 RetainPtr<WKRemoteObjectRegistry> _remoteObjectRegistry;
76 _WKRenderingProgressEvents _observedRenderingProgressEvents;
79 RetainPtr<WKScrollView> _scrollView;
80 RetainPtr<WKContentView> _contentView;
82 BOOL _isWaitingForNewLayerTreeAfterDidCommitLoad;
83 BOOL _hasStaticMinimumLayoutSize;
84 CGSize _minimumLayoutSizeOverride;
86 UIEdgeInsets _obscuredInsets;
87 bool _isChangingObscuredInsetsInteractively;
88 CGFloat _lastAdjustmentForScroller;
90 std::unique_ptr<WebKit::ViewGestureController> _gestureController;
91 BOOL _allowsBackForwardNavigationGestures;
94 RetainPtr<WKView> _wkView;
98 - (instancetype)initWithFrame:(CGRect)frame
100 return [self initWithFrame:frame configuration:adoptNS([[WKWebViewConfiguration alloc] init]).get()];
103 - (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration
105 if (!(self = [super initWithFrame:frame]))
108 _configuration = adoptNS([configuration copy]);
110 if (WKWebView *relatedWebView = [_configuration _relatedWebView]) {
111 WKProcessPool *processPool = [_configuration processPool];
112 WKProcessPool *relatedWebViewProcessPool = [relatedWebView->_configuration processPool];
113 if (processPool && processPool != relatedWebViewProcessPool)
114 [NSException raise:NSInvalidArgumentException format:@"Related web view %@ has process pool %@ but configuration specifies a different process pool %@", relatedWebView, relatedWebViewProcessPool, configuration.processPool];
116 [_configuration setProcessPool:relatedWebViewProcessPool];
119 if (![_configuration processPool])
120 [_configuration setProcessPool:adoptNS([[WKProcessPool alloc] init]).get()];
122 if (![_configuration preferences])
123 [_configuration setPreferences:adoptNS([[WKPreferences alloc] init]).get()];
125 CGRect bounds = self.bounds;
127 WebKit::WebContext& context = *[_configuration processPool]->_context;
129 WebKit::WebPageConfiguration webPageConfiguration;
130 webPageConfiguration.preferences = [_configuration preferences]->_preferences.get();
131 if (WKWebView *relatedWebView = [_configuration _relatedWebView])
132 webPageConfiguration.relatedPage = relatedWebView->_page.get();
135 _scrollView = adoptNS([[WKScrollView alloc] initWithFrame:bounds]);
136 [_scrollView setInternalDelegate:self];
137 [_scrollView setBouncesZoom:YES];
139 [self addSubview:_scrollView.get()];
141 _contentView = adoptNS([[WKContentView alloc] initWithFrame:bounds context:context configuration:std::move(webPageConfiguration)]);
142 _page = _contentView->_page;
143 [_contentView setDelegate:self];
144 [_contentView layer].anchorPoint = CGPointZero;
145 [_contentView setFrame:bounds];
146 [_scrollView addSubview:_contentView.get()];
148 [self _frameOrBoundsChanged];
150 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
151 [center addObserver:self selector:@selector(_keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];
152 [center addObserver:self selector:@selector(_keyboardDidChangeFrame:) name:UIKeyboardDidChangeFrameNotification object:nil];
153 [center addObserver:self selector:@selector(_keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
154 [center addObserver:self selector:@selector(_keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
158 _wkView = [[WKView alloc] initWithFrame:bounds context:context configuration:std::move(webPageConfiguration)];
159 [self addSubview:_wkView.get()];
160 _page = WebKit::toImpl([_wkView pageRef]);
163 _navigationState = std::make_unique<WebKit::NavigationState>(self);
164 _page->setPolicyClient(_navigationState->createPolicyClient());
165 _page->setLoaderClient(_navigationState->createLoaderClient());
167 _page->setUIClient(std::make_unique<WebKit::UIClient>(self));
174 [_remoteObjectRegistry _invalidate];
176 [[NSNotificationCenter defaultCenter] removeObserver:self];
182 - (WKWebViewConfiguration *)configuration
184 return [[_configuration copy] autorelease];
187 - (WKBackForwardList *)backForwardList
189 return wrapper(_page->backForwardList());
192 - (id <WKNavigationDelegate>)navigationDelegate
194 return [_navigationState->navigationDelegate().leakRef() autorelease];
197 - (void)setNavigationDelegate:(id <WKNavigationDelegate>)navigationDelegate
199 _navigationState->setNavigationDelegate(navigationDelegate);
202 - (id <WKUIDelegate>)UIDelegate
204 return [static_cast<WebKit::UIClient&>(_page->uiClient()).delegate().leakRef() autorelease];
207 - (void)setUIDelegate:(id<WKUIDelegate>)UIDelegate
209 static_cast<WebKit::UIClient&>(_page->uiClient()).setDelegate(UIDelegate);
212 - (WKNavigation *)loadRequest:(NSURLRequest *)request
214 uint64_t navigationID = _page->loadRequest(request);
215 auto navigation = _navigationState->createLoadRequestNavigation(navigationID, request);
217 return [navigation.leakRef() autorelease];
220 - (WKNavigation *)goToBackForwardListItem:(WKBackForwardListItem *)item
222 _page->goToBackForwardItem(&item._item);
224 // FIXME: return a WKNavigation object.
228 - (IBAction)stopLoading:(id)sender
230 _page->stopLoading();
235 return _page->pageLoadState().title();
240 return [NSURL _web_URLWithWTFString:_page->pageLoadState().activeURL()];
245 return _page->pageLoadState().isLoading();
248 - (double)estimatedProgress
250 return _page->pageLoadState().estimatedProgress();
253 - (BOOL)hasOnlySecureContent
255 return _page->pageLoadState().hasOnlySecureContent();
258 // FIXME: This should be KVO compliant.
261 return !!_page->backForwardList().backItem();
264 // FIXME: This should be KVO compliant.
267 return !!_page->backForwardList().forwardItem();
270 // FIXME: This should return a WKNavigation object.
276 // FIXME: This should return a WKNavigation object.
282 #pragma mark iOS-specific methods
285 - (void)setFrame:(CGRect)frame
287 CGRect oldFrame = self.frame;
288 [super setFrame:frame];
290 if (!CGSizeEqualToSize(oldFrame.size, frame.size))
291 [self _frameOrBoundsChanged];
294 - (void)setBounds:(CGRect)bounds
296 CGRect oldBounds = self.bounds;
297 [super setBounds:bounds];
299 if (!CGSizeEqualToSize(oldBounds.size, bounds.size))
300 [self _frameOrBoundsChanged];
303 - (UIScrollView *)scrollView
305 return _scrollView.get();
308 - (WKBrowsingContextController *)browsingContextController
310 return [_contentView browsingContextController];
313 #pragma mark WKContentViewDelegate
315 - (void)contentViewDidCommitLoadForMainFrame:(WKContentView *)contentView
317 _isWaitingForNewLayerTreeAfterDidCommitLoad = YES;
320 - (void)contentView:(WKContentView *)contentView didCommitLayerTree:(const WebKit::RemoteLayerTreeTransaction&)layerTreeTransaction
322 [_scrollView setMinimumZoomScale:layerTreeTransaction.minimumScaleFactor()];
323 [_scrollView setMaximumZoomScale:layerTreeTransaction.maximumScaleFactor()];
324 [_scrollView setZoomEnabled:layerTreeTransaction.allowsUserScaling()];
325 if (![_scrollView isZooming] && ![_scrollView isZoomBouncing])
326 [_scrollView setZoomScale:layerTreeTransaction.pageScaleFactor()];
328 if (_gestureController)
329 _gestureController->setRenderTreeSize(layerTreeTransaction.renderTreeSize());
331 if (_isWaitingForNewLayerTreeAfterDidCommitLoad) {
332 UIEdgeInsets inset = [_scrollView contentInset];
333 [_scrollView setContentOffset:CGPointMake(-inset.left, -inset.top)];
334 _isWaitingForNewLayerTreeAfterDidCommitLoad = NO;
339 - (RetainPtr<CGImageRef>)takeViewSnapshotForContentView:(WKContentView *)contentView
341 // FIXME: We should be able to use acquire an IOSurface directly, instead of going to CGImage here and back in ViewSnapshotStore.
342 UIGraphicsBeginImageContextWithOptions(self.bounds.size, YES, self.window.screen.scale);
343 [self drawViewHierarchyInRect:[self bounds] afterScreenUpdates:NO];
344 UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
345 UIGraphicsEndImageContext();
346 return image.CGImage;
349 #pragma mark - UIScrollViewDelegate
351 - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
353 ASSERT(_scrollView == scrollView);
354 return _contentView.get();
357 - (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view
359 if (scrollView.pinchGestureRecognizer.state == UIGestureRecognizerStateBegan)
360 [_contentView willStartUserTriggeredZoom];
361 [_contentView willStartZoomOrScroll];
364 - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
366 if (scrollView.panGestureRecognizer.state == UIGestureRecognizerStateBegan)
367 [_contentView willStartUserTriggeredScroll];
368 [_contentView willStartZoomOrScroll];
371 - (void)_didFinishScrolling
373 [self _updateVisibleContentRects];
374 [_contentView didFinishScrolling];
377 - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
379 // If we're decelerating, scroll offset will be updated when scrollViewDidFinishDecelerating: is called.
381 [self _didFinishScrolling];
384 - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
386 [self _didFinishScrolling];
389 - (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView
391 [self _didFinishScrolling];
394 - (void)scrollViewDidScroll:(UIScrollView *)scrollView
396 [self _updateVisibleContentRects];
399 - (void)scrollViewDidZoom:(UIScrollView *)scrollView
401 [self _updateVisibleContentRects];
404 - (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale
406 ASSERT(scrollView == _scrollView);
407 [self _updateVisibleContentRects];
408 [_contentView didZoomToScale:scale];
411 - (void)_frameOrBoundsChanged
413 CGRect bounds = self.bounds;
415 if (!_hasStaticMinimumLayoutSize)
416 [_contentView setMinimumLayoutSize:bounds.size];
417 [_scrollView setFrame:bounds];
418 [_contentView setMinimumSize:bounds.size];
419 [self _updateVisibleContentRects];
422 - (void)_updateVisibleContentRects
424 CGRect fullViewRect = self.bounds;
425 CGRect visibleRectInContentCoordinates = [self convertRect:fullViewRect toView:_contentView.get()];
427 CGRect unobscuredRect = UIEdgeInsetsInsetRect(fullViewRect, _obscuredInsets);
428 CGRect unobscuredRectInContentCoordinates = [self convertRect:unobscuredRect toView:_contentView.get()];
430 CGFloat scaleFactor = [_scrollView zoomScale];
432 [_contentView didUpdateVisibleRect:visibleRectInContentCoordinates unobscuredRect:unobscuredRectInContentCoordinates scale:scaleFactor];
435 - (void)_keyboardChangedWithInfo:(NSDictionary *)keyboardInfo adjustScrollView:(BOOL)adjustScrollView
437 // FIXME: We will also need to adjust the unobscured rect by taking into account the keyboard rect and the obscured insets.
438 if (adjustScrollView)
439 [_scrollView _adjustForAutomaticKeyboardInfo:keyboardInfo animated:YES lastAdjustment:&_lastAdjustmentForScroller];
442 - (void)_keyboardWillChangeFrame:(NSNotification *)notification
444 if ([_contentView isAssistingNode])
445 [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES];
448 - (void)_keyboardDidChangeFrame:(NSNotification *)notification
450 [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:NO];
453 - (void)_keyboardWillShow:(NSNotification *)notification
455 if ([_contentView isAssistingNode])
456 [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES];
459 - (void)_keyboardWillHide:(NSNotification *)notification
461 // Ignore keyboard will hide notifications sent during rotation. They're just there for
462 // backwards compatibility reasons and processing the will hide notification would
463 // temporarily screw up the the unobscured view area.
464 if ([[UIPeripheralHost sharedInstance] rotationState])
467 [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES];
470 - (void)setAllowsBackForwardNavigationGestures:(BOOL)allowsBackForwardNavigationGestures
472 if (_allowsBackForwardNavigationGestures == allowsBackForwardNavigationGestures)
475 _allowsBackForwardNavigationGestures = allowsBackForwardNavigationGestures;
477 if (allowsBackForwardNavigationGestures) {
478 if (!_gestureController) {
479 _gestureController = std::make_unique<WebKit::ViewGestureController>(*_page);
480 _gestureController->installSwipeHandler(self, [self scrollView]);
483 _gestureController = nullptr;
485 _page->setShouldRecordNavigationSnapshots(allowsBackForwardNavigationGestures);
488 - (BOOL)allowsBackForwardNavigationGestures
490 return _allowsBackForwardNavigationGestures;
495 #pragma mark OS X-specific methods
499 - (void)resizeSubviewsWithOldSize:(NSSize)oldSize
501 [_wkView setFrame:self.bounds];
504 - (void)setAllowsBackForwardNavigationGestures:(BOOL)allowsBackForwardNavigationGestures
506 [_wkView setAllowsBackForwardNavigationGestures:allowsBackForwardNavigationGestures];
509 - (BOOL)allowsBackForwardNavigationGestures
511 return [_wkView allowsBackForwardNavigationGestures];
514 - (void)setAllowsMagnification:(BOOL)allowsMagnification
516 [_wkView setAllowsMagnification:allowsMagnification];
519 - (BOOL)allowsMagnification
521 return [_wkView allowsMagnification];
524 - (void)setMagnification:(CGFloat)magnification
526 [_wkView setMagnification:magnification];
529 - (CGFloat)magnification
531 return [_wkView magnification];
534 - (void)setMagnification:(CGFloat)magnification centeredAtPoint:(CGPoint)point
536 [_wkView setMagnification:magnification centeredAtPoint:NSPointFromCGPoint(point)];
543 @implementation WKWebView (WKPrivate)
545 - (WKRemoteObjectRegistry *)_remoteObjectRegistry
547 if (!_remoteObjectRegistry) {
548 _remoteObjectRegistry = adoptNS([[WKRemoteObjectRegistry alloc] _initWithMessageSender:*_page]);
549 _page->process().context().addMessageReceiver(Messages::RemoteObjectRegistry::messageReceiverName(), _page->pageID(), [_remoteObjectRegistry remoteObjectRegistry]);
552 return _remoteObjectRegistry.get();
555 - (WKBrowsingContextHandle *)_handle
557 return [[[WKBrowsingContextHandle alloc] _initWithPageID:_page->pageID()] autorelease];
560 - (_WKRenderingProgressEvents)_observedRenderingProgressEvents
562 return _observedRenderingProgressEvents;
565 - (id <WKHistoryDelegatePrivate>)_historyDelegate
567 return [_navigationState->historyDelegate().leakRef() autorelease];
570 - (void)_setHistoryDelegate:(id <WKHistoryDelegatePrivate>)historyDelegate
572 _navigationState->setHistoryDelegate(historyDelegate);
575 - (NSURL *)_unreachableURL
577 return [NSURL _web_URLWithWTFString:_page->pageLoadState().unreachableURL()];
580 - (void)_loadAlternateHTMLString:(NSString *)string baseURL:(NSURL *)baseURL forUnreachableURL:(NSURL *)unreachableURL
582 _page->loadAlternateHTMLString(string, [baseURL _web_originalDataAsWTFString], [unreachableURL _web_originalDataAsWTFString]);
585 - (WKNavigation *)_reload
587 _page->reload(false);
589 // FIXME: return a WKNavigation object.
593 - (NSArray *)_certificateChain
595 if (WebKit::WebFrameProxy* mainFrame = _page->mainFrame())
596 return mainFrame->certificateInfo() ? (NSArray *)mainFrame->certificateInfo()->certificateInfo().certificateChain() : nil;
601 - (NSURL *)_committedURL
603 return [NSURL _web_URLWithWTFString:_page->pageLoadState().url()];
606 - (NSString *)_applicationNameForUserAgent
608 return _page->applicationNameForUserAgent();
611 - (void)_setApplicationNameForUserAgent:(NSString *)applicationNameForUserAgent
613 _page->setApplicationNameForUserAgent(applicationNameForUserAgent);
616 - (pid_t)_webProcessIdentifier
618 return _page->processIdentifier();
621 - (NSData *)_sessionState
623 return [wrapper(*_page->sessionStateData(nullptr, nullptr).leakRef()) autorelease];
626 static void releaseNSData(unsigned char*, const void* data)
628 [(NSData *)data release];
631 - (void)_restoreFromSessionState:(NSData *)sessionState
633 [sessionState retain];
634 _page->restoreFromSessionStateData(API::Data::createWithoutCopying((const unsigned char*)sessionState.bytes, sessionState.length, releaseNSData, sessionState).get());
637 - (BOOL)_privateBrowsingEnabled
639 return [_configuration preferences]->_preferences->privateBrowsingEnabled();
642 - (void)_setPrivateBrowsingEnabled:(BOOL)privateBrowsingEnabled
644 [_configuration preferences]->_preferences->setPrivateBrowsingEnabled(privateBrowsingEnabled);
647 static inline WebCore::LayoutMilestones layoutMilestones(_WKRenderingProgressEvents events)
649 WebCore::LayoutMilestones milestones = 0;
651 if (events & _WKRenderingProgressEventFirstLayout)
652 milestones |= WebCore::DidFirstLayout;
654 if (events & _WKRenderingProgressEventFirstPaintWithSignificantArea)
655 milestones |= WebCore::DidHitRelevantRepaintedObjectsAreaThreshold;
660 - (void)_setObservedRenderingProgressEvents:(_WKRenderingProgressEvents)observedRenderingProgressEvents
662 _observedRenderingProgressEvents = observedRenderingProgressEvents;
663 _page->listenForLayoutMilestones(layoutMilestones(observedRenderingProgressEvents));
666 #pragma mark iOS-specific methods
670 - (CGSize)_minimumLayoutSizeOverride
672 ASSERT(_hasStaticMinimumLayoutSize);
673 return _minimumLayoutSizeOverride;
676 - (void)_setMinimumLayoutSizeOverride:(CGSize)minimumLayoutSizeOverride
678 _hasStaticMinimumLayoutSize = YES;
679 _minimumLayoutSizeOverride = minimumLayoutSizeOverride;
680 [_contentView setMinimumLayoutSize:minimumLayoutSizeOverride];
683 - (UIEdgeInsets)_obscuredInsets
685 return _obscuredInsets;
688 - (void)_setObscuredInsets:(UIEdgeInsets)obscuredInsets
690 ASSERT(obscuredInsets.top >= 0);
691 ASSERT(obscuredInsets.left >= 0);
692 ASSERT(obscuredInsets.bottom >= 0);
693 ASSERT(obscuredInsets.right >= 0);
694 _obscuredInsets = obscuredInsets;
695 [self _updateVisibleContentRects];
698 - (UIColor *)_pageExtendedBackgroundColor
700 WebCore::Color color = _page->pageExtendedBackgroundColor();
701 if (!color.isValid())
704 return [UIColor colorWithRed:(color.red() / 255.0) green:(color.green() / 255.0) blue:(color.blue() / 255.0) alpha:(color.alpha() / 255.0)];
707 - (void)_setBackgroundExtendsBeyondPage:(BOOL)backgroundExtends
709 _page->setBackgroundExtendsBeyondPage(backgroundExtends);
712 - (BOOL)_backgroundExtendsBeyondPage
714 return _page->backgroundExtendsBeyondPage();
717 - (void)_beginInteractiveObscuredInsetsChange
719 ASSERT(!_isChangingObscuredInsetsInteractively);
720 _isChangingObscuredInsetsInteractively = YES;
723 - (void)_endInteractiveObscuredInsetsChange
725 ASSERT(_isChangingObscuredInsetsInteractively);
726 _isChangingObscuredInsetsInteractively = NO;
733 #endif // WK_API_ENABLED