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