1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
6 #include "core/rendering/compositing/CompositingReasonFinder.h"
8 #include "CSSPropertyNames.h"
10 #include "core/animation/ActiveAnimations.h"
11 #include "core/frame/FrameView.h"
12 #include "core/frame/LocalFrame.h"
13 #include "core/frame/Settings.h"
14 #include "core/html/HTMLCanvasElement.h"
15 #include "core/html/HTMLIFrameElement.h"
16 #include "core/html/HTMLMediaElement.h"
17 #include "core/html/canvas/CanvasRenderingContext.h"
18 #include "core/page/Chrome.h"
19 #include "core/page/Page.h"
20 #include "core/rendering/RenderApplet.h"
21 #include "core/rendering/RenderEmbeddedObject.h"
22 #include "core/rendering/RenderFullScreen.h"
23 #include "core/rendering/RenderGeometryMap.h"
24 #include "core/rendering/RenderIFrame.h"
25 #include "core/rendering/RenderLayer.h"
26 #include "core/rendering/RenderLayerStackingNode.h"
27 #include "core/rendering/RenderLayerStackingNodeIterator.h"
28 #include "core/rendering/RenderReplica.h"
29 #include "core/rendering/RenderVideo.h"
30 #include "core/rendering/RenderView.h"
31 #include "core/rendering/compositing/RenderLayerCompositor.h"
35 CompositingReasonFinder::CompositingReasonFinder(RenderView& renderView)
36 : m_renderView(renderView)
37 , m_compositingTriggers(static_cast<CompositingTriggerFlags>(AllCompositingTriggers))
41 void CompositingReasonFinder::updateTriggers()
43 m_compositingTriggers = m_renderView.document().page()->chrome().client().allowedCompositingTriggers();
46 bool CompositingReasonFinder::has3DTransformTrigger() const
48 return m_compositingTriggers & ThreeDTransformTrigger;
51 bool CompositingReasonFinder::hasAnimationTrigger() const
53 return m_compositingTriggers & AnimationTrigger;
56 bool CompositingReasonFinder::isMainFrame() const
58 // FIXME: LocalFrame::isMainFrame() is probably better.
59 return !m_renderView.document().ownerElement();
62 CompositingReasons CompositingReasonFinder::directReasons(const RenderLayer* layer, bool* needToRecomputeCompositingRequirements) const
64 CompositingReasons styleReasons = layer->styleDeterminedCompositingReasons();
65 ASSERT(styleDeterminedReasons(layer->renderer()) == styleReasons);
66 return styleReasons | nonStyleDeterminedDirectReasons(layer, needToRecomputeCompositingRequirements);
69 // This information doesn't appear to be incorporated into CompositingReasons.
70 bool CompositingReasonFinder::requiresCompositingForScrollableFrame() const
72 // Need this done first to determine overflow.
73 ASSERT(!m_renderView.needsLayout());
77 if (!(m_compositingTriggers & ScrollableInnerFrameTrigger))
80 FrameView* frameView = m_renderView.frameView();
81 return frameView->isScrollable();
84 CompositingReasons CompositingReasonFinder::styleDeterminedReasons(RenderObject* renderer) const
86 CompositingReasons directReasons = CompositingReasonNone;
88 if (requiresCompositingForTransform(renderer))
89 directReasons |= CompositingReason3DTransform;
91 if (requiresCompositingForBackfaceVisibilityHidden(renderer))
92 directReasons |= CompositingReasonBackfaceVisibilityHidden;
94 if (requiresCompositingForFilters(renderer))
95 directReasons |= CompositingReasonFilters;
97 if (requiresCompositingForWillChange(renderer))
98 directReasons |= CompositingReasonWillChange;
100 ASSERT(!(directReasons & ~CompositingReasonComboAllStyleDeterminedReasons));
101 return directReasons;
104 bool CompositingReasonFinder::requiresCompositingForTransform(RenderObject* renderer) const
106 if (!(m_compositingTriggers & ThreeDTransformTrigger))
109 // Note that we ask the renderer if it has a transform, because the style may have transforms,
110 // but the renderer may be an inline that doesn't suppport them.
111 return renderer->hasTransform() && renderer->style()->transform().has3DOperation();
114 bool CompositingReasonFinder::requiresCompositingForBackfaceVisibilityHidden(RenderObject* renderer) const
116 if (!(m_compositingTriggers & ThreeDTransformTrigger))
119 return renderer->style()->backfaceVisibility() == BackfaceVisibilityHidden;
122 bool CompositingReasonFinder::requiresCompositingForFilters(RenderObject* renderer) const
124 if (!(m_compositingTriggers & FilterTrigger))
127 return renderer->hasFilter();
130 bool CompositingReasonFinder::requiresCompositingForWillChange(const RenderObject* renderer) const
132 if (renderer->style()->hasWillChangeCompositingHint())
135 if (!(m_compositingTriggers & GPURasterizationTrigger))
138 return renderer->style()->hasWillChangeGpuRasterizationHint();
141 CompositingReasons CompositingReasonFinder::nonStyleDeterminedDirectReasons(const RenderLayer* layer, bool* needToRecomputeCompositingRequirements) const
143 CompositingReasons directReasons = CompositingReasonNone;
144 RenderObject* renderer = layer->renderer();
146 if (requiresCompositingForAnimation(renderer))
147 directReasons |= CompositingReasonActiveAnimation;
149 if (m_renderView.compositorDrivenAcceleratedScrollingEnabled()) {
150 if (requiresCompositingForOutOfFlowClipping(layer))
151 directReasons |= CompositingReasonOutOfFlowClipping;
153 if (requiresCompositingForOverflowScrollingParent(layer))
154 directReasons |= CompositingReasonOverflowScrollingParent;
157 if (requiresCompositingForOverflowScrolling(layer))
158 directReasons |= CompositingReasonOverflowScrollingTouch;
160 if (requiresCompositingForPosition(renderer, layer, 0, needToRecomputeCompositingRequirements))
161 directReasons |= renderer->style()->position() == FixedPosition ? CompositingReasonPositionFixed : CompositingReasonPositionSticky;
163 directReasons |= renderer->additionalCompositingReasons(m_compositingTriggers);
165 ASSERT(!(directReasons & CompositingReasonComboAllStyleDeterminedReasons));
166 return directReasons;
169 bool CompositingReasonFinder::requiresCompositingForAnimation(RenderObject* renderer) const
171 if (!(m_compositingTriggers & AnimationTrigger))
174 return shouldCompositeForActiveAnimations(*renderer);
177 bool CompositingReasonFinder::requiresCompositingForOutOfFlowClipping(const RenderLayer* layer) const
179 return layer->isUnclippedDescendant();
182 bool CompositingReasonFinder::requiresCompositingForOverflowScrollingParent(const RenderLayer* layer) const
184 return layer->scrollParent();
187 bool CompositingReasonFinder::requiresCompositingForOverflowScrolling(const RenderLayer* layer) const
189 return layer->needsCompositedScrolling();
192 bool CompositingReasonFinder::isViewportConstrainedFixedOrStickyLayer(const RenderLayer* layer)
194 if (layer->renderer()->isStickyPositioned())
195 return !layer->enclosingOverflowClipLayer(ExcludeSelf);
197 if (layer->renderer()->style()->position() != FixedPosition)
200 for (const RenderLayerStackingNode* stackingContainer = layer->stackingNode(); stackingContainer;
201 stackingContainer = stackingContainer->ancestorStackingContainerNode()) {
202 if (stackingContainer->layer()->compositingState() != NotComposited
203 && stackingContainer->layer()->renderer()->style()->position() == FixedPosition)
210 bool CompositingReasonFinder::requiresCompositingForPosition(RenderObject* renderer, const RenderLayer* layer, RenderLayer::ViewportConstrainedNotCompositedReason* viewportConstrainedNotCompositedReason, bool* needToRecomputeCompositingRequirements) const
212 // position:fixed elements that create their own stacking context (e.g. have an explicit z-index,
213 // opacity, transform) can get their own composited layer. A stacking context is required otherwise
214 // z-index and clipping will be broken.
215 if (!renderer->isPositioned())
218 EPosition position = renderer->style()->position();
219 bool isFixed = renderer->isOutOfFlowPositioned() && position == FixedPosition;
220 // FIXME: The isStackingContainer check here is redundant. Fixed position elements are always stacking contexts.
221 if (isFixed && !layer->stackingNode()->isStackingContainer())
224 bool isSticky = renderer->isInFlowPositioned() && position == StickyPosition;
225 if (!isFixed && !isSticky)
228 // FIXME: acceleratedCompositingForFixedPositionEnabled should probably be renamed acceleratedCompositingForViewportConstrainedPositionEnabled().
229 if (Settings* settings = m_renderView.document().settings()) {
230 if (!settings->acceleratedCompositingForFixedPositionEnabled())
235 return isViewportConstrainedFixedOrStickyLayer(layer);
237 RenderObject* container = renderer->container();
238 // If the renderer is not hooked up yet then we have to wait until it is.
240 *needToRecomputeCompositingRequirements = true;
244 // Don't promote fixed position elements that are descendants of a non-view container, e.g. transformed elements.
245 // They will stay fixed wrt the container rather than the enclosing frame.
246 if (container != &m_renderView) {
247 if (viewportConstrainedNotCompositedReason)
248 *viewportConstrainedNotCompositedReason = RenderLayer::NotCompositedForNonViewContainer;
252 // If the fixed-position element does not have any scrollable ancestor between it and
253 // its container, then we do not need to spend compositor resources for it. Start by
254 // assuming we can opt-out (i.e. no scrollable ancestor), and refine the answer below.
255 bool hasScrollableAncestor = false;
257 // The FrameView has the scrollbars associated with the top level viewport, so we have to
258 // check the FrameView in addition to the hierarchy of ancestors.
259 FrameView* frameView = m_renderView.frameView();
260 if (frameView && frameView->isScrollable())
261 hasScrollableAncestor = true;
263 RenderLayer* ancestor = layer->parent();
264 while (ancestor && !hasScrollableAncestor) {
265 if (frameView->containsScrollableArea(ancestor->scrollableArea()))
266 hasScrollableAncestor = true;
267 if (ancestor->renderer() == &m_renderView)
269 ancestor = ancestor->parent();
272 if (!hasScrollableAncestor) {
273 if (viewportConstrainedNotCompositedReason)
274 *viewportConstrainedNotCompositedReason = RenderLayer::NotCompositedForUnscrollableAncestors;
278 // Subsequent tests depend on layout. If we can't tell now, just keep things the way they are until layout is done.
279 if (m_renderView.document().lifecycle().state() < DocumentLifecycle::LayoutClean) {
280 *needToRecomputeCompositingRequirements = true;
281 return layer->hasCompositedLayerMapping();
284 bool paintsContent = layer->isVisuallyNonEmpty() || layer->hasVisibleDescendant();
285 if (!paintsContent) {
286 if (viewportConstrainedNotCompositedReason)
287 *viewportConstrainedNotCompositedReason = RenderLayer::NotCompositedForNoVisibleContent;
291 // Fixed position elements that are invisible in the current view don't get their own layer.
292 if (FrameView* frameView = m_renderView.frameView()) {
293 LayoutRect viewBounds = frameView->viewportConstrainedVisibleContentRect();
294 LayoutRect layerBounds = layer->calculateLayerBounds(layer->compositor()->rootRenderLayer(), 0,
295 RenderLayer::DefaultCalculateLayerBoundsFlags
296 | RenderLayer::ExcludeHiddenDescendants
297 | RenderLayer::DontConstrainForMask
298 | RenderLayer::IncludeCompositedDescendants
299 | RenderLayer::PretendLayerHasOwnBacking);
300 if (!viewBounds.intersects(enclosingIntRect(layerBounds))) {
301 if (viewportConstrainedNotCompositedReason) {
302 *viewportConstrainedNotCompositedReason = RenderLayer::NotCompositedForBoundsOutOfView;
303 *needToRecomputeCompositingRequirements = true;