diff --git a/layout/base/nsCounterManager.cpp b/layout/base/nsCounterManager.cpp index 6adf34d06..90a0268cd 100644 --- a/layout/base/nsCounterManager.cpp +++ b/layout/base/nsCounterManager.cpp @@ -186,11 +186,7 @@ nsCounterList::RecalcAll() { mDirty = false; - nsCounterNode *node = First(); - if (!node) - return; - - do { + for (nsCounterNode* node = First(); node; node = Next(node)) { SetScope(node); node->Calc(this); @@ -205,7 +201,7 @@ nsCounterList::RecalcAll() useNode->mText->SetData(text); } } - } while ((node = Next(node)) != First()); + } } nsCounterManager::nsCounterManager() @@ -291,21 +287,17 @@ nsCounterManager::SetAllCounterStylesDirty() { for (auto iter = mNames.Iter(); !iter.Done(); iter.Next()) { nsCounterList* list = iter.UserData(); - nsCounterNode* first = list->First(); - if (first) { - bool changed = false; - nsCounterNode* node = first; - do { - if (node->mType == nsCounterNode::USE) { - node->UseNode()->SetCounterStyleDirty(); - changed = true; - } - } while ((node = list->Next(node)) != first); - - if (changed) { - list->SetDirty(); + bool changed = false; + for (nsCounterNode* node = list->First(); node; node = list->Next(node)) { + if (node->mType == nsCounterNode::USE) { + node->UseNode()->SetCounterStyleDirty(); + changed = true; } } + + if (changed) { + list->SetDirty(); + } } } @@ -327,32 +319,29 @@ nsCounterManager::DestroyNodesFor(nsIFrame *aFrame) void nsCounterManager::Dump() { - printf("\n\nCounter Manager Lists:\n"); - for (auto iter = mNames.Iter(); !iter.Done(); iter.Next()) { - printf("Counter named \"%s\":\n", - NS_ConvertUTF16toUTF8(iter.Key()).get()); + printf("\n\nCounter Manager Lists:\n"); + for (auto iter = mNames.Iter(); !iter.Done(); iter.Next()) { + printf("Counter named \"%s\":\n", + NS_ConvertUTF16toUTF8(iter.Key()).get()); - nsCounterList* list = iter.UserData(); - nsCounterNode* node = list->First(); - if (node) { - int32_t i = 0; - do { - const char* types[] = { "RESET", "INCREMENT", "USE" }; - printf(" Node #%d @%p frame=%p index=%d type=%s valAfter=%d\n" - " scope-start=%p scope-prev=%p", - i++, (void*)node, (void*)node->mPseudoFrame, - node->mContentIndex, types[node->mType], - node->mValueAfter, (void*)node->mScopeStart, - (void*)node->mScopePrev); - if (node->mType == nsCounterNode::USE) { - nsAutoString text; - node->UseNode()->GetText(text); - printf(" text=%s", NS_ConvertUTF16toUTF8(text).get()); - } - printf("\n"); - } while ((node = list->Next(node)) != list->First()); - } + nsCounterList* list = iter.UserData(); + int32_t i = 0; + for (nsCounterNode* node = list->First(); node; node = list->Next(node)) { + const char* types[] = { "RESET", "INCREMENT", "USE" }; + printf(" Node #%d @%p frame=%p index=%d type=%s valAfter=%d\n" + " scope-start=%p scope-prev=%p", + i++, (void*)node, (void*)node->mPseudoFrame, + node->mContentIndex, types[node->mType], + node->mValueAfter, (void*)node->mScopeStart, + (void*)node->mScopePrev); + if (node->mType == nsCounterNode::USE) { + nsAutoString text; + node->UseNode()->GetText(text); + printf(" text=%s", NS_ConvertUTF16toUTF8(text).get()); + } + printf("\n"); } - printf("\n\n"); + } + printf("\n\n"); } #endif diff --git a/layout/base/nsCounterManager.h b/layout/base/nsCounterManager.h index 2eb0a8c03..e8ace6368 100644 --- a/layout/base/nsCounterManager.h +++ b/layout/base/nsCounterManager.h @@ -187,7 +187,7 @@ public: } nsCounterNode* First() { - return static_cast(mFirstNode); + return static_cast(mList.getFirst()); } static nsCounterNode* Next(nsCounterNode* aNode) { diff --git a/layout/base/nsGenConList.cpp b/layout/base/nsGenConList.cpp index 06425bbb6..1842a1041 100644 --- a/layout/base/nsGenConList.cpp +++ b/layout/base/nsGenConList.cpp @@ -13,55 +13,39 @@ void nsGenConList::Clear() { - //Delete entire list - if (!mFirstNode) - return; - for (nsGenConNode *node = Next(mFirstNode); node != mFirstNode; - node = Next(mFirstNode)) - { - Remove(node); + // Delete entire list. + mNodes.Clear(); + while (nsGenConNode* node = mList.popFirst()) { delete node; } - delete mFirstNode; - - mFirstNode = nullptr; mSize = 0; + mLastInserted = nullptr; } 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 - Remove(mFirstNode); - delete 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); - Remove(node); - delete node; - node = nextNode; - } else { - node = Next(node); - } + MOZ_ASSERT(node->mPseudoFrame == aFrame); + + while (node && node->mPseudoFrame == aFrame) { + nsGenConNode* nextNode = Next(node); + Destroy(node); + node = nextNode; } - return destroyed; + + // Modification of the list invalidates the cached pointer. + mLastInserted = nullptr; + + return true; } /** @@ -118,62 +102,101 @@ nsGenConList::NodeAfter(const nsGenConNode* aNode1, const nsGenConNode* aNode2) // 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. - if (NodeAfter(aNode, Prev(mFirstNode))) { - PR_INSERT_BEFORE(aNode, mFirstNode); - } - else { - // Binary search. + // Check for append. + if (mList.isEmpty() || NodeAfter(aNode, mList.getLast())) { + mList.insertBack(aNode); + } else if (mLastInserted && mLastInserted != mList.getLast() && + NodeAfter(aNode, mLastInserted) && + NodeAfter(Next(mLastInserted), aNode)) { + // Fast path for inserting many consecutive nodes in one place + mLastInserted->setNext(aNode); + } else { + // Binary search. - // the range of indices at which |aNode| could end up. - // (We already know it can't be at index mSize.) - uint32_t first = 0, last = mSize - 1; + // the range of indices at which |aNode| could end up. + // (We already know it can't be at index mSize.) + uint32_t first = 0, last = mSize - 1; - // A cursor to avoid walking more than the length of the list. - nsGenConNode *curNode = Prev(mFirstNode); - uint32_t curIndex = mSize - 1; + // A cursor to avoid walking more than the length of the list. + nsGenConNode* curNode = mList.getLast(); + uint32_t curIndex = mSize - 1; - while (first != last) { - uint32_t test = (first + last) / 2; - if (last == curIndex) { - for ( ; curIndex != test; --curIndex) - curNode = Prev(curNode); - } else { - for ( ; curIndex != test; ++curIndex) - curNode = Next(curNode); - } - - if (NodeAfter(aNode, curNode)) { - first = test + 1; - // if we exit the loop, we need curNode to be right - ++curIndex; + while (first != last) { + uint32_t test = (first + last) / 2; + if (last == curIndex) { + for ( ; curIndex != test; --curIndex) + curNode = Prev(curNode); + } else { + for ( ; curIndex != test; ++curIndex) curNode = Next(curNode); - } else { - last = test; - } } - PR_INSERT_BEFORE(aNode, curNode); - if (curNode == mFirstNode) { - mFirstNode = aNode; + + if (NodeAfter(aNode, curNode)) { + first = test + 1; + // if we exit the loop, we need curNode to be right + ++curIndex; + curNode = Next(curNode); + } else { + last = test; } } - } - else { - // initialize list with first node - PR_INIT_CLIST(aNode); - mFirstNode = aNode; + curNode->setPrevious(aNode); } ++mSize; - NS_ASSERTION(aNode == mFirstNode || NodeAfter(aNode, Prev(aNode)), + mLastInserted = aNode; + + // 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 (IsFirst(aNode) || + 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. + if (!IsFirst(aNode) || !IsLast(aNode)) { + nsGenConNode* nextNode = Next(aNode); + MOZ_ASSERT(!nextNode || 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 (!IsFirst(aNode) && !IsLast(aNode)) { + 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(!IsFirst(curNode), + "The newly-inserted node should be in a contiguous run after " + "frameFirstNode, thus frameFirstNode should be reached before " + "the first node of mList."); + } +#endif + } + + NS_ASSERTION(IsFirst(aNode) || NodeAfter(aNode, Prev(aNode)), "sorting error"); NS_ASSERTION(IsLast(aNode) || NodeAfter(Next(aNode), aNode), "sorting error"); diff --git a/layout/base/nsGenConList.h b/layout/base/nsGenConList.h index 2d0ad9f3e..58421d430 100644 --- a/layout/base/nsGenConList.h +++ b/layout/base/nsGenConList.h @@ -8,15 +8,15 @@ #ifndef nsGenConList_h___ #define nsGenConList_h___ +#include "mozilla/LinkedList.h" #include "nsIFrame.h" #include "nsStyleStruct.h" -#include "prclist.h" #include "nsCSSPseudoElements.h" #include "nsTextNode.h" class nsGenConList; -struct nsGenConNode : public PRCList { +struct nsGenConNode : public mozilla::LinkedListElement { // The wrapper frame for all of the pseudo-element's content. This // frame generally has useful style data and has the // NS_FRAME_GENERATED_CONTENT bit set (so we use it to track removal), @@ -51,7 +51,7 @@ struct nsGenConNode : public PRCList { * @return true iff this marked the list dirty */ virtual bool InitTextFrame(nsGenConList* aList, nsIFrame* aPseudoFrame, - nsIFrame* aTextFrame) + nsIFrame* aTextFrame) { mPseudoFrame = aPseudoFrame; CheckFrameAssertions(); @@ -82,28 +82,55 @@ protected: class nsGenConList { protected: - nsGenConNode* mFirstNode; + mozilla::LinkedList mList; uint32_t mSize; + public: - nsGenConList() : mFirstNode(nullptr), mSize(0) {} + nsGenConList() : mSize(0), mLastInserted(nullptr) {} ~nsGenConList() { Clear(); } void Clear(); static nsGenConNode* Next(nsGenConNode* aNode) { - return static_cast(PR_NEXT_LINK(aNode)); + MOZ_ASSERT(aNode, "aNode cannot be nullptr!"); + return aNode->getNext(); } static nsGenConNode* Prev(nsGenConNode* aNode) { - return static_cast(PR_PREV_LINK(aNode)); + MOZ_ASSERT(aNode, "aNode cannot be nullptr!"); + return aNode->getPrevious(); } void Insert(nsGenConNode* aNode); - // returns whether any nodes have been destroyed - bool DestroyNodesFor(nsIFrame* aFrame); //destroy all nodes with aFrame as parent + + // Destroy all nodes with aFrame as parent. Returns true if some nodes + // have been destroyed; otherwise false. + bool DestroyNodesFor(nsIFrame* aFrame); // Return true if |aNode1| is after |aNode2|. static bool NodeAfter(const nsGenConNode* aNode1, - const nsGenConNode* aNode2); + const nsGenConNode* aNode2); - void Remove(nsGenConNode* aNode) { PR_REMOVE_LINK(aNode); mSize--; } - bool IsLast(nsGenConNode* aNode) { return (Next(aNode) == mFirstNode); } + bool IsFirst(nsGenConNode* aNode) { + MOZ_ASSERT(aNode, "aNode cannot be nullptr!"); + return aNode == mList.getFirst(); + } + + bool IsLast(nsGenConNode* aNode) { + MOZ_ASSERT(aNode, "aNode cannot be nullptr!"); + return aNode == mList.getLast(); + } + +private: + void Destroy(nsGenConNode* aNode) + { + MOZ_ASSERT(aNode, "aNode cannot be nullptr!"); + delete aNode; + mSize--; + } + + // Map from frame to the first nsGenConNode of it in the list. + nsDataHashtable, nsGenConNode*> mNodes; + + // A weak pointer to the node most recently inserted, used to avoid repeated + // list traversals in Insert(). + nsGenConNode* mLastInserted; }; #endif /* nsGenConList_h___ */ diff --git a/layout/base/nsQuoteList.cpp b/layout/base/nsQuoteList.cpp index e11d72c60..0441fe485 100644 --- a/layout/base/nsQuoteList.cpp +++ b/layout/base/nsQuoteList.cpp @@ -73,20 +73,13 @@ nsQuoteList::Calc(nsQuoteNode* aNode) void nsQuoteList::RecalcAll() { - nsQuoteNode *node = FirstNode(); - if (!node) - return; - - do { + for (nsQuoteNode* node = FirstNode(); node; node = Next(node)) { int32_t oldDepth = node->mDepthBefore; Calc(node); if (node->mDepthBefore != oldDepth && node->mText && node->IsRealQuote()) node->mText->SetData(*node->Text()); - - // Next node - node = Next(node); - } while (node != FirstNode()); + } } #ifdef DEBUG @@ -94,11 +87,7 @@ void nsQuoteList::PrintChain() { printf("Chain: \n"); - if (!FirstNode()) { - return; - } - nsQuoteNode* node = FirstNode(); - do { + for (nsQuoteNode* node = FirstNode(); node; node = Next(node)) { printf(" %p %d - ", static_cast(node), node->mDepthBefore); switch(node->mType) { case (eStyleContentType_OpenQuote): @@ -123,7 +112,6 @@ nsQuoteList::PrintChain() printf(" \"%s\",", NS_ConvertUTF16toUTF8(data).get()); } printf("\n"); - node = Next(node); - } while (node != FirstNode()); + } } #endif diff --git a/layout/base/nsQuoteList.h b/layout/base/nsQuoteList.h index 12afc3928..19aa791ce 100644 --- a/layout/base/nsQuoteList.h +++ b/layout/base/nsQuoteList.h @@ -70,7 +70,7 @@ struct nsQuoteNode : public nsGenConNode { class nsQuoteList : public nsGenConList { private: - nsQuoteNode* FirstNode() { return static_cast(mFirstNode); } + nsQuoteNode* FirstNode() { return static_cast(mList.getFirst()); } public: // assign the correct |mDepthBefore| value to a node that has been inserted // Should be called immediately after calling |Insert|. diff --git a/xpcom/glue/nsDataHashtable.h b/xpcom/glue/nsDataHashtable.h index 18aa210b7..19c0728b4 100644 --- a/xpcom/glue/nsDataHashtable.h +++ b/xpcom/glue/nsDataHashtable.h @@ -9,6 +9,7 @@ #include "nsHashKeys.h" #include "nsBaseHashtable.h" +#include "mozilla/Maybe.h" /** * templated hashtable class maps keys to simple datatypes. @@ -22,12 +23,36 @@ template class nsDataHashtable : public nsBaseHashtable { +private: + typedef nsBaseHashtable BaseClass; + public: + using typename BaseClass::KeyType; + using typename BaseClass::EntryType; + nsDataHashtable() {} explicit nsDataHashtable(uint32_t aInitLength) - : nsBaseHashtable(aInitLength) + : BaseClass(aInitLength) { } + + /** + * Retrieve the value for a key and remove the corresponding entry at + * the same time. + * + * @param aKey the key to retrieve and remove + * @return the found value, or Nothing if no entry was found with the + * given key. + */ + mozilla::Maybe GetAndRemove(KeyType aKey) + { + mozilla::Maybe value; + if (EntryType* ent = this->GetEntry(aKey)) { + value.emplace(mozilla::Move(ent->mData)); + this->RemoveEntry(ent); + } + return value; + } }; #endif // nsDataHashtable_h__