Bug 1291707 part 3 - Use hashtable to track nsGenConNodes related to a frame. r?bz draft
authorXidorn Quan <xidorn+moz@upsuper.org>
Tue, 16 Aug 2016 09:28:47 +1000
changeset 400844 00251e8763546c2cccdf9e637e5c58ea38a26612
parent 400843 acea60ceaa93726f482779ab1d8578fb4c1d7cb2
child 400845 8ffea7f768b79315c6a4214228a14a3d2a3bd742
push id26297
push userxquan@mozilla.com
push dateMon, 15 Aug 2016 23:30:49 +0000
reviewersbz
bugs1291707
milestone51.0a1
Bug 1291707 part 3 - Use hashtable to track nsGenConNodes related to a frame. r?bz MozReview-Commit-ID: IgT90GN2R4Z
layout/base/nsGenConList.cpp
layout/base/nsGenConList.h
--- a/layout/base/nsGenConList.cpp
+++ b/layout/base/nsGenConList.cpp
@@ -11,59 +11,59 @@
 #include "nsIContent.h"
 
 void
 nsGenConList::Clear()
 {
   //Delete entire list
   if (!mFirstNode)
     return;
+
+  mNodes.Clear();
   for (nsGenConNode *node = Next(mFirstNode); node != mFirstNode;
        node = Next(mFirstNode))
   {
     Destroy(node);
   }
   delete mFirstNode;
 
   mFirstNode = nullptr;
   mSize = 0;
 }
 
 bool
 nsGenConList::DestroyNodesFor(nsIFrame* aFrame)
 {
-  if (!mFirstNode)
-    return false; // list empty
-  nsGenConNode* node;
-  bool destroyed = false;
-  while (mFirstNode->mPseudoFrame == aFrame) {
-    destroyed = true;
-    node = Next(mFirstNode);
-    bool isLastNode = node == mFirstNode; // before they're dangling
-    Destroy(mFirstNode);
-    if (isLastNode) {
-      mFirstNode = nullptr;
-      return true;
-    }
-    else {
-      mFirstNode = node;
-    }
+  // This algorithm relies on the invariant that nodes of a frame are
+  // put contiguously in the linked list. This is guaranteed because
+  // each frame is mapped to only one (nsIContent, pseudoType) pair,
+  // and the nodes in the linked list are put in the tree order based
+  // on that pair and offset inside frame.
+  nsGenConNode* node = mNodes.GetAndRemove(aFrame).valueOr(nullptr);
+  if (!node) {
+    return false;
   }
-  node = Next(mFirstNode);
-  while (node != mFirstNode) {
-    if (node->mPseudoFrame == aFrame) {
-      destroyed = true;
-      nsGenConNode *nextNode = Next(node);
-      Destroy(node);
-      node = nextNode;
-    } else {
-      node = Next(node);
-    }
+  MOZ_ASSERT(node->mPseudoFrame == aFrame);
+  // Clear following nodes first as they must not be mFirstNode.
+  // If mFirstNode refers to a node of this frame, it should be the
+  // one retrieved from mNodes.
+  for (nsGenConNode* curNode = Next(node);
+       curNode->mPseudoFrame == aFrame && curNode != node;
+       /* curNode updated in loop body */) {
+    MOZ_ASSERT(curNode != mFirstNode);
+    nsGenConNode* nextNode = Next(curNode);
+    Destroy(curNode);
+    curNode = nextNode;
   }
-  return destroyed;
+  if (node == mFirstNode) {
+    nsGenConNode* nextNode = Next(mFirstNode);
+    mFirstNode = nextNode == mFirstNode ? nullptr : nextNode;
+  }
+  Destroy(node);
+  return true;
 }
 
 /**
  * Compute the type of the pseudo and the content for the pseudo that
  * we'll use for comparison purposes.
  * @param aContent the content to use is stored here; it's the element
  * that generated the ::before or ::after content, or (if not for generated
  * content), the frame's own element
@@ -110,17 +110,17 @@ nsGenConList::NodeAfter(const nsGenConNo
     if (content1 == content2) {
       NS_ASSERTION(pseudoType1 != pseudoType2, "identical");
       return pseudoType1 == 1;
     }
   }
   // XXX Switch to the frame version of DoCompareTreePosition?
   int32_t cmp = nsLayoutUtils::DoCompareTreePosition(content1, content2,
                                                      pseudoType1, -pseudoType2);
-  NS_ASSERTION(cmp != 0, "same content, different frames");
+  MOZ_ASSERT(cmp != 0, "same content, different frames");
   return cmp > 0;
 }
 
 void
 nsGenConList::Insert(nsGenConNode* aNode)
 {
   if (mFirstNode) {
     // Check for append.
@@ -165,13 +165,55 @@ nsGenConList::Insert(nsGenConNode* aNode
   }
   else {
     // initialize list with first node
     PR_INIT_CLIST(aNode);
     mFirstNode = aNode;
   }
   ++mSize;
 
+  // Set the mapping only if it is the first node of the frame.
+  // The DEBUG blocks below are for ensuring the invariant required by
+  // nsGenConList::DestroyNodesFor. See comment there.
+  if (aNode == mFirstNode ||
+      Prev(aNode)->mPseudoFrame != aNode->mPseudoFrame) {
+#ifdef DEBUG
+    if (nsGenConNode* oldFrameFirstNode = mNodes.Get(aNode->mPseudoFrame)) {
+      MOZ_ASSERT(Next(aNode) == oldFrameFirstNode,
+                 "oldFrameFirstNode should now be immediately after "
+                 "the newly-inserted one.");
+    } else {
+      // If the node is not the only node in the list.
+      nsGenConNode* nextNode = Next(aNode);
+      if (nextNode != aNode) {
+        MOZ_ASSERT(nextNode->mPseudoFrame != aNode->mPseudoFrame,
+                   "There shouldn't exist any node for this frame.");
+        // If the node is neither the first nor the last node
+        if (aNode != mFirstNode && nextNode != mFirstNode) {
+          MOZ_ASSERT(Prev(aNode)->mPseudoFrame != nextNode->mPseudoFrame,
+                     "New node should not break contiguity of nodes of "
+                     "the same frame.");
+        }
+      }
+    }
+#endif
+    mNodes.Put(aNode->mPseudoFrame, aNode);
+  } else {
+#ifdef DEBUG
+    nsGenConNode* frameFirstNode = mNodes.Get(aNode->mPseudoFrame);
+    MOZ_ASSERT(frameFirstNode, "There should exist node map for the frame.");
+    for (nsGenConNode* curNode = Prev(aNode);
+         curNode != frameFirstNode; curNode = Prev(curNode)) {
+      MOZ_ASSERT(curNode->mPseudoFrame == aNode->mPseudoFrame,
+                 "Every node between frameFirstNode and the new node inserted "
+                 "should refer to the same frame.");
+      MOZ_ASSERT(curNode != mFirstNode, "The newly-inserted node should be in "
+                 "a contiguous run after frameFirstNode, thus frameFirstNode "
+                 "should be reached before mFirstNode.");
+    }
+#endif
+  }
+
   NS_ASSERTION(aNode == mFirstNode || NodeAfter(aNode, Prev(aNode)),
                "sorting error");
   NS_ASSERTION(IsLast(aNode) || NodeAfter(Next(aNode), aNode),
                "sorting error");
 }
--- a/layout/base/nsGenConList.h
+++ b/layout/base/nsGenConList.h
@@ -105,11 +105,14 @@ public:
   bool IsLast(nsGenConNode* aNode) { return (Next(aNode) == mFirstNode); }
 private:
   void Destroy(nsGenConNode* aNode)
   {
     PR_REMOVE_LINK(aNode);
     delete aNode;
     mSize--;
   }
+
+  // Map from frame to the first nsGenConNode of it in the list.
+  nsDataHashtable<nsPtrHashKey<nsIFrame>, nsGenConNode*> mNodes;
 };
 
 #endif /* nsGenConList_h___ */