85f0ca2cdee582a961b30b1f715507c55ee8fe50
[WebKit-https.git] / Source / WebKit / WebProcess / WebPage / mac / TiledCoreAnimationDrawingArea.mm
1 /*
2  * Copyright (C) 2011-2018 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 "TiledCoreAnimationDrawingArea.h"
28
29 #if PLATFORM(MAC)
30
31 #import "ColorSpaceData.h"
32 #import "DrawingAreaProxyMessages.h"
33 #import "LayerHostingContext.h"
34 #import "LayerTreeContext.h"
35 #import "Logging.h"
36 #import "ViewGestureControllerMessages.h"
37 #import "WebFrame.h"
38 #import "WebPage.h"
39 #import "WebPageCreationParameters.h"
40 #import "WebPageProxyMessages.h"
41 #import "WebProcess.h"
42 #import <QuartzCore/QuartzCore.h>
43 #import <WebCore/DebugPageOverlays.h>
44 #import <WebCore/Frame.h>
45 #import <WebCore/FrameView.h>
46 #import <WebCore/GraphicsContext.h>
47 #import <WebCore/GraphicsLayerCA.h>
48 #import <WebCore/InspectorController.h>
49 #import <WebCore/Page.h>
50 #import <WebCore/PlatformCAAnimationCocoa.h>
51 #import <WebCore/RenderLayerBacking.h>
52 #import <WebCore/RenderLayerCompositor.h>
53 #import <WebCore/RenderView.h>
54 #import <WebCore/RunLoopObserver.h>
55 #import <WebCore/ScrollbarTheme.h>
56 #import <WebCore/Settings.h>
57 #import <WebCore/TiledBacking.h>
58 #import <WebCore/WebActionDisablingCALayerDelegate.h>
59 #import <pal/spi/cocoa/QuartzCoreSPI.h>
60 #import <wtf/MachSendRight.h>
61 #import <wtf/MainThread.h>
62
63 #if ENABLE(ASYNC_SCROLLING)
64 #import <WebCore/AsyncScrollingCoordinator.h>
65 #import <WebCore/ScrollingThread.h>
66 #import <WebCore/ScrollingTree.h>
67 #endif
68
69 @interface CATransaction (Details)
70 + (void)synchronize;
71 @end
72
73 namespace WebKit {
74 using namespace WebCore;
75
76 TiledCoreAnimationDrawingArea::TiledCoreAnimationDrawingArea(WebPage& webPage, const WebPageCreationParameters& parameters)
77     : DrawingArea(DrawingAreaTypeTiledCoreAnimation, parameters.drawingAreaIdentifier, webPage)
78     , m_layerFlushThrottlingTimer(*this, &TiledCoreAnimationDrawingArea::layerFlushThrottlingTimerFired)
79     , m_sendDidUpdateActivityStateTimer(RunLoop::main(), this, &TiledCoreAnimationDrawingArea::didUpdateActivityStateTimerFired)
80     , m_isPaintingSuspended(!(parameters.activityState & ActivityState::IsVisible))
81 {
82     m_webPage.corePage()->settings().setForceCompositingMode(true);
83
84     m_hostingLayer = [CALayer layer];
85     [m_hostingLayer setDelegate:[WebActionDisablingCALayerDelegate shared]];
86     [m_hostingLayer setFrame:m_webPage.bounds()];
87     [m_hostingLayer setOpaque:YES];
88     [m_hostingLayer setGeometryFlipped:YES];
89
90     m_layerFlushRunLoopObserver = std::make_unique<WebCore::RunLoopObserver>(static_cast<CFIndex>(RunLoopObserver::WellKnownRunLoopOrders::LayerFlush), [this]() {
91         this->layerFlushRunLoopCallback();
92     });
93
94     updateLayerHostingContext();
95     setColorSpace(parameters.colorSpace);
96 }
97
98 TiledCoreAnimationDrawingArea::~TiledCoreAnimationDrawingArea()
99 {
100     invalidateLayerFlushRunLoopObserver();
101 }
102
103 void TiledCoreAnimationDrawingArea::sendEnterAcceleratedCompositingModeIfNeeded()
104 {
105     if (!m_rootLayer)
106         return;
107
108     if (!m_needsSendEnterAcceleratedCompositingMode)
109         return;
110     m_needsSendEnterAcceleratedCompositingMode = false;
111
112     // Let the first commit complete before sending.
113     RunLoop::main().dispatch([this, weakThis = makeWeakPtr(*this)] {
114         if (!weakThis)
115             return;
116         LayerTreeContext layerTreeContext;
117         layerTreeContext.contextID = m_layerHostingContext->contextID();
118         send(Messages::DrawingAreaProxy::EnterAcceleratedCompositingMode(0, layerTreeContext));
119     });
120 }
121
122 void TiledCoreAnimationDrawingArea::setNeedsDisplay()
123 {
124 }
125
126 void TiledCoreAnimationDrawingArea::setNeedsDisplayInRect(const IntRect& rect)
127 {
128 }
129
130 void TiledCoreAnimationDrawingArea::scroll(const IntRect& scrollRect, const IntSize& scrollDelta)
131 {
132     updateScrolledExposedRect();
133 }
134
135 void TiledCoreAnimationDrawingArea::setRootCompositingLayer(GraphicsLayer* graphicsLayer)
136 {
137     CALayer *rootLayer = graphicsLayer ? graphicsLayer->platformLayer() : nil;
138
139     if (m_layerTreeStateIsFrozen) {
140         m_pendingRootLayer = rootLayer;
141         return;
142     }
143
144     setRootCompositingLayer(rootLayer);
145 }
146
147 void TiledCoreAnimationDrawingArea::forceRepaint()
148 {
149     if (m_layerTreeStateIsFrozen)
150         return;
151
152     for (Frame* frame = &m_webPage.corePage()->mainFrame(); frame; frame = frame->tree().traverseNext()) {
153         FrameView* frameView = frame->view();
154         if (!frameView || !frameView->tiledBacking())
155             continue;
156
157         frameView->tiledBacking()->forceRepaint();
158     }
159
160     flushLayers();
161     [CATransaction flush];
162     [CATransaction synchronize];
163 }
164
165 bool TiledCoreAnimationDrawingArea::forceRepaintAsync(CallbackID callbackID)
166 {
167     if (m_layerTreeStateIsFrozen)
168         return false;
169
170     dispatchAfterEnsuringUpdatedScrollPosition([this, callbackID] {
171         m_webPage.drawingArea()->forceRepaint();
172         m_webPage.send(Messages::WebPageProxy::VoidCallback(callbackID));
173     });
174     return true;
175 }
176
177 void TiledCoreAnimationDrawingArea::setLayerTreeStateIsFrozen(bool layerTreeStateIsFrozen)
178 {
179     if (m_layerTreeStateIsFrozen == layerTreeStateIsFrozen)
180         return;
181
182     m_layerTreeStateIsFrozen = layerTreeStateIsFrozen;
183
184     if (m_layerTreeStateIsFrozen) {
185         invalidateLayerFlushRunLoopObserver();
186         m_layerFlushThrottlingTimer.stop();
187     } else {
188         // Immediate flush as any delay in unfreezing can result in flashes.
189         scheduleLayerFlushRunLoopObserver();
190     }
191 }
192
193 bool TiledCoreAnimationDrawingArea::layerTreeStateIsFrozen() const
194 {
195     return m_layerTreeStateIsFrozen;
196 }
197
198 void TiledCoreAnimationDrawingArea::scheduleInitialDeferredPaint()
199 {
200 }
201
202 void TiledCoreAnimationDrawingArea::scheduleCompositingLayerFlush()
203 {
204     if (m_layerTreeStateIsFrozen) {
205         m_isLayerFlushThrottlingTemporarilyDisabledForInteraction = false;
206         return;
207     }
208
209     if (m_isLayerFlushThrottlingTemporarilyDisabledForInteraction) {
210         m_isLayerFlushThrottlingTemporarilyDisabledForInteraction = false;
211         scheduleLayerFlushRunLoopObserver();
212         m_layerFlushThrottlingTimer.stop();
213         return;
214     }
215
216     if (m_layerFlushThrottlingTimer.isActive()) {
217         ASSERT(m_isThrottlingLayerFlushes);
218         return;
219     }
220
221     scheduleLayerFlushRunLoopObserver();
222 }
223
224 void TiledCoreAnimationDrawingArea::scheduleCompositingLayerFlushImmediately()
225 {
226     scheduleLayerFlushRunLoopObserver();
227 }
228
229 void TiledCoreAnimationDrawingArea::updatePreferences(const WebPreferencesStore&)
230 {
231     Settings& settings = m_webPage.corePage()->settings();
232
233 #if ENABLE(ASYNC_SCROLLING)
234     if (AsyncScrollingCoordinator* scrollingCoordinator = downcast<AsyncScrollingCoordinator>(m_webPage.corePage()->scrollingCoordinator())) {
235         bool scrollingPerformanceLoggingEnabled = m_webPage.scrollingPerformanceLoggingEnabled();
236         
237         RefPtr<ScrollingTree> scrollingTree = scrollingCoordinator->scrollingTree();
238         ScrollingThread::dispatch([scrollingTree, scrollingPerformanceLoggingEnabled] {
239             scrollingTree->setScrollingPerformanceLoggingEnabled(scrollingPerformanceLoggingEnabled);
240         });
241     }
242 #endif
243
244     // Fixed position elements need to be composited and create stacking contexts
245     // in order to be scrolled by the ScrollingCoordinator.
246     settings.setAcceleratedCompositingForFixedPositionEnabled(true);
247
248     DebugPageOverlays::settingsChanged(*m_webPage.corePage());
249
250     bool showTiledScrollingIndicator = settings.showTiledScrollingIndicator();
251     if (showTiledScrollingIndicator == !!m_debugInfoLayer)
252         return;
253
254     updateDebugInfoLayer(showTiledScrollingIndicator);
255 }
256
257 void TiledCoreAnimationDrawingArea::updateRootLayers()
258 {
259     if (!m_rootLayer) {
260         [m_hostingLayer setSublayers:@[ ]];
261         return;
262     }
263
264     [m_hostingLayer setSublayers:m_viewOverlayRootLayer ? @[ m_rootLayer.get(), m_viewOverlayRootLayer->platformLayer() ] : @[ m_rootLayer.get() ]];
265     
266     if (m_debugInfoLayer)
267         [m_hostingLayer addSublayer:m_debugInfoLayer.get()];
268 }
269
270 void TiledCoreAnimationDrawingArea::attachViewOverlayGraphicsLayer(GraphicsLayer* viewOverlayRootLayer)
271 {
272     m_viewOverlayRootLayer = viewOverlayRootLayer;
273     updateRootLayers();
274     scheduleCompositingLayerFlush();
275 }
276
277 void TiledCoreAnimationDrawingArea::mainFrameContentSizeChanged(const IntSize& size)
278 {
279 }
280
281 void TiledCoreAnimationDrawingArea::updateIntrinsicContentSizeIfNeeded()
282 {
283     if (!m_webPage.viewLayoutSize().width())
284         return;
285
286     FrameView* frameView = m_webPage.mainFrameView();
287     if (!frameView)
288         return;
289
290     if (frameView->needsLayout())
291         return;
292
293     IntSize contentSize = frameView->autoSizingIntrinsicContentSize();
294     if (m_lastSentIntrinsicContentSize == contentSize)
295         return;
296
297     m_lastSentIntrinsicContentSize = contentSize;
298     send(Messages::DrawingAreaProxy::IntrinsicContentSizeDidChange(contentSize));
299 }
300
301 void TiledCoreAnimationDrawingArea::setShouldScaleViewToFitDocument(bool shouldScaleView)
302 {
303     if (m_shouldScaleViewToFitDocument == shouldScaleView)
304         return;
305
306     m_shouldScaleViewToFitDocument = shouldScaleView;
307     scheduleCompositingLayerFlush();
308 }
309
310 void TiledCoreAnimationDrawingArea::scaleViewToFitDocumentIfNeeded()
311 {
312     const int maximumDocumentWidthForScaling = 1440;
313     const float minimumViewScale = 0.1;
314
315     if (!m_shouldScaleViewToFitDocument)
316         return;
317
318     LOG(Resize, "TiledCoreAnimationDrawingArea %p scaleViewToFitDocumentIfNeeded", this);
319     m_webPage.layoutIfNeeded();
320
321     if (!m_webPage.mainFrameView() || !m_webPage.mainFrameView()->renderView())
322         return;
323
324     int viewWidth = m_webPage.size().width();
325     int documentWidth = m_webPage.mainFrameView()->renderView()->unscaledDocumentRect().width();
326
327     bool documentWidthChanged = m_lastDocumentSizeForScaleToFit.width() != documentWidth;
328     bool viewWidthChanged = m_lastViewSizeForScaleToFit.width() != viewWidth;
329
330     LOG(Resize, "  documentWidthChanged=%d, viewWidthChanged=%d", documentWidthChanged, viewWidthChanged);
331
332     if (!documentWidthChanged && !viewWidthChanged)
333         return;
334
335     // The view is now bigger than the document, so we'll re-evaluate whether we have to scale.
336     if (m_isScalingViewToFitDocument && viewWidth >= m_lastDocumentSizeForScaleToFit.width())
337         m_isScalingViewToFitDocument = false;
338
339     // Our current understanding of the document width is still up to date, and we're in scaling mode.
340     // Update the viewScale without doing an extra layout to re-determine the document width.
341     if (m_isScalingViewToFitDocument) {
342         if (!documentWidthChanged) {
343             m_lastViewSizeForScaleToFit = m_webPage.size();
344             float viewScale = (float)viewWidth / (float)m_lastDocumentSizeForScaleToFit.width();
345             if (viewScale < minimumViewScale) {
346                 viewScale = minimumViewScale;
347                 documentWidth = std::ceil(viewWidth / viewScale);
348             }
349             IntSize fixedLayoutSize(documentWidth, std::ceil((m_webPage.size().height() - m_webPage.corePage()->topContentInset()) / viewScale));
350             m_webPage.setFixedLayoutSize(fixedLayoutSize);
351             m_webPage.scaleView(viewScale);
352
353             LOG(Resize, "  using fixed layout at %dx%d. document width %d unchanged, scaled to %.4f to fit view width %d", fixedLayoutSize.width(), fixedLayoutSize.height(), documentWidth, viewScale, viewWidth);
354             return;
355         }
356     
357         IntSize fixedLayoutSize = m_webPage.fixedLayoutSize();
358         if (documentWidth > fixedLayoutSize.width()) {
359             LOG(Resize, "  page laid out wider than fixed layout width. Not attempting to re-scale");
360             return;
361         }
362     }
363
364     LOG(Resize, "  doing unconstrained layout");
365
366     // Lay out at the view size.
367     m_webPage.setUseFixedLayout(false);
368     m_webPage.layoutIfNeeded();
369
370     if (!m_webPage.mainFrameView() || !m_webPage.mainFrameView()->renderView())
371         return;
372
373     IntSize documentSize = m_webPage.mainFrameView()->renderView()->unscaledDocumentRect().size();
374     m_lastViewSizeForScaleToFit = m_webPage.size();
375     m_lastDocumentSizeForScaleToFit = documentSize;
376
377     documentWidth = documentSize.width();
378
379     float viewScale = 1;
380
381     LOG(Resize, "  unscaled document size %dx%d. need to scale down: %d", documentSize.width(), documentSize.height(), documentWidth && documentWidth < maximumDocumentWidthForScaling && viewWidth < documentWidth);
382
383     // Avoid scaling down documents that don't fit in a certain width, to allow
384     // sites that want horizontal scrollbars to continue to have them.
385     if (documentWidth && documentWidth < maximumDocumentWidthForScaling && viewWidth < documentWidth) {
386         // If the document doesn't fit in the view, scale it down but lay out at the view size.
387         m_isScalingViewToFitDocument = true;
388         m_webPage.setUseFixedLayout(true);
389         viewScale = (float)viewWidth / (float)documentWidth;
390         if (viewScale < minimumViewScale) {
391             viewScale = minimumViewScale;
392             documentWidth = std::ceil(viewWidth / viewScale);
393         }
394         IntSize fixedLayoutSize(documentWidth, std::ceil((m_webPage.size().height() - m_webPage.corePage()->topContentInset()) / viewScale));
395         m_webPage.setFixedLayoutSize(fixedLayoutSize);
396
397         LOG(Resize, "  using fixed layout at %dx%d. document width %d, scaled to %.4f to fit view width %d", fixedLayoutSize.width(), fixedLayoutSize.height(), documentWidth, viewScale, viewWidth);
398     }
399
400     m_webPage.scaleView(viewScale);
401 }
402
403 void TiledCoreAnimationDrawingArea::dispatchAfterEnsuringUpdatedScrollPosition(WTF::Function<void ()>&& function)
404 {
405 #if ENABLE(ASYNC_SCROLLING)
406     if (!m_webPage.corePage()->scrollingCoordinator()) {
407         function();
408         return;
409     }
410
411     m_webPage.ref();
412     m_webPage.corePage()->scrollingCoordinator()->commitTreeStateIfNeeded();
413
414     if (!m_layerTreeStateIsFrozen)
415         invalidateLayerFlushRunLoopObserver();
416
417     // It is possible for the drawing area to be destroyed before the bound block
418     // is invoked, so grab a reference to the web page here so we can access the drawing area through it.
419     // (The web page is already kept alive by dispatchAfterEnsuringUpdatedScrollPosition).
420     WebPage* webPage = &m_webPage;
421
422     ScrollingThread::dispatchBarrier([this, webPage, function = WTFMove(function)] {
423         DrawingArea* drawingArea = webPage->drawingArea();
424         if (!drawingArea)
425             return;
426
427         function();
428
429         if (!m_layerTreeStateIsFrozen)
430             scheduleLayerFlushRunLoopObserver();
431
432         webPage->deref();
433     });
434 #else
435     function();
436 #endif
437 }
438
439 void TiledCoreAnimationDrawingArea::sendPendingNewlyReachedPaintingMilestones()
440 {
441     if (!m_pendingNewlyReachedPaintingMilestones)
442         return;
443
444     m_webPage.send(Messages::WebPageProxy::DidReachLayoutMilestone(m_pendingNewlyReachedPaintingMilestones));
445     m_pendingNewlyReachedPaintingMilestones = { };
446 }
447
448 void TiledCoreAnimationDrawingArea::addTransactionCallbackID(CallbackID callbackID)
449 {
450     m_pendingCallbackIDs.append(callbackID);
451     scheduleCompositingLayerFlush();
452 }
453
454 void TiledCoreAnimationDrawingArea::flushLayers(FlushType flushType)
455 {
456     if (layerTreeStateIsFrozen())
457         return;
458
459     @autoreleasepool {
460         scaleViewToFitDocumentIfNeeded();
461
462         m_webPage.updateRendering();
463         m_webPage.flushPendingEditorStateUpdate();
464
465         updateIntrinsicContentSizeIfNeeded();
466
467         if (m_pendingRootLayer) {
468             setRootCompositingLayer(m_pendingRootLayer.get());
469             m_pendingRootLayer = nullptr;
470         }
471
472         FloatRect visibleRect = [m_hostingLayer frame];
473         if (m_scrolledViewExposedRect)
474             visibleRect.intersect(m_scrolledViewExposedRect.value());
475
476         // Because our view-relative overlay root layer is not attached to the main GraphicsLayer tree, we need to flush it manually.
477         if (m_viewOverlayRootLayer)
478             m_viewOverlayRootLayer->flushCompositingState(visibleRect);
479
480         RefPtr<WebPage> retainedPage = &m_webPage;
481         [CATransaction addCommitHandler:[retainedPage] {
482             if (Page* corePage = retainedPage->corePage()) {
483                 if (Frame* coreFrame = retainedPage->mainFrame())
484                     corePage->inspectorController().didComposite(*coreFrame);
485             }
486             if (auto drawingArea = static_cast<TiledCoreAnimationDrawingArea*>(retainedPage->drawingArea()))
487                 drawingArea->sendPendingNewlyReachedPaintingMilestones();
488         } forPhase:kCATransactionPhasePostCommit];
489
490         bool didFlushAllFrames = m_webPage.mainFrameView()->flushCompositingStateIncludingSubframes();
491
492 #if ENABLE(ASYNC_SCROLLING)
493         if (auto* scrollingCoordinator = m_webPage.corePage()->scrollingCoordinator()) {
494             scrollingCoordinator->commitTreeStateIfNeeded();
495             if (flushType == FlushType::Normal)
496                 scrollingCoordinator->applyScrollingTreeLayerPositions();
497         }
498 #endif
499
500         // If we have an active transient zoom, we want the zoom to win over any changes
501         // that WebCore makes to the relevant layers, so re-apply our changes after flushing.
502         if (m_transientZoomScale != 1)
503             applyTransientZoomToLayers(m_transientZoomScale, m_transientZoomOrigin);
504
505         if (!m_pendingCallbackIDs.isEmpty()) {
506             send(Messages::DrawingAreaProxy::DispatchPresentationCallbacksAfterFlushingLayers(m_pendingCallbackIDs));
507             m_pendingCallbackIDs.clear();
508         }
509
510         if (didFlushAllFrames) {
511             sendEnterAcceleratedCompositingModeIfNeeded();
512             invalidateLayerFlushRunLoopObserver();
513         }
514
515         if (m_isThrottlingLayerFlushes)
516             startLayerFlushThrottlingTimer();
517         else
518             m_layerFlushThrottlingTimer.stop();
519     }
520 }
521
522 void TiledCoreAnimationDrawingArea::activityStateDidChange(OptionSet<WebCore::ActivityState::Flag> changed, ActivityStateChangeID activityStateChangeID, const Vector<CallbackID>& nextActivityStateChangeCallbackIDs)
523 {
524     m_nextActivityStateChangeCallbackIDs.appendVector(nextActivityStateChangeCallbackIDs);
525     m_activityStateChangeID = std::max(m_activityStateChangeID, activityStateChangeID);
526
527     if (changed & ActivityState::IsVisible) {
528         if (m_webPage.isVisible())
529             resumePainting();
530         else
531             suspendPainting();
532     }
533
534     if (m_activityStateChangeID != ActivityStateChangeAsynchronous || !m_nextActivityStateChangeCallbackIDs.isEmpty())
535         m_sendDidUpdateActivityStateTimer.startOneShot(0_s);
536 }
537
538 void TiledCoreAnimationDrawingArea::didUpdateActivityStateTimerFired()
539 {
540     [CATransaction flush];
541
542     if (m_activityStateChangeID != ActivityStateChangeAsynchronous)
543         m_webPage.send(Messages::WebPageProxy::DidUpdateActivityState());
544
545     for (const auto& callbackID : m_nextActivityStateChangeCallbackIDs)
546         m_webPage.send(Messages::WebPageProxy::VoidCallback(callbackID));
547
548     m_nextActivityStateChangeCallbackIDs.clear();
549     m_activityStateChangeID = ActivityStateChangeAsynchronous;
550 }
551
552 void TiledCoreAnimationDrawingArea::suspendPainting()
553 {
554     ASSERT(!m_isPaintingSuspended);
555     m_isPaintingSuspended = true;
556
557     [m_hostingLayer setValue:@YES forKey:@"NSCAViewRenderPaused"];
558     [[NSNotificationCenter defaultCenter] postNotificationName:@"NSCAViewRenderDidPauseNotification" object:nil userInfo:[NSDictionary dictionaryWithObject:m_hostingLayer.get() forKey:@"layer"]];
559 }
560
561 void TiledCoreAnimationDrawingArea::resumePainting()
562 {
563     if (!m_isPaintingSuspended) {
564         // FIXME: We can get a call to resumePainting when painting is not suspended.
565         // This happens when sending a synchronous message to create a new page. See <rdar://problem/8976531>.
566         return;
567     }
568     m_isPaintingSuspended = false;
569
570     [m_hostingLayer setValue:@NO forKey:@"NSCAViewRenderPaused"];
571     [[NSNotificationCenter defaultCenter] postNotificationName:@"NSCAViewRenderDidResumeNotification" object:nil userInfo:[NSDictionary dictionaryWithObject:m_hostingLayer.get() forKey:@"layer"]];
572 }
573
574 void TiledCoreAnimationDrawingArea::setViewExposedRect(Optional<WebCore::FloatRect> viewExposedRect)
575 {
576     m_viewExposedRect = viewExposedRect;
577     updateScrolledExposedRect();
578 }
579
580 void TiledCoreAnimationDrawingArea::updateScrolledExposedRect()
581 {
582     FrameView* frameView = m_webPage.mainFrameView();
583     if (!frameView)
584         return;
585
586     m_scrolledViewExposedRect = m_viewExposedRect;
587
588 #if !PLATFORM(IOS_FAMILY)
589     if (m_viewExposedRect) {
590         ScrollOffset scrollOffset = frameView->scrollOffsetFromPosition(frameView->scrollPosition());
591         m_scrolledViewExposedRect.value().moveBy(scrollOffset);
592     }
593 #endif
594
595     frameView->setViewExposedRect(m_scrolledViewExposedRect);
596 }
597
598 void TiledCoreAnimationDrawingArea::updateGeometry(const IntSize& viewSize, bool flushSynchronously, const WTF::MachSendRight& fencePort)
599 {
600     m_inUpdateGeometry = true;
601
602     IntSize size = viewSize;
603     IntSize contentSize = IntSize(-1, -1);
604
605     if (!m_webPage.viewLayoutSize().width() || m_webPage.autoSizingShouldExpandToViewHeight())
606         m_webPage.setSize(size);
607
608     FrameView* frameView = m_webPage.mainFrameView();
609
610     if (m_webPage.autoSizingShouldExpandToViewHeight() && frameView)
611         frameView->setAutoSizeFixedMinimumHeight(viewSize.height());
612
613     m_webPage.layoutIfNeeded();
614
615     if (m_webPage.viewLayoutSize().width() && frameView) {
616         contentSize = frameView->autoSizingIntrinsicContentSize();
617         size = contentSize;
618     }
619
620     flushLayers();
621
622     [CATransaction begin];
623     [CATransaction setDisableActions:YES];
624
625     [m_hostingLayer setFrame:CGRectMake(0, 0, viewSize.width(), viewSize.height())];
626
627     [CATransaction commit];
628
629     if (flushSynchronously)
630         [CATransaction flush];
631
632     send(Messages::DrawingAreaProxy::DidUpdateGeometry());
633
634     m_inUpdateGeometry = false;
635
636     m_layerHostingContext->setFencePort(fencePort.sendRight());
637 }
638
639 void TiledCoreAnimationDrawingArea::setDeviceScaleFactor(float deviceScaleFactor)
640 {
641     m_webPage.setDeviceScaleFactor(deviceScaleFactor);
642 }
643
644 void TiledCoreAnimationDrawingArea::setLayerHostingMode(LayerHostingMode)
645 {
646     updateLayerHostingContext();
647
648     // Finally, inform the UIProcess that the context has changed.
649     LayerTreeContext layerTreeContext;
650     layerTreeContext.contextID = m_layerHostingContext->contextID();
651     send(Messages::DrawingAreaProxy::UpdateAcceleratedCompositingMode(0, layerTreeContext));
652 }
653
654 void TiledCoreAnimationDrawingArea::setColorSpace(const ColorSpaceData& colorSpace)
655 {
656     m_layerHostingContext->setColorSpace(colorSpace.cgColorSpace.get());
657 }
658
659 void TiledCoreAnimationDrawingArea::updateLayerHostingContext()
660 {
661     RetainPtr<CGColorSpaceRef> colorSpace;
662
663     // Invalidate the old context.
664     if (m_layerHostingContext) {
665         colorSpace = m_layerHostingContext->colorSpace();
666         m_layerHostingContext->invalidate();
667         m_layerHostingContext = nullptr;
668     }
669
670     // Create a new context and set it up.
671     switch (m_webPage.layerHostingMode()) {
672     case LayerHostingMode::InProcess:
673         m_layerHostingContext = LayerHostingContext::createForPort(WebProcess::singleton().compositingRenderServerPort());
674         break;
675 #if HAVE(OUT_OF_PROCESS_LAYER_HOSTING)
676     case LayerHostingMode::OutOfProcess:
677         m_layerHostingContext = LayerHostingContext::createForExternalHostingProcess();
678         break;
679 #endif
680     }
681
682     if (m_rootLayer)
683         m_layerHostingContext->setRootLayer(m_hostingLayer.get());
684
685     if (colorSpace)
686         m_layerHostingContext->setColorSpace(colorSpace.get());
687 }
688
689 void TiledCoreAnimationDrawingArea::setRootCompositingLayer(CALayer *layer)
690 {
691     ASSERT(!m_layerTreeStateIsFrozen);
692
693     [CATransaction begin];
694     [CATransaction setDisableActions:YES];
695
696     bool hadRootLayer = !!m_rootLayer;
697     m_rootLayer = layer;
698
699     updateRootLayers();
700
701     if (hadRootLayer != !!layer)
702         m_layerHostingContext->setRootLayer(layer ? m_hostingLayer.get() : nil);
703
704     updateDebugInfoLayer(layer && m_webPage.corePage()->settings().showTiledScrollingIndicator());
705
706     [CATransaction commit];
707 }
708
709 TiledBacking* TiledCoreAnimationDrawingArea::mainFrameTiledBacking() const
710 {
711     FrameView* frameView = m_webPage.mainFrameView();
712     return frameView ? frameView->tiledBacking() : nullptr;
713 }
714
715 void TiledCoreAnimationDrawingArea::updateDebugInfoLayer(bool showLayer)
716 {
717     if (m_debugInfoLayer) {
718         [m_debugInfoLayer removeFromSuperlayer];
719         m_debugInfoLayer = nil;
720     }
721     
722     if (showLayer) {
723         if (TiledBacking* tiledBacking = mainFrameTiledBacking()) {
724             if (PlatformCALayer* indicatorLayer = tiledBacking->tiledScrollingIndicatorLayer())
725                 m_debugInfoLayer = indicatorLayer->platformLayer();
726         }
727
728         if (m_debugInfoLayer) {
729             [m_debugInfoLayer setName:@"Debug Info"];
730             [m_hostingLayer addSublayer:m_debugInfoLayer.get()];
731         }
732     }
733 }
734
735 bool TiledCoreAnimationDrawingArea::shouldUseTiledBackingForFrameView(const FrameView& frameView)
736 {
737     return frameView.frame().isMainFrame() || m_webPage.corePage()->settings().asyncFrameScrollingEnabled();
738 }
739
740 PlatformCALayer* TiledCoreAnimationDrawingArea::layerForTransientZoom() const
741 {
742     RenderLayerBacking* renderViewBacking = m_webPage.mainFrameView()->renderView()->layer()->backing();
743
744     if (GraphicsLayer* contentsContainmentLayer = renderViewBacking->contentsContainmentLayer())
745         return downcast<GraphicsLayerCA>(*contentsContainmentLayer).platformCALayer();
746
747     return downcast<GraphicsLayerCA>(*renderViewBacking->graphicsLayer()).platformCALayer();
748 }
749
750 PlatformCALayer* TiledCoreAnimationDrawingArea::shadowLayerForTransientZoom() const
751 {
752     RenderLayerCompositor& renderLayerCompositor = m_webPage.mainFrameView()->renderView()->compositor();
753
754     if (GraphicsLayer* shadowGraphicsLayer = renderLayerCompositor.layerForContentShadow())
755         return downcast<GraphicsLayerCA>(*shadowGraphicsLayer).platformCALayer();
756
757     return nullptr;
758 }
759     
760 static FloatPoint shadowLayerPositionForFrame(FrameView& frameView, FloatPoint origin)
761 {
762     // FIXME: correct for b-t documents?
763     FloatPoint position = frameView.positionForRootContentLayer();
764     return position + origin.expandedTo(FloatPoint());
765 }
766
767 static FloatRect shadowLayerBoundsForFrame(FrameView& frameView, float transientScale)
768 {
769     FloatRect clipLayerFrame(frameView.renderView()->documentRect());
770     FloatRect shadowLayerFrame = clipLayerFrame;
771     
772     shadowLayerFrame.scale(transientScale / frameView.frame().page()->pageScaleFactor());
773     shadowLayerFrame.intersect(clipLayerFrame);
774     
775     return shadowLayerFrame;
776 }
777
778 void TiledCoreAnimationDrawingArea::applyTransientZoomToLayers(double scale, FloatPoint origin)
779 {
780     // FIXME: Scrollbars should stay in-place and change height while zooming.
781
782     if (!m_hostingLayer)
783         return;
784
785     TransformationMatrix transform;
786     transform.translate(origin.x(), origin.y());
787     transform.scale(scale);
788
789     PlatformCALayer* zoomLayer = layerForTransientZoom();
790     zoomLayer->setTransform(transform);
791     zoomLayer->setAnchorPoint(FloatPoint3D());
792     zoomLayer->setPosition(FloatPoint3D());
793     
794     if (PlatformCALayer* shadowLayer = shadowLayerForTransientZoom()) {
795         FrameView& frameView = *m_webPage.mainFrameView();
796         shadowLayer->setBounds(shadowLayerBoundsForFrame(frameView, scale));
797         shadowLayer->setPosition(shadowLayerPositionForFrame(frameView, origin));
798     }
799
800     m_transientZoomScale = scale;
801     m_transientZoomOrigin = origin;
802 }
803
804 void TiledCoreAnimationDrawingArea::adjustTransientZoom(double scale, FloatPoint origin)
805 {
806     scale *= m_webPage.viewScaleFactor();
807
808     applyTransientZoomToLayers(scale, origin);
809
810     double currentPageScale = m_webPage.totalScaleFactor();
811     if (scale > currentPageScale)
812         return;
813
814     FrameView* frameView = m_webPage.mainFrameView();
815     FloatRect tileCoverageRect = frameView->visibleContentRectIncludingScrollbars();
816     tileCoverageRect.moveBy(-origin);
817     tileCoverageRect.scale(currentPageScale / scale);
818     frameView->renderView()->layer()->backing()->tiledBacking()->prepopulateRect(tileCoverageRect);
819 }
820
821 static RetainPtr<CABasicAnimation> transientZoomSnapAnimationForKeyPath(String keyPath)
822 {
823     const float transientZoomSnapBackDuration = 0.25;
824
825     RetainPtr<CABasicAnimation> animation = [CABasicAnimation animationWithKeyPath:keyPath];
826     [animation setDuration:transientZoomSnapBackDuration];
827     [animation setFillMode:kCAFillModeForwards];
828     [animation setRemovedOnCompletion:false];
829     [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
830
831     return animation;
832 }
833
834 void TiledCoreAnimationDrawingArea::commitTransientZoom(double scale, FloatPoint origin)
835 {
836     scale *= m_webPage.viewScaleFactor();
837
838     FrameView& frameView = *m_webPage.mainFrameView();
839     FloatRect visibleContentRect = frameView.visibleContentRectIncludingScrollbars();
840
841     FloatPoint constrainedOrigin = visibleContentRect.location();
842     constrainedOrigin.moveBy(-origin);
843
844     IntSize scaledTotalContentsSize = frameView.totalContentsSize();
845     scaledTotalContentsSize.scale(scale / m_webPage.totalScaleFactor());
846
847     // Scaling may have exposed the overhang area, so we need to constrain the final
848     // layer position exactly like scrolling will once it's committed, to ensure that
849     // scrolling doesn't make the view jump.
850     constrainedOrigin = ScrollableArea::constrainScrollPositionForOverhang(roundedIntRect(visibleContentRect), scaledTotalContentsSize, roundedIntPoint(constrainedOrigin), frameView.scrollOrigin(), frameView.headerHeight(), frameView.footerHeight());
851     constrainedOrigin.moveBy(-visibleContentRect.location());
852     constrainedOrigin = -constrainedOrigin;
853
854     if (m_transientZoomScale == scale && roundedIntPoint(m_transientZoomOrigin) == roundedIntPoint(constrainedOrigin)) {
855         // We're already at the right scale and position, so we don't need to animate.
856         applyTransientZoomToPage(scale, origin);
857         return;
858     }
859
860     TransformationMatrix transform;
861     transform.translate(constrainedOrigin.x(), constrainedOrigin.y());
862     transform.scale(scale);
863
864     RetainPtr<CABasicAnimation> renderViewAnimationCA = transientZoomSnapAnimationForKeyPath("transform");
865     auto renderViewAnimation = PlatformCAAnimationCocoa::create(renderViewAnimationCA.get());
866     renderViewAnimation->setToValue(transform);
867
868     RetainPtr<CALayer> shadowCALayer;
869     if (PlatformCALayer* shadowLayer = shadowLayerForTransientZoom())
870         shadowCALayer = shadowLayer->platformLayer();
871
872     RefPtr<PlatformCALayer> zoomLayer = layerForTransientZoom();
873     RefPtr<WebPage> page = &m_webPage;
874
875     [CATransaction begin];
876     [CATransaction setCompletionBlock:[zoomLayer, shadowCALayer, page, scale, origin] () {
877         zoomLayer->removeAnimationForKey("transientZoomCommit");
878         if (shadowCALayer)
879             [shadowCALayer removeAllAnimations];
880
881         if (TiledCoreAnimationDrawingArea* drawingArea = static_cast<TiledCoreAnimationDrawingArea*>(page->drawingArea()))
882             drawingArea->applyTransientZoomToPage(scale, origin);
883     }];
884
885     zoomLayer->addAnimationForKey("transientZoomCommit", renderViewAnimation.get());
886
887     if (shadowCALayer) {
888         FloatRect shadowBounds = shadowLayerBoundsForFrame(frameView, scale);
889         RetainPtr<CGPathRef> shadowPath = adoptCF(CGPathCreateWithRect(shadowBounds, NULL));
890
891         RetainPtr<CABasicAnimation> shadowBoundsAnimation = transientZoomSnapAnimationForKeyPath("bounds");
892         [shadowBoundsAnimation setToValue:[NSValue valueWithRect:shadowBounds]];
893         RetainPtr<CABasicAnimation> shadowPositionAnimation = transientZoomSnapAnimationForKeyPath("position");
894         [shadowPositionAnimation setToValue:[NSValue valueWithPoint:shadowLayerPositionForFrame(frameView, constrainedOrigin)]];
895         RetainPtr<CABasicAnimation> shadowPathAnimation = transientZoomSnapAnimationForKeyPath("shadowPath");
896         [shadowPathAnimation setToValue:(__bridge id)shadowPath.get()];
897
898         [shadowCALayer addAnimation:shadowBoundsAnimation.get() forKey:@"transientZoomCommitShadowBounds"];
899         [shadowCALayer addAnimation:shadowPositionAnimation.get() forKey:@"transientZoomCommitShadowPosition"];
900         [shadowCALayer addAnimation:shadowPathAnimation.get() forKey:@"transientZoomCommitShadowPath"];
901     }
902
903     [CATransaction commit];
904 }
905
906 void TiledCoreAnimationDrawingArea::applyTransientZoomToPage(double scale, FloatPoint origin)
907 {
908     // If the page scale is already the target scale, setPageScaleFactor() will short-circuit
909     // and not apply the transform, so we can't depend on it to do so.
910     TransformationMatrix finalTransform;
911     finalTransform.scale(scale);
912     layerForTransientZoom()->setTransform(finalTransform);
913     
914     FrameView& frameView = *m_webPage.mainFrameView();
915
916     if (PlatformCALayer* shadowLayer = shadowLayerForTransientZoom()) {
917         shadowLayer->setBounds(shadowLayerBoundsForFrame(frameView, 1));
918         shadowLayer->setPosition(shadowLayerPositionForFrame(frameView, FloatPoint()));
919     }
920
921     FloatPoint unscrolledOrigin(origin);
922     FloatRect unobscuredContentRect = frameView.unobscuredContentRectIncludingScrollbars();
923     unscrolledOrigin.moveBy(-unobscuredContentRect.location());
924     m_webPage.scalePage(scale / m_webPage.viewScaleFactor(), roundedIntPoint(-unscrolledOrigin));
925     m_transientZoomScale = 1;
926     flushLayers(FlushType::TransientZoom);
927 }
928
929 void TiledCoreAnimationDrawingArea::addFence(const MachSendRight& fencePort)
930 {
931     m_layerHostingContext->setFencePort(fencePort.sendRight());
932 }
933
934 void TiledCoreAnimationDrawingArea::layerFlushRunLoopCallback()
935 {
936     flushLayers();
937 }
938
939 void TiledCoreAnimationDrawingArea::invalidateLayerFlushRunLoopObserver()
940 {
941     m_layerFlushRunLoopObserver->invalidate();
942 }
943
944 void TiledCoreAnimationDrawingArea::scheduleLayerFlushRunLoopObserver()
945 {
946     m_layerFlushRunLoopObserver->schedule(CFRunLoopGetCurrent());
947 }
948
949 bool TiledCoreAnimationDrawingArea::adjustLayerFlushThrottling(WebCore::LayerFlushThrottleState::Flags flags)
950 {
951     bool wasThrottlingLayerFlushes = m_isThrottlingLayerFlushes;
952     m_isThrottlingLayerFlushes = flags & WebCore::LayerFlushThrottleState::Enabled;
953     m_isLayerFlushThrottlingTemporarilyDisabledForInteraction = flags & WebCore::LayerFlushThrottleState::UserIsInteracting;
954
955     if (wasThrottlingLayerFlushes == m_isThrottlingLayerFlushes)
956         return true;
957
958     m_layerFlushThrottlingTimer.stop();
959
960     if (m_layerTreeStateIsFrozen)
961         return true;
962
963     if (m_isThrottlingLayerFlushes) {
964         invalidateLayerFlushRunLoopObserver();
965         startLayerFlushThrottlingTimer();
966     } else
967         scheduleLayerFlushRunLoopObserver();
968
969     return true;
970 }
971
972 bool TiledCoreAnimationDrawingArea::layerFlushThrottlingIsActive() const
973 {
974     return m_isThrottlingLayerFlushes && !m_layerTreeStateIsFrozen;
975 }
976
977 void TiledCoreAnimationDrawingArea::startLayerFlushThrottlingTimer()
978 {
979     ASSERT(m_isThrottlingLayerFlushes);
980
981     const auto throttledFlushDelay = 500_ms;
982     m_layerFlushThrottlingTimer.startOneShot(throttledFlushDelay);
983 }
984
985 void TiledCoreAnimationDrawingArea::layerFlushThrottlingTimerFired()
986 {
987     if (m_layerTreeStateIsFrozen)
988         return;
989     scheduleLayerFlushRunLoopObserver();
990 }
991
992 } // namespace WebKit
993
994 #endif // PLATFORM(MAC)