blob: d0cbde1a9ad959655d2ed417a9f8f83d272b0dab [file] [log] [blame]
/*
* Copyright (C) 2013-2016 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "config.h"
#import "WKContentViewInteraction.h"
#if PLATFORM(IOS)
#import "APIPageConfiguration.h"
#import "AccessibilityIOS.h"
#import "FullscreenClient.h"
#import "InputViewUpdateDeferrer.h"
#import "Logging.h"
#import "PageClientImplIOS.h"
#import "PrintInfo.h"
#import "RemoteLayerTreeDrawingAreaProxy.h"
#import "RemoteScrollingCoordinatorProxy.h"
#import "SmartMagnificationController.h"
#import "UIKitSPI.h"
#import "WKBrowsingContextControllerInternal.h"
#import "WKBrowsingContextGroupPrivate.h"
#import "WKInspectorHighlightView.h"
#import "WKPreferencesInternal.h"
#import "WKProcessGroupPrivate.h"
#import "WKWebViewConfiguration.h"
#import "WKWebViewInternal.h"
#import "WebFrameProxy.h"
#import "WebKit2Initialize.h"
#import "WebPageGroup.h"
#import "WebProcessPool.h"
#import "_WKFrameHandleInternal.h"
#import "_WKWebViewPrintFormatterInternal.h"
#import <CoreGraphics/CoreGraphics.h>
#import <WebCore/FloatQuad.h>
#import <WebCore/FrameView.h>
#import <WebCore/InspectorOverlay.h>
#import <WebCore/NotImplemented.h>
#import <WebCore/PlatformScreen.h>
#import <pal/spi/cocoa/QuartzCoreSPI.h>
#import <wtf/RetainPtr.h>
#import <wtf/text/TextStream.h>
using namespace WebCore;
using namespace WebKit;
namespace WebKit {
class HistoricalVelocityData {
public:
struct VelocityData {
VelocityData()
: horizontalVelocity(0)
, verticalVelocity(0)
, scaleChangeRate(0)
{
}
VelocityData(double horizontalVelocity, double verticalVelocity, double scaleChangeRate)
: horizontalVelocity(horizontalVelocity)
, verticalVelocity(verticalVelocity)
, scaleChangeRate(scaleChangeRate)
{
}
double horizontalVelocity;
double verticalVelocity;
double scaleChangeRate;
};
HistoricalVelocityData()
: m_historySize(0)
, m_latestDataIndex(0)
{
}
VelocityData velocityForNewData(CGPoint newPosition, double scale, MonotonicTime timestamp)
{
// Due to all the source of rect update, the input is very noisy. To smooth the output, we accumulate all changes
// within 1 frame as a single update. No speed computation is ever done on data within the same frame.
const Seconds filteringThreshold(1.0 / 60);
VelocityData velocityData;
if (m_historySize > 0) {
unsigned oldestDataIndex;
unsigned distanceToLastHistoricalData = m_historySize - 1;
if (distanceToLastHistoricalData <= m_latestDataIndex)
oldestDataIndex = m_latestDataIndex - distanceToLastHistoricalData;
else
oldestDataIndex = m_historySize - (distanceToLastHistoricalData - m_latestDataIndex);
Seconds timeDelta = timestamp - m_history[oldestDataIndex].timestamp;
if (timeDelta > filteringThreshold) {
Data& oldestData = m_history[oldestDataIndex];
velocityData = VelocityData((newPosition.x - oldestData.position.x) / timeDelta.seconds(), (newPosition.y - oldestData.position.y) / timeDelta.seconds(), (scale - oldestData.scale) / timeDelta.seconds());
}
}
Seconds timeSinceLastAppend = timestamp - m_lastAppendTimestamp;
if (timeSinceLastAppend > filteringThreshold)
append(newPosition, scale, timestamp);
else
m_history[m_latestDataIndex] = { timestamp, newPosition, scale };
return velocityData;
}
void clear() { m_historySize = 0; }
private:
void append(CGPoint newPosition, double scale, MonotonicTime timestamp)
{
m_latestDataIndex = (m_latestDataIndex + 1) % maxHistoryDepth;
m_history[m_latestDataIndex] = { timestamp, newPosition, scale };
unsigned size = m_historySize + 1;
if (size <= maxHistoryDepth)
m_historySize = size;
m_lastAppendTimestamp = timestamp;
}
static const unsigned maxHistoryDepth = 3;
unsigned m_historySize;
unsigned m_latestDataIndex;
MonotonicTime m_lastAppendTimestamp;
struct Data {
MonotonicTime timestamp;
CGPoint position;
double scale;
} m_history[maxHistoryDepth];
};
} // namespace WebKit
@interface WKInspectorIndicationView : UIView
@end
@implementation WKInspectorIndicationView
- (instancetype)initWithFrame:(CGRect)frame
{
if (!(self = [super initWithFrame:frame]))
return nil;
self.userInteractionEnabled = NO;
self.backgroundColor = [UIColor colorWithRed:(111.0 / 255.0) green:(168.0 / 255.0) blue:(220.0 / 255.0) alpha:0.66f];
return self;
}
@end
@implementation WKContentView {
std::unique_ptr<PageClientImpl> _pageClient;
RetainPtr<WKBrowsingContextController> _browsingContextController;
RetainPtr<UIView> _rootContentView;
RetainPtr<UIView> _fixedClippingView;
RetainPtr<WKInspectorIndicationView> _inspectorIndicationView;
RetainPtr<WKInspectorHighlightView> _inspectorHighlightView;
HistoricalVelocityData _historicalKinematicData;
RetainPtr<NSUndoManager> _undoManager;
BOOL _isPrintingToPDF;
RetainPtr<CGPDFDocumentRef> _printedDocument;
}
- (instancetype)_commonInitializationWithProcessPool:(WebKit::WebProcessPool&)processPool configuration:(Ref<API::PageConfiguration>&&)configuration
{
ASSERT(_pageClient);
_page = processPool.createWebPage(*_pageClient, WTFMove(configuration));
_page->initializeWebPage();
_page->setIntrinsicDeviceScaleFactor(screenScaleFactor([UIScreen mainScreen]));
_page->setUseFixedLayout(true);
_page->setDelegatesScrolling(true);
#if ENABLE(FULLSCREEN_API) && WK_API_ENABLED
_page->setFullscreenClient(std::make_unique<WebKit::FullscreenClient>(_webView));
#endif
WebProcessPool::statistics().wkViewCount++;
_rootContentView = adoptNS([[UIView alloc] init]);
[_rootContentView layer].name = @"RootContent";
[_rootContentView layer].masksToBounds = NO;
[_rootContentView setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight];
_fixedClippingView = adoptNS([[UIView alloc] init]);
[_fixedClippingView layer].name = @"FixedClipping";
[_fixedClippingView layer].masksToBounds = YES;
[_fixedClippingView layer].anchorPoint = CGPointZero;
[self addSubview:_fixedClippingView.get()];
[_fixedClippingView addSubview:_rootContentView.get()];
[self setupInteraction];
[self setUserInteractionEnabled:YES];
self.layer.hitTestsAsOpaque = YES;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_applicationWillResignActive:) name:UIApplicationWillResignActiveNotification object:[UIApplication sharedApplication]];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_applicationDidBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:[UIApplication sharedApplication]];
return self;
}
- (instancetype)initWithFrame:(CGRect)frame processPool:(WebKit::WebProcessPool&)processPool configuration:(Ref<API::PageConfiguration>&&)configuration webView:(WKWebView *)webView
{
if (!(self = [super initWithFrame:frame webView:webView]))
return nil;
InitializeWebKit2();
_pageClient = std::make_unique<PageClientImpl>(self, webView);
_webView = webView;
return [self _commonInitializationWithProcessPool:processPool configuration:WTFMove(configuration)];
}
- (void)dealloc
{
[self cleanupInteraction];
[[NSNotificationCenter defaultCenter] removeObserver:self];
_page->close();
WebProcessPool::statistics().wkViewCount--;
[super dealloc];
}
- (WebPageProxy*)page
{
return _page.get();
}
- (void)willMoveToWindow:(UIWindow *)newWindow
{
[super willMoveToWindow:newWindow];
NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
UIWindow *window = self.window;
if (window)
[defaultCenter removeObserver:self name:UIWindowDidMoveToScreenNotification object:window];
if (newWindow) {
[defaultCenter addObserver:self selector:@selector(_windowDidMoveToScreenNotification:) name:UIWindowDidMoveToScreenNotification object:newWindow];
[self _updateForScreen:newWindow.screen];
}
}
- (WKBrowsingContextController *)browsingContextController
{
if (!_browsingContextController)
_browsingContextController = adoptNS([[WKBrowsingContextController alloc] _initWithPageRef:toAPI(_page.get())]);
return _browsingContextController.get();
}
- (WKPageRef)_pageRef
{
return toAPI(_page.get());
}
- (BOOL)isAssistingNode
{
return [self isEditable];
}
- (void)_showInspectorHighlight:(const WebCore::Highlight&)highlight
{
if (!_inspectorHighlightView) {
_inspectorHighlightView = adoptNS([[WKInspectorHighlightView alloc] initWithFrame:CGRectZero]);
[self insertSubview:_inspectorHighlightView.get() aboveSubview:_rootContentView.get()];
}
[_inspectorHighlightView update:highlight];
}
- (void)_hideInspectorHighlight
{
if (_inspectorHighlightView) {
[_inspectorHighlightView removeFromSuperview];
_inspectorHighlightView = nil;
}
}
- (BOOL)isShowingInspectorIndication
{
return !!_inspectorIndicationView;
}
- (void)setShowingInspectorIndication:(BOOL)show
{
if (show) {
if (!_inspectorIndicationView) {
_inspectorIndicationView = adoptNS([[WKInspectorIndicationView alloc] initWithFrame:[self bounds]]);
[_inspectorIndicationView setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight];
[self insertSubview:_inspectorIndicationView.get() aboveSubview:_rootContentView.get()];
}
} else {
if (_inspectorIndicationView) {
[_inspectorIndicationView removeFromSuperview];
_inspectorIndicationView = nil;
}
}
}
- (void)updateFixedClippingView:(FloatRect)fixedPositionRectForUI
{
FloatRect clippingBounds = [self bounds];
clippingBounds.unite(fixedPositionRectForUI);
[_fixedClippingView setCenter:clippingBounds.location()]; // Not really the center since we set an anchor point.
[_fixedClippingView setBounds:clippingBounds];
}
- (void)_didExitStableState
{
_needsDeferredEndScrollingSelectionUpdate = self.shouldHideSelectionWhenScrolling;
if (!_needsDeferredEndScrollingSelectionUpdate)
return;
[_textSelectionAssistant deactivateSelection];
#if !ENABLE(MINIMAL_SIMULATOR)
[[_webSelectionAssistant selectionView] setHidden:YES];
#endif
}
- (CGRect)_computeUnobscuredContentRectRespectingInputViewBounds:(CGRect)unobscuredContentRect inputViewBounds:(CGRect)inputViewBounds
{
// The input view bounds are in window coordinates, but the unobscured rect is in content coordinates. Account for this by converting input view bounds to content coordinates.
CGRect inputViewBoundsInContentCoordinates = [self.window convertRect:inputViewBounds toView:self];
if (CGRectGetHeight(inputViewBoundsInContentCoordinates))
unobscuredContentRect.size.height = std::min<float>(CGRectGetHeight(unobscuredContentRect), CGRectGetMinY(inputViewBoundsInContentCoordinates) - CGRectGetMinY(unobscuredContentRect));
return unobscuredContentRect;
}
- (void)didUpdateVisibleRect:(CGRect)visibleContentRect
unobscuredRect:(CGRect)unobscuredContentRect
unobscuredRectInScrollViewCoordinates:(CGRect)unobscuredRectInScrollViewCoordinates
obscuredInsets:(UIEdgeInsets)obscuredInsets
unobscuredSafeAreaInsets:(UIEdgeInsets)unobscuredSafeAreaInsets
inputViewBounds:(CGRect)inputViewBounds
scale:(CGFloat)zoomScale minimumScale:(CGFloat)minimumScale
inStableState:(BOOL)isStableState
isChangingObscuredInsetsInteractively:(BOOL)isChangingObscuredInsetsInteractively
enclosedInScrollableAncestorView:(BOOL)enclosedInScrollableAncestorView
{
auto drawingArea = _page->drawingArea();
if (!drawingArea)
return;
MonotonicTime timestamp = MonotonicTime::now();
HistoricalVelocityData::VelocityData velocityData;
if (!isStableState)
velocityData = _historicalKinematicData.velocityForNewData(visibleContentRect.origin, zoomScale, timestamp);
else
_historicalKinematicData.clear();
RemoteScrollingCoordinatorProxy* scrollingCoordinator = _page->scrollingCoordinatorProxy();
CGRect unobscuredContentRectRespectingInputViewBounds = [self _computeUnobscuredContentRectRespectingInputViewBounds:unobscuredContentRect inputViewBounds:inputViewBounds];
FloatRect fixedPositionRectForLayout = _page->computeCustomFixedPositionRect(unobscuredContentRect, unobscuredContentRectRespectingInputViewBounds, _page->customFixedPositionRect(), zoomScale, FrameView::LayoutViewportConstraint::ConstrainedToDocumentRect, scrollingCoordinator->visualViewportEnabled());
VisibleContentRectUpdateInfo visibleContentRectUpdateInfo(
visibleContentRect,
unobscuredContentRect,
unobscuredRectInScrollViewCoordinates,
unobscuredContentRectRespectingInputViewBounds,
fixedPositionRectForLayout,
WebCore::FloatBoxExtent(obscuredInsets.top, obscuredInsets.right, obscuredInsets.bottom, obscuredInsets.left),
WebCore::FloatBoxExtent(unobscuredSafeAreaInsets.top, unobscuredSafeAreaInsets.right, unobscuredSafeAreaInsets.bottom, unobscuredSafeAreaInsets.left),
zoomScale,
isStableState,
_sizeChangedSinceLastVisibleContentRectUpdate,
isChangingObscuredInsetsInteractively,
_webView._allowsViewportShrinkToFit,
enclosedInScrollableAncestorView,
timestamp,
velocityData.horizontalVelocity,
velocityData.verticalVelocity,
velocityData.scaleChangeRate,
downcast<RemoteLayerTreeDrawingAreaProxy>(*drawingArea).lastCommittedLayerTreeTransactionID());
LOG_WITH_STREAM(VisibleRects, stream << "-[WKContentView didUpdateVisibleRect]" << visibleContentRectUpdateInfo.dump());
bool wasStableState = _page->inStableState();
_page->updateVisibleContentRects(visibleContentRectUpdateInfo);
_sizeChangedSinceLastVisibleContentRectUpdate = NO;
FloatRect fixedPositionRect = _page->computeCustomFixedPositionRect(_page->unobscuredContentRect(), _page->unobscuredContentRectRespectingInputViewBounds(), _page->customFixedPositionRect(), zoomScale, FrameView::LayoutViewportConstraint::Unconstrained, scrollingCoordinator->visualViewportEnabled());
scrollingCoordinator->viewportChangedViaDelegatedScrolling(scrollingCoordinator->rootScrollingNodeID(), fixedPositionRect, zoomScale);
drawingArea->updateDebugIndicator();
[self updateFixedClippingView:fixedPositionRect];
if (wasStableState && !isStableState)
[self _didExitStableState];
}
- (void)didFinishScrolling
{
[self _didEndScrollingOrZooming];
}
- (void)didInterruptScrolling
{
_historicalKinematicData.clear();
}
- (void)willStartZoomOrScroll
{
[self _willStartScrollingOrZooming];
}
- (void)didZoomToScale:(CGFloat)scale
{
[self _didEndScrollingOrZooming];
}
- (NSUndoManager *)undoManager
{
if (!_undoManager)
_undoManager = adoptNS([[NSUndoManager alloc] init]);
return _undoManager.get();
}
#pragma mark Internal
- (void)_windowDidMoveToScreenNotification:(NSNotification *)notification
{
ASSERT(notification.object == self.window);
UIScreen *screen = notification.userInfo[UIWindowNewScreenUserInfoKey];
[self _updateForScreen:screen];
}
- (void)_updateForScreen:(UIScreen *)screen
{
ASSERT(screen);
_page->setIntrinsicDeviceScaleFactor(screenScaleFactor(screen));
[self _accessibilityRegisterUIProcessTokens];
}
- (void)_setAccessibilityWebProcessToken:(NSData *)data
{
// This means the web process has checked in and we should send information back to that process.
[self _accessibilityRegisterUIProcessTokens];
}
static void storeAccessibilityRemoteConnectionInformation(id element, pid_t pid, mach_port_t sendPort, NSUUID *uuid)
{
// The accessibility bundle needs to know the uuid, pid and mach_port that this object will refer to.
objc_setAssociatedObject(element, (void*)[@"ax-uuid" hash], uuid, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_setAssociatedObject(element, (void*)[@"ax-pid" hash], @(pid), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_setAssociatedObject(element, (void*)[@"ax-machport" hash], @(sendPort), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)_accessibilityRegisterUIProcessTokens
{
auto uuid = [NSUUID UUID];
NSData *remoteElementToken = newAccessibilityRemoteToken(uuid);
// Store information about the WebProcess that can later be retrieved by the iOS Accessibility runtime.
if (_page->process().state() == WebProcessProxy::State::Running) {
IPC::Connection* connection = _page->process().connection();
storeAccessibilityRemoteConnectionInformation(self, _page->process().processIdentifier(), connection->identifier().port, uuid);
IPC::DataReference elementToken = IPC::DataReference(reinterpret_cast<const uint8_t*>([remoteElementToken bytes]), [remoteElementToken length]);
_page->registerUIProcessAccessibilityTokens(elementToken, elementToken);
}
}
- (void)_webViewDestroyed
{
_webView = nil;
}
#pragma mark PageClientImpl methods
- (std::unique_ptr<DrawingAreaProxy>)_createDrawingAreaProxy
{
return std::make_unique<RemoteLayerTreeDrawingAreaProxy>(*_page);
}
- (void)_processDidExit
{
[self cleanupInteraction];
[self setShowingInspectorIndication:NO];
[self _hideInspectorHighlight];
}
- (void)_didRelaunchProcess
{
[self _accessibilityRegisterUIProcessTokens];
[self setupInteraction];
}
- (void)_didCommitLoadForMainFrame
{
[self _stopAssistingNode];
[self _cancelLongPressGestureRecognizer];
[_webView _didCommitLoadForMainFrame];
}
- (void)_didCommitLayerTree:(const WebKit::RemoteLayerTreeTransaction&)layerTreeTransaction
{
CGSize contentsSize = layerTreeTransaction.contentsSize();
CGPoint scrollOrigin = -layerTreeTransaction.scrollOrigin();
CGRect contentBounds = { scrollOrigin, contentsSize };
LOG_WITH_STREAM(VisibleRects, stream << "-[WKContentView _didCommitLayerTree:] transactionID " << layerTreeTransaction.transactionID() << " contentBounds " << FloatRect(contentBounds));
BOOL boundsChanged = !CGRectEqualToRect([self bounds], contentBounds);
if (boundsChanged)
[self setBounds:contentBounds];
[_webView _didCommitLayerTree:layerTreeTransaction];
if (_interactionViewsContainerView) {
FloatPoint scaledOrigin = layerTreeTransaction.scrollOrigin();
float scale = [[_webView scrollView] zoomScale];
scaledOrigin.scale(scale);
[_interactionViewsContainerView setFrame:CGRectMake(scaledOrigin.x(), scaledOrigin.y(), 0, 0)];
}
if (boundsChanged) {
// FIXME: factor computeCustomFixedPositionRect() into something that gives us this rect.
FloatRect fixedPositionRect = _page->computeCustomFixedPositionRect(_page->unobscuredContentRect(), _page->unobscuredContentRectRespectingInputViewBounds(), _page->customFixedPositionRect(), [[_webView scrollView] zoomScale]);
[self updateFixedClippingView:fixedPositionRect];
// We need to push the new content bounds to the webview to update fixed position rects.
[_webView _scheduleVisibleContentRectUpdate];
}
// Updating the selection requires a full editor state. If the editor state is missing post layout
// data then it means there is a layout pending and we're going to be called again after the layout
// so we delay the selection update.
if (!_page->editorState().isMissingPostLayoutData)
[self _updateChangedSelection];
}
- (void)_layerTreeCommitComplete
{
[_webView _layerTreeCommitComplete];
}
- (void)_setAcceleratedCompositingRootView:(UIView *)rootView
{
for (UIView* subview in [_rootContentView subviews])
[subview removeFromSuperview];
[_rootContentView addSubview:rootView];
}
- (BOOL)_scrollToRect:(CGRect)targetRect withOrigin:(CGPoint)origin minimumScrollDistance:(CGFloat)minimumScrollDistance
{
return [_webView _scrollToRect:targetRect origin:origin minimumScrollDistance:minimumScrollDistance];
}
- (void)_zoomToFocusRect:(CGRect)rectToFocus selectionRect:(CGRect)selectionRect insideFixed:(BOOL)insideFixed fontSize:(float)fontSize minimumScale:(double)minimumScale maximumScale:(double)maximumScale allowScaling:(BOOL)allowScaling forceScroll:(BOOL)forceScroll
{
[_webView _zoomToFocusRect:rectToFocus
selectionRect:selectionRect
insideFixed:insideFixed
fontSize:fontSize
minimumScale:minimumScale
maximumScale:maximumScale
allowScaling:allowScaling
forceScroll:forceScroll];
}
- (BOOL)_zoomToRect:(CGRect)targetRect withOrigin:(CGPoint)origin fitEntireRect:(BOOL)fitEntireRect minimumScale:(double)minimumScale maximumScale:(double)maximumScale minimumScrollDistance:(CGFloat)minimumScrollDistance
{
return [_webView _zoomToRect:targetRect withOrigin:origin fitEntireRect:fitEntireRect minimumScale:minimumScale maximumScale:maximumScale minimumScrollDistance:minimumScrollDistance];
}
- (void)_zoomOutWithOrigin:(CGPoint)origin
{
return [_webView _zoomOutWithOrigin:origin animated:YES];
}
- (void)_zoomToInitialScaleWithOrigin:(CGPoint)origin
{
return [_webView _zoomToInitialScaleWithOrigin:origin animated:YES];
}
- (void)_applicationWillResignActive:(NSNotification*)notification
{
_page->applicationWillResignActive();
}
- (void)_applicationDidCreateWindowContext
{
[super _applicationDidCreateWindowContext];
if (auto drawingArea = _page->drawingArea())
drawingArea->hideContentUntilAnyUpdate();
}
- (void)_applicationDidBecomeActive:(NSNotification*)notification
{
_page->applicationDidBecomeActive();
}
@end
#pragma mark Printing
#if !ENABLE(MINIMAL_SIMULATOR)
@interface WKContentView (_WKWebViewPrintFormatter) <_WKWebViewPrintProvider>
@end
@implementation WKContentView (_WKWebViewPrintFormatter)
- (NSUInteger)_wk_pageCountForPrintFormatter:(_WKWebViewPrintFormatter *)printFormatter
{
if (_isPrintingToPDF)
return 0;
uint64_t frameID;
if (_WKFrameHandle *handle = printFormatter.frameToPrint)
frameID = handle._frameID;
else if (auto mainFrame = _page->mainFrame())
frameID = mainFrame->frameID();
else
return 0;
// The first page can have a smaller content rect than subsequent pages if a top content inset
// is specified. Since WebKit requires a uniform content rect for each page during layout, use
// the intersection of the first and non-first page rects.
// FIXME: Teach WebCore::PrintContext to accept an initial content offset when paginating.
CGRect printingRect = CGRectIntersection([printFormatter _pageContentRect:YES], [printFormatter _pageContentRect:NO]);
if (CGRectIsEmpty(printingRect))
return 0;
PrintInfo printInfo;
printInfo.pageSetupScaleFactor = 1;
printInfo.snapshotFirstPage = printFormatter.snapshotFirstPage;
if (printInfo.snapshotFirstPage) {
static const CGFloat maximumPDFHeight = 200 * 72; // maximum PDF height for a single page is 200 inches
CGSize contentSize = self.bounds.size;
printingRect = (CGRect) { CGPointZero, { contentSize.width, std::min(contentSize.height, maximumPDFHeight) } };
[printFormatter _setSnapshotPaperRect:printingRect];
}
printInfo.availablePaperWidth = CGRectGetWidth(printingRect);
printInfo.availablePaperHeight = CGRectGetHeight(printingRect);
_isPrintingToPDF = YES;
auto retainedSelf = retainPtr(self);
return _page->computePagesForPrintingAndDrawToPDF(frameID, printInfo, [retainedSelf](const IPC::DataReference& pdfData, CallbackBase::Error error) {
retainedSelf->_isPrintingToPDF = NO;
if (error != CallbackBase::Error::None)
return;
auto data = adoptCF(CFDataCreate(kCFAllocatorDefault, pdfData.data(), pdfData.size()));
auto dataProvider = adoptCF(CGDataProviderCreateWithCFData(data.get()));
retainedSelf->_printedDocument = adoptCF(CGPDFDocumentCreateWithProvider(dataProvider.get()));
});
}
- (CGPDFDocumentRef)_wk_printedDocument
{
if (_isPrintingToPDF) {
if (!_page->process().connection()->waitForAndDispatchImmediately<Messages::WebPageProxy::DrawToPDFCallback>(_page->pageID(), Seconds::infinity())) {
ASSERT_NOT_REACHED();
return nullptr;
}
ASSERT(!_isPrintingToPDF);
}
return _printedDocument.autorelease();
}
@end
#endif // !ENABLE(MINIMAL_SIMULATOR)
#endif // PLATFORM(IOS)