2cbbc61b1c238ad7f183f7d690ced92df3ae4c97
[WebKit-https.git] / Source / WebKit2 / WebProcess / WebPage / mac / TiledCoreAnimationDrawingArea.mm
1 /*
2  * Copyright (C) 2011 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)
30
31 #import "ColorSpaceData.h"
32 #import "DrawingAreaProxyMessages.h"
33 #import "LayerHostingContext.h"
34 #import "LayerTreeContext.h"
35 #import "ViewGestureControllerMessages.h"
36 #import "WebFrame.h"
37 #import "WebPage.h"
38 #import "WebPageCreationParameters.h"
39 #import "WebPageProxyMessages.h"
40 #import "WebProcess.h"
41 #import <QuartzCore/QuartzCore.h>
42 #import <WebCore/DebugPageOverlays.h>
43 #import <WebCore/FrameView.h>
44 #import <WebCore/GraphicsContext.h>
45 #import <WebCore/GraphicsLayerCA.h>
46 #import <WebCore/MachSendRight.h>
47 #import <WebCore/MainFrame.h>
48 #import <WebCore/Page.h>
49 #import <WebCore/PlatformCAAnimationMac.h>
50 #import <WebCore/RenderLayerBacking.h>
51 #import <WebCore/RenderLayerCompositor.h>
52 #import <WebCore/RenderView.h>
53 #import <WebCore/ScrollbarTheme.h>
54 #import <WebCore/Settings.h>
55 #import <WebCore/TiledBacking.h>
56 #import <WebCore/WebActionDisablingCALayerDelegate.h>
57 #import <wtf/MainThread.h>
58
59 #if ENABLE(ASYNC_SCROLLING)
60 #import <WebCore/AsyncScrollingCoordinator.h>
61 #import <WebCore/ScrollingThread.h>
62 #import <WebCore/ScrollingTree.h>
63 #endif
64
65 @interface CATransaction (Details)
66 + (void)synchronize;
67 @end
68
69 using namespace WebCore;
70
71 namespace WebKit {
72
73 TiledCoreAnimationDrawingArea::TiledCoreAnimationDrawingArea(WebPage& webPage, const WebPageCreationParameters& parameters)
74     : DrawingArea(DrawingAreaTypeTiledCoreAnimation, webPage)
75     , m_layerTreeStateIsFrozen(false)
76     , m_layerFlushScheduler(this)
77     , m_isPaintingSuspended(!(parameters.viewState & ViewState::IsVisible))
78     , m_exposedRect(FloatRect::infiniteRect())
79     , m_scrolledExposedRect(FloatRect::infiniteRect())
80     , m_transientZoomScale(1)
81     , m_sendDidUpdateViewStateTimer(RunLoop::main(), this, &TiledCoreAnimationDrawingArea::didUpdateViewStateTimerFired)
82     , m_wantsDidUpdateViewState(false)
83     , m_viewOverlayRootLayer(nullptr)
84 {
85     m_webPage.corePage()->settings().setForceCompositingMode(true);
86
87     m_hostingLayer = [CALayer layer];
88     [m_hostingLayer setDelegate:[WebActionDisablingCALayerDelegate shared]];
89     [m_hostingLayer setFrame:m_webPage.bounds()];
90     [m_hostingLayer setOpaque:YES];
91     [m_hostingLayer setGeometryFlipped:YES];
92
93     updateLayerHostingContext();
94     setColorSpace(parameters.colorSpace);
95
96     LayerTreeContext layerTreeContext;
97     layerTreeContext.contextID = m_layerHostingContext->contextID();
98     m_webPage.send(Messages::DrawingAreaProxy::EnterAcceleratedCompositingMode(0, layerTreeContext));
99 }
100
101 TiledCoreAnimationDrawingArea::~TiledCoreAnimationDrawingArea()
102 {
103     m_layerFlushScheduler.invalidate();
104 }
105
106 void TiledCoreAnimationDrawingArea::setNeedsDisplay()
107 {
108 }
109
110 void TiledCoreAnimationDrawingArea::setNeedsDisplayInRect(const IntRect& rect)
111 {
112 }
113
114 void TiledCoreAnimationDrawingArea::scroll(const IntRect& scrollRect, const IntSize& scrollDelta)
115 {
116     updateScrolledExposedRect();
117 }
118
119 void TiledCoreAnimationDrawingArea::setRootCompositingLayer(GraphicsLayer* graphicsLayer)
120 {
121     CALayer *rootLayer = graphicsLayer ? graphicsLayer->platformLayer() : nil;
122
123     if (m_layerTreeStateIsFrozen) {
124         m_pendingRootLayer = rootLayer;
125         return;
126     }
127
128     setRootCompositingLayer(rootLayer);
129 }
130
131 void TiledCoreAnimationDrawingArea::forceRepaint()
132 {
133     if (m_layerTreeStateIsFrozen)
134         return;
135
136     for (Frame* frame = &m_webPage.corePage()->mainFrame(); frame; frame = frame->tree().traverseNext()) {
137         FrameView* frameView = frame->view();
138         if (!frameView || !frameView->tiledBacking())
139             continue;
140
141         frameView->tiledBacking()->forceRepaint();
142     }
143
144     flushLayers();
145     [CATransaction flush];
146     [CATransaction synchronize];
147 }
148
149 bool TiledCoreAnimationDrawingArea::forceRepaintAsync(uint64_t callbackID)
150 {
151     if (m_layerTreeStateIsFrozen)
152         return false;
153
154     dispatchAfterEnsuringUpdatedScrollPosition([this, callbackID] {
155         m_webPage.drawingArea()->forceRepaint();
156         m_webPage.send(Messages::WebPageProxy::VoidCallback(callbackID));
157     });
158     return true;
159 }
160
161 void TiledCoreAnimationDrawingArea::setLayerTreeStateIsFrozen(bool layerTreeStateIsFrozen)
162 {
163     if (m_layerTreeStateIsFrozen == layerTreeStateIsFrozen)
164         return;
165
166     m_layerTreeStateIsFrozen = layerTreeStateIsFrozen;
167     if (m_layerTreeStateIsFrozen)
168         m_layerFlushScheduler.suspend();
169     else
170         m_layerFlushScheduler.resume();
171 }
172
173 bool TiledCoreAnimationDrawingArea::layerTreeStateIsFrozen() const
174 {
175     return m_layerTreeStateIsFrozen;
176 }
177
178 void TiledCoreAnimationDrawingArea::scheduleCompositingLayerFlush()
179 {
180     m_layerFlushScheduler.schedule();
181 }
182
183 void TiledCoreAnimationDrawingArea::scheduleCompositingLayerFlushImmediately()
184 {
185     scheduleCompositingLayerFlush();
186 }
187
188 void TiledCoreAnimationDrawingArea::updatePreferences(const WebPreferencesStore&)
189 {
190     Settings& settings = m_webPage.corePage()->settings();
191
192 #if ENABLE(ASYNC_SCROLLING)
193     if (AsyncScrollingCoordinator* scrollingCoordinator = downcast<AsyncScrollingCoordinator>(m_webPage.corePage()->scrollingCoordinator())) {
194         bool scrollingPerformanceLoggingEnabled = m_webPage.scrollingPerformanceLoggingEnabled();
195         
196         RefPtr<ScrollingTree> scrollingTree = scrollingCoordinator->scrollingTree();
197         ScrollingThread::dispatch([scrollingTree, scrollingPerformanceLoggingEnabled] {
198             scrollingTree->setScrollingPerformanceLoggingEnabled(scrollingPerformanceLoggingEnabled);
199         });
200     }
201 #endif
202
203     // Fixed position elements need to be composited and create stacking contexts
204     // in order to be scrolled by the ScrollingCoordinator. We also want to keep
205     // Settings:setFixedPositionCreatesStackingContext() enabled for iOS. See
206     // <rdar://problem/9813262> for more details.
207     settings.setAcceleratedCompositingForFixedPositionEnabled(true);
208     settings.setFixedPositionCreatesStackingContext(true);
209
210     if (MainFrame* mainFrame = m_webPage.mainFrame())
211         DebugPageOverlays::settingsChanged(*mainFrame);
212
213     bool showTiledScrollingIndicator = settings.showTiledScrollingIndicator();
214     if (showTiledScrollingIndicator == !!m_debugInfoLayer)
215         return;
216
217     updateDebugInfoLayer(showTiledScrollingIndicator);
218 }
219
220 void TiledCoreAnimationDrawingArea::updateRootLayers()
221 {
222     if (!m_rootLayer) {
223         [m_hostingLayer setSublayers:@[ ]];
224         return;
225     }
226
227     [m_hostingLayer setSublayers:m_viewOverlayRootLayer ? @[ m_rootLayer.get(), m_viewOverlayRootLayer->platformLayer() ] : @[ m_rootLayer.get() ]];
228 }
229
230 void TiledCoreAnimationDrawingArea::attachViewOverlayGraphicsLayer(Frame* frame, GraphicsLayer* viewOverlayRootLayer)
231 {
232     if (!frame->isMainFrame())
233         return;
234
235     m_viewOverlayRootLayer = viewOverlayRootLayer;
236     updateRootLayers();
237 }
238
239 void TiledCoreAnimationDrawingArea::mainFrameContentSizeChanged(const IntSize& size)
240 {
241
242 }
243
244 void TiledCoreAnimationDrawingArea::updateIntrinsicContentSizeIfNeeded()
245 {
246     if (!m_webPage.minimumLayoutSize().width())
247         return;
248
249     FrameView* frameView = m_webPage.mainFrameView();
250     if (!frameView)
251         return;
252
253     if (frameView->needsLayout())
254         return;
255
256     IntSize contentSize = frameView->autoSizingIntrinsicContentSize();
257     if (m_lastSentIntrinsicContentSize == contentSize)
258         return;
259
260     m_lastSentIntrinsicContentSize = contentSize;
261     m_webPage.send(Messages::DrawingAreaProxy::IntrinsicContentSizeDidChange(contentSize));
262 }
263
264 void TiledCoreAnimationDrawingArea::dispatchAfterEnsuringUpdatedScrollPosition(std::function<void ()> function)
265 {
266 #if ENABLE(ASYNC_SCROLLING)
267     if (!m_webPage.corePage()->scrollingCoordinator()) {
268         function();
269         return;
270     }
271
272     m_webPage.ref();
273     m_webPage.corePage()->scrollingCoordinator()->commitTreeStateIfNeeded();
274
275     if (!m_layerTreeStateIsFrozen)
276         m_layerFlushScheduler.suspend();
277
278     // It is possible for the drawing area to be destroyed before the bound block
279     // is invoked, so grab a reference to the web page here so we can access the drawing area through it.
280     // (The web page is already kept alive by dispatchAfterEnsuringUpdatedScrollPosition).
281     WebPage* webPage = &m_webPage;
282
283     ScrollingThread::dispatchBarrier([this, webPage, function] {
284         DrawingArea* drawingArea = webPage->drawingArea();
285         if (!drawingArea)
286             return;
287
288         function();
289
290         if (!m_layerTreeStateIsFrozen)
291             m_layerFlushScheduler.resume();
292
293         webPage->deref();
294     });
295 #else
296     function();
297 #endif
298 }
299
300 bool TiledCoreAnimationDrawingArea::flushLayers()
301 {
302     ASSERT(!m_layerTreeStateIsFrozen);
303
304     @autoreleasepool {
305         m_webPage.layoutIfNeeded();
306
307         updateIntrinsicContentSizeIfNeeded();
308
309         if (m_pendingRootLayer) {
310             setRootCompositingLayer(m_pendingRootLayer.get());
311             m_pendingRootLayer = nullptr;
312         }
313
314         FloatRect visibleRect = [m_hostingLayer frame];
315         visibleRect.intersect(m_scrolledExposedRect);
316
317         // Because our view-relative overlay root layer is not attached to the main GraphicsLayer tree, we need to flush it manually.
318         if (m_viewOverlayRootLayer)
319             m_viewOverlayRootLayer->flushCompositingState(visibleRect);
320
321         bool returnValue = m_webPage.mainFrameView()->flushCompositingStateIncludingSubframes();
322 #if ENABLE(ASYNC_SCROLLING)
323         if (ScrollingCoordinator* scrollingCoordinator = m_webPage.corePage()->scrollingCoordinator())
324             scrollingCoordinator->commitTreeStateIfNeeded();
325 #endif
326
327         // If we have an active transient zoom, we want the zoom to win over any changes
328         // that WebCore makes to the relevant layers, so re-apply our changes after flushing.
329         if (m_transientZoomScale != 1)
330             applyTransientZoomToLayers(m_transientZoomScale, m_transientZoomOrigin);
331
332         if (!m_fenceCallbacksForAfterNextFlush.isEmpty()) {
333             MachSendRight fencePort = m_layerHostingContext->createFencePort();
334
335             for (auto callbackID : m_fenceCallbacksForAfterNextFlush)
336                 m_webPage.send(Messages::WebPageProxy::MachSendRightCallback(fencePort, callbackID));
337             m_fenceCallbacksForAfterNextFlush.clear();
338
339             m_layerHostingContext->setFencePort(fencePort.sendRight());
340         }
341
342         return returnValue;
343     }
344 }
345
346 void TiledCoreAnimationDrawingArea::viewStateDidChange(ViewState::Flags changed, bool wantsDidUpdateViewState, const Vector<uint64_t>& nextViewStateChangeCallbackIDs)
347 {
348     m_nextViewStateChangeCallbackIDs.appendVector(nextViewStateChangeCallbackIDs);
349     m_wantsDidUpdateViewState |= wantsDidUpdateViewState;
350
351     if (changed & ViewState::IsVisible) {
352         if (m_webPage.isVisible())
353             resumePainting();
354         else
355             suspendPainting();
356     }
357
358     if (m_wantsDidUpdateViewState || !m_nextViewStateChangeCallbackIDs.isEmpty())
359         m_sendDidUpdateViewStateTimer.startOneShot(0);
360 }
361
362 void TiledCoreAnimationDrawingArea::didUpdateViewStateTimerFired()
363 {
364     [CATransaction flush];
365
366     if (m_wantsDidUpdateViewState)
367         m_webPage.send(Messages::WebPageProxy::DidUpdateViewState());
368
369     for (uint64_t callbackID : m_nextViewStateChangeCallbackIDs)
370         m_webPage.send(Messages::WebPageProxy::VoidCallback(callbackID));
371
372     m_nextViewStateChangeCallbackIDs.clear();
373     m_wantsDidUpdateViewState = false;
374 }
375
376 void TiledCoreAnimationDrawingArea::suspendPainting()
377 {
378     ASSERT(!m_isPaintingSuspended);
379     m_isPaintingSuspended = true;
380
381     [m_hostingLayer setValue:@YES forKey:@"NSCAViewRenderPaused"];
382     [[NSNotificationCenter defaultCenter] postNotificationName:@"NSCAViewRenderDidPauseNotification" object:nil userInfo:[NSDictionary dictionaryWithObject:m_hostingLayer.get() forKey:@"layer"]];
383 }
384
385 void TiledCoreAnimationDrawingArea::resumePainting()
386 {
387     if (!m_isPaintingSuspended) {
388         // FIXME: We can get a call to resumePainting when painting is not suspended.
389         // This happens when sending a synchronous message to create a new page. See <rdar://problem/8976531>.
390         return;
391     }
392     m_isPaintingSuspended = false;
393
394     [m_hostingLayer setValue:@NO forKey:@"NSCAViewRenderPaused"];
395     [[NSNotificationCenter defaultCenter] postNotificationName:@"NSCAViewRenderDidResumeNotification" object:nil userInfo:[NSDictionary dictionaryWithObject:m_hostingLayer.get() forKey:@"layer"]];
396 }
397
398 void TiledCoreAnimationDrawingArea::setExposedRect(const FloatRect& exposedRect)
399 {
400     m_exposedRect = exposedRect;
401     updateScrolledExposedRect();
402 }
403
404 void TiledCoreAnimationDrawingArea::updateScrolledExposedRect()
405 {
406     FrameView* frameView = m_webPage.mainFrameView();
407     if (!frameView)
408         return;
409
410     m_scrolledExposedRect = m_exposedRect;
411
412 #if !PLATFORM(IOS)
413     if (!m_exposedRect.isInfinite()) {
414         IntPoint scrollPositionWithOrigin = frameView->scrollPosition() + toIntSize(frameView->scrollOrigin());
415         m_scrolledExposedRect.moveBy(scrollPositionWithOrigin);
416     }
417 #endif
418
419     frameView->setExposedRect(m_scrolledExposedRect);
420 }
421
422 void TiledCoreAnimationDrawingArea::updateGeometry(const IntSize& viewSize, const IntSize& layerPosition, bool flushSynchronously)
423 {
424     m_inUpdateGeometry = true;
425
426     IntSize size = viewSize;
427     IntSize contentSize = IntSize(-1, -1);
428
429     if (!m_webPage.minimumLayoutSize().width() || m_webPage.autoSizingShouldExpandToViewHeight())
430         m_webPage.setSize(size);
431
432     FrameView* frameView = m_webPage.mainFrameView();
433
434     if (m_webPage.autoSizingShouldExpandToViewHeight() && frameView)
435         frameView->setAutoSizeFixedMinimumHeight(viewSize.height());
436
437     m_webPage.layoutIfNeeded();
438
439     if (m_webPage.minimumLayoutSize().width() && frameView) {
440         contentSize = frameView->autoSizingIntrinsicContentSize();
441         size = contentSize;
442     }
443
444     if (!m_layerTreeStateIsFrozen)
445         flushLayers();
446
447     [CATransaction begin];
448     [CATransaction setDisableActions:YES];
449
450     [m_hostingLayer setFrame:CGRectMake(layerPosition.width(), layerPosition.height(), viewSize.width(), viewSize.height())];
451
452     [CATransaction commit];
453
454     if (flushSynchronously) {
455         [CATransaction flush];
456         [CATransaction synchronize];
457     }
458
459     m_webPage.send(Messages::DrawingAreaProxy::DidUpdateGeometry());
460
461     m_inUpdateGeometry = false;
462 }
463
464 void TiledCoreAnimationDrawingArea::setDeviceScaleFactor(float deviceScaleFactor)
465 {
466     m_webPage.setDeviceScaleFactor(deviceScaleFactor);
467 }
468
469 void TiledCoreAnimationDrawingArea::setLayerHostingMode(LayerHostingMode)
470 {
471     updateLayerHostingContext();
472
473     // Finally, inform the UIProcess that the context has changed.
474     LayerTreeContext layerTreeContext;
475     layerTreeContext.contextID = m_layerHostingContext->contextID();
476     m_webPage.send(Messages::DrawingAreaProxy::UpdateAcceleratedCompositingMode(0, layerTreeContext));
477 }
478
479 void TiledCoreAnimationDrawingArea::setColorSpace(const ColorSpaceData& colorSpace)
480 {
481     m_layerHostingContext->setColorSpace(colorSpace.cgColorSpace.get());
482 }
483
484 void TiledCoreAnimationDrawingArea::updateLayerHostingContext()
485 {
486     RetainPtr<CGColorSpaceRef> colorSpace;
487
488     // Invalidate the old context.
489     if (m_layerHostingContext) {
490         colorSpace = m_layerHostingContext->colorSpace();
491         m_layerHostingContext->invalidate();
492         m_layerHostingContext = nullptr;
493     }
494
495     // Create a new context and set it up.
496     switch (m_webPage.layerHostingMode()) {
497     case LayerHostingMode::InProcess:
498         m_layerHostingContext = LayerHostingContext::createForPort(WebProcess::singleton().compositingRenderServerPort());
499         break;
500 #if HAVE(OUT_OF_PROCESS_LAYER_HOSTING)
501     case LayerHostingMode::OutOfProcess:
502         m_layerHostingContext = LayerHostingContext::createForExternalHostingProcess();
503         break;
504 #endif
505     }
506
507     if (m_rootLayer)
508         m_layerHostingContext->setRootLayer(m_hostingLayer.get());
509
510     if (colorSpace)
511         m_layerHostingContext->setColorSpace(colorSpace.get());
512 }
513
514 void TiledCoreAnimationDrawingArea::setRootCompositingLayer(CALayer *layer)
515 {
516     ASSERT(!m_layerTreeStateIsFrozen);
517
518     [CATransaction begin];
519     [CATransaction setDisableActions:YES];
520
521     bool hadRootLayer = !!m_rootLayer;
522     m_rootLayer = layer;
523     [m_rootLayer setSublayerTransform:m_transform];
524
525     updateRootLayers();
526
527     if (hadRootLayer != !!layer)
528         m_layerHostingContext->setRootLayer(layer ? m_hostingLayer.get() : 0);
529
530     updateDebugInfoLayer(m_webPage.corePage()->settings().showTiledScrollingIndicator());
531
532     [CATransaction commit];
533 }
534
535 TiledBacking* TiledCoreAnimationDrawingArea::mainFrameTiledBacking() const
536 {
537     FrameView* frameView = m_webPage.mainFrameView();
538     return frameView ? frameView->tiledBacking() : nullptr;
539 }
540
541 void TiledCoreAnimationDrawingArea::updateDebugInfoLayer(bool showLayer)
542 {
543     if (showLayer) {
544         if (TiledBacking* tiledBacking = mainFrameTiledBacking()) {
545             if (PlatformCALayer* indicatorLayer = tiledBacking->tiledScrollingIndicatorLayer())
546                 m_debugInfoLayer = indicatorLayer->platformLayer();
547         }
548
549         if (m_debugInfoLayer) {
550 #ifndef NDEBUG
551             [m_debugInfoLayer setName:@"Debug Info"];
552 #endif
553             [m_hostingLayer addSublayer:m_debugInfoLayer.get()];
554         }
555     } else if (m_debugInfoLayer) {
556         [m_debugInfoLayer removeFromSuperlayer];
557         m_debugInfoLayer = nullptr;
558     }
559 }
560
561 bool TiledCoreAnimationDrawingArea::shouldUseTiledBackingForFrameView(const FrameView* frameView)
562 {
563     return frameView && frameView->frame().isMainFrame();
564 }
565
566 PlatformCALayer* TiledCoreAnimationDrawingArea::layerForTransientZoom() const
567 {
568     RenderLayerBacking* renderViewBacking = m_webPage.mainFrameView()->renderView()->layer()->backing();
569
570     if (GraphicsLayer* contentsContainmentLayer = renderViewBacking->contentsContainmentLayer())
571         return downcast<GraphicsLayerCA>(*contentsContainmentLayer).platformCALayer();
572
573     return downcast<GraphicsLayerCA>(*renderViewBacking->graphicsLayer()).platformCALayer();
574 }
575
576 PlatformCALayer* TiledCoreAnimationDrawingArea::shadowLayerForTransientZoom() const
577 {
578     RenderLayerCompositor& renderLayerCompositor = m_webPage.mainFrameView()->renderView()->compositor();
579
580     if (GraphicsLayer* shadowGraphicsLayer = renderLayerCompositor.layerForContentShadow())
581         return downcast<GraphicsLayerCA>(*shadowGraphicsLayer).platformCALayer();
582
583     return nullptr;
584 }
585     
586 static FloatPoint shadowLayerPositionForFrame(FrameView& frameView, FloatPoint origin)
587 {
588     FloatPoint position = frameView.renderView()->documentRect().location() + FloatPoint(0, frameView.yPositionForRootContentLayer());
589     return position + origin.expandedTo(FloatPoint());
590 }
591
592 static FloatRect shadowLayerBoundsForFrame(FrameView& frameView, float transientScale)
593 {
594     FloatRect clipLayerFrame(frameView.renderView()->documentRect());
595     FloatRect shadowLayerFrame = clipLayerFrame;
596     
597     shadowLayerFrame.scale(transientScale / frameView.frame().page()->pageScaleFactor());
598     shadowLayerFrame.intersect(clipLayerFrame);
599     
600     return shadowLayerFrame;
601 }
602
603 void TiledCoreAnimationDrawingArea::applyTransientZoomToLayers(double scale, FloatPoint origin)
604 {
605     // FIXME: Scrollbars should stay in-place and change height while zooming.
606
607     if (!m_hostingLayer)
608         return;
609
610     TransformationMatrix transform;
611     transform.translate(origin.x(), origin.y());
612     transform.scale(scale);
613
614     PlatformCALayer* zoomLayer = layerForTransientZoom();
615     zoomLayer->setTransform(transform);
616     zoomLayer->setAnchorPoint(FloatPoint3D());
617     zoomLayer->setPosition(FloatPoint3D());
618     
619     if (PlatformCALayer* shadowLayer = shadowLayerForTransientZoom()) {
620         FrameView& frameView = *m_webPage.mainFrameView();
621         shadowLayer->setBounds(shadowLayerBoundsForFrame(frameView, scale));
622         shadowLayer->setPosition(shadowLayerPositionForFrame(frameView, origin));
623     }
624
625     m_transientZoomScale = scale;
626     m_transientZoomOrigin = origin;
627 }
628
629 void TiledCoreAnimationDrawingArea::adjustTransientZoom(double scale, FloatPoint origin)
630 {
631     scale *= m_webPage.viewScaleFactor();
632
633     applyTransientZoomToLayers(scale, origin);
634
635     double currentPageScale = m_webPage.totalScaleFactor();
636     if (scale > currentPageScale)
637         return;
638
639     FrameView* frameView = m_webPage.mainFrameView();
640     FloatRect tileCoverageRect = frameView->visibleContentRectIncludingScrollbars();
641     tileCoverageRect.moveBy(-origin);
642     tileCoverageRect.scale(currentPageScale / scale);
643     frameView->renderView()->layer()->backing()->tiledBacking()->prepopulateRect(tileCoverageRect);
644 }
645
646 static RetainPtr<CABasicAnimation> transientZoomSnapAnimationForKeyPath(String keyPath)
647 {
648     const float transientZoomSnapBackDuration = 0.25;
649
650     RetainPtr<CABasicAnimation> animation = [CABasicAnimation animationWithKeyPath:keyPath];
651     [animation setDuration:transientZoomSnapBackDuration];
652     [animation setFillMode:kCAFillModeForwards];
653     [animation setRemovedOnCompletion:false];
654     [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
655
656     return animation;
657 }
658
659 void TiledCoreAnimationDrawingArea::commitTransientZoom(double scale, FloatPoint origin)
660 {
661     scale *= m_webPage.viewScaleFactor();
662
663     FrameView& frameView = *m_webPage.mainFrameView();
664     FloatRect visibleContentRect = frameView.visibleContentRectIncludingScrollbars();
665
666     FloatPoint constrainedOrigin = visibleContentRect.location();
667     constrainedOrigin.moveBy(-origin);
668
669     IntSize scaledTotalContentsSize = frameView.totalContentsSize();
670     scaledTotalContentsSize.scale(scale / m_webPage.totalScaleFactor());
671
672     // Scaling may have exposed the overhang area, so we need to constrain the final
673     // layer position exactly like scrolling will once it's committed, to ensure that
674     // scrolling doesn't make the view jump.
675     constrainedOrigin = ScrollableArea::constrainScrollPositionForOverhang(roundedIntRect(visibleContentRect), scaledTotalContentsSize, roundedIntPoint(constrainedOrigin), frameView.scrollOrigin(), frameView.headerHeight(), frameView.footerHeight());
676     constrainedOrigin.moveBy(-visibleContentRect.location());
677     constrainedOrigin = -constrainedOrigin;
678
679     if (m_transientZoomScale == scale && roundedIntPoint(m_transientZoomOrigin) == roundedIntPoint(constrainedOrigin)) {
680         // We're already at the right scale and position, so we don't need to animate.
681         applyTransientZoomToPage(scale, origin);
682         return;
683     }
684
685     TransformationMatrix transform;
686     transform.translate(constrainedOrigin.x(), constrainedOrigin.y());
687     transform.scale(scale);
688
689     RetainPtr<CABasicAnimation> renderViewAnimationCA = transientZoomSnapAnimationForKeyPath("transform");
690     RefPtr<PlatformCAAnimation> renderViewAnimation = PlatformCAAnimationMac::create(renderViewAnimationCA.get());
691     renderViewAnimation->setToValue(transform);
692
693     RetainPtr<CALayer> shadowCALayer;
694     if (PlatformCALayer* shadowLayer = shadowLayerForTransientZoom())
695         shadowCALayer = shadowLayer->platformLayer();
696
697     RefPtr<PlatformCALayer> zoomLayer = layerForTransientZoom();
698     RefPtr<WebPage> page = &m_webPage;
699
700     [CATransaction begin];
701     [CATransaction setCompletionBlock:[zoomLayer, shadowCALayer, page, scale, origin] () {
702         zoomLayer->removeAnimationForKey("transientZoomCommit");
703         if (shadowCALayer)
704             [shadowCALayer removeAllAnimations];
705
706         if (TiledCoreAnimationDrawingArea* drawingArea = static_cast<TiledCoreAnimationDrawingArea*>(page->drawingArea()))
707             drawingArea->applyTransientZoomToPage(scale, origin);
708     }];
709
710     zoomLayer->addAnimationForKey("transientZoomCommit", *renderViewAnimation);
711
712     if (shadowCALayer) {
713         FloatRect shadowBounds = shadowLayerBoundsForFrame(frameView, scale);
714         RetainPtr<CGPathRef> shadowPath = adoptCF(CGPathCreateWithRect(shadowBounds, NULL)).get();
715
716         RetainPtr<CABasicAnimation> shadowBoundsAnimation = transientZoomSnapAnimationForKeyPath("bounds");
717         [shadowBoundsAnimation setToValue:[NSValue valueWithRect:shadowBounds]];
718         RetainPtr<CABasicAnimation> shadowPositionAnimation = transientZoomSnapAnimationForKeyPath("position");
719         [shadowPositionAnimation setToValue:[NSValue valueWithPoint:shadowLayerPositionForFrame(frameView, constrainedOrigin)]];
720         RetainPtr<CABasicAnimation> shadowPathAnimation = transientZoomSnapAnimationForKeyPath("shadowPath");
721         [shadowPathAnimation setToValue:(id)shadowPath.get()];
722
723         [shadowCALayer addAnimation:shadowBoundsAnimation.get() forKey:@"transientZoomCommitShadowBounds"];
724         [shadowCALayer addAnimation:shadowPositionAnimation.get() forKey:@"transientZoomCommitShadowPosition"];
725         [shadowCALayer addAnimation:shadowPathAnimation.get() forKey:@"transientZoomCommitShadowPath"];
726     }
727
728     [CATransaction commit];
729 }
730
731 void TiledCoreAnimationDrawingArea::applyTransientZoomToPage(double scale, FloatPoint origin)
732 {
733     // If the page scale is already the target scale, setPageScaleFactor() will short-circuit
734     // and not apply the transform, so we can't depend on it to do so.
735     TransformationMatrix finalTransform;
736     finalTransform.scale(scale);
737     layerForTransientZoom()->setTransform(finalTransform);
738     
739     FrameView& frameView = *m_webPage.mainFrameView();
740
741     if (PlatformCALayer* shadowLayer = shadowLayerForTransientZoom()) {
742         shadowLayer->setBounds(shadowLayerBoundsForFrame(frameView, 1));
743         shadowLayer->setPosition(shadowLayerPositionForFrame(frameView, FloatPoint()));
744     }
745
746     FloatPoint unscrolledOrigin(origin);
747     FloatRect unobscuredContentRect = frameView.unobscuredContentRectIncludingScrollbars();
748     unscrolledOrigin.moveBy(-unobscuredContentRect.location());
749     m_webPage.scalePage(scale / m_webPage.viewScaleFactor(), roundedIntPoint(-unscrolledOrigin));
750     m_transientZoomScale = 1;
751     flushLayers();
752 }
753
754 void TiledCoreAnimationDrawingArea::addFence(const MachSendRight& fencePort)
755 {
756     m_layerHostingContext->setFencePort(fencePort.sendRight());
757 }
758
759 void TiledCoreAnimationDrawingArea::replyWithFenceAfterNextFlush(uint64_t callbackID)
760 {
761     m_fenceCallbacksForAfterNextFlush.append(callbackID);
762 }
763
764 } // namespace WebKit
765
766 #endif // !PLATFORM(IOS)