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. 4 5 #include "config.h" 6 #include "core/rendering/compositing/CompositingReasonFinder.h" 7 8 #include "core/frame/FrameView.h" 9 #include "core/frame/Settings.h" 10 #include "core/page/Page.h" 11 #include "core/rendering/RenderView.h" 12 #include "core/rendering/compositing/RenderLayerCompositor.h" 13 14 namespace WebCore { 15 16 CompositingReasonFinder::CompositingReasonFinder(RenderView& renderView) 17 : m_renderView(renderView) 18 , m_compositingTriggers(static_cast<CompositingTriggerFlags>(AllCompositingTriggers)) 19 { 20 updateTriggers(); 21 } 22 23 void CompositingReasonFinder::updateTriggers() 24 { 25 m_compositingTriggers = 0; 26 27 Settings& settings = m_renderView.document().page()->settings(); 28 if (settings.acceleratedCompositingForVideoEnabled()) 29 m_compositingTriggers |= VideoTrigger; 30 if (settings.acceleratedCompositingForCanvasEnabled()) 31 m_compositingTriggers |= CanvasTrigger; 32 if (settings.compositedScrollingForFramesEnabled()) 33 m_compositingTriggers |= ScrollableInnerFrameTrigger; 34 if (settings.acceleratedCompositingForFiltersEnabled()) 35 m_compositingTriggers |= FilterTrigger; 36 37 // We map both these settings to universal overlow scrolling. 38 // FIXME: Replace these settings with a generic compositing setting for HighDPI. 39 if (settings.acceleratedCompositingForOverflowScrollEnabled() || settings.compositorDrivenAcceleratedScrollingEnabled()) 40 m_compositingTriggers |= OverflowScrollTrigger; 41 42 // FIXME: acceleratedCompositingForFixedPositionEnabled should be renamed acceleratedCompositingForViewportConstrainedPositionEnabled(). 43 // Or the sticky and fixed position elements should be behind different flags. 44 if (settings.acceleratedCompositingForFixedPositionEnabled()) 45 m_compositingTriggers |= ViewportConstrainedPositionedTrigger; 46 } 47 48 bool CompositingReasonFinder::hasOverflowScrollTrigger() const 49 { 50 return m_compositingTriggers & OverflowScrollTrigger; 51 } 52 53 bool CompositingReasonFinder::isMainFrame() const 54 { 55 // FIXME: LocalFrame::isMainFrame() is probably better. 56 return !m_renderView.document().ownerElement(); 57 } 58 59 CompositingReasons CompositingReasonFinder::directReasons(const RenderLayer* layer) const 60 { 61 CompositingReasons styleReasons = layer->styleDeterminedCompositingReasons(); 62 ASSERT(styleDeterminedReasons(layer->renderer()) == styleReasons); 63 return styleReasons | nonStyleDeterminedDirectReasons(layer); 64 } 65 66 // This information doesn't appear to be incorporated into CompositingReasons. 67 bool CompositingReasonFinder::requiresCompositingForScrollableFrame() const 68 { 69 // Need this done first to determine overflow. 70 ASSERT(!m_renderView.needsLayout()); 71 if (isMainFrame()) 72 return false; 73 74 if (!(m_compositingTriggers & ScrollableInnerFrameTrigger)) 75 return false; 76 77 return m_renderView.frameView()->isScrollable(); 78 } 79 80 CompositingReasons CompositingReasonFinder::styleDeterminedReasons(RenderObject* renderer) const 81 { 82 CompositingReasons directReasons = CompositingReasonNone; 83 84 RenderStyle* style = renderer->style(); 85 86 if (requiresCompositingForTransform(renderer)) 87 directReasons |= CompositingReason3DTransform; 88 89 if (requiresCompositingForFilters(renderer)) 90 directReasons |= CompositingReasonFilters; 91 92 if (style->backfaceVisibility() == BackfaceVisibilityHidden) 93 directReasons |= CompositingReasonBackfaceVisibilityHidden; 94 95 if (requiresCompositingForAnimation(style)) 96 directReasons |= CompositingReasonActiveAnimation; 97 98 if (style->hasWillChangeCompositingHint() && !style->subtreeWillChangeContents()) 99 directReasons |= CompositingReasonWillChangeCompositingHint; 100 101 ASSERT(!(directReasons & ~CompositingReasonComboAllStyleDeterminedReasons)); 102 return directReasons; 103 } 104 105 bool CompositingReasonFinder::requiresCompositingForTransform(RenderObject* renderer) const 106 { 107 // Note that we ask the renderer if it has a transform, because the style may have transforms, 108 // but the renderer may be an inline that doesn't suppport them. 109 return renderer->hasTransform() && renderer->style()->transform().has3DOperation(); 110 } 111 112 bool CompositingReasonFinder::requiresCompositingForFilters(RenderObject* renderer) const 113 { 114 if (!(m_compositingTriggers & FilterTrigger)) 115 return false; 116 117 return renderer->hasFilter(); 118 } 119 120 CompositingReasons CompositingReasonFinder::nonStyleDeterminedDirectReasons(const RenderLayer* layer) const 121 { 122 CompositingReasons directReasons = CompositingReasonNone; 123 RenderObject* renderer = layer->renderer(); 124 125 if (hasOverflowScrollTrigger()) { 126 // IsUnclippedDescendant is only actually stale during the chicken/egg code path. 127 // FIXME: Use compositingInputs().isUnclippedDescendant to ASSERT that 128 // this value isn't stale. 129 if (layer->compositingInputs().isUnclippedDescendant) 130 directReasons |= CompositingReasonOutOfFlowClipping; 131 132 if (layer->scrollParent()) 133 directReasons |= CompositingReasonOverflowScrollingParent; 134 135 if (layer->needsCompositedScrolling()) 136 directReasons |= CompositingReasonOverflowScrollingTouch; 137 } 138 139 if (requiresCompositingForPositionFixed(renderer, layer, 0)) 140 directReasons |= CompositingReasonPositionFixed; 141 142 directReasons |= renderer->additionalCompositingReasons(m_compositingTriggers); 143 144 ASSERT(!(directReasons & CompositingReasonComboAllStyleDeterminedReasons)); 145 return directReasons; 146 } 147 148 bool CompositingReasonFinder::requiresCompositingForAnimation(RenderStyle* style) const 149 { 150 if (style->subtreeWillChangeContents()) 151 return style->isRunningAnimationOnCompositor(); 152 153 return style->shouldCompositeForCurrentAnimations(); 154 } 155 156 bool CompositingReasonFinder::requiresCompositingForPositionFixed(RenderObject* renderer, const RenderLayer* layer, RenderLayer::ViewportConstrainedNotCompositedReason* viewportConstrainedNotCompositedReason) const 157 { 158 if (!(m_compositingTriggers & ViewportConstrainedPositionedTrigger)) 159 return false; 160 161 if (renderer->style()->position() != FixedPosition) 162 return false; 163 164 RenderObject* container = renderer->container(); 165 // If the renderer is not hooked up yet then we have to wait until it is. 166 if (!container) { 167 ASSERT(m_renderView.document().lifecycle().state() < DocumentLifecycle::InCompositingUpdate); 168 // FIXME: Remove this and ASSERT(container) once we get rid of the incremental 169 // allocateOrClearCompositedLayerMapping compositing update. This happens when 170 // adding the renderer to the tree because we setStyle before addChild in 171 // createRendererForElementIfNeeded. 172 return false; 173 } 174 175 // Don't promote fixed position elements that are descendants of a non-view container, e.g. transformed elements. 176 // They will stay fixed wrt the container rather than the enclosing frame. 177 if (container != &m_renderView) { 178 if (viewportConstrainedNotCompositedReason) 179 *viewportConstrainedNotCompositedReason = RenderLayer::NotCompositedForNonViewContainer; 180 return false; 181 } 182 183 // If the fixed-position element does not have any scrollable ancestor between it and 184 // its container, then we do not need to spend compositor resources for it. Start by 185 // assuming we can opt-out (i.e. no scrollable ancestor), and refine the answer below. 186 bool hasScrollableAncestor = false; 187 188 // The FrameView has the scrollbars associated with the top level viewport, so we have to 189 // check the FrameView in addition to the hierarchy of ancestors. 190 FrameView* frameView = m_renderView.frameView(); 191 if (frameView && frameView->isScrollable()) 192 hasScrollableAncestor = true; 193 194 RenderLayer* ancestor = layer->parent(); 195 while (ancestor && !hasScrollableAncestor) { 196 if (ancestor->scrollsOverflow()) 197 hasScrollableAncestor = true; 198 if (ancestor->renderer() == &m_renderView) 199 break; 200 ancestor = ancestor->parent(); 201 } 202 203 if (!hasScrollableAncestor) { 204 if (viewportConstrainedNotCompositedReason) 205 *viewportConstrainedNotCompositedReason = RenderLayer::NotCompositedForUnscrollableAncestors; 206 return false; 207 } 208 209 // Subsequent tests depend on layout. If we can't tell now, just keep things the way they are until layout is done. 210 // FIXME: Get rid of this codepath once we get rid of the incremental compositing update in RenderLayer::styleChanged. 211 if (m_renderView.document().lifecycle().state() < DocumentLifecycle::LayoutClean) 212 return layer->hasCompositedLayerMapping(); 213 214 bool paintsContent = layer->isVisuallyNonEmpty() || layer->hasVisibleDescendant(); 215 if (!paintsContent) { 216 if (viewportConstrainedNotCompositedReason) 217 *viewportConstrainedNotCompositedReason = RenderLayer::NotCompositedForNoVisibleContent; 218 return false; 219 } 220 221 // Fixed position elements that are invisible in the current view don't get their own layer. 222 if (FrameView* frameView = m_renderView.frameView()) { 223 ASSERT(m_renderView.document().lifecycle().state() == DocumentLifecycle::InCompositingUpdate); 224 LayoutRect viewBounds = frameView->viewportConstrainedVisibleContentRect(); 225 LayoutRect layerBounds = layer->boundingBoxForCompositing(layer->compositor()->rootRenderLayer(), RenderLayer::ApplyBoundsChickenEggHacks); 226 if (!viewBounds.intersects(enclosingIntRect(layerBounds))) { 227 if (viewportConstrainedNotCompositedReason) 228 *viewportConstrainedNotCompositedReason = RenderLayer::NotCompositedForBoundsOutOfView; 229 return false; 230 } 231 } 232 233 return true; 234 } 235 236 } 237