9d432d3aa32a3a06879b45b133c9999df6ab52a1
[WebKit.git] / Source / WebKit2 / UIProcess / ios / WKContentView.mm
1 /*
2  * Copyright (C) 2013-2016 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 "WKContentViewInteraction.h"
28
29 #if PLATFORM(IOS)
30
31 #import "APIPageConfiguration.h"
32 #import "AccessibilityIOS.h"
33 #import "ApplicationStateTracker.h"
34 #import "Logging.h"
35 #import "PageClientImplIOS.h"
36 #import "PrintInfo.h"
37 #import "RemoteLayerTreeDrawingAreaProxy.h"
38 #import "RemoteScrollingCoordinatorProxy.h"
39 #import "SmartMagnificationController.h"
40 #import "UIKitSPI.h"
41 #import "WKBrowsingContextControllerInternal.h"
42 #import "WKBrowsingContextGroupPrivate.h"
43 #import "WKInspectorHighlightView.h"
44 #import "WKPreferencesInternal.h"
45 #import "WKProcessGroupPrivate.h"
46 #import "WKWebViewConfiguration.h"
47 #import "WKWebViewInternal.h"
48 #import "WebFrameProxy.h"
49 #import "WebKit2Initialize.h"
50 #import "WebPageGroup.h"
51 #import "WebProcessPool.h"
52 #import "WebSystemInterface.h"
53 #import "_WKFrameHandleInternal.h"
54 #import "_WKWebViewPrintFormatterInternal.h"
55 #import <CoreGraphics/CoreGraphics.h>
56 #import <WebCore/FloatQuad.h>
57 #import <WebCore/FrameView.h>
58 #import <WebCore/InspectorOverlay.h>
59 #import <WebCore/NotImplemented.h>
60 #import <WebCore/PlatformScreen.h>
61 #import <WebCore/QuartzCoreSPI.h>
62 #import <WebCore/TextStream.h>
63 #import <wtf/CurrentTime.h>
64 #import <wtf/RetainPtr.h>
65
66 using namespace WebCore;
67 using namespace WebKit;
68
69 namespace WebKit {
70 class HistoricalVelocityData {
71 public:
72     struct VelocityData {
73         VelocityData()
74             : horizontalVelocity(0)
75             , verticalVelocity(0)
76             , scaleChangeRate(0)
77         {
78         }
79
80         VelocityData(double horizontalVelocity, double verticalVelocity, double scaleChangeRate)
81             : horizontalVelocity(horizontalVelocity)
82             , verticalVelocity(verticalVelocity)
83             , scaleChangeRate(scaleChangeRate)
84         {
85         }
86
87         double horizontalVelocity;
88         double verticalVelocity;
89         double scaleChangeRate;
90     };
91
92     HistoricalVelocityData()
93         : m_historySize(0)
94         , m_latestDataIndex(0)
95     {
96     }
97
98     VelocityData velocityForNewData(CGPoint newPosition, double scale, MonotonicTime timestamp)
99     {
100         // Due to all the source of rect update, the input is very noisy. To smooth the output, we accumulate all changes
101         // within 1 frame as a single update. No speed computation is ever done on data within the same frame.
102         const Seconds filteringThreshold(1.0 / 60);
103
104         VelocityData velocityData;
105         if (m_historySize > 0) {
106             unsigned oldestDataIndex;
107             unsigned distanceToLastHistoricalData = m_historySize - 1;
108             if (distanceToLastHistoricalData <= m_latestDataIndex)
109                 oldestDataIndex = m_latestDataIndex - distanceToLastHistoricalData;
110             else
111                 oldestDataIndex = m_historySize - (distanceToLastHistoricalData - m_latestDataIndex);
112
113             Seconds timeDelta = timestamp - m_history[oldestDataIndex].timestamp;
114             if (timeDelta > filteringThreshold) {
115                 Data& oldestData = m_history[oldestDataIndex];
116                 velocityData = VelocityData((newPosition.x - oldestData.position.x) / timeDelta.seconds(), (newPosition.y - oldestData.position.y) / timeDelta.seconds(), (scale - oldestData.scale) / timeDelta.seconds());
117             }
118         }
119
120         Seconds timeSinceLastAppend = timestamp - m_lastAppendTimestamp;
121         if (timeSinceLastAppend > filteringThreshold)
122             append(newPosition, scale, timestamp);
123         else
124             m_history[m_latestDataIndex] = { timestamp, newPosition, scale };
125         return velocityData;
126     }
127
128     void clear() { m_historySize = 0; }
129
130 private:
131     void append(CGPoint newPosition, double scale, MonotonicTime timestamp)
132     {
133         m_latestDataIndex = (m_latestDataIndex + 1) % maxHistoryDepth;
134         m_history[m_latestDataIndex] = { timestamp, newPosition, scale };
135
136         unsigned size = m_historySize + 1;
137         if (size <= maxHistoryDepth)
138             m_historySize = size;
139
140         m_lastAppendTimestamp = timestamp;
141     }
142
143
144     static const unsigned maxHistoryDepth = 3;
145
146     unsigned m_historySize;
147     unsigned m_latestDataIndex;
148     MonotonicTime m_lastAppendTimestamp;
149
150     struct Data {
151         MonotonicTime timestamp;
152         CGPoint position;
153         double scale;
154     } m_history[maxHistoryDepth];
155 };
156 } // namespace WebKit
157
158 @interface WKInspectorIndicationView : UIView
159 @end
160
161 @implementation WKInspectorIndicationView
162
163 - (instancetype)initWithFrame:(CGRect)frame
164 {
165     if (!(self = [super initWithFrame:frame]))
166         return nil;
167     self.userInteractionEnabled = NO;
168     self.backgroundColor = [UIColor colorWithRed:(111.0 / 255.0) green:(168.0 / 255.0) blue:(220.0 / 255.0) alpha:0.66f];
169     return self;
170 }
171
172 @end
173
174 @implementation WKContentView {
175     std::unique_ptr<PageClientImpl> _pageClient;
176     RetainPtr<WKBrowsingContextController> _browsingContextController;
177
178     RetainPtr<UIView> _rootContentView;
179     RetainPtr<UIView> _fixedClippingView;
180     RetainPtr<WKInspectorIndicationView> _inspectorIndicationView;
181     RetainPtr<WKInspectorHighlightView> _inspectorHighlightView;
182
183     HistoricalVelocityData _historicalKinematicData;
184
185     RetainPtr<NSUndoManager> _undoManager;
186
187     std::unique_ptr<ApplicationStateTracker> _applicationStateTracker;
188
189     BOOL _isPrintingToPDF;
190     RetainPtr<CGPDFDocumentRef> _printedDocument;
191 }
192
193 - (instancetype)_commonInitializationWithProcessPool:(WebKit::WebProcessPool&)processPool configuration:(Ref<API::PageConfiguration>&&)configuration
194 {
195     ASSERT(_pageClient);
196
197     _page = processPool.createWebPage(*_pageClient, WTFMove(configuration));
198     _page->initializeWebPage();
199     _page->setIntrinsicDeviceScaleFactor(screenScaleFactor([UIScreen mainScreen]));
200     _page->setUseFixedLayout(true);
201     _page->setDelegatesScrolling(true);
202
203     WebProcessPool::statistics().wkViewCount++;
204
205     _rootContentView = adoptNS([[UIView alloc] init]);
206     [_rootContentView layer].masksToBounds = NO;
207     [_rootContentView setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight];
208
209     _fixedClippingView = adoptNS([[UIView alloc] init]);
210     [_fixedClippingView layer].masksToBounds = YES;
211     [_fixedClippingView layer].anchorPoint = CGPointZero;
212 #ifndef NDEBUG
213     [[_fixedClippingView layer] setName:@"Fixed clipping"];
214 #endif
215
216     [self addSubview:_fixedClippingView.get()];
217     [_fixedClippingView addSubview:_rootContentView.get()];
218
219     [self setupInteraction];
220     [self setUserInteractionEnabled:YES];
221
222     self.layer.hitTestsAsOpaque = YES;
223
224     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_applicationWillResignActive:) name:UIApplicationWillResignActiveNotification object:[UIApplication sharedApplication]];
225     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_applicationDidBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:[UIApplication sharedApplication]];
226
227     return self;
228 }
229
230 - (instancetype)initWithFrame:(CGRect)frame processPool:(WebKit::WebProcessPool&)processPool configuration:(Ref<API::PageConfiguration>&&)configuration webView:(WKWebView *)webView
231 {
232     if (!(self = [super initWithFrame:frame]))
233         return nil;
234
235     InitializeWebKit2();
236
237     _pageClient = std::make_unique<PageClientImpl>(self, webView);
238     _webView = webView;
239
240     return [self _commonInitializationWithProcessPool:processPool configuration:WTFMove(configuration)];
241 }
242
243 - (void)dealloc
244 {
245     [self cleanupInteraction];
246
247     [[NSNotificationCenter defaultCenter] removeObserver:self];
248
249     _page->close();
250
251     WebProcessPool::statistics().wkViewCount--;
252
253     [super dealloc];
254 }
255
256 - (WebPageProxy*)page
257 {
258     return _page.get();
259 }
260
261 - (void)willMoveToWindow:(UIWindow *)newWindow
262 {
263     NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
264     UIWindow *window = self.window;
265
266     if (window) {
267         [defaultCenter removeObserver:self name:UIWindowDidMoveToScreenNotification object:window];
268
269         if (!newWindow) {
270             ASSERT(_applicationStateTracker);
271             _applicationStateTracker = nullptr;
272         }
273     }
274
275     if (newWindow) {
276         [defaultCenter addObserver:self selector:@selector(_windowDidMoveToScreenNotification:) name:UIWindowDidMoveToScreenNotification object:newWindow];
277
278         [self _updateForScreen:newWindow.screen];
279     }
280 }
281
282 - (void)didMoveToWindow
283 {
284     if (!self.window)
285         return;
286
287     ASSERT(!_applicationStateTracker);
288     _applicationStateTracker = std::make_unique<ApplicationStateTracker>(self, @selector(_applicationDidEnterBackground), @selector(_applicationDidCreateWindowContext), @selector(_applicationDidFinishSnapshottingAfterEnteringBackground), @selector(_applicationWillEnterForeground));
289 }
290
291 - (WKBrowsingContextController *)browsingContextController
292 {
293     if (!_browsingContextController)
294         _browsingContextController = adoptNS([[WKBrowsingContextController alloc] _initWithPageRef:toAPI(_page.get())]);
295
296     return _browsingContextController.get();
297 }
298
299 - (WKPageRef)_pageRef
300 {
301     return toAPI(_page.get());
302 }
303
304 - (BOOL)isAssistingNode
305 {
306     return [self isEditable];
307 }
308
309 - (BOOL)isBackground
310 {
311     if (!_applicationStateTracker)
312         return YES;
313
314     return _applicationStateTracker->isInBackground();
315 }
316
317 - (void)_showInspectorHighlight:(const WebCore::Highlight&)highlight
318 {
319     if (!_inspectorHighlightView) {
320         _inspectorHighlightView = adoptNS([[WKInspectorHighlightView alloc] initWithFrame:CGRectZero]);
321         [self insertSubview:_inspectorHighlightView.get() aboveSubview:_rootContentView.get()];
322     }
323
324     [_inspectorHighlightView update:highlight];
325 }
326
327 - (void)_hideInspectorHighlight
328 {
329     if (_inspectorHighlightView) {
330         [_inspectorHighlightView removeFromSuperview];
331         _inspectorHighlightView = nil;
332     }
333 }
334
335 - (BOOL)isShowingInspectorIndication
336 {
337     return !!_inspectorIndicationView;
338 }
339
340 - (void)setShowingInspectorIndication:(BOOL)show
341 {
342     if (show) {
343         if (!_inspectorIndicationView) {
344             _inspectorIndicationView = adoptNS([[WKInspectorIndicationView alloc] initWithFrame:[self bounds]]);
345             [_inspectorIndicationView setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight];
346             [self insertSubview:_inspectorIndicationView.get() aboveSubview:_rootContentView.get()];
347         }
348     } else {
349         if (_inspectorIndicationView) {
350             [_inspectorIndicationView removeFromSuperview];
351             _inspectorIndicationView = nil;
352         }
353     }
354 }
355
356 - (void)updateFixedClippingView:(FloatRect)fixedPositionRectForUI
357 {
358     FloatRect clippingBounds = [self bounds];
359     clippingBounds.unite(fixedPositionRectForUI);
360
361     [_fixedClippingView setCenter:clippingBounds.location()]; // Not really the center since we set an anchor point.
362     [_fixedClippingView setBounds:clippingBounds];
363 }
364
365 - (void)didUpdateVisibleRect:(CGRect)visibleContentRect
366     unobscuredRect:(CGRect)unobscuredContentRect
367     unobscuredRectInScrollViewCoordinates:(CGRect)unobscuredRectInScrollViewCoordinates
368     obscuredInset:(CGSize)obscuredInset
369     scale:(CGFloat)zoomScale minimumScale:(CGFloat)minimumScale
370     inStableState:(BOOL)isStableState
371     isChangingObscuredInsetsInteractively:(BOOL)isChangingObscuredInsetsInteractively
372     enclosedInScrollableAncestorView:(BOOL)enclosedInScrollableAncestorView
373 {
374     auto drawingArea = _page->drawingArea();
375     if (!drawingArea)
376         return;
377
378     MonotonicTime timestamp = MonotonicTime::now();
379     HistoricalVelocityData::VelocityData velocityData;
380     if (!isStableState)
381         velocityData = _historicalKinematicData.velocityForNewData(visibleContentRect.origin, zoomScale, timestamp);
382     else
383         _historicalKinematicData.clear();
384
385     RemoteScrollingCoordinatorProxy* scrollingCoordinator = _page->scrollingCoordinatorProxy();
386     FloatRect fixedPositionRectForLayout = _page->computeCustomFixedPositionRect(unobscuredContentRect, _page->customFixedPositionRect(), zoomScale, WebPageProxy::UnobscuredRectConstraint::ConstrainedToDocumentRect, scrollingCoordinator->visualViewportEnabled());
387
388     VisibleContentRectUpdateInfo visibleContentRectUpdateInfo(
389         visibleContentRect,
390         unobscuredContentRect,
391         unobscuredRectInScrollViewCoordinates,
392         fixedPositionRectForLayout,
393         WebCore::FloatSize(obscuredInset),
394         zoomScale,
395         isStableState,
396         _sizeChangedSinceLastVisibleContentRectUpdate,
397         isChangingObscuredInsetsInteractively,
398         _webView._allowsViewportShrinkToFit,
399         enclosedInScrollableAncestorView,
400         timestamp,
401         velocityData.horizontalVelocity,
402         velocityData.verticalVelocity,
403         velocityData.scaleChangeRate,
404         downcast<RemoteLayerTreeDrawingAreaProxy>(*drawingArea).lastCommittedLayerTreeTransactionID());
405
406     LOG_WITH_STREAM(VisibleRects, stream << "-[WKContentView didUpdateVisibleRect]" << visibleContentRectUpdateInfo.dump());
407
408     _page->updateVisibleContentRects(visibleContentRectUpdateInfo);
409
410     _sizeChangedSinceLastVisibleContentRectUpdate = NO;
411
412     FloatRect fixedPositionRect = _page->computeCustomFixedPositionRect(_page->unobscuredContentRect(), _page->customFixedPositionRect(), zoomScale, WebPageProxy::UnobscuredRectConstraint::Unconstrained, scrollingCoordinator->visualViewportEnabled());
413     scrollingCoordinator->viewportChangedViaDelegatedScrolling(scrollingCoordinator->rootScrollingNodeID(), fixedPositionRect, zoomScale);
414
415     drawingArea->updateDebugIndicator();
416     
417     [self updateFixedClippingView:fixedPositionRect];
418 }
419
420 - (void)didFinishScrolling
421 {
422     [self _didEndScrollingOrZooming];
423 }
424
425 - (void)didInterruptScrolling
426 {
427     _historicalKinematicData.clear();
428 }
429
430 - (void)willStartZoomOrScroll
431 {
432     [self _willStartScrollingOrZooming];
433 }
434
435 - (void)didZoomToScale:(CGFloat)scale
436 {
437     [self _didEndScrollingOrZooming];
438 }
439
440 - (NSUndoManager *)undoManager
441 {
442     if (!_undoManager)
443         _undoManager = adoptNS([[NSUndoManager alloc] init]);
444
445     return _undoManager.get();
446 }
447
448 #pragma mark Internal
449
450 - (void)_windowDidMoveToScreenNotification:(NSNotification *)notification
451 {
452     ASSERT(notification.object == self.window);
453
454     UIScreen *screen = notification.userInfo[UIWindowNewScreenUserInfoKey];
455     [self _updateForScreen:screen];
456 }
457
458 - (void)_updateForScreen:(UIScreen *)screen
459 {
460     ASSERT(screen);
461     _page->setIntrinsicDeviceScaleFactor(screenScaleFactor(screen));
462     [self _accessibilityRegisterUIProcessTokens];
463 }
464
465 - (void)_setAccessibilityWebProcessToken:(NSData *)data
466 {
467     // This means the web process has checked in and we should send information back to that process.
468     [self _accessibilityRegisterUIProcessTokens];
469 }
470
471 static void storeAccessibilityRemoteConnectionInformation(id element, pid_t pid, mach_port_t sendPort, NSUUID *uuid)
472 {
473     // The accessibility bundle needs to know the uuid, pid and mach_port that this object will refer to.
474     objc_setAssociatedObject(element, (void*)[@"ax-uuid" hash], uuid, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
475     objc_setAssociatedObject(element, (void*)[@"ax-pid" hash], @(pid), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
476     objc_setAssociatedObject(element, (void*)[@"ax-machport" hash], @(sendPort), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
477 }
478
479 - (void)_accessibilityRegisterUIProcessTokens
480 {
481     auto uuid = [NSUUID UUID];
482     NSData *remoteElementToken = newAccessibilityRemoteToken(uuid);
483
484     // Store information about the WebProcess that can later be retrieved by the iOS Accessibility runtime.
485     if (_page->process().state() == WebProcessProxy::State::Running) {
486         IPC::Connection* connection = _page->process().connection();
487         storeAccessibilityRemoteConnectionInformation(self, _page->process().processIdentifier(), connection->identifier().port, uuid);
488
489         IPC::DataReference elementToken = IPC::DataReference(reinterpret_cast<const uint8_t*>([remoteElementToken bytes]), [remoteElementToken length]);
490         _page->registerUIProcessAccessibilityTokens(elementToken, elementToken);
491     }
492 }
493
494 - (void)_webViewDestroyed
495 {
496     _webView = nil;
497 }
498
499 #pragma mark PageClientImpl methods
500
501 - (std::unique_ptr<DrawingAreaProxy>)_createDrawingAreaProxy
502 {
503     return std::make_unique<RemoteLayerTreeDrawingAreaProxy>(*_page);
504 }
505
506 - (void)_processDidExit
507 {
508     [self cleanupInteraction];
509
510     [self setShowingInspectorIndication:NO];
511     [self _hideInspectorHighlight];
512 }
513
514 - (void)_didRelaunchProcess
515 {
516     [self _accessibilityRegisterUIProcessTokens];
517     [self setupInteraction];
518 }
519
520 - (void)_didCommitLoadForMainFrame
521 {
522     [self _stopAssistingNode];
523     [self _cancelLongPressGestureRecognizer];
524     [_webView _didCommitLoadForMainFrame];
525 }
526
527 - (void)_didCommitLayerTree:(const WebKit::RemoteLayerTreeTransaction&)layerTreeTransaction
528 {
529     CGSize contentsSize = layerTreeTransaction.contentsSize();
530     CGPoint scrollOrigin = -layerTreeTransaction.scrollOrigin();
531     CGRect contentBounds = { scrollOrigin, contentsSize };
532
533     BOOL boundsChanged = !CGRectEqualToRect([self bounds], contentBounds);
534     if (boundsChanged)
535         [self setBounds:contentBounds];
536
537     [_webView _didCommitLayerTree:layerTreeTransaction];
538
539     if (_interactionViewsContainerView) {
540         FloatPoint scaledOrigin = layerTreeTransaction.scrollOrigin();
541         float scale = [[_webView scrollView] zoomScale];
542         scaledOrigin.scale(scale);
543         [_interactionViewsContainerView setFrame:CGRectMake(scaledOrigin.x(), scaledOrigin.y(), 0, 0)];
544     }
545     
546     if (boundsChanged) {
547         // FIXME: factor computeCustomFixedPositionRect() into something that gives us this rect.
548         FloatRect fixedPositionRect = _page->computeCustomFixedPositionRect(_page->unobscuredContentRect(), _page->customFixedPositionRect(), [[_webView scrollView] zoomScale]);
549         [self updateFixedClippingView:fixedPositionRect];
550
551         // We need to push the new content bounds to the webview to update fixed position rects.
552         [_webView _updateVisibleContentRects];
553     }
554     
555     // Updating the selection requires a full editor state. If the editor state is missing post layout
556     // data then it means there is a layout pending and we're going to be called again after the layout
557     // so we delay the selection update.
558     if (!_page->editorState().isMissingPostLayoutData)
559         [self _updateChangedSelection];
560 }
561
562 - (void)_layerTreeCommitComplete
563 {
564     [_webView _layerTreeCommitComplete];
565 }
566
567 - (void)_setAcceleratedCompositingRootView:(UIView *)rootView
568 {
569     for (UIView* subview in [_rootContentView subviews])
570         [subview removeFromSuperview];
571
572     [_rootContentView addSubview:rootView];
573 }
574
575 - (BOOL)_scrollToRect:(CGRect)targetRect withOrigin:(CGPoint)origin minimumScrollDistance:(CGFloat)minimumScrollDistance
576 {
577     return [_webView _scrollToRect:targetRect origin:origin minimumScrollDistance:minimumScrollDistance];
578 }
579
580 - (void)_zoomToFocusRect:(CGRect)rectToFocus selectionRect:(CGRect)selectionRect fontSize:(float)fontSize minimumScale:(double)minimumScale maximumScale:(double)maximumScale allowScaling:(BOOL)allowScaling forceScroll:(BOOL)forceScroll
581 {
582     [_webView _zoomToFocusRect:rectToFocus
583                  selectionRect:selectionRect
584                       fontSize:fontSize
585                   minimumScale:minimumScale
586                   maximumScale:maximumScale
587               allowScaling:allowScaling
588                    forceScroll:forceScroll];
589 }
590
591 - (BOOL)_zoomToRect:(CGRect)targetRect withOrigin:(CGPoint)origin fitEntireRect:(BOOL)fitEntireRect minimumScale:(double)minimumScale maximumScale:(double)maximumScale minimumScrollDistance:(CGFloat)minimumScrollDistance
592 {
593     return [_webView _zoomToRect:targetRect withOrigin:origin fitEntireRect:fitEntireRect minimumScale:minimumScale maximumScale:maximumScale minimumScrollDistance:minimumScrollDistance];
594 }
595
596 - (void)_zoomOutWithOrigin:(CGPoint)origin
597 {
598     return [_webView _zoomOutWithOrigin:origin animated:YES];
599 }
600
601 - (void)_zoomToInitialScaleWithOrigin:(CGPoint)origin
602 {
603     return [_webView _zoomToInitialScaleWithOrigin:origin animated:YES];
604 }
605
606 - (void)_applicationWillResignActive:(NSNotification*)notification
607 {
608     _page->applicationWillResignActive();
609 }
610
611 - (void)_applicationDidEnterBackground
612 {
613     _page->applicationDidEnterBackground();
614     _page->activityStateDidChange(ActivityState::AllFlags & ~ActivityState::IsInWindow);
615 }
616
617 - (void)_applicationDidCreateWindowContext
618 {
619     if (auto drawingArea = _page->drawingArea())
620         drawingArea->hideContentUntilAnyUpdate();
621 }
622
623 - (void)_applicationDidFinishSnapshottingAfterEnteringBackground
624 {
625     _page->applicationDidFinishSnapshottingAfterEnteringBackground();
626 }
627
628 - (void)_applicationWillEnterForeground
629 {
630     _page->applicationWillEnterForeground();
631     _page->activityStateDidChange(ActivityState::AllFlags & ~ActivityState::IsInWindow, true, WebPageProxy::ActivityStateChangeDispatchMode::Immediate);
632 }
633
634 - (void)_applicationDidBecomeActive:(NSNotification*)notification
635 {
636     _page->applicationDidBecomeActive();
637 }
638
639 @end
640
641 #pragma mark Printing
642
643 @interface WKContentView (_WKWebViewPrintFormatter) <_WKWebViewPrintProvider>
644 @end
645
646 @implementation WKContentView (_WKWebViewPrintFormatter)
647
648 - (NSUInteger)_wk_pageCountForPrintFormatter:(_WKWebViewPrintFormatter *)printFormatter
649 {
650     if (_isPrintingToPDF)
651         return 0;
652
653     uint64_t frameID;
654     if (_WKFrameHandle *handle = printFormatter.frameToPrint)
655         frameID = handle._frameID;
656     else if (auto mainFrame = _page->mainFrame())
657         frameID = mainFrame->frameID();
658     else
659         return 0;
660
661     // The first page can have a smaller content rect than subsequent pages if a top content inset
662     // is specified. Since WebKit requires a uniform content rect for each page during layout, use
663     // the intersection of the first and non-first page rects.
664     // FIXME: Teach WebCore::PrintContext to accept an initial content offset when paginating.
665     CGRect printingRect = CGRectIntersection([printFormatter _pageContentRect:YES], [printFormatter _pageContentRect:NO]);
666     if (CGRectIsEmpty(printingRect))
667         return 0;
668
669     PrintInfo printInfo;
670     printInfo.pageSetupScaleFactor = 1;
671     printInfo.availablePaperWidth = CGRectGetWidth(printingRect);
672     printInfo.availablePaperHeight = CGRectGetHeight(printingRect);
673
674     _isPrintingToPDF = YES;
675     auto retainedSelf = retainPtr(self);
676     return _page->computePagesForPrintingAndDrawToPDF(frameID, printInfo, [retainedSelf](const IPC::DataReference& pdfData, CallbackBase::Error error) {
677         retainedSelf->_isPrintingToPDF = NO;
678         if (error != CallbackBase::Error::None)
679             return;
680
681         auto data = adoptCF(CFDataCreate(kCFAllocatorDefault, pdfData.data(), pdfData.size()));
682         auto dataProvider = adoptCF(CGDataProviderCreateWithCFData(data.get()));
683         retainedSelf->_printedDocument = adoptCF(CGPDFDocumentCreateWithProvider(dataProvider.get()));
684     });
685 }
686
687 - (CGPDFDocumentRef)_wk_printedDocument
688 {
689     if (_isPrintingToPDF) {
690         if (!_page->process().connection()->waitForAndDispatchImmediately<Messages::WebPageProxy::DrawToPDFCallback>(_page->pageID(), Seconds::infinity())) {
691             ASSERT_NOT_REACHED();
692             return nullptr;
693         }
694         ASSERT(!_isPrintingToPDF);
695     }
696
697     return _printedDocument.autorelease();
698 }
699
700 @end
701
702 #endif // PLATFORM(IOS)