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