Bug 815666 - Add a helper class to coalesce frames that need their overflow updated. r=roc
authorMatt Woodrow <mwoodrow@mozilla.com>
Mon, 10 Dec 2012 15:32:55 +1300
changeset 124558 767accc64cf8e3b97d547462aecc0913dff841ae
parent 124557 fe0abb4decb66a99b3a9603190f4580e30f4e328
child 124559 0319dd845d5318f55396e3e4a23fb2046d74242c
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 - Add a helper class to coalesce frames that need their overflow updated. r=roc
layout/base/RestyleTracker.h
layout/generic/nsIFrame.h
--- a/layout/base/RestyleTracker.h
+++ b/layout/base/RestyleTracker.h
@@ -9,22 +9,146 @@
  */
 
 #ifndef mozilla_css_RestyleTracker_h
 #define mozilla_css_RestyleTracker_h
 
 #include "mozilla/dom/Element.h"
 #include "nsDataHashtable.h"
 #include "nsIFrame.h"
+#include "nsTPriorityQueue.h"
 
 class nsCSSFrameConstructor;
 
 namespace mozilla {
 namespace css {
 
+/** 
+ * Helper class that collects a list of frames that need
+ * UpdateOverflow() called on them, and coalesces them
+ * to avoid walking up the same ancestor tree multiple times.
+ */
+class OverflowChangedTracker
+{
+public:
+
+  /**
+   * Add a frame that has had a style change, and needs its
+   * overflow updated.
+   *
+   * If there are pre-transform overflow areas stored for this
+   * frame, then we will call FinishAndStoreOverflow with those
+   * areas instead of UpdateOverflow().
+   *
+   * If the overflow area changes, then UpdateOverflow will also
+   * be called on the parent.
+   */
+  void AddFrame(nsIFrame* aFrame) {
+    mEntryList.Push(Entry(aFrame, true));
+  }
+
+  /**
+   * Update the overflow of all added frames, and clear the entry list.
+   *
+   * Start from those deepest in the frame tree and works upwards. This stops 
+   * us from processing the same frame twice.
+   */
+  void Flush() {
+    while (!mEntryList.IsEmpty()) {
+      Entry entry = mEntryList.Pop();
+
+      // Pop off any duplicate entries and copy back mInitial
+      // if any have it set.
+      while (!mEntryList.IsEmpty() &&
+             mEntryList.Top().mFrame == entry.mFrame) {
+        Entry next = mEntryList.Pop();
+
+        if (next.mInitial) {
+          entry.mInitial = true;
+        }
+      }
+      nsIFrame *frame = entry.mFrame;
+
+      bool updateParent = false;
+      if (entry.mInitial) {
+        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());
+          // We can't tell if the overflow changed, so update the parent regardless
+          updateParent = true;
+        }
+      }
+      
+      // If the overflow changed, then we want to also update the parent's
+      // overflow. We always update the parent for initial frames.
+      if (!updateParent) {
+        updateParent = frame->UpdateOverflow() || entry.mInitial;
+      }
+      if (updateParent) {
+        nsIFrame *parent = frame->GetParent();
+        if (parent) {
+          mEntryList.Push(Entry(parent, entry.mDepth - 1, false));
+        }
+      }
+    }
+  }
+  
+private:
+  struct Entry
+  {
+    Entry(nsIFrame* aFrame, bool aInitial)
+      : mFrame(aFrame)
+      , mDepth(aFrame->GetDepthInFrameTree())
+      , mInitial(aInitial)
+    {}
+    
+    Entry(nsIFrame* aFrame, uint32_t aDepth, bool aInitial)
+      : mFrame(aFrame)
+      , mDepth(aDepth)
+      , mInitial(aInitial)
+    {}
+
+    bool operator==(const Entry& aOther) const
+    {
+      return mFrame == aOther.mFrame;
+    }
+ 
+    /**
+     * Sort by the depth in the frame tree, and then
+     * the frame pointer.
+     */
+    bool operator<(const Entry& aOther) const
+    {
+      if (mDepth != aOther.mDepth) {
+        // nsTPriorityQueue implements a min-heap and we
+        // want the highest depth first, so reverse this check.
+        return mDepth > aOther.mDepth;
+      }
+
+      return mFrame < aOther.mFrame;
+    }
+
+    nsIFrame* mFrame;
+    /* Depth in the frame tree */
+    uint32_t mDepth;
+    /**
+     * True if the frame had the actual style change, and we
+     * want to check for pre-transform overflow areas.
+     */
+    bool mInitial;
+  };
+
+  /* A list of frames to process, sorted by their depth in the frame tree */
+  nsTPriorityQueue<Entry> mEntryList;
+};
+
 class RestyleTracker {
 public:
   typedef mozilla::dom::Element Element;
 
   RestyleTracker(uint32_t aRestyleBits,
                  nsCSSFrameConstructor* aFrameConstructor) :
     mRestyleBits(aRestyleBits), mFrameConstructor(aFrameConstructor),
     mHaveLaterSiblingRestyles(false)
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -1310,16 +1310,28 @@ public:
   bool ChildrenHavePerspective() const;
 
   // Calculate the overflow size of all child frames, taking preserve-3d into account
   void ComputePreserve3DChildrenOverflow(nsOverflowAreas& aOverflowAreas, const nsRect& aBounds);
 
   void RecomputePerspectiveChildrenOverflow(const nsStyleContext* aStartStyle, const nsRect* aBounds);
 
   /**
+   * Returns the number of ancestors between this and the root of our frame tree
+   */
+  uint32_t GetDepthInFrameTree() {
+    uint32_t result = 0;
+    for (nsIFrame* ancestor = GetParent(); ancestor;
+         ancestor = ancestor->GetParent()) {
+      result++;
+    }
+    return result;
+  }
+
+  /**
    * Event handling of GUI events.
    *
    * @param   aEvent event structure describing the type of event and rge widget
    *            where the event originated
    *          The |point| member of this is in the coordinate system of the
    *          view returned by GetOffsetFromView.
    * @param   aEventStatus a return value indicating whether the event was handled
    *            and whether default processing should be done