REGRESSION (r242687): Flicker when pinch-zooming pages in macOS Safari
[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::sendPendingNewlyReachedLayoutMilestones()
440 {
441     if (!m_pendingNewlyReachedLayoutMilestones)
442         return;
443
444     m_webPage.send(Messages::WebPageProxy::DidReachLayoutMilestone(m_pendingNewlyReachedLayoutMilestones));
445     m_pendingNewlyReachedLayoutMilestones = { };
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.layoutIfNeeded();
463         m_webPage.flushPendingEditorStateUpdate();
464         m_webPage.willDisplayPage();
465
466         updateIntrinsicContentSizeIfNeeded();
467
468         if (m_pendingRootLayer) {
469             setRootCompositingLayer(m_pendingRootLayer.get());
470             m_pendingRootLayer = nullptr;
471         }
472
473         FloatRect visibleRect = [m_hostingLayer frame];
474         if (m_scrolledViewExposedRect)
475             visibleRect.intersect(m_scrolledViewExposedRect.value());
476
477         // Because our view-relative overlay root layer is not attached to the main GraphicsLayer tree, we need to flush it manually.
478         if (m_viewOverlayRootLayer)
479             m_viewOverlayRootLayer->flushCompositingState(visibleRect);
480
481         RefPtr<WebPage> retainedPage = &m_webPage;
482         [CATransaction addCommitHandler:[retainedPage] {
483             if (Page* corePage = retainedPage->corePage()) {
484                 if (Frame* coreFrame = retainedPage->mainFrame())
485                     corePage->inspectorController().didComposite(*coreFrame);
486             }
487             if (auto drawingArea = static_cast<TiledCoreAnimationDrawingArea*>(retainedPage->drawingArea()))
488                 drawingArea->sendPendingNewlyReachedLayoutMilestones();
489         } forPhase:kCATransactionPhasePostCommit];
490
491         bool didFlushAllFrames = m_webPage.mainFrameView()->flushCompositingStateIncludingSubframes();
492
493 #if ENABLE(ASYNC_SCROLLING)
494         if (auto* scrollingCoordinator = m_webPage.corePage()->scrollingCoordinator()) {
495             scrollingCoordinator->commitTreeStateIfNeeded();
496             if (flushType == FlushType::TransientZoom)
497                 scrollingCoordinator->applyScrollingTreeLayerPositions();
498         }
499 #endif
500
501         // If we have an active transient zoom, we want the zoom to win over any changes
502         // that WebCore makes to the relevant layers, so re-apply our changes after flushing.
503         if (m_transientZoomScale != 1)
504             applyTransientZoomToLayers(m_transientZoomScale, m_transientZoomOrigin);
505
506         if (!m_pendingCallbackIDs.isEmpty()) {
507             send(Messages::DrawingAreaProxy::DispatchPresentationCallbacksAfterFlushingLayers(m_pendingCallbackIDs));
508             m_pendingCallbackIDs.clear();
509         }
510
511         if (didFlushAllFrames) {
512             sendEnterAcceleratedCompositingModeIfNeeded();
513             invalidateLayerFlushRunLoopObserver();
514         }
515
516         if (m_isThrottlingLayerFlushes)
517             startLayerFlushThrottlingTimer();
518         else
519             m_layerFlushThrottlingTimer.stop();
520     }
521 }
522
523 void TiledCoreAnimationDrawingArea::activityStateDidChange(OptionSet<WebCore::ActivityState::Flag> changed, ActivityStateChangeID activityStateChangeID, const Vector<CallbackID>& nextActivityStateChangeCallbackIDs)
524 {
525     m_nextActivityStateChangeCallbackIDs.appendVector(nextActivityStateChangeCallbackIDs);
526     m_activityStateChangeID = std::max(m_activityStateChangeID, activityStateChangeID);
527
528     if (changed & ActivityState::IsVisible) {
529         if (m_webPage.isVisible())
530             resumePainting();
531         else
532             suspendPainting();
533     }
534
535     if (m_activityStateChangeID != ActivityStateChangeAsynchronous || !m_nextActivityStateChangeCallbackIDs.isEmpty())
536         m_sendDidUpdateActivityStateTimer.startOneShot(0_s);
537 }
538
539 void TiledCoreAnimationDrawingArea::didUpdateActivityStateTimerFired()
540 {
541     [CATransaction flush];
542
543     if (m_activityStateChangeID != ActivityStateChangeAsynchronous)
544         m_webPage.send(Messages::WebPageProxy::DidUpdateActivityState());
545
546     for (const auto& callbackID : m_nextActivityStateChangeCallbackIDs)
547         m_webPage.send(Messages::WebPageProxy::VoidCallback(callbackID));
548
549     m_nextActivityStateChangeCallbackIDs.clear();
550     m_activityStateChangeID = ActivityStateChangeAsynchronous;
551 }
552
553 void TiledCoreAnimationDrawingArea::suspendPainting()
554 {
555     ASSERT(!m_isPaintingSuspended);
556     m_isPaintingSuspended = true;
557
558     [m_hostingLayer setValue:@YES forKey:@"NSCAViewRenderPaused"];
559     [[NSNotificationCenter defaultCenter] postNotificationName:@"NSCAViewRenderDidPauseNotification" object:nil userInfo:[NSDictionary dictionaryWithObject:m_hostingLayer.get() forKey:@"layer"]];
560 }
561
562 void TiledCoreAnimationDrawingArea::resumePainting()
563 {
564     if (!m_isPaintingSuspended) {
565         // FIXME: We can get a call to resumePainting when painting is not suspended.
566         // This happens when sending a synchronous message to create a new page. See <rdar://problem/8976531>.
567         return;
568     }
569     m_isPaintingSuspended = false;
570
571     [m_hostingLayer setValue:@NO forKey:@"NSCAViewRenderPaused"];
572     [[NSNotificationCenter defaultCenter] postNotificationName:@"NSCAViewRenderDidResumeNotification" object:nil userInfo:[NSDictionary dictionaryWithObject:m_hostingLayer.get() forKey:@"layer"]];
573 }
574
575 void TiledCoreAnimationDrawingArea::setViewExposedRect(Optional<WebCore::FloatRect> viewExposedRect)
576 {
577     m_viewExposedRect = viewExposedRect;
578     updateScrolledExposedRect();
579 }
580
581 void TiledCoreAnimationDrawingArea::updateScrolledExposedRect()
582 {
583     FrameView* frameView = m_webPage.mainFrameView();
584     if (!frameView)
585         return;
586
587     m_scrolledViewExposedRect = m_viewExposedRect;
588
589 #if !PLATFORM(IOS_FAMILY)
590     if (m_viewExposedRect) {
591         ScrollOffset scrollOffset = frameView->scrollOffsetFromPosition(frameView->scrollPosition());
592         m_scrolledViewExposedRect.value().moveBy(scrollOffset);
593     }
594 #endif
595
596     frameView->setViewExposedRect(m_scrolledViewExposedRect);
597 }
598
599 void TiledCoreAnimationDrawingArea::updateGeometry(const IntSize& viewSize, bool flushSynchronously, const WTF::MachSendRight& fencePort)
600 {
601     m_inUpdateGeometry = true;
602
603     IntSize size = viewSize;
604     IntSize contentSize = IntSize(-1, -1);
605
606     if (!m_webPage.viewLayoutSize().width() || m_webPage.autoSizingShouldExpandToViewHeight())
607         m_webPage.setSize(size);
608
609     FrameView* frameView = m_webPage.mainFrameView();
610
611     if (m_webPage.autoSizingShouldExpandToViewHeight() && frameView)
612         frameView->setAutoSizeFixedMinimumHeight(viewSize.height());
613
614     m_webPage.layoutIfNeeded();
615
616     if (m_webPage.viewLayoutSize().width() && frameView) {
617         contentSize = frameView->autoSizingIntrinsicContentSize();
618         size = contentSize;
619     }
620
621     flushLayers();
622
623     [CATransaction begin];
624     [CATransaction setDisableActions:YES];
625
626     [m_hostingLayer setFrame:CGRectMake(0, 0, viewSize.width(), viewSize.height())];
627
628     [CATransaction commit];
629
630     if (flushSynchronously)
631         [CATransaction flush];
632
633     send(Messages::DrawingAreaProxy::DidUpdateGeometry());
634
635     m_inUpdateGeometry = false;
636
637     m_layerHostingContext->setFencePort(fencePort.sendRight());
638 }
639
640 void TiledCoreAnimationDrawingArea::setDeviceScaleFactor(float deviceScaleFactor)
641 {
642     m_webPage.setDeviceScaleFactor(deviceScaleFactor);
643 }
644
645 void TiledCoreAnimationDrawingArea::setLayerHostingMode(LayerHostingMode)
646 {
647     updateLayerHostingContext();
648
649     // Finally, inform the UIProcess that the context has changed.
650     LayerTreeContext layerTreeContext;
651     layerTreeContext.contextID = m_layerHostingContext->contextID();
652     send(Messages::DrawingAreaProxy::UpdateAcceleratedCompositingMode(0, layerTreeContext));
653 }
654
655 void TiledCoreAnimationDrawingArea::setColorSpace(const ColorSpaceData& colorSpace)
656 {
657     m_layerHostingContext->setColorSpace(colorSpace.cgColorSpace.get());
658 }
659
660 void TiledCoreAnimationDrawingArea::updateLayerHostingContext()
661 {
662     RetainPtr<CGColorSpaceRef> colorSpace;
663
664     // Invalidate the old context.
665     if (m_layerHostingContext) {
666         colorSpace = m_layerHostingContext->colorSpace();
667         m_layerHostingContext->invalidate();
668         m_layerHostingContext = nullptr;
669     }
670
671     // Create a new context and set it up.
672     switch (m_webPage.layerHostingMode()) {
673     case LayerHostingMode::InProcess:
674         m_layerHostingContext = LayerHostingContext::createForPort(WebProcess::singleton().compositingRenderServerPort());
675         break;
676 #if HAVE(OUT_OF_PROCESS_LAYER_HOSTING)
677     case LayerHostingMode::OutOfProcess:
678         m_layerHostingContext = LayerHostingContext::createForExternalHostingProcess();
679         break;
680 #endif
681     }
682
683     if (m_rootLayer)
684         m_layerHostingContext->setRootLayer(m_hostingLayer.get());
685
686     if (colorSpace)
687         m_layerHostingContext->setColorSpace(colorSpace.get());
688 }
689
690 void TiledCoreAnimationDrawingArea::setRootCompositingLayer(CALayer *layer)
691 {
692     ASSERT(!m_layerTreeStateIsFrozen);
693
694     [CATransaction begin];
695     [CATransaction setDisableActions:YES];
696
697     bool hadRootLayer = !!m_rootLayer;
698     m_rootLayer = layer;
699
700     updateRootLayers();
701
702     if (hadRootLayer != !!layer)
703         m_layerHostingContext->setRootLayer(layer ? m_hostingLayer.get() : nil);
704
705     updateDebugInfoLayer(layer && m_webPage.corePage()->settings().showTiledScrollingIndicator());
706
707     [CATransaction commit];
708 }
709
710 TiledBacking* TiledCoreAnimationDrawingArea::mainFrameTiledBacking() const
711 {
712     FrameView* frameView = m_webPage.mainFrameView();
713     return frameView ? frameView->tiledBacking() : nullptr;
714 }
715
716 void TiledCoreAnimationDrawingArea::updateDebugInfoLayer(bool showLayer)
717 {
718     if (m_debugInfoLayer) {
719         [m_debugInfoLayer removeFromSuperlayer];
720         m_debugInfoLayer = nil;
721     }
722     
723     if (showLayer) {
724         if (TiledBacking* tiledBacking = mainFrameTiledBacking()) {
725             if (PlatformCALayer* indicatorLayer = tiledBacking->tiledScrollingIndicatorLayer())
726                 m_debugInfoLayer = indicatorLayer->platformLayer();
727         }
728
729         if (m_debugInfoLayer) {
730             [m_debugInfoLayer setName:@"Debug Info"];
731             [m_hostingLayer addSublayer:m_debugInfoLayer.get()];
732         }
733     }
734 }
735
736 bool TiledCoreAnimationDrawingArea::shouldUseTiledBackingForFrameView(const FrameView& frameView)
737 {
738     return frameView.frame().isMainFrame() || m_webPage.corePage()->settings().asyncFrameScrollingEnabled();
739 }
740
741 PlatformCALayer* TiledCoreAnimationDrawingArea::layerForTransientZoom() const
742 {
743     RenderLayerBacking* renderViewBacking = m_webPage.mainFrameView()->renderView()->layer()->backing();
744
745     if (GraphicsLayer* contentsContainmentLayer = renderViewBacking->contentsContainmentLayer())
746         return downcast<GraphicsLayerCA>(*contentsContainmentLayer).platformCALayer();
747
748     return downcast<GraphicsLayerCA>(*renderViewBacking->graphicsLayer()).platformCALayer();
749 }
750
751 PlatformCALayer* TiledCoreAnimationDrawingArea::shadowLayerForTransientZoom() const
752 {
753     RenderLayerCompositor& renderLayerCompositor = m_webPage.mainFrameView()->renderView()->compositor();
754
755     if (GraphicsLayer* shadowGraphicsLayer = renderLayerCompositor.layerForContentShadow())
756         return downcast<GraphicsLayerCA>(*shadowGraphicsLayer).platformCALayer();
757
758     return nullptr;
759 }
760     
761 static FloatPoint shadowLayerPositionForFrame(FrameView& frameView, FloatPoint origin)
762 {
763     // FIXME: correct for b-t documents?
764     FloatPoint position = frameView.positionForRootContentLayer();
765     return position + origin.expandedTo(FloatPoint());
766 }
767
768 static FloatRect shadowLayerBoundsForFrame(FrameView& frameView, float transientScale)
769 {
770     FloatRect clipLayerFrame(frameView.renderView()->documentRect());
771     FloatRect shadowLayerFrame = clipLayerFrame;
772     
773     shadowLayerFrame.scale(transientScale / frameView.frame().page()->pageScaleFactor());
774     shadowLayerFrame.intersect(clipLayerFrame);
775     
776     return shadowLayerFrame;
777 }
778
779 void TiledCoreAnimationDrawingArea::applyTransientZoomToLayers(double scale, FloatPoint origin)
780 {
781     // FIXME: Scrollbars should stay in-place and change height while zooming.
782
783     if (!m_hostingLayer)
784         return;
785
786     TransformationMatrix transform;
787     transform.translate(origin.x(), origin.y());
788     transform.scale(scale);
789
790     PlatformCALayer* zoomLayer = layerForTransientZoom();
791     zoomLayer->setTransform(transform);
792     zoomLayer->setAnchorPoint(FloatPoint3D());
793     zoomLayer->setPosition(FloatPoint3D());
794     
795     if (PlatformCALayer* shadowLayer = shadowLayerForTransientZoom()) {
796         FrameView& frameView = *m_webPage.mainFrameView();
797         shadowLayer->setBounds(shadowLayerBoundsForFrame(frameView, scale));
798         shadowLayer->setPosition(shadowLayerPositionForFrame(frameView, origin));
799     }
800
801     m_transientZoomScale = scale;
802     m_transientZoomOrigin = origin;
803 }
804
805 void TiledCoreAnimationDrawingArea::adjustTransientZoom(double scale, FloatPoint origin)
806 {
807     scale *= m_webPage.viewScaleFactor();
808
809     applyTransientZoomToLayers(scale, origin);
810
811     double currentPageScale = m_webPage.totalScaleFactor();
812     if (scale > currentPageScale)
813         return;
814
815     FrameView* frameView = m_webPage.mainFrameView();
816     FloatRect tileCoverageRect = frameView->visibleContentRectIncludingScrollbars();
817     tileCoverageRect.moveBy(-origin);
818     tileCoverageRect.scale(currentPageScale / scale);
819     frameView->renderView()->layer()->backing()->tiledBacking()->prepopulateRect(tileCoverageRect);
820 }
821
822 static RetainPtr<CABasicAnimation> transientZoomSnapAnimationForKeyPath(String keyPath)
823 {
824     const float transientZoomSnapBackDuration = 0.25;
825
826     RetainPtr<CABasicAnimation> animation = [CABasicAnimation animationWithKeyPath:keyPath];
827     [animation setDuration:transientZoomSnapBackDuration];
828     [animation setFillMode:kCAFillModeForwards];
829     [animation setRemovedOnCompletion:false];
830     [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
831
832     return animation;
833 }
834
835 void TiledCoreAnimationDrawingArea::commitTransientZoom(double scale, FloatPoint origin)
836 {
837     scale *= m_webPage.viewScaleFactor();
838
839     FrameView& frameView = *m_webPage.mainFrameView();
840     FloatRect visibleContentRect = frameView.visibleContentRectIncludingScrollbars();
841
842     FloatPoint constrainedOrigin = visibleContentRect.location();
843     constrainedOrigin.moveBy(-origin);
844
845     IntSize scaledTotalContentsSize = frameView.totalContentsSize();
846     scaledTotalContentsSize.scale(scale / m_webPage.totalScaleFactor());
847
848     // Scaling may have exposed the overhang area, so we need to constrain the final
849     // layer position exactly like scrolling will once it's committed, to ensure that
850     // scrolling doesn't make the view jump.
851     constrainedOrigin = ScrollableArea::constrainScrollPositionForOverhang(roundedIntRect(visibleContentRect), scaledTotalContentsSize, roundedIntPoint(constrainedOrigin), frameView.scrollOrigin(), frameView.headerHeight(), frameView.footerHeight());
852     constrainedOrigin.moveBy(-visibleContentRect.location());
853     constrainedOrigin = -constrainedOrigin;
854
855     if (m_transientZoomScale == scale && roundedIntPoint(m_transientZoomOrigin) == roundedIntPoint(constrainedOrigin)) {
856         // We're already at the right scale and position, so we don't need to animate.
857         applyTransientZoomToPage(scale, origin);
858         return;
859     }
860
861     TransformationMatrix transform;
862     transform.translate(constrainedOrigin.x(), constrainedOrigin.y());
863     transform.scale(scale);
864
865     RetainPtr<CABasicAnimation> renderViewAnimationCA = transientZoomSnapAnimationForKeyPath("transform");
866     auto renderViewAnimation = PlatformCAAnimationCocoa::create(renderViewAnimationCA.get());
867     renderViewAnimation->setToValue(transform);
868
869     RetainPtr<CALayer> shadowCALayer;
870     if (PlatformCALayer* shadowLayer = shadowLayerForTransientZoom())
871         shadowCALayer = shadowLayer->platformLayer();
872
873     RefPtr<PlatformCALayer> zoomLayer = layerForTransientZoom();
874     RefPtr<WebPage> page = &m_webPage;
875
876     [CATransaction begin];
877     [CATransaction setCompletionBlock:[zoomLayer, shadowCALayer, page, scale, origin] () {
878         zoomLayer->removeAnimationForKey("transientZoomCommit");
879         if (shadowCALayer)
880             [shadowCALayer removeAllAnimations];
881
882         if (TiledCoreAnimationDrawingArea* drawingArea = static_cast<TiledCoreAnimationDrawingArea*>(page->drawingArea()))
883             drawingArea->applyTransientZoomToPage(scale, origin);
884     }];
885
886     zoomLayer->addAnimationForKey("transientZoomCommit", renderViewAnimation.get());
887
888     if (shadowCALayer) {
889         FloatRect shadowBounds = shadowLayerBoundsForFrame(frameView, scale);
890         RetainPtr<CGPathRef> shadowPath = adoptCF(CGPathCreateWithRect(shadowBounds, NULL));
891
892         RetainPtr<CABasicAnimation> shadowBoundsAnimation = transientZoomSnapAnimationForKeyPath("bounds");
893         [shadowBoundsAnimation setToValue:[NSValue valueWithRect:shadowBounds]];
894         RetainPtr<CABasicAnimation> shadowPositionAnimation = transientZoomSnapAnimationForKeyPath("position");
895         [shadowPositionAnimation setToValue:[NSValue valueWithPoint:shadowLayerPositionForFrame(frameView, constrainedOrigin)]];
896         RetainPtr<CABasicAnimation> shadowPathAnimation = transientZoomSnapAnimationForKeyPath("shadowPath");
897         [shadowPathAnimation setToValue:(__bridge id)shadowPath.get()];
898
899         [shadowCALayer addAnimation:shadowBoundsAnimation.get() forKey:@"transientZoomCommitShadowBounds"];
900         [shadowCALayer addAnimation:shadowPositionAnimation.get() forKey:@"transientZoomCommitShadowPosition"];
901         [shadowCALayer addAnimation:shadowPathAnimation.get() forKey:@"transientZoomCommitShadowPath"];
902     }
903
904     [CATransaction commit];
905 }
906
907 void TiledCoreAnimationDrawingArea::applyTransientZoomToPage(double scale, FloatPoint origin)
908 {
909     // If the page scale is already the target scale, setPageScaleFactor() will short-circuit
910     // and not apply the transform, so we can't depend on it to do so.
911     TransformationMatrix finalTransform;
912     finalTransform.scale(scale);
913     layerForTransientZoom()->setTransform(finalTransform);
914     
915     FrameView& frameView = *m_webPage.mainFrameView();
916
917     if (PlatformCALayer* shadowLayer = shadowLayerForTransientZoom()) {
918         shadowLayer->setBounds(shadowLayerBoundsForFrame(frameView, 1));
919         shadowLayer->setPosition(shadowLayerPositionForFrame(frameView, FloatPoint()));
920     }
921
922     FloatPoint unscrolledOrigin(origin);
923     FloatRect unobscuredContentRect = frameView.unobscuredContentRectIncludingScrollbars();
924     unscrolledOrigin.moveBy(-unobscuredContentRect.location());
925     m_webPage.scalePage(scale / m_webPage.viewScaleFactor(), roundedIntPoint(-unscrolledOrigin));
926     m_transientZoomScale = 1;
927     flushLayers(FlushType::TransientZoom);
928 }
929
930 void TiledCoreAnimationDrawingArea::addFence(const MachSendRight& fencePort)
931 {
932     m_layerHostingContext->setFencePort(fencePort.sendRight());
933 }
934
935 bool TiledCoreAnimationDrawingArea::dispatchDidReachLayoutMilestone(OptionSet<WebCore::LayoutMilestone> layoutMilestones)
936 {
937     m_pendingNewlyReachedLayoutMilestones.add(layoutMilestones);
938     return true;
939 }
940
941 void TiledCoreAnimationDrawingArea::layerFlushRunLoopCallback()
942 {
943     flushLayers();
944 }
945
946 void TiledCoreAnimationDrawingArea::invalidateLayerFlushRunLoopObserver()
947 {
948     m_layerFlushRunLoopObserver->invalidate();
949 }
950
951 void TiledCoreAnimationDrawingArea::scheduleLayerFlushRunLoopObserver()
952 {
953     m_layerFlushRunLoopObserver->schedule(CFRunLoopGetCurrent());
954 }
955
956 bool TiledCoreAnimationDrawingArea::adjustLayerFlushThrottling(WebCore::LayerFlushThrottleState::Flags flags)
957 {
958     bool wasThrottlingLayerFlushes = m_isThrottlingLayerFlushes;
959     m_isThrottlingLayerFlushes = flags & WebCore::LayerFlushThrottleState::Enabled;
960     m_isLayerFlushThrottlingTemporarilyDisabledForInteraction = flags & WebCore::LayerFlushThrottleState::UserIsInteracting;
961
962     if (wasThrottlingLayerFlushes == m_isThrottlingLayerFlushes)
963         return true;
964
965     m_layerFlushThrottlingTimer.stop();
966
967     if (m_layerTreeStateIsFrozen)
968         return true;
969
970     if (m_isThrottlingLayerFlushes) {
971         invalidateLayerFlushRunLoopObserver();
972         startLayerFlushThrottlingTimer();
973     } else
974         scheduleLayerFlushRunLoopObserver();
975
976     return true;
977 }
978
979 bool TiledCoreAnimationDrawingArea::layerFlushThrottlingIsActive() const
980 {
981     return m_isThrottlingLayerFlushes && !m_layerTreeStateIsFrozen;
982 }
983
984 void TiledCoreAnimationDrawingArea::startLayerFlushThrottlingTimer()
985 {
986     ASSERT(m_isThrottlingLayerFlushes);
987
988     const auto throttledFlushDelay = 500_ms;
989     m_layerFlushThrottlingTimer.startOneShot(throttledFlushDelay);
990 }
991
992 void TiledCoreAnimationDrawingArea::layerFlushThrottlingTimerFired()
993 {
994     if (m_layerTreeStateIsFrozen)
995         return;
996     scheduleLayerFlushRunLoopObserver();
997 }
998
999 } // namespace WebKit
1000
1001 #endif // PLATFORM(MAC)