Avoid crashes on GCC-compiled binaries by avoiding a use-after-move
[WebKit-https.git] / Source / WebKit / 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_FAMILY)
30
31 #import "APIPageConfiguration.h"
32 #import "AccessibilityIOS.h"
33 #import "FullscreenClient.h"
34 #import "InputViewUpdateDeferrer.h"
35 #import "Logging.h"
36 #import "PageClientImplIOS.h"
37 #import "PrintInfo.h"
38 #import "RemoteLayerTreeDrawingAreaProxy.h"
39 #import "SmartMagnificationController.h"
40 #import "UIKitSPI.h"
41 #import "VersionChecks.h"
42 #import "WKBrowsingContextControllerInternal.h"
43 #import "WKBrowsingContextGroupPrivate.h"
44 #import "WKInspectorHighlightView.h"
45 #import "WKPreferencesInternal.h"
46 #import "WKProcessGroupPrivate.h"
47 #import "WKWebViewConfiguration.h"
48 #import "WKWebViewInternal.h"
49 #import "WebFrameProxy.h"
50 #import "WebKit2Initialize.h"
51 #import "WebPageGroup.h"
52 #import "WebProcessPool.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/Quirks.h>
62 #import <WebCore/RuntimeApplicationChecks.h>
63 #import <WebCore/VelocityData.h>
64 #import <objc/message.h>
65 #import <pal/spi/cocoa/QuartzCoreSPI.h>
66 #import <wtf/RetainPtr.h>
67 #import <wtf/text/TextStream.h>
68
69
70 @interface WKInspectorIndicationView : UIView
71 @end
72
73 @implementation WKInspectorIndicationView
74
75 - (instancetype)initWithFrame:(CGRect)frame
76 {
77     if (!(self = [super initWithFrame:frame]))
78         return nil;
79     self.userInteractionEnabled = NO;
80     self.backgroundColor = [UIColor colorWithRed:(111.0 / 255.0) green:(168.0 / 255.0) blue:(220.0 / 255.0) alpha:0.66f];
81     return self;
82 }
83
84 @end
85
86 @interface WKQuirkyNSUndoManager : NSUndoManager
87 @property (readonly, weak) WKContentView *contentView;
88 @end
89
90 @implementation WKQuirkyNSUndoManager
91 - (instancetype)initWithContentView:(WKContentView *)contentView
92 {
93     if (!(self = [super init]))
94         return nil;
95     _contentView = contentView;
96     return self;
97 }
98
99 - (BOOL)canUndo 
100 {
101     return YES;
102 }
103
104 - (BOOL)canRedo 
105 {
106     return YES;
107 }
108
109 - (void)undo 
110 {
111     [self.contentView generateSyntheticEditingCommand:WebKit::SyntheticEditingCommandType::Undo];
112 }
113
114 - (void)redo 
115 {
116     [self.contentView generateSyntheticEditingCommand:WebKit::SyntheticEditingCommandType::Redo];
117 }
118
119 @end
120
121 @implementation WKContentView {
122     std::unique_ptr<WebKit::PageClientImpl> _pageClient;
123     ALLOW_DEPRECATED_DECLARATIONS_BEGIN
124     RetainPtr<WKBrowsingContextController> _browsingContextController;
125     ALLOW_DEPRECATED_DECLARATIONS_END
126
127     RetainPtr<UIView> _rootContentView;
128     RetainPtr<UIView> _fixedClippingView;
129     RetainPtr<WKInspectorIndicationView> _inspectorIndicationView;
130     RetainPtr<WKInspectorHighlightView> _inspectorHighlightView;
131
132 #if HAVE(VISIBILITY_PROPAGATION_VIEW)
133     RetainPtr<_UILayerHostView> _visibilityPropagationView;
134 #endif
135
136     WebCore::HistoricalVelocityData _historicalKinematicData;
137
138     RetainPtr<NSUndoManager> _undoManager;
139     RetainPtr<WKQuirkyNSUndoManager> _quirkyUndoManager;
140
141     BOOL _isPrintingToPDF;
142     RetainPtr<CGPDFDocumentRef> _printedDocument;
143 }
144
145 #if USE(UIKIT_KEYBOARD_ADDITIONS)
146
147 // Evernote expects to swizzle -keyCommands on WKContentView or they crash. Remove this hack
148 // as soon as reasonably possible. See <rdar://problem/51759247>.
149 static NSArray *keyCommandsPlaceholderHackForEvernote(id self, SEL _cmd)
150 {
151     struct objc_super super = { self, class_getSuperclass(object_getClass(self)) };
152     using SuperKeyCommandsFunction = NSArray *(*)(struct objc_super*, SEL);
153     return reinterpret_cast<SuperKeyCommandsFunction>(&objc_msgSendSuper)(&super, @selector(keyCommands));
154 }
155
156 #endif
157
158 - (instancetype)_commonInitializationWithProcessPool:(WebKit::WebProcessPool&)processPool configuration:(Ref<API::PageConfiguration>&&)configuration
159 {
160     ASSERT(_pageClient);
161
162     _page = processPool.createWebPage(*_pageClient, WTFMove(configuration));
163     _page->initializeWebPage();
164     _page->setIntrinsicDeviceScaleFactor(WebCore::screenScaleFactor([UIScreen mainScreen]));
165     _page->setUseFixedLayout(true);
166     _page->setDelegatesScrolling(true);
167
168 #if ENABLE(FULLSCREEN_API)
169     _page->setFullscreenClient(makeUnique<WebKit::FullscreenClient>(_webView));
170 #endif
171
172     WebKit::WebProcessPool::statistics().wkViewCount++;
173
174     _rootContentView = adoptNS([[UIView alloc] init]);
175     [_rootContentView layer].name = @"RootContent";
176     [_rootContentView layer].masksToBounds = NO;
177     [_rootContentView setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight];
178
179     _fixedClippingView = adoptNS([[UIView alloc] init]);
180     [_fixedClippingView layer].name = @"FixedClipping";
181     [_fixedClippingView layer].masksToBounds = YES;
182     [_fixedClippingView layer].anchorPoint = CGPointZero;
183
184     [self addSubview:_fixedClippingView.get()];
185     [_fixedClippingView addSubview:_rootContentView.get()];
186
187     if (!linkedOnOrAfter(WebKit::SDKVersion::FirstWithLazyGestureRecognizerInstallation))
188         [self setupInteraction];
189     [self setUserInteractionEnabled:YES];
190
191     self.layer.hitTestsAsOpaque = YES;
192
193 #if PLATFORM(MACCATALYST)
194     [self _setFocusRingType:UIFocusRingTypeNone];
195 #endif
196
197 #if HAVE(VISIBILITY_PROPAGATION_VIEW)
198     [self _setupVisibilityPropagationView];
199 #endif
200
201     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_applicationWillResignActive:) name:UIApplicationWillResignActiveNotification object:[UIApplication sharedApplication]];
202     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_applicationDidBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:[UIApplication sharedApplication]];
203
204 #if USE(UIKIT_KEYBOARD_ADDITIONS)
205     if (WebCore::IOSApplication::isEvernote() && !linkedOnOrAfter(WebKit::SDKVersion::FirstWhereWKContentViewDoesNotOverrideKeyCommands))
206         class_addMethod(self.class, @selector(keyCommands), reinterpret_cast<IMP>(&keyCommandsPlaceholderHackForEvernote), method_getTypeEncoding(class_getInstanceMethod(self.class, @selector(keyCommands))));
207 #endif
208
209     return self;
210 }
211
212 #if HAVE(VISIBILITY_PROPAGATION_VIEW)
213 - (void)_setupVisibilityPropagationView
214 {
215     auto processIdentifier = _page->process().processIdentifier();
216     auto contextID = _page->contextIDForVisibilityPropagation();
217     if (!processIdentifier || !contextID)
218         return;
219
220     ASSERT(!_visibilityPropagationView);
221     // Propagate the view's visibility state to the WebContent process so that it is marked as "Foreground Running" when necessary.
222     _visibilityPropagationView = adoptNS([[_UILayerHostView alloc] initWithFrame:CGRectZero pid:processIdentifier contextID:contextID]);
223     RELEASE_LOG(Process, "Created visibility propagation view %p for WebContent process with PID %d", _visibilityPropagationView.get(), processIdentifier);
224     [self addSubview:_visibilityPropagationView.get()];
225 }
226
227 - (void)_removeVisibilityPropagationView
228 {
229     [_visibilityPropagationView removeFromSuperview];
230     _visibilityPropagationView = nullptr;
231 }
232 #endif
233
234 - (instancetype)initWithFrame:(CGRect)frame processPool:(WebKit::WebProcessPool&)processPool configuration:(Ref<API::PageConfiguration>&&)configuration webView:(WKWebView *)webView
235 {
236     if (!(self = [super initWithFrame:frame webView:webView]))
237         return nil;
238
239     WebKit::InitializeWebKit2();
240
241     _pageClient = makeUnique<WebKit::PageClientImpl>(self, webView);
242     _webView = webView;
243
244     return [self _commonInitializationWithProcessPool:processPool configuration:WTFMove(configuration)];
245 }
246
247 - (void)dealloc
248 {
249     [self cleanupInteraction];
250
251     [[NSNotificationCenter defaultCenter] removeObserver:self];
252
253     _page->close();
254
255     WebKit::WebProcessPool::statistics().wkViewCount--;
256
257     [super dealloc];
258 }
259
260 - (WebKit::WebPageProxy*)page
261 {
262     return _page.get();
263 }
264
265 - (void)willMoveToWindow:(UIWindow *)newWindow
266 {
267     [super willMoveToWindow:newWindow];
268
269     NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
270     UIWindow *window = self.window;
271
272     if (window)
273         [defaultCenter removeObserver:self name:UIWindowDidMoveToScreenNotification object:window];
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     [super didMoveToWindow];
285
286     if (self.window)
287         [self setupInteraction];
288 }
289
290 ALLOW_DEPRECATED_DECLARATIONS_BEGIN
291 - (WKBrowsingContextController *)browsingContextController
292 {
293     if (!_browsingContextController)
294         _browsingContextController = adoptNS([[WKBrowsingContextController alloc] _initWithPageRef:toAPI(_page.get())]);
295
296     return _browsingContextController.get();
297 }
298 ALLOW_DEPRECATED_DECLARATIONS_END
299
300 - (WKPageRef)_pageRef
301 {
302     return toAPI(_page.get());
303 }
304
305 - (BOOL)isFocusingElement
306 {
307     return [self isEditable];
308 }
309
310 - (void)_showInspectorHighlight:(const WebCore::Highlight&)highlight
311 {
312     if (!_inspectorHighlightView) {
313         _inspectorHighlightView = adoptNS([[WKInspectorHighlightView alloc] initWithFrame:CGRectZero]);
314         [self insertSubview:_inspectorHighlightView.get() aboveSubview:_rootContentView.get()];
315     }
316
317     [_inspectorHighlightView update:highlight];
318 }
319
320 - (void)_hideInspectorHighlight
321 {
322     if (_inspectorHighlightView) {
323         [_inspectorHighlightView removeFromSuperview];
324         _inspectorHighlightView = nil;
325     }
326 }
327
328 - (BOOL)isShowingInspectorIndication
329 {
330     return !!_inspectorIndicationView;
331 }
332
333 - (void)setShowingInspectorIndication:(BOOL)show
334 {
335     if (show) {
336         if (!_inspectorIndicationView) {
337             _inspectorIndicationView = adoptNS([[WKInspectorIndicationView alloc] initWithFrame:[self bounds]]);
338             [_inspectorIndicationView setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight];
339             [self insertSubview:_inspectorIndicationView.get() aboveSubview:_rootContentView.get()];
340         }
341     } else {
342         if (_inspectorIndicationView) {
343             [_inspectorIndicationView removeFromSuperview];
344             _inspectorIndicationView = nil;
345         }
346     }
347 }
348
349 - (void)updateFixedClippingView:(WebCore::FloatRect)fixedPositionRectForUI
350 {
351     WebCore::FloatRect clippingBounds = [self bounds];
352     clippingBounds.unite(fixedPositionRectForUI);
353
354     [_fixedClippingView setCenter:clippingBounds.location()]; // Not really the center since we set an anchor point.
355     [_fixedClippingView setBounds:clippingBounds];
356 }
357
358 - (void)_didExitStableState
359 {
360     _needsDeferredEndScrollingSelectionUpdate = self.shouldHideSelectionWhenScrolling;
361     if (!_needsDeferredEndScrollingSelectionUpdate)
362         return;
363
364     [_textSelectionAssistant deactivateSelection];
365 }
366
367 static WebCore::FloatBoxExtent floatBoxExtent(UIEdgeInsets insets)
368 {
369     return WebCore::FloatBoxExtent(insets.top, insets.right, insets.bottom, insets.left);
370 }
371
372 - (CGRect)_computeUnobscuredContentRectRespectingInputViewBounds:(CGRect)unobscuredContentRect inputViewBounds:(CGRect)inputViewBounds
373 {
374     // 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.
375     CGRect inputViewBoundsInContentCoordinates = [self.window convertRect:inputViewBounds toView:self];
376     if (CGRectGetHeight(inputViewBoundsInContentCoordinates))
377         unobscuredContentRect.size.height = std::min<float>(CGRectGetHeight(unobscuredContentRect), CGRectGetMinY(inputViewBoundsInContentCoordinates) - CGRectGetMinY(unobscuredContentRect));
378     return unobscuredContentRect;
379 }
380
381 - (void)didUpdateVisibleRect:(CGRect)visibleContentRect
382     unobscuredRect:(CGRect)unobscuredContentRect
383     contentInsets:(UIEdgeInsets)contentInsets
384     unobscuredRectInScrollViewCoordinates:(CGRect)unobscuredRectInScrollViewCoordinates
385     obscuredInsets:(UIEdgeInsets)obscuredInsets
386     unobscuredSafeAreaInsets:(UIEdgeInsets)unobscuredSafeAreaInsets
387     inputViewBounds:(CGRect)inputViewBounds
388     scale:(CGFloat)zoomScale minimumScale:(CGFloat)minimumScale
389     inStableState:(BOOL)isStableState
390     isChangingObscuredInsetsInteractively:(BOOL)isChangingObscuredInsetsInteractively
391     enclosedInScrollableAncestorView:(BOOL)enclosedInScrollableAncestorView
392 {
393     auto drawingArea = _page->drawingArea();
394     if (!drawingArea)
395         return;
396
397     MonotonicTime timestamp = MonotonicTime::now();
398     WebCore::VelocityData velocityData;
399     if (!isStableState)
400         velocityData = _historicalKinematicData.velocityForNewData(visibleContentRect.origin, zoomScale, timestamp);
401     else {
402         _historicalKinematicData.clear();
403         velocityData = { 0, 0, 0, timestamp };
404     }
405
406     CGRect unobscuredContentRectRespectingInputViewBounds = [self _computeUnobscuredContentRectRespectingInputViewBounds:unobscuredContentRect inputViewBounds:inputViewBounds];
407     WebCore::FloatRect fixedPositionRectForLayout = _page->computeCustomFixedPositionRect(unobscuredContentRect, unobscuredContentRectRespectingInputViewBounds, _page->customFixedPositionRect(), zoomScale, WebCore::FrameView::LayoutViewportConstraint::ConstrainedToDocumentRect);
408
409     WebKit::VisibleContentRectUpdateInfo visibleContentRectUpdateInfo(
410         visibleContentRect,
411         unobscuredContentRect,
412         floatBoxExtent(contentInsets),
413         unobscuredRectInScrollViewCoordinates,
414         unobscuredContentRectRespectingInputViewBounds,
415         fixedPositionRectForLayout,
416         floatBoxExtent(obscuredInsets),
417         floatBoxExtent(unobscuredSafeAreaInsets),
418         zoomScale,
419         isStableState,
420         _sizeChangedSinceLastVisibleContentRectUpdate,
421         isChangingObscuredInsetsInteractively,
422         _webView._allowsViewportShrinkToFit,
423         enclosedInScrollableAncestorView,
424         velocityData,
425         downcast<WebKit::RemoteLayerTreeDrawingAreaProxy>(*drawingArea).lastCommittedLayerTreeTransactionID());
426
427     LOG_WITH_STREAM(VisibleRects, stream << "-[WKContentView didUpdateVisibleRect]" << visibleContentRectUpdateInfo.dump());
428
429     bool wasStableState = _page->inStableState();
430
431     _page->updateVisibleContentRects(visibleContentRectUpdateInfo);
432
433     auto layoutViewport = _page->unconstrainedLayoutViewportRect();
434     _page->adjustLayersForLayoutViewport(layoutViewport);
435
436     _sizeChangedSinceLastVisibleContentRectUpdate = NO;
437
438     drawingArea->updateDebugIndicator();
439     
440     [self updateFixedClippingView:layoutViewport];
441
442     if (wasStableState && !isStableState)
443         [self _didExitStableState];
444 }
445
446 - (void)didFinishScrolling
447 {
448     [self _didEndScrollingOrZooming];
449 }
450
451 - (void)didInterruptScrolling
452 {
453     _historicalKinematicData.clear();
454 }
455
456 - (void)willStartZoomOrScroll
457 {
458     [self _willStartScrollingOrZooming];
459 }
460
461 - (void)didZoomToScale:(CGFloat)scale
462 {
463     [self _didEndScrollingOrZooming];
464 }
465
466 - (NSUndoManager *)undoManager
467 {
468     if (self.focusedElementInformation.shouldSynthesizeKeyEventsForEditing && self.hasHiddenContentEditable) {
469         if (!_quirkyUndoManager)
470             _quirkyUndoManager = adoptNS([[WKQuirkyNSUndoManager alloc] initWithContentView:self]);
471         return _quirkyUndoManager.get();
472     }
473     if (!_undoManager)
474         _undoManager = adoptNS([[NSUndoManager alloc] init]);
475     return _undoManager.get();
476 }
477
478 - (UIInterfaceOrientation)interfaceOrientation
479 {
480 #if HAVE(UISCENE)
481     return self.window.windowScene.interfaceOrientation;
482 #else
483     return UIApp.interfaceOrientation;
484 #endif
485 }
486
487 #pragma mark Internal
488
489 - (void)_windowDidMoveToScreenNotification:(NSNotification *)notification
490 {
491     ASSERT(notification.object == self.window);
492
493     UIScreen *screen = notification.userInfo[UIWindowNewScreenUserInfoKey];
494     [self _updateForScreen:screen];
495 }
496
497 - (void)_updateForScreen:(UIScreen *)screen
498 {
499     ASSERT(screen);
500     _page->setIntrinsicDeviceScaleFactor(WebCore::screenScaleFactor(screen));
501     [self _accessibilityRegisterUIProcessTokens];
502 }
503
504 - (void)_setAccessibilityWebProcessToken:(NSData *)data
505 {
506     // This means the web process has checked in and we should send information back to that process.
507     [self _accessibilityRegisterUIProcessTokens];
508 }
509
510 static void storeAccessibilityRemoteConnectionInformation(id element, pid_t pid, mach_port_t sendPort, NSUUID *uuid)
511 {
512     // The accessibility bundle needs to know the uuid, pid and mach_port that this object will refer to.
513     objc_setAssociatedObject(element, (void*)[@"ax-uuid" hash], uuid, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
514     objc_setAssociatedObject(element, (void*)[@"ax-pid" hash], @(pid), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
515     objc_setAssociatedObject(element, (void*)[@"ax-machport" hash], @(sendPort), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
516 }
517
518 - (void)_accessibilityRegisterUIProcessTokens
519 {
520     auto uuid = [NSUUID UUID];
521     NSData *remoteElementToken = WebKit::newAccessibilityRemoteToken(uuid);
522
523     // Store information about the WebProcess that can later be retrieved by the iOS Accessibility runtime.
524     if (_page->process().state() == WebKit::WebProcessProxy::State::Running) {
525         IPC::Connection* connection = _page->process().connection();
526         storeAccessibilityRemoteConnectionInformation(self, _page->process().processIdentifier(), connection->identifier().port, uuid);
527
528         IPC::DataReference elementToken = IPC::DataReference(reinterpret_cast<const uint8_t*>([remoteElementToken bytes]), [remoteElementToken length]);
529         _page->registerUIProcessAccessibilityTokens(elementToken, elementToken);
530     }
531 }
532
533 - (void)_webViewDestroyed
534 {
535     _webView = nil;
536 }
537
538 #pragma mark PageClientImpl methods
539
540 - (std::unique_ptr<WebKit::DrawingAreaProxy>)_createDrawingAreaProxy:(WebKit::WebProcessProxy&)process
541 {
542     return makeUnique<WebKit::RemoteLayerTreeDrawingAreaProxy>(*_page, process);
543 }
544
545 - (void)_processDidExit
546 {
547     [self cleanupInteraction];
548
549     [self setShowingInspectorIndication:NO];
550     [self _hideInspectorHighlight];
551
552 #if HAVE(VISIBILITY_PROPAGATION_VIEW)
553     [self _removeVisibilityPropagationView];
554 #endif
555
556     _isPrintingToPDF = NO;
557 }
558
559 - (void)_processWillSwap
560 {
561     // FIXME: Should we do something differently?
562     [self _processDidExit];
563 }
564
565 - (void)_didRelaunchProcess
566 {
567     [self _accessibilityRegisterUIProcessTokens];
568     [self setupInteraction];
569 #if HAVE(VISIBILITY_PROPAGATION_VIEW)
570     [self _setupVisibilityPropagationView];
571 #endif
572 }
573
574 #if HAVE(VISIBILITY_PROPAGATION_VIEW)
575 - (void)_processDidCreateContextForVisibilityPropagation
576 {
577     [self _setupVisibilityPropagationView];
578 }
579 #endif
580
581 - (void)_didCommitLayerTree:(const WebKit::RemoteLayerTreeTransaction&)layerTreeTransaction
582 {
583     CGSize contentsSize = layerTreeTransaction.contentsSize();
584     CGPoint scrollOrigin = -layerTreeTransaction.scrollOrigin();
585     CGRect contentBounds = { scrollOrigin, contentsSize };
586
587     LOG_WITH_STREAM(VisibleRects, stream << "-[WKContentView _didCommitLayerTree:] transactionID " <<  layerTreeTransaction.transactionID() << " contentBounds " << WebCore::FloatRect(contentBounds));
588
589     BOOL boundsChanged = !CGRectEqualToRect([self bounds], contentBounds);
590     if (boundsChanged)
591         [self setBounds:contentBounds];
592
593     [_webView _didCommitLayerTree:layerTreeTransaction];
594
595     if (_interactionViewsContainerView) {
596         WebCore::FloatPoint scaledOrigin = layerTreeTransaction.scrollOrigin();
597         float scale = [[_webView scrollView] zoomScale];
598         scaledOrigin.scale(scale);
599         [_interactionViewsContainerView setFrame:CGRectMake(scaledOrigin.x(), scaledOrigin.y(), 0, 0)];
600     }
601     
602     if (boundsChanged) {
603         // FIXME: factor computeCustomFixedPositionRect() into something that gives us this rect.
604         WebCore::FloatRect fixedPositionRect = _page->computeCustomFixedPositionRect(_page->unobscuredContentRect(), _page->unobscuredContentRectRespectingInputViewBounds(), _page->customFixedPositionRect(), [[_webView scrollView] zoomScale]);
605         [self updateFixedClippingView:fixedPositionRect];
606
607         // We need to push the new content bounds to the webview to update fixed position rects.
608         [_webView _scheduleVisibleContentRectUpdate];
609     }
610     
611     // Updating the selection requires a full editor state. If the editor state is missing post layout
612     // data then it means there is a layout pending and we're going to be called again after the layout
613     // so we delay the selection update.
614     if (!_page->editorState().isMissingPostLayoutData)
615         [self _updateChangedSelection];
616 }
617
618 - (void)_layerTreeCommitComplete
619 {
620     [_webView _layerTreeCommitComplete];
621 }
622
623 - (void)_setAcceleratedCompositingRootView:(UIView *)rootView
624 {
625     for (UIView* subview in [_rootContentView subviews])
626         [subview removeFromSuperview];
627
628     [_rootContentView addSubview:rootView];
629 }
630
631 - (BOOL)_scrollToRect:(CGRect)targetRect withOrigin:(CGPoint)origin minimumScrollDistance:(CGFloat)minimumScrollDistance
632 {
633     return [_webView _scrollToRect:targetRect origin:origin minimumScrollDistance:minimumScrollDistance];
634 }
635
636 - (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
637 {
638     [_webView _zoomToFocusRect:rectToFocus
639                  selectionRect:selectionRect
640                    insideFixed:insideFixed
641                       fontSize:fontSize
642                   minimumScale:minimumScale
643                   maximumScale:maximumScale
644               allowScaling:allowScaling
645                    forceScroll:forceScroll];
646 }
647
648 - (BOOL)_zoomToRect:(CGRect)targetRect withOrigin:(CGPoint)origin fitEntireRect:(BOOL)fitEntireRect minimumScale:(double)minimumScale maximumScale:(double)maximumScale minimumScrollDistance:(CGFloat)minimumScrollDistance
649 {
650     return [_webView _zoomToRect:targetRect withOrigin:origin fitEntireRect:fitEntireRect minimumScale:minimumScale maximumScale:maximumScale minimumScrollDistance:minimumScrollDistance];
651 }
652
653 - (void)_zoomOutWithOrigin:(CGPoint)origin
654 {
655     return [_webView _zoomOutWithOrigin:origin animated:YES];
656 }
657
658 - (void)_zoomToInitialScaleWithOrigin:(CGPoint)origin
659 {
660     return [_webView _zoomToInitialScaleWithOrigin:origin animated:YES];
661 }
662
663 - (double)_initialScaleFactor
664 {
665     return [_webView _initialScaleFactor];
666 }
667
668 - (double)_contentZoomScale
669 {
670     return [_webView _contentZoomScale];
671 }
672
673 - (double)_targetContentZoomScaleForRect:(const WebCore::FloatRect&)targetRect currentScale:(double)currentScale fitEntireRect:(BOOL)fitEntireRect minimumScale:(double)minimumScale maximumScale:(double)maximumScale
674 {
675     return [_webView _targetContentZoomScaleForRect:targetRect currentScale:currentScale fitEntireRect:fitEntireRect minimumScale:minimumScale maximumScale:maximumScale];
676 }
677
678 - (void)_applicationWillResignActive:(NSNotification*)notification
679 {
680     _page->applicationWillResignActive();
681 }
682
683 - (void)_applicationDidBecomeActive:(NSNotification*)notification
684 {
685     _page->applicationDidBecomeActive();
686 }
687
688 @end
689
690 #pragma mark Printing
691
692 @interface WKContentView (_WKWebViewPrintFormatter) <_WKWebViewPrintProvider>
693 @end
694
695 @implementation WKContentView (_WKWebViewPrintFormatter)
696
697 - (NSUInteger)_wk_pageCountForPrintFormatter:(_WKWebViewPrintFormatter *)printFormatter
698 {
699     if (_isPrintingToPDF)
700         [self _waitForDrawToPDFCallback];
701
702     WebCore::FrameIdentifier frameID;
703     if (_WKFrameHandle *handle = printFormatter.frameToPrint)
704         frameID = WebCore::frameIdentifierFromID(handle._frameID);
705     else if (auto mainFrame = _page->mainFrame())
706         frameID = mainFrame->frameID();
707     else
708         return 0;
709
710     // The first page can have a smaller content rect than subsequent pages if a top content inset
711     // is specified. Since WebKit requires a uniform content rect for each page during layout, use
712     // the intersection of the first and non-first page rects.
713     // FIXME: Teach WebCore::PrintContext to accept an initial content offset when paginating.
714     CGRect printingRect = CGRectIntersection([printFormatter _pageContentRect:YES], [printFormatter _pageContentRect:NO]);
715     if (CGRectIsEmpty(printingRect))
716         return 0;
717
718     WebKit::PrintInfo printInfo;
719     printInfo.pageSetupScaleFactor = 1;
720     printInfo.snapshotFirstPage = printFormatter.snapshotFirstPage;
721
722     // FIXME: Paginate when exporting PDFs taller than 200"
723     if (printInfo.snapshotFirstPage) {
724         static const CGFloat maximumPDFHeight = 200 * 72; // maximum PDF height for a single page is 200 inches
725         CGSize contentSize = self.bounds.size;
726         printingRect = (CGRect) { CGPointZero, { contentSize.width, std::min(contentSize.height, maximumPDFHeight) } };
727         [printFormatter _setSnapshotPaperRect:printingRect];
728     }
729     printInfo.availablePaperWidth = CGRectGetWidth(printingRect);
730     printInfo.availablePaperHeight = CGRectGetHeight(printingRect);
731
732     _isPrintingToPDF = YES;
733     auto retainedSelf = retainPtr(self);
734     return _page->computePagesForPrintingAndDrawToPDF(frameID, printInfo, [retainedSelf](const IPC::DataReference& pdfData, WebKit::CallbackBase::Error error) {
735         retainedSelf->_isPrintingToPDF = NO;
736         if (error != WebKit::CallbackBase::Error::None)
737             return;
738
739         auto data = adoptCF(CFDataCreate(kCFAllocatorDefault, pdfData.data(), pdfData.size()));
740         auto dataProvider = adoptCF(CGDataProviderCreateWithCFData(data.get()));
741         retainedSelf->_printedDocument = adoptCF(CGPDFDocumentCreateWithProvider(dataProvider.get()));
742     });
743 }
744
745 - (BOOL)_waitForDrawToPDFCallback
746 {
747     if (!_page->process().connection()->waitForAndDispatchImmediately<Messages::WebPageProxy::DrawToPDFCallback>(_page->webPageID(), Seconds::infinity()))
748         return false;
749     ASSERT(!_isPrintingToPDF);
750     return true;
751 }
752
753 - (CGPDFDocumentRef)_wk_printedDocument
754 {
755     if (_isPrintingToPDF) {
756         if (![self _waitForDrawToPDFCallback])
757             return nullptr;
758     }
759
760     return _printedDocument.get();
761 }
762
763 @end
764
765 #endif // PLATFORM(IOS_FAMILY)