Chromium Code Reviews| Index: Source/core/rendering/RenderLayer.cpp |
| diff --git a/Source/core/rendering/RenderLayer.cpp b/Source/core/rendering/RenderLayer.cpp |
| index dffb6999ea46b40e65a624eb5517db9e241a1a2c..5efb48c6f0db303fa755dcf6d4c33d21b57de424 100644 |
| --- a/Source/core/rendering/RenderLayer.cpp |
| +++ b/Source/core/rendering/RenderLayer.cpp |
| @@ -69,6 +69,7 @@ |
| #include "core/page/UseCounter.h" |
| #include "core/page/animation/AnimationController.h" |
| #include "core/page/scrolling/ScrollingCoordinator.h" |
| +#include "core/platform/chromium/TraceEvent.h" |
| #include "core/platform/FloatConversion.h" |
| #include "core/platform/HistogramSupport.h" |
| #include "core/platform/PlatformMouseEvent.h" |
| @@ -113,6 +114,7 @@ |
| #include "core/rendering/svg/RenderSVGResourceClipper.h" |
| #include <wtf/MemoryInstrumentationVector.h> |
| #include <wtf/StdLibExtras.h> |
| +#include <wtf/TemporaryChange.h> |
| #include <wtf/text/CString.h> |
| #include <wtf/UnusedParam.h> |
| @@ -143,10 +145,11 @@ RenderLayer::RenderLayer(RenderLayerModelObject* renderer) |
| , m_hasSelfPaintingLayerDescendant(false) |
| , m_hasSelfPaintingLayerDescendantDirty(false) |
| , m_hasOutOfFlowPositionedDescendant(false) |
| - , m_hasOutOfFlowPositionedDescendantDirty(true) |
| + , m_forceNeedsCompositedScrolling(DoNotForceCompositedScrolling) |
| , m_needsCompositedScrolling(false) |
| - , m_descendantsAreContiguousInStackingOrder(false) |
| - , m_descendantsAreContiguousInStackingOrderDirty(true) |
| + , m_updatingNeedsCompositedScrolling(false) |
| + , m_canBePromotedToStackingContainer(false) |
| + , m_updatingCanBePromotedToStackingContainer(false) |
| , m_isRootLayer(renderer->isRenderView()) |
| , m_usedTransparency(false) |
| , m_paintingInsideReflection(false) |
| @@ -482,173 +485,249 @@ bool RenderLayer::acceleratedCompositingForOverflowScrollEnabled() const |
| && renderer()->frame()->page()->settings()->acceleratedCompositingForOverflowScrollEnabled(); |
| } |
| -// If we are a stacking container, then this function will determine if our |
| -// descendants for a contiguous block in stacking order. This is required in |
| -// order for an element to be safely promoted to a stacking container. It is safe |
| -// to become a stacking container if this change would not alter the stacking |
| -// order of layers on the page. That can only happen if a non-descendant appear |
| -// between us and our descendants in stacking order. Here's an example: |
| -// |
| -// this |
| -// / | \. |
| -// A B C |
| -// /\ | /\. |
| -// 0 -8 D 2 7 |
| -// | |
| -// 5 |
| -// |
| -// I've labeled our normal flow descendants A, B, C, and D, our stacking |
| -// container descendants with their z indices, and us with 'this' (we're a |
| -// stacking container and our zIndex doesn't matter here). These nodes appear in |
| -// three lists: posZOrder, negZOrder, and normal flow (keep in mind that normal |
| -// flow layers don't overlap). So if we arrange these lists in order we get our |
| -// stacking order: |
| -// |
| -// [-8], [A-D], [0, 2, 5, 7]--> pos z-order. |
| -// | | |
| -// Neg z-order. <-+ +--> Normal flow descendants. |
| -// |
| -// We can then assign new, 'stacking' order indices to these elements as follows: |
| -// |
| -// [-8], [A-D], [0, 2, 5, 7] |
| -// 'Stacking' indices: -1 0 1 2 3 4 |
| -// |
| -// Note that the normal flow descendants can share an index because they don't |
| -// stack/overlap. Now our problem becomes very simple: a layer can safely become |
| -// a stacking container if the stacking-order indices of it and its descendants |
| -// appear in a contiguous block in the list of stacking indices. This problem |
| -// can be solved very efficiently by calculating the min/max stacking indices in |
| -// the subtree, and the number stacking container descendants. Once we have this |
| -// information, we know that the subtree's indices form a contiguous block if: |
| -// |
| -// maxStackIndex - minStackIndex == numSCDescendants |
| +static inline bool isPositionedContainer(const RenderLayer* layer) |
| +{ |
| + // FIXME: This is not in sync with containingBlock. |
| + // RenderObject::canContainFixedPositionedObject() should probably be used |
| + // instead. |
| + RenderLayerModelObject* layerRenderer = layer->renderer(); |
| + return layer->isRootLayer() || layerRenderer->isPositioned() || layer->hasTransform(); |
| +} |
| + |
| +enum StackingOrderDirection { FromBackground, FromForeground }; |
| + |
| +// We'd like to be able to iterate through a single paint order list, but for |
| +// efficiency's sake, we hang onto two lists instead (namely, the pos and neg |
| +// z-order lists produced by CollectLayers). This function allows us to index |
| +// into these two lists as if they were one. It also allows us to index into |
| +// this virtual list either from the start or from the end (i.e., in either |
| +// stacking order direction). |
| +static const RenderLayer* getStackingOrderElementAt(const Vector<RenderLayer*>* posZOrderList, const Vector<RenderLayer*>* negZOrderList, const StackingOrderDirection direction, const size_t index) |
| +{ |
| + size_t negZOrderListSize = negZOrderList ? negZOrderList->size() : 0; |
| + |
| + if (direction == FromBackground) { |
| + if (index < negZOrderListSize) |
| + return negZOrderList->at(index); |
| + |
| + return posZOrderList->at(index - negZOrderListSize); |
| + } |
| + |
| + size_t posZOrderListSize = posZOrderList ? posZOrderList->size() : 0; |
| + |
| + if (index < posZOrderListSize) |
| + return posZOrderList->at(posZOrderListSize - index - 1); |
| + |
| + return negZOrderList->at(negZOrderListSize - (index - posZOrderListSize) - 1); |
| +} |
| + |
| +// After promotion, the paint order consists of |
| +// a) the non-descendants which precede the promoted layer, |
| +// b) the promoted layer, and |
| +// c) the non-descendants which succeed the promoted layer. |
| // |
| -// So for node A in the example above we would have: |
| -// maxStackIndex = 1 |
| -// minStackIndex = -1 |
| -// numSCDecendants = 2 |
| +// If the current layer's descendants form a contiguous block in paint order |
| +// before promotion, the paint order will consist of |
| +// a) the non-descendants which precede the current layer and its descendants, |
| +// b) the current layer and its descendants |
| +// c) The non-descendants which succeed the current layer and its descendants. |
| // |
| -// and so, |
| -// maxStackIndex - minStackIndex == numSCDescendants |
| -// ===> 1 - (-1) == 2 |
| -// ===> 2 == 2 |
| +// Sub-lists (a) and (c) should be identical in both paint order lists if |
| +// and only if the descendants form a contiguous block. In fact, this is the |
| +// only check we need to perform since the order of the descendants with |
| +// respect to each other cannot be affected by promotion (i.e., we don't |
| +// need to worry about sub-list (b)). |
| // |
| -// Since this is true, A can safely become a stacking container. |
| -// Now, for node C we have: |
| +// Some examples: |
| +// C = currentLayer |
| +// - = negative z-order child of currentLayer |
| +// + = positive z-order child of currentLayer |
| +// A = positioned ancestor of currentLayer |
| +// x = any other RenderLayer in the list |
| // |
| -// maxStackIndex = 4 |
| -// minStackIndex = 0 <-- because C has stacking index 0. |
| -// numSCDecendants = 2 |
| +// original | zOrderListBeforePromote | zOrderListAfterPromote |
| +// zOrderListBeforePromote | after inserting C as above | |
| +// --------------------------------------------------------------------------------------- |
| +// (1) x---+++x | x---C+++x | xCx |
| +// (2) x---+A++x | x---+AC++x | xACx |
| +// (3) x-x--+++x | x-x--C+++x | xxCx |
| +// (4) xxA++x | xxAC++x | xxACx |
| // |
| -// and so, |
| -// maxStackIndex - minStackIndex == numSCDescendants |
| -// ===> 4 - 0 == 2 |
| -// ===> 4 == 2 |
| +// In example (1), we compare sub-list (a) by marching from the left of both |
| +// lists (zOrderListBeforePromote after inserting C and |
| +// zOrderListAfterPromote). The first mismatch is at index 1 when we hit '-' |
| +// and 'C'. That means we have neg z-order descendants. This is a problem if |
| +// we have a background. Before promotion, this bg would get painted with |
| +// the current layer (i.e., after the neg z-order descendants), but after |
| +// promotion the bg would get painted before them. This is a stacking order |
| +// violation and we can't promote. However, if we don't have a background, |
| +// we would continue on to the second pass. When comparing from the right, |
| +// we mismatch on '+' and 'C'. Since we hit 'C' on zOrderListAfterPromote, |
| +// we know that the children are contiguous, and we will promote. |
| // |
| -// Since this is false, C cannot be safely promoted to a stacking container. This |
| -// happened because of the elements with z-index 5 and 0. Now if 5 had been a |
| -// child of C rather than D, and A had no child with Z index 0, we would have had: |
| +// In example (2), when marching from the left, we'll hit a mismatch again |
| +// on the second element we look at. This time, since this element is an 'A' |
| +// in zOrderListAfterPromote, this indicates that there is an extra layer |
| +// (the 'A') mixed in with the children. This could cause a change in paint |
| +// order if we promote, so we decide not to and break out of the loop. Note |
| +// that if the current layer has a background, this would provide a second |
| +// reason not to opt in, since again we have negative z-order children who |
| +// would change paint order with respect to our background if we promoted. |
| // |
| -// maxStackIndex = 3 |
| -// minStackIndex = 0 <-- because C has stacking index 0. |
| -// numSCDecendants = 3 |
| +// In example (3), the discontiguity of the negative z-order children causes |
| +// us to fail early in our "FromBackground" pass when we try to compare '-' |
| +// from zOrderListBeforePromote with 'x' in zOrderListAfterPromote. |
| // |
| -// and so, |
| -// maxStackIndex - minStackIndex == numSCDescendants |
| -// ===> 3 - 0 == 3 |
| -// ===> 3 == 3 |
| +// Finally in example (4), we would match 'xxAC' from the left, then stop |
| +// since we hit 'C'. Then we would match 'x' from the right, and mismatch |
| +// on '+' and 'C'. Since we're at 'C' on the zOrderListAfterPromote, we |
| +// conclude that all the children are contiguous. Since there are no |
| +// negative z-order children, a background layer is irrelevant in this case. |
| +// We will opt in, keeping paint order constant. |
| +static bool compareLayerListsBeforeAndAfterPromote(const RenderLayer* currentLayer, |
| + const Vector<RenderLayer*>* posZOrderListBeforePromote, |
| + const Vector<RenderLayer*>* negZOrderListBeforePromote, |
| + const Vector<RenderLayer*>* posZOrderListAfterPromote, |
| + const Vector<RenderLayer*>* negZOrderListAfterPromote, |
| + const size_t sizeBeforePromote, |
| + const size_t sizeAfterPromote, |
| + const StackingOrderDirection direction) |
| +{ |
| + for (size_t index = 0; index < sizeBeforePromote && index < sizeAfterPromote; index++) { |
| + const RenderLayer* layerBeforePromote = getStackingOrderElementAt(posZOrderListBeforePromote, negZOrderListBeforePromote, direction, index); |
| + const RenderLayer* layerAfterPromote = getStackingOrderElementAt(posZOrderListAfterPromote, negZOrderListAfterPromote, direction, index); |
| + |
| + if (layerBeforePromote != layerAfterPromote) { |
| + // If we find a mismatch, the only situation where we haven't |
| + // necessarily changed paint order yet is if layerAfterPromote |
| + // is currentLayer. |
| + if (layerAfterPromote != currentLayer) |
| + return false; |
| + |
| + // Also, if the current layer has a background, then any |
| + // negative z-order children will get between the background |
| + // and the rest of the layer. |
| + if (direction == FromBackground && currentLayer->renderer()->hasBackground()) |
| + return false; |
| + } |
| + |
| + // To compare the sub-lists (a) and (c) from the comment above, we only |
| + // need to march until we hit the currentLayer in the |
| + // zOrderListAfterPromote from each direction. |
| + if (layerAfterPromote == currentLayer) |
| + break; |
| + } |
| + |
| + return true; |
| +} |
| + |
| +// Determine whether the current layer can be promoted to a stacking container, |
| +// given its closest stacking context ancestor. We do this by computing what |
| +// positive and negative z-order lists would look like before and after |
| +// promotion, and ensuring that proper stacking order is preserved between the |
| +// two sets of lists. |
| // |
| -// And we would conclude that C could be promoted. |
| -void RenderLayer::updateDescendantsAreContiguousInStackingOrder() |
| +// For more details on how the lists will be compared, see the comment and |
| +// examples for compareLayerListsBeforeAndAfterPromote(). |
| +void RenderLayer::updateCanBeStackingContainer(RenderLayer* ancestorStackingContext) |
| { |
| - if (!m_descendantsAreContiguousInStackingOrderDirty || !isStackingContext() || !acceleratedCompositingForOverflowScrollEnabled()) |
| + TRACE_EVENT0("blink", "RenderLayer::updateCanBeStackingContainer"); |
| + ASSERT(!isStackingContext()); |
| + |
| + if (m_updatingCanBePromotedToStackingContainer || !acceleratedCompositingForOverflowScrollEnabled()) |
| return; |
| - ASSERT(!m_normalFlowListDirty); |
| - ASSERT(!m_zOrderListsDirty); |
| + FrameView* frameView = renderer()->view()->frameView(); |
| + if (!frameView || !frameView->containsScrollableArea(this)) |
| + return; |
| - OwnPtr<Vector<RenderLayer*> > posZOrderList; |
| - OwnPtr<Vector<RenderLayer*> > negZOrderList; |
| - rebuildZOrderLists(StopAtStackingContexts, posZOrderList, negZOrderList); |
| + m_updatingCanBePromotedToStackingContainer = true; |
| - // Create a reverse lookup. |
| - HashMap<const RenderLayer*, int> lookup; |
| + OwnPtr<Vector<RenderLayer*> > posZOrderListBeforePromote; |
| + OwnPtr<Vector<RenderLayer*> > negZOrderListBeforePromote; |
| + OwnPtr<Vector<RenderLayer*> > posZOrderListAfterPromote; |
| + OwnPtr<Vector<RenderLayer*> > negZOrderListAfterPromote; |
| + size_t posZOrderListSizeBeforePromote, negZOrderListSizeBeforePromote, posZOrderListSizeAfterPromote, negZOrderListSizeAfterPromote; |
| - if (negZOrderList) { |
| - int stackingOrderIndex = -1; |
| - size_t listSize = negZOrderList->size(); |
| - for (size_t i = 0; i < listSize; ++i) { |
| - RenderLayer* currentLayer = negZOrderList->at(listSize - i - 1); |
| - if (!currentLayer->isStackingContext()) |
| - continue; |
| - lookup.set(currentLayer, stackingOrderIndex--); |
| - } |
| - } |
| + collectBeforePromotionZOrderList(ancestorStackingContext, posZOrderListBeforePromote, negZOrderListBeforePromote); |
| + collectAfterPromotionZOrderList(ancestorStackingContext, posZOrderListAfterPromote, negZOrderListAfterPromote); |
| - if (posZOrderList) { |
| - size_t listSize = posZOrderList->size(); |
| - int stackingOrderIndex = 1; |
| - for (size_t i = 0; i < listSize; ++i) { |
| - RenderLayer* currentLayer = posZOrderList->at(i); |
| - if (!currentLayer->isStackingContext()) |
| - continue; |
| - lookup.set(currentLayer, stackingOrderIndex++); |
| - } |
| - } |
| + size_t sizeBeforePromote = 0; |
| + if (posZOrderListBeforePromote) |
| + sizeBeforePromote += posZOrderListBeforePromote->size(); |
| + if (negZOrderListBeforePromote) |
| + sizeBeforePromote += negZOrderListBeforePromote->size(); |
| - int minIndex = 0; |
| - int maxIndex = 0; |
| - int count = 0; |
| - bool firstIteration = true; |
| - updateDescendantsAreContiguousInStackingOrderRecursive(lookup, minIndex, maxIndex, count, firstIteration); |
| + size_t sizeAfterPromote = 0; |
| + if (posZOrderListAfterPromote) |
| + sizeAfterPromote += posZOrderListAfterPromote->size(); |
| + if (negZOrderListAfterPromote) |
| + sizeAfterPromote += negZOrderListAfterPromote->size(); |
| - m_descendantsAreContiguousInStackingOrderDirty = false; |
| + bool canPromote = compareLayerListsBeforeAndAfterPromote(this, posZOrderListBeforePromote.get(), negZOrderListBeforePromote.get(), |
| + posZOrderListAfterPromote.get(), negZOrderListAfterPromote.get(), |
| + sizeBeforePromote, sizeAfterPromote, FromBackground) |
| + && compareLayerListsBeforeAndAfterPromote(this, posZOrderListBeforePromote.get(), negZOrderListBeforePromote.get(), |
| + posZOrderListAfterPromote.get(), negZOrderListAfterPromote.get(), |
| + sizeBeforePromote, sizeAfterPromote, FromForeground); |
| + |
| + bool didUpdate = (canPromote != m_canBePromotedToStackingContainer); |
| + |
| + m_canBePromotedToStackingContainer = canPromote; |
| + m_updatingCanBePromotedToStackingContainer = false; |
| } |
| -void RenderLayer::updateDescendantsAreContiguousInStackingOrderRecursive(const HashMap<const RenderLayer*, int>& lookup, int& minIndex, int& maxIndex, int& count, bool firstIteration) |
| +void RenderLayer::updateCanBeStackingContainerRecursively(RenderLayer* ancestorStackingContext) |
| { |
| - if (isStackingContext() && !firstIteration) { |
| - if (lookup.contains(this)) { |
| - minIndex = std::min(minIndex, lookup.get(this)); |
| - maxIndex = std::max(maxIndex, lookup.get(this)); |
| - count++; |
| - } |
| - return; |
| - } |
| + if (this != ancestorStackingContext) { |
| + if (isStackingContext()) |
| + return; |
| - for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) { |
| - int childMinIndex = 0; |
| - int childMaxIndex = 0; |
| - int childCount = 0; |
| - child->updateDescendantsAreContiguousInStackingOrderRecursive(lookup, childMinIndex, childMaxIndex, childCount, false); |
| - if (childCount) { |
| - count += childCount; |
| - minIndex = std::min(minIndex, childMinIndex); |
| - maxIndex = std::max(maxIndex, childMaxIndex); |
| - } |
| + updateCanBeStackingContainer(ancestorStackingContext); |
| } |
| - if (!isStackingContext()) { |
| - bool newValue = maxIndex - minIndex == count; |
| - bool didUpdate = newValue != m_descendantsAreContiguousInStackingOrder; |
| - m_descendantsAreContiguousInStackingOrder = newValue; |
| - if (didUpdate) |
| - updateNeedsCompositedScrolling(); |
| + if (m_hasVisibleDescendant) { |
| + for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) { |
| + // Ignore reflections. |
| + if (!m_reflection || reflectionLayer() != child) |
| + child->updateCanBeStackingContainerRecursively(ancestorStackingContext); |
| + } |
| } |
| } |
| -static inline bool isPositionedContainer(const RenderLayer* layer) |
| -{ |
| - // FIXME: This is not in sync with containingBlock. |
| - // RenderObject::canContainFixedPositionedObject() should probably be used |
| - // instead. |
| - RenderLayerModelObject* layerRenderer = layer->renderer(); |
| - return layer->isRootLayer() || layerRenderer->isPositioned() || layer->hasTransform(); |
| -} |
| - |
| +// Compute what positive and negative z-order lists would look like before and |
| +// after promotion, so we can later ensure that proper stacking order is |
| +// preserved between the two sets of lists. |
| +// |
| +// A few examples: |
| +// c = currentLayer |
| +// - = negative z-order child of currentLayer |
| +// + = positive z-order child of currentLayer |
| +// a = positioned ancestor of currentLayer |
| +// x = any other RenderLayer in the list |
| +// |
| +// (a) xxxxx-----++a+++x |
| +// (b) xxx-----c++++++xx |
| +// |
| +// |
| +// Normally the current layer would be painted in the normal flow list if it |
| +// doesn't already appear in the positive z-order list. However, in the case |
| +// that the layer has a positioned ancestor, it will paint directly after the |
| +// positioned ancestor. In example (a), the current layer would be painted in |
| +// the middle of its own positive z-order children, so promoting would cause a |
| +// change in paint order (since a promoted layer will paint all of its positive |
| +// z-order children strictly after it paints itself). |
| +// |
| +// In example (b), it is ok to promote the current layer only if it does not |
| +// have a background. If it has a background, the background gets painted before |
| +// the layer's negative z-order children, so again, a promotion would cause a |
| +// change in paint order (causing the background to get painted after the |
| +// negative z-order children instead of before). |
| void RenderLayer::collectBeforePromotionZOrderList(RenderLayer* ancestorStackingContext, OwnPtr<Vector<RenderLayer*> >& posZOrderListBeforePromote, OwnPtr<Vector<RenderLayer*> >& negZOrderListBeforePromote) |
| { |
| - // FIXME: TemporaryChange should support bit fields. |
| + // We can't use TemporaryChange<> here since m_needsCompositedScrolling and |
| + // m_isNormalFlowOnly are both bitfields, so we have to do it the |
| + // old-fashioned way. |
| bool oldNeedsCompositedScrolling = m_needsCompositedScrolling; |
| bool oldIsNormalFlowOnly = m_isNormalFlowOnly; |
| @@ -1062,7 +1141,7 @@ bool RenderLayer::canBeStackingContainer() const |
| if (isStackingContext() || !ancestorStackingContainer()) |
| return true; |
| - return m_descendantsAreContiguousInStackingOrder; |
| + return m_canBePromotedToStackingContainer; |
| } |
| void RenderLayer::setHasVisibleContent() |
| @@ -1118,56 +1197,62 @@ void RenderLayer::setAncestorChainHasVisibleDescendant() |
| } |
| } |
| -void RenderLayer::updateDescendantDependentFlags(HashSet<const RenderObject*>* outOfFlowDescendantContainingBlocks) |
| +void RenderLayer::updateHasOutOfFlowPositionedDescendant(HashSet<const RenderObject*>* containingBlocks) |
| { |
| - if (m_visibleDescendantStatusDirty || m_hasSelfPaintingLayerDescendantDirty || m_hasOutOfFlowPositionedDescendantDirty) { |
| - const bool hadVisibleDescendant = m_hasVisibleDescendant; |
| - const bool hadOutOfFlowPositionedDescendant = m_hasOutOfFlowPositionedDescendant; |
| + const bool hadOutOfFlowPositionedDescendant = hasOutOfFlowPositionedDescendant(); |
| + m_hasOutOfFlowPositionedDescendant = false; |
| + |
| + HashSet<const RenderObject*> childContainingBlocks; |
| + for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) { |
| + childContainingBlocks.clear(); |
| + child->updateHasOutOfFlowPositionedDescendant(&childContainingBlocks); |
| + |
| + const bool childIsOutOfFlowPositioned = child->renderer() |
| + && child->renderer()->isOutOfFlowPositioned() |
| + && (child->hasVisibleDescendant() || child->hasVisibleContent()); |
| + |
| + if (childIsOutOfFlowPositioned) { |
| + childContainingBlocks.add(child->renderer()->containingBlock()); |
| + |
| + childContainingBlocks.remove(renderer()); |
| + |
| + if (containingBlocks && !childContainingBlocks.isEmpty()) { |
| + HashSet<const RenderObject*>::const_iterator it = childContainingBlocks.begin(); |
| + for (; it != childContainingBlocks.end(); ++it) |
| + containingBlocks->add(*it); |
| + } |
| + |
| + m_hasOutOfFlowPositionedDescendant |= !childContainingBlocks.isEmpty(); |
| + } |
| + if (m_hasOutOfFlowPositionedDescendant != hadOutOfFlowPositionedDescendant) |
| + compositor()->setNeedsUpdateCompositingRequirementsState(); |
| +} |
| + |
| +void RenderLayer::updateDescendantDependentFlags() |
| +{ |
| + if (m_visibleDescendantStatusDirty || m_hasSelfPaintingLayerDescendantDirty) { |
| m_hasVisibleDescendant = false; |
| m_hasSelfPaintingLayerDescendant = false; |
| - m_hasOutOfFlowPositionedDescendant = false; |
| - HashSet<const RenderObject*> childOutOfFlowDescendantContainingBlocks; |
| for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) { |
| - childOutOfFlowDescendantContainingBlocks.clear(); |
| - child->updateDescendantDependentFlags(&childOutOfFlowDescendantContainingBlocks); |
| - |
| - bool childIsOutOfFlowPositioned = child->renderer() && child->renderer()->isOutOfFlowPositioned(); |
| - if (childIsOutOfFlowPositioned) |
| - childOutOfFlowDescendantContainingBlocks.add(child->renderer()->containingBlock()); |
| - |
| - if (outOfFlowDescendantContainingBlocks) { |
| - HashSet<const RenderObject*>::const_iterator it = childOutOfFlowDescendantContainingBlocks.begin(); |
| - for (; it != childOutOfFlowDescendantContainingBlocks.end(); ++it) |
| - outOfFlowDescendantContainingBlocks->add(*it); |
| - } |
| + child->updateDescendantDependentFlags(); |
| bool hasVisibleDescendant = child->m_hasVisibleContent || child->m_hasVisibleDescendant; |
| bool hasSelfPaintingLayerDescendant = child->isSelfPaintingLayer() || child->hasSelfPaintingLayerDescendant(); |
| - bool hasOutOfFlowPositionedDescendant = hasVisibleDescendant && (!childOutOfFlowDescendantContainingBlocks.isEmpty() || child->hasOutOfFlowPositionedDescendant()); |
| m_hasVisibleDescendant |= hasVisibleDescendant; |
| m_hasSelfPaintingLayerDescendant |= hasSelfPaintingLayerDescendant; |
| - m_hasOutOfFlowPositionedDescendant |= hasOutOfFlowPositionedDescendant; |
| - if (m_hasVisibleDescendant && m_hasSelfPaintingLayerDescendant && m_hasOutOfFlowPositionedDescendant) |
| + if (m_hasVisibleDescendant && m_hasSelfPaintingLayerDescendant) |
| break; |
| } |
| - if (outOfFlowDescendantContainingBlocks && renderer()) |
| - outOfFlowDescendantContainingBlocks->remove(renderer()); |
| - |
| m_visibleDescendantStatusDirty = false; |
| m_hasSelfPaintingLayerDescendantDirty = false; |
| - m_hasOutOfFlowPositionedDescendantDirty = false; |
| - |
| - if (m_hasVisibleDescendant != hadVisibleDescendant || m_hasOutOfFlowPositionedDescendant != hadOutOfFlowPositionedDescendant) |
| - updateNeedsCompositedScrolling(); |
| } |
| if (m_visibleContentStatusDirty) { |
| - const bool hadVisibleContent = m_hasVisibleContent; |
| if (renderer()->style()->visibility() == VISIBLE) |
| m_hasVisibleContent = true; |
| else { |
| @@ -1193,10 +1278,8 @@ void RenderLayer::updateDescendantDependentFlags(HashSet<const RenderObject*>* o |
| r = r->nextSibling(); |
| } |
| } |
| - } |
| - m_visibleContentStatusDirty = false; |
| - if (hadVisibleContent != m_hasVisibleContent) |
| - updateNeedsCompositedScrolling(); |
| + } |
| + m_visibleContentStatusDirty = false; |
| } |
| } |
| @@ -1767,16 +1850,6 @@ void RenderLayer::addChild(RenderLayer* child, RenderLayer* beforeChild) |
| // case where we're building up generated content layers. This is ok, since the lists will start |
| // off dirty in that case anyway. |
| child->dirtyStackingContainerZOrderLists(); |
| - |
| - // Adding an out of flow positioned descendant can only affect |
| - // the opt-in decision for layers beneath and including our |
| - // containing block. |
| - RenderObject* containingBlock = child->renderer()->containingBlock(); |
| - for (RenderLayer* layer = child; layer; layer = layer->parent()) { |
| - layer->updateNeedsCompositedScrolling(); |
| - if (layer->renderer() == containingBlock) |
| - break; |
| - } |
| } |
| child->updateDescendantDependentFlags(); |
| @@ -1787,7 +1860,7 @@ void RenderLayer::addChild(RenderLayer* child, RenderLayer* beforeChild) |
| setAncestorChainHasSelfPaintingLayerDescendant(); |
| if (child->renderer() && (child->renderer()->isOutOfFlowPositioned() || child->hasOutOfFlowPositionedDescendant())) |
| - setAncestorChainHasOutOfFlowPositionedDescendant(child->renderer()->containingBlock()); |
| + compositor()->setNeedsUpdateCompositingRequirementsState(); |
| compositor()->layerWasAdded(this, child); |
| } |
| @@ -1815,28 +1888,16 @@ RenderLayer* RenderLayer::removeChild(RenderLayer* oldChild) |
| // reattachment process in removeOnlyThisLayer, the layer may already be disconnected |
| // from the main layer tree, so we need to null-check the |stackingContainer| value. |
| oldChild->dirtyStackingContainerZOrderLists(); |
| - |
| - // This could affect whether or not a layer has an out of flow |
| - // positioned descendant so we need to schedule some updates. |
| - // Removing an out of flow positioned descendant can only affect |
| - // the opt-in decision for layers beneath and including the old child's |
| - // containing block. |
| - RenderObject* containingBlock = oldChild->renderer()->containingBlock(); |
| - for (RenderLayer* layer = this; layer; layer = layer->parent()) { |
| - layer->updateNeedsCompositedScrolling(); |
| - if (layer->renderer() == containingBlock) |
| - break; |
| - } |
| } |
| - if ((oldChild->renderer() && oldChild->renderer()->isOutOfFlowPositioned()) || oldChild->hasOutOfFlowPositionedDescendant()) |
| - dirtyAncestorChainHasOutOfFlowPositionedDescendantStatus(); |
| - |
| oldChild->setPreviousSibling(0); |
| oldChild->setNextSibling(0); |
| oldChild->setParent(0); |
| oldChild->updateDescendantDependentFlags(); |
| + if ((oldChild->renderer() && oldChild->renderer()->isOutOfFlowPositioned()) || oldChild->hasOutOfFlowPositionedDescendant()) |
| + compositor()->setNeedsUpdateCompositingRequirementsState(); |
| + |
| if (oldChild->m_hasVisibleContent || oldChild->m_hasVisibleDescendant) |
| dirtyAncestorChainVisibleDescendantStatus(); |
| @@ -2051,20 +2112,47 @@ bool RenderLayer::usesCompositedScrolling() const |
| bool RenderLayer::needsCompositedScrolling() const |
| { |
| + switch (m_forceNeedsCompositedScrolling) { |
| + case DoNotForceCompositedScrolling: |
| + return m_needsCompositedScrolling; |
| + case ForceCompositedScrollingOn: |
| + return true; |
| + case ForceCompositedScrollingOff: |
| + return false; |
| + } |
| return m_needsCompositedScrolling; |
| } |
| void RenderLayer::updateNeedsCompositedScrolling() |
| { |
| - bool needsCompositedScrolling = false; |
| + TRACE_EVENT0("blink-rendering", "RenderLayer::updateNeedsCompositedScrolling\n"); |
|
hartmanng
2013/05/02 14:04:01
Might not be a bad idea to leave these trace event
|
| +#ifndef NDEBUG |
| + fprintf(stderr, "vollick: RenderLayer::updateNeedsCompositedScrolling %s\n", debugName().ascii().data()); |
| +#endif |
| + if (m_updatingNeedsCompositedScrolling || m_updatingCanBePromotedToStackingContainer) |
| + return; |
| + |
| + bool needsCompositedScrolling = isStackingContext(); |
| FrameView* frameView = renderer()->view()->frameView(); |
| - if (frameView && frameView->containsScrollableArea(this)) { |
| + if (!needsCompositedScrolling && acceleratedCompositingForOverflowScrollEnabled() && frameView && frameView->containsScrollableArea(this)) { |
| + m_updatingNeedsCompositedScrolling = true; |
| updateDescendantDependentFlags(); |
| + RenderLayer* stackingContext = ancestorStackingContext(); |
| + updateCanBeStackingContainer(stackingContext); |
| - bool forceUseCompositedScrolling = acceleratedCompositingForOverflowScrollEnabled() |
| - && canBeStackingContainer() |
| - && !hasOutOfFlowPositionedDescendant(); |
| + bool forceUseCompositedScrolling = canBeStackingContainer() && !hasOutOfFlowPositionedDescendant(); |
| + |
| +#ifndef NDEBUG |
| + fprintf(stderr, "vollick: RenderLayer::updateNeedsCompositedScrolling %d %d %s\n", canBeStackingContainer(), hasOutOfFlowPositionedDescendant(), debugName().ascii().data()); |
| +#endif |
| + |
| + TRACE_EVENT_INSTANT2("blink-rendering", |
| + "RenderLayer::updateNeedsCompositedScrolling", |
| + "Can be a stacking container", |
| + canBeStackingContainer(), |
| + "Has out of flow positioned descendant", |
| + hasOutOfFlowPositionedDescendant()); |
| #if ENABLE(ACCELERATED_OVERFLOW_SCROLLING) |
| needsCompositedScrolling = forceUseCompositedScrolling || renderer()->style()->useTouchOverflowScrolling(); |
| @@ -2077,13 +2165,35 @@ void RenderLayer::updateNeedsCompositedScrolling() |
| // number of composited overflow divs. |
| if (acceleratedCompositingForOverflowScrollEnabled()) |
| HistogramSupport::histogramEnumeration("Renderer.NeedsCompositedScrolling", needsCompositedScrolling, 2); |
| + |
| + m_updatingNeedsCompositedScrolling = false; |
| } |
| + setNeedsCompositedScrolling(needsCompositedScrolling); |
| +} |
| + |
| +void RenderLayer::setNeedsCompositedScrolling(bool needsCompositedScrolling) |
| +{ |
| if (m_needsCompositedScrolling == needsCompositedScrolling) |
| return; |
| m_needsCompositedScrolling = needsCompositedScrolling; |
| + // Note, the z-order lists may need to be rebuilt, but our code guarantees |
| + // that we have not affected stacking, so we will not dirty |
| + // m_canBePromotedToStackingContainer for either us or our stacking context |
| + // or container. |
| + didSetNeedsCompositedScrolling(); |
| +} |
| + |
| +void RenderLayer::setForceNeedsCompositedScrolling(RenderLayer::ForceNeedsCompositedScrollingMode mode) |
| +{ |
| + m_forceNeedsCompositedScrolling = mode; |
| + didSetNeedsCompositedScrolling(); |
| +} |
| + |
| +void RenderLayer::didSetNeedsCompositedScrolling() |
| +{ |
| updateIsNormalFlowOnly(); |
| updateSelfPaintingLayer(); |
| @@ -5607,7 +5717,7 @@ void RenderLayer::dirtyZOrderLists() |
| m_negZOrderList->clear(); |
| m_zOrderListsDirty = true; |
| - m_descendantsAreContiguousInStackingOrderDirty = true; |
| + compositor()->setNeedsUpdateCompositingRequirementsState(); |
| if (!renderer()->documentBeingDestroyed()) { |
| compositor()->setCompositingLayersNeedRebuild(); |
| @@ -5712,18 +5822,28 @@ void RenderLayer::collectLayers(bool includeHiddenLayers, CollectLayersBehavior |
| bool isStacking = false; |
| switch (behavior) { |
| - case StopAtStackingContexts: |
| - isStacking = (this == layerToForceAsStackingContainer) || isStackingContext(); |
| - break; |
| + case StopAtStackingContexts: |
| + isStacking = (this == layerToForceAsStackingContainer) || isStackingContext(); |
| + break; |
| - case StopAtStackingContainers: |
| - isStacking = (this == layerToForceAsStackingContainer) || isStackingContainer(); |
| - break; |
| + case StopAtStackingContainers: |
| + isStacking = (this == layerToForceAsStackingContainer) || isStackingContainer(); |
| + break; |
| + } |
| + |
| + // This value could be affected by opt in. What we really need to know is |
| + // are you normal flow only, regardless of your opt in status. This is very |
| + // similar to asking if a layer is a stacking context rather than a |
| + // stacking container. |
| + bool isNormalFlowWithoutCompositedScrolling = isNormalFlowOnly(); |
| + if (this != layerToForceAsStackingContainer && behavior == StopAtStackingContexts) { |
| + TemporaryChange<ForceNeedsCompositedScrollingMode> forceOff(m_forceNeedsCompositedScrolling, ForceCompositedScrollingOff); |
| + isNormalFlowWithoutCompositedScrolling = shouldBeNormalFlowOnly(); |
| } |
| // Overflow layers are just painted by their enclosing layers, so they don't get put in zorder lists. |
| bool includeHiddenLayer = includeHiddenLayers || (m_hasVisibleContent || (m_hasVisibleDescendant && isStacking)); |
| - if (includeHiddenLayer && !isNormalFlowOnly() && !isOutOfFlowRenderFlowThread()) { |
| + if (includeHiddenLayer && !isNormalFlowWithoutCompositedScrolling && !isOutOfFlowRenderFlowThread()) { |
| // Determine which buffer the child should be in. |
| OwnPtr<Vector<RenderLayer*> >& buffer = (zIndex() >= 0) ? posBuffer : negBuffer; |
| @@ -5748,7 +5868,8 @@ void RenderLayer::collectLayers(bool includeHiddenLayers, CollectLayersBehavior |
| void RenderLayer::updateLayerListsIfNeeded() |
| { |
| - bool shouldUpdateDescendantsAreContiguousInStackingOrder = acceleratedCompositingForOverflowScrollEnabled() && isStackingContext() && (m_zOrderListsDirty || m_normalFlowListDirty) && m_descendantsAreContiguousInStackingOrderDirty; |
| +// bool shouldUpdateCanBeStackingContainer = acceleratedCompositingForOverflowScrollEnabled() && isStackingContext() && (m_zOrderListsDirty || m_normalFlowListDirty) && m_canBePromotedToStackingContainerDirty; |
| + bool shouldUpdateCanBeStackingContainer = false; |
| updateZOrderLists(); |
| updateNormalFlowList(); |
| @@ -5757,8 +5878,11 @@ void RenderLayer::updateLayerListsIfNeeded() |
| reflectionLayer->updateNormalFlowList(); |
| } |
| - if (shouldUpdateDescendantsAreContiguousInStackingOrder) { |
| - updateDescendantsAreContiguousInStackingOrder(); |
| + if (shouldUpdateCanBeStackingContainer) { |
| + // Call UpdateCanBeStackingContainer for all descendants, |
| + // passing self in as ancestor stacking context. |
| + updateCanBeStackingContainerRecursively(this); |
| + |
| // The above function can cause us to update m_needsCompositedScrolling |
| // and dirty our layer lists. Refresh them if necessary. |
| updateZOrderLists(); |
| @@ -5943,9 +6067,7 @@ void RenderLayer::updateVisibilityAfterStyleChange(const RenderStyle* oldStyle) |
| return; |
| if (renderer()->style()->visibility() == VISIBLE) |
| - setAncestorChainHasOutOfFlowPositionedDescendant(renderer()->containingBlock()); |
| - else |
| - dirtyAncestorChainHasOutOfFlowPositionedDescendantStatus(); |
| + compositor()->setNeedsUpdateCompositingRequirementsState(); |
| } |
| void RenderLayer::updateStackingContextsAfterStyleChange(const RenderStyle* oldStyle) |
| @@ -5967,7 +6089,10 @@ void RenderLayer::updateStackingContextsAfterStyleChange(const RenderStyle* oldS |
| else |
| clearZOrderLists(); |
| - updateNeedsCompositedScrolling(); |
| + // FIXME: This should be done via a function so that we can flip a bit on |
| + // the renderview allowing us to skip updates in frames where no comp-scroll |
| + // state actually changed. |
| + compositor()->setNeedsUpdateCompositingRequirementsState(); |
| } |
| static bool overflowRequiresScrollbar(EOverflow overflow) |
| @@ -6016,46 +6141,10 @@ void RenderLayer::updateScrollbarsAfterStyleChange(const RenderStyle* oldStyle) |
| updateScrollableAreaSet(hasScrollableHorizontalOverflow() || hasScrollableVerticalOverflow()); |
| } |
| -void RenderLayer::setAncestorChainHasOutOfFlowPositionedDescendant(RenderObject* containingBlock) |
| -{ |
| - for (RenderLayer* layer = this; layer; layer = layer->parent()) { |
| - if (!layer->m_hasOutOfFlowPositionedDescendantDirty && layer->hasOutOfFlowPositionedDescendant()) |
| - break; |
| - |
| - layer->m_hasOutOfFlowPositionedDescendantDirty = false; |
| - layer->m_hasOutOfFlowPositionedDescendant = true; |
| - layer->updateNeedsCompositedScrolling(); |
| - |
| - if (layer->renderer() && layer->renderer() == containingBlock) |
| - break; |
| - } |
| -} |
| - |
| -void RenderLayer::dirtyAncestorChainHasOutOfFlowPositionedDescendantStatus() |
| -{ |
| - if (m_hasOutOfFlowPositionedDescendant) { |
| - m_hasOutOfFlowPositionedDescendantDirty = true; |
| - // FIXME It would be nice to avoid this when we clean up render layer |
| - // updating. We shouldn't have to update the composited scrolling state |
| - // nearly as frequently if all the updates happen in a single, well |
| - // defined phase. |
| - updateNeedsCompositedScrolling(); |
| - } |
| - |
| - if (parent()) |
| - parent()->dirtyAncestorChainHasOutOfFlowPositionedDescendantStatus(); |
| -} |
| - |
| void RenderLayer::updateOutOfFlowPositioned(const RenderStyle* oldStyle) |
| { |
| - bool wasOutOfFlowPositioned = oldStyle && (oldStyle->position() == AbsolutePosition || oldStyle->position() == FixedPosition); |
| - bool isOutOfFlowPositioned = renderer()->isOutOfFlowPositioned(); |
| - if (parent() && isOutOfFlowPositioned != wasOutOfFlowPositioned) { |
| - if (isOutOfFlowPositioned) |
| - parent()->setAncestorChainHasOutOfFlowPositionedDescendant(renderer()->containingBlock()); |
| - else |
| - parent()->dirtyAncestorChainHasOutOfFlowPositionedDescendantStatus(); |
| - } |
| + if (!oldStyle || (renderer()->style()->position() != oldStyle->position())) |
| + compositor()->setNeedsUpdateCompositingRequirementsState(); |
| } |
| static bool hasOrHadFilters(const RenderStyle* oldStyle, const RenderStyle* newStyle) |
| @@ -6213,7 +6302,7 @@ void RenderLayer::updateScrollableAreaSet(bool hasOverflow) |
| updatedScrollableAreaSet = frameView->removeScrollableArea(this); |
| if (updatedScrollableAreaSet) |
| - updateNeedsCompositedScrolling(); |
| + compositor()->setNeedsUpdateCompositingRequirementsState(); |
| } |
| void RenderLayer::updateScrollCornerStyle() |