Bug 815666 - Use OverflowUpdateTracker to avoid calling UpdateOverflow on the same frame multiple times. r=roc
authorMatt Woodrow <mwoodrow@mozilla.com>
Mon, 10 Dec 2012 15:33:04 +1300
changeset 124559 0319dd845d5318f55396e3e4a23fb2046d74242c
parent 124558 767accc64cf8e3b97d547462aecc0913dff841ae
child 124560 1ffb3bd63f71ea20e4101cbbd7d891d41976a043
push id2151
push userlsblakk@mozilla.com
push dateTue, 19 Feb 2013 18:06:57 +0000
treeherdermozilla-beta@4952e88741ec [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs815666
milestone20.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 815666 - Use OverflowUpdateTracker to avoid calling UpdateOverflow on the same frame multiple times. r=roc
layout/base/RestyleTracker.cpp
layout/base/RestyleTracker.h
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsCSSFrameConstructor.h
layout/base/nsPresShell.cpp
layout/generic/nsFrame.cpp
layout/generic/nsIFrame.h
--- a/layout/base/RestyleTracker.cpp
+++ b/layout/base/RestyleTracker.cpp
@@ -104,49 +104,53 @@ CollectRestyles(nsISupports* aElement,
   *restyleArrayPtr = currentRestyle + 1;
 
   return PL_DHASH_NEXT;
 }
 
 inline void
 RestyleTracker::ProcessOneRestyle(Element* aElement,
                                   nsRestyleHint aRestyleHint,
-                                  nsChangeHint aChangeHint)
+                                  nsChangeHint aChangeHint,
+                                  OverflowChangedTracker& aTracker)
 {
   NS_PRECONDITION((aRestyleHint & eRestyle_LaterSiblings) == 0,
                   "Someone should have handled this before calling us");
   NS_PRECONDITION(Document(), "Must have a document");
   NS_PRECONDITION(aElement->GetCurrentDoc() == Document(),
                   "Element has unexpected document");
 
   nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
   if (aRestyleHint & (eRestyle_Self | eRestyle_Subtree)) {
     mFrameConstructor->RestyleElement(aElement, primaryFrame, aChangeHint,
                                       *this,
-                                      (aRestyleHint & eRestyle_Subtree) != 0);
+                                      (aRestyleHint & eRestyle_Subtree) != 0, 
+                                      aTracker);
   } else if (aChangeHint &&
              (primaryFrame ||
               (aChangeHint & nsChangeHint_ReconstructFrame))) {
     // Don't need to recompute style; just apply the hint
     nsStyleChangeList changeList;
     changeList.AppendChange(primaryFrame, aElement, aChangeHint);
-    mFrameConstructor->ProcessRestyledFrames(changeList);
+    mFrameConstructor->ProcessRestyledFrames(changeList, aTracker);
   }
 }
 
 void
 RestyleTracker::DoProcessRestyles()
 {
   SAMPLE_LABEL("CSS", "ProcessRestyles");
   // Make sure to not rebuild quote or counter lists while we're
   // processing restyles
   mFrameConstructor->BeginUpdate();
 
   mFrameConstructor->mInStyleRefresh = true;
 
+  OverflowChangedTracker tracker;
+
   // loop so that we process any restyle events generated by processing
   while (mPendingRestyles.Count()) {
     if (mHaveLaterSiblingRestyles) {
       // Convert them to individual restyles on all the later siblings
       nsAutoTArray<nsRefPtr<Element>, RESTYLE_ARRAY_STACKSIZE> laterSiblingArr;
       LaterSiblingCollector siblingCollector = { this, &laterSiblingArr };
       mPendingRestyles.Enumerate(CollectLaterSiblings, &siblingCollector);
       for (uint32_t i = 0; i < laterSiblingArr.Length(); ++i) {
@@ -201,17 +205,17 @@ RestyleTracker::DoProcessRestyles()
         continue;
       }
 
       RestyleData data;
       if (!GetRestyleData(element, &data)) {
         continue;
       }
 
-      ProcessOneRestyle(element, data.mRestyleHint, data.mChangeHint);
+      ProcessOneRestyle(element, data.mRestyleHint, data.mChangeHint, tracker);
     }
 
     if (mHaveLaterSiblingRestyles) {
       // Keep processing restyles for now
       continue;
     }
 
     // Now we only have entries with change hints left.  To be safe in
@@ -230,21 +234,24 @@ RestyleTracker::DoProcessRestyles()
       // Clear the hashtable now that we don't need it anymore
       mPendingRestyles.Clear();
 
       for (RestyleEnumerateData* currentRestyle = restylesToProcess;
            currentRestyle != lastRestyle;
            ++currentRestyle) {
         ProcessOneRestyle(currentRestyle->mElement,
                           currentRestyle->mRestyleHint,
-                          currentRestyle->mChangeHint);
+                          currentRestyle->mChangeHint,
+                          tracker);
       }
     }
   }
 
+  tracker.Flush();
+
   // Set mInStyleRefresh to false now, since the EndUpdate call might
   // add more restyles.
   mFrameConstructor->mInStyleRefresh = false;
 
   mFrameConstructor->EndUpdate();
 
 #ifdef DEBUG
   mFrameConstructor->mPresShell->VerifyStyleTree();
--- a/layout/base/RestyleTracker.h
+++ b/layout/base/RestyleTracker.h
@@ -233,17 +233,18 @@ public:
 private:
   /**
    * Handle a single mPendingRestyles entry.  aRestyleHint must not
    * include eRestyle_LaterSiblings; that needs to be dealt with
    * before calling this function.
    */
   inline void ProcessOneRestyle(Element* aElement,
                                 nsRestyleHint aRestyleHint,
-                                nsChangeHint aChangeHint);
+                                nsChangeHint aChangeHint,
+                                OverflowChangedTracker& aTracker);
 
   /**
    * The guts of our restyle processing.
    */
   void DoProcessRestyles();
 
   typedef nsDataHashtable<nsISupportsHashKey, RestyleData> PendingRestyleTable;
   typedef nsAutoTArray< nsRefPtr<Element>, 32> RestyleRootArray;
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -7759,17 +7759,16 @@ DoApplyRenderingChangeToTree(nsIFrame* a
     if (aChange & nsChangeHint_UpdateOpacityLayer) {
       // FIXME/bug 796697: we can get away with empty transactions for
       // opacity updates in many cases.
       needInvalidatingPaint = true;
       aFrame->MarkLayersActive(nsChangeHint_UpdateOpacityLayer);
     }
     if (aChange & nsChangeHint_UpdateTransformLayer) {
       aFrame->MarkLayersActive(nsChangeHint_UpdateTransformLayer);
-      aFrame->AddStateBits(NS_FRAME_TRANSFORM_CHANGED);
       // If we're not already going to do an invalidating paint, see
       // if we can get away with only updating the transform on a
       // layer for this frame, and not scheduling an invalidating
       // paint.
       if (!needInvalidatingPaint) {
         needInvalidatingPaint |= !aFrame->TryUpdateTransformOnly();
       }
     }
@@ -8052,17 +8051,18 @@ NeedToReframeForAddingOrRemovingTransfor
     if (FrameHasPositionedPlaceholderDescendants(f, positionMask)) {
       return true;
     }
   }
   return false;
 }
 
 nsresult
-nsCSSFrameConstructor::ProcessRestyledFrames(nsStyleChangeList& aChangeList)
+nsCSSFrameConstructor::ProcessRestyledFrames(nsStyleChangeList& aChangeList,
+                                             OverflowChangedTracker& aTracker)
 {
   NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
                "Someone forgot a script blocker");
   int32_t count = aChangeList.Count();
   if (!count)
     return NS_OK;
 
   SAMPLE_LABEL("CSS", "ProcessRestyledFrames");
@@ -8204,40 +8204,20 @@ nsCSSFrameConstructor::ProcessRestyledFr
                          "SVG child frame not expected to have different parent");
           }
         }
         // If |frame| is dirty or has dirty children, we don't bother updating
         // overflows since that will happen when it's reflowed.
         if (!(frame->GetStateBits() &
               (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN))) {
           while (frame) {
-            nsOverflowAreas* pre = static_cast<nsOverflowAreas*>
-              (frame->Properties().Get(frame->PreTransformOverflowAreasProperty()));
-            if (pre) {
-              // FinishAndStoreOverflow will change the overflow areas passed in,
-              // so make a copy.
-              nsOverflowAreas overflowAreas = *pre;
-              frame->FinishAndStoreOverflow(overflowAreas, frame->GetSize());
-            } else {
-              frame->UpdateOverflow();
-            }
-
-            nsIFrame* next =
+            aTracker.AddFrame(frame);
+
+            frame =
               nsLayoutUtils::GetNextContinuationOrSpecialSibling(frame);
-            // Update the ancestors' overflow after we have updated the overflow
-            // for all the continuations with the same parent.
-            if (!next || frame->GetParent() != next->GetParent()) {
-              for (nsIFrame* ancestor = frame->GetParent(); ancestor;
-                   ancestor = ancestor->GetParent()) {
-                if (!ancestor->UpdateOverflow()) {
-                  break;
-                }
-              }
-            }
-            frame = next;
           }
         }
       }
       if (hint & nsChangeHint_UpdateCursor) {
         mPresShell->SynthesizeMouseMove(false);
       }
     }
   }
@@ -8272,17 +8252,18 @@ nsCSSFrameConstructor::ProcessRestyledFr
   return NS_OK;
 }
 
 void
 nsCSSFrameConstructor::RestyleElement(Element        *aElement,
                                       nsIFrame       *aPrimaryFrame,
                                       nsChangeHint   aMinHint,
                                       RestyleTracker& aRestyleTracker,
-                                      bool            aRestyleDescendants)
+                                      bool            aRestyleDescendants,
+                                      OverflowChangedTracker& aTracker)
 {
   NS_ASSERTION(aPrimaryFrame == aElement->GetPrimaryFrame(),
                "frame/content mismatch");
   if (aPrimaryFrame && aPrimaryFrame->GetContent() != aElement) {
     // XXXbz this is due to image maps messing with the primary frame pointer
     // of <area>s.  See bug 135040.  We can remove this block once that's fixed.
     aPrimaryFrame = nullptr;
   }
@@ -8309,17 +8290,17 @@ nsCSSFrameConstructor::RestyleElement(El
   }
 
   if (aMinHint & nsChangeHint_ReconstructFrame) {
     RecreateFramesForContent(aElement, false);
   } else if (aPrimaryFrame) {
     nsStyleChangeList changeList;
     ComputeStyleChangeFor(aPrimaryFrame, &changeList, aMinHint,
                           aRestyleTracker, aRestyleDescendants);
-    ProcessRestyledFrames(changeList);
+    ProcessRestyledFrames(changeList, aTracker);
   } else {
     // no frames, reconstruct for content
     MaybeRecreateFramesForElement(aElement);
   }
 }
 
 nsresult
 nsCSSFrameConstructor::ContentStateChanged(nsIContent* aContent,
@@ -12064,17 +12045,19 @@ nsCSSFrameConstructor::DoRebuildAllStyle
   nsStyleChangeList changeList;
   // XXX Does it matter that we're passing aExtraHint to the real root
   // frame and not the root node's primary frame?
   // Note: The restyle tracker we pass in here doesn't matter.
   ComputeStyleChangeFor(mPresShell->GetRootFrame(),
                         &changeList, aExtraHint,
                         aRestyleTracker, true);
   // Process the required changes
-  ProcessRestyledFrames(changeList);
+  OverflowChangedTracker tracker;
+  ProcessRestyledFrames(changeList, tracker);
+  tracker.Flush();
 
   // Tell the style set it's safe to destroy the old rule tree.  We
   // must do this after the ProcessRestyledFrames call in case the
   // change list has frame reconstructs in it (since frames to be
   // reconstructed will still have their old style context pointers
   // until they are destroyed).
   mPresShell->StyleSet()->EndReconstruct();
 }
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -48,16 +48,17 @@ class nsFrameConstructorSaveState;
 
 class nsCSSFrameConstructor : public nsFrameManager
 {
   friend class nsRefreshDriver;
 
 public:
   typedef mozilla::dom::Element Element;
   typedef mozilla::css::RestyleTracker RestyleTracker;
+  typedef mozilla::css::OverflowChangedTracker OverflowChangedTracker;
 
   nsCSSFrameConstructor(nsIDocument *aDocument, nsIPresShell* aPresShell);
   ~nsCSSFrameConstructor(void) {
     NS_ASSERTION(mUpdateCount == 0, "Dying in the middle of our own update?");
   }
 
   struct RestyleData;
   friend struct RestyleData;
@@ -231,17 +232,18 @@ public:
   // as a result of a change to the :hover content state.
   uint32_t GetHoverGeneration() const { return mHoverGeneration; }
 
   // Note: It's the caller's responsibility to make sure to wrap a
   // ProcessRestyledFrames call in a view update batch and a script blocker.
   // This function does not call ProcessAttachedQueue() on the binding manager.
   // If the caller wants that to happen synchronously, it needs to handle that
   // itself.
-  nsresult ProcessRestyledFrames(nsStyleChangeList& aRestyleArray);
+  nsresult ProcessRestyledFrames(nsStyleChangeList& aRestyleArray,
+                                 OverflowChangedTracker& aTracker);
 
 private:
 
   friend class mozilla::css::RestyleTracker;
 
   void RestyleForEmptyChange(Element* aContainer);
 
 public:
@@ -385,17 +387,18 @@ private:
                               nsIFrame*&     aCanvasFrame);
 
   /* aMinHint is the minimal change that should be made to the element */
   // XXXbz do we really need the aPrimaryFrame argument here?
   void RestyleElement(Element* aElement,
                       nsIFrame*       aPrimaryFrame,
                       nsChangeHint    aMinHint,
                       RestyleTracker& aRestyleTracker,
-                      bool            aRestyleDescendants);
+                      bool            aRestyleDescendants,
+                      OverflowChangedTracker& aTracker);
 
   nsresult InitAndRestoreFrame (const nsFrameConstructorState& aState,
                                 nsIContent*                    aContent,
                                 nsIFrame*                      aParentFrame,
                                 nsIFrame*                      aPrevInFlow,
                                 nsIFrame*                      aNewFrame,
                                 bool                           aAllowCounters = true);
 
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -2720,17 +2720,19 @@ PresShell::RecreateFramesFor(nsIContent*
 
   nsAutoScriptBlocker scriptBlocker;
 
   nsStyleChangeList changeList;
   changeList.AppendChange(nullptr, aContent, nsChangeHint_ReconstructFrame);
 
   // Mark ourselves as not safe to flush while we're doing frame construction.
   ++mChangeNestCount;
-  nsresult rv = mFrameConstructor->ProcessRestyledFrames(changeList);
+  css::OverflowChangedTracker tracker;
+  nsresult rv = mFrameConstructor->ProcessRestyledFrames(changeList, tracker);
+  tracker.Flush();
   --mChangeNestCount;
   
   return rv;
 }
 
 void
 nsIPresShell::PostRecreateFramesFor(Element* aElement)
 {
@@ -7835,17 +7837,19 @@ PresShell::Observe(nsISupports* aSubject
         nsStyleChangeList changeList;
         WalkFramesThroughPlaceholders(mPresContext, rootFrame,
                                       ReframeImageBoxes, &changeList);
         // Mark ourselves as not safe to flush while we're doing frame
         // construction.
         {
           nsAutoScriptBlocker scriptBlocker;
           ++mChangeNestCount;
-          mFrameConstructor->ProcessRestyledFrames(changeList);
+          css::OverflowChangedTracker tracker;
+          mFrameConstructor->ProcessRestyledFrames(changeList, tracker);
+          tracker.Flush();
           --mChangeNestCount;
         }
       }
     }
     return NS_OK;
   }
 #endif
 
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -7015,30 +7015,28 @@ nsIFrame::FinishAndStoreOverflow(nsOverf
      * ensured us we'll use.
      */
     nsRect newBounds(nsPoint(0, 0), aNewSize);
     // Transform affects both overflow areas.
     NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
       nsRect& o = aOverflowAreas.Overflow(otype);
       o = nsDisplayTransform::TransformRect(o, this, nsPoint(0, 0), &newBounds);
     }
-    if ((sizeChanged || HasAnyStateBits(NS_FRAME_TRANSFORM_CHANGED)) && Preserves3DChildren()) {
+    if (Preserves3DChildren()) {
       ComputePreserve3DChildrenOverflow(aOverflowAreas, newBounds);
     } else if (sizeChanged && ChildrenHavePerspective()) {
       RecomputePerspectiveChildrenOverflow(this->GetStyleContext(), &newBounds);
     }
   } else {
     Properties().Delete(nsIFrame::PreTransformOverflowAreasProperty());
     if (ChildrenHavePerspective() && sizeChanged) {
       nsRect newBounds(nsPoint(0, 0), aNewSize);
       RecomputePerspectiveChildrenOverflow(this->GetStyleContext(), &newBounds);
     }
   }
-  RemoveStateBits(NS_FRAME_TRANSFORM_CHANGED);
-    
 
   bool anyOverflowChanged;
   if (aOverflowAreas != nsOverflowAreas(bounds, bounds)) {
     anyOverflowChanged = SetOverflowAreas(aOverflowAreas);
   } else {
     anyOverflowChanged = ClearOverflowRects();
   }
 
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -248,20 +248,16 @@ typedef uint64_t nsFrameState;
 // TEXT_FORCE_TRIM_WHITESPACE.  That's OK because we only check the
 // NS_FRAME_IS_PUSHED_FLOAT bit on frames which we already know are
 // out-of-flow.
 #define NS_FRAME_IS_PUSHED_FLOAT                    NS_FRAME_STATE_BIT(32)
 
 // This bit acts as a loop flag for recursive paint server drawing.
 #define NS_FRAME_DRAWING_AS_PAINTSERVER             NS_FRAME_STATE_BIT(33)
 
-// Marks the frame as having a changed transform between processing
-// nsChangeHint_UpdateTransformLayer and calling FinishAndStoreOverflow.
-#define NS_FRAME_TRANSFORM_CHANGED                  NS_FRAME_STATE_BIT(34)
-
 // Frame is a display root and the retained layer tree needs to be updated
 // at the next paint via display list construction.
 // Only meaningful for display roots, so we don't really need a global state
 // bit; we could free up this bit with a little extra complexity.
 #define NS_FRAME_UPDATE_LAYER_TREE                  NS_FRAME_STATE_BIT(36)
 
 // Frame can accept absolutely positioned children.
 #define NS_FRAME_HAS_ABSPOS_CHILDREN                NS_FRAME_STATE_BIT(37)