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