diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /layout/base/nsFrameManager.cpp | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | uxp-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz |
Add m-esr52 at 52.6.0
Diffstat (limited to 'layout/base/nsFrameManager.cpp')
-rw-r--r-- | layout/base/nsFrameManager.cpp | 835 |
1 files changed, 835 insertions, 0 deletions
diff --git a/layout/base/nsFrameManager.cpp b/layout/base/nsFrameManager.cpp new file mode 100644 index 0000000000..97b022da8b --- /dev/null +++ b/layout/base/nsFrameManager.cpp @@ -0,0 +1,835 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim:cindent:ts=2:et:sw=2: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This Original Code has been modified by IBM Corporation. Modifications made by IBM + * described herein are Copyright (c) International Business Machines Corporation, 2000. + * Modifications to Mozilla code or documentation identified per MPL Section 3.3 + * + * Date Modified by Description of modification + * 04/20/2000 IBM Corp. OS/2 VisualAge build. + */ + +/* storage of the frame tree and information about it */ + +#include "nscore.h" +#include "nsIPresShell.h" +#include "nsStyleContext.h" +#include "nsCOMPtr.h" +#include "plhash.h" +#include "nsPlaceholderFrame.h" +#include "nsGkAtoms.h" +#include "nsILayoutHistoryState.h" +#include "nsPresState.h" +#include "mozilla/dom/Element.h" +#include "nsIDocument.h" + +#include "nsContentUtils.h" +#include "nsError.h" +#include "nsAutoPtr.h" +#include "nsAbsoluteContainingBlock.h" +#include "ChildIterator.h" + +#include "nsFrameManager.h" +#include "GeckoProfiler.h" +#include "nsIStatefulFrame.h" +#include "nsContainerFrame.h" + + #ifdef DEBUG + //#define DEBUG_UNDISPLAYED_MAP + //#define DEBUG_DISPLAY_CONTENTS_MAP + #else + #undef DEBUG_UNDISPLAYED_MAP + #undef DEBUG_DISPLAY_CONTENTS_MAP + #endif + +using namespace mozilla; +using namespace mozilla::dom; + +//---------------------------------------------------------------------- + +struct PlaceholderMapEntry : public PLDHashEntryHdr { + // key (the out of flow frame) can be obtained through placeholder frame + nsPlaceholderFrame *placeholderFrame; +}; + +static bool +PlaceholderMapMatchEntry(const PLDHashEntryHdr *hdr, const void *key) +{ + const PlaceholderMapEntry *entry = + static_cast<const PlaceholderMapEntry*>(hdr); + NS_ASSERTION(entry->placeholderFrame->GetOutOfFlowFrame() != + (void*)0xdddddddd, + "Dead placeholder in placeholder map"); + return entry->placeholderFrame->GetOutOfFlowFrame() == key; +} + +static const PLDHashTableOps PlaceholderMapOps = { + PLDHashTable::HashVoidPtrKeyStub, + PlaceholderMapMatchEntry, + PLDHashTable::MoveEntryStub, + PLDHashTable::ClearEntryStub, + nullptr +}; + +nsFrameManagerBase::nsFrameManagerBase() + : mPresShell(nullptr) + , mRootFrame(nullptr) + , mPlaceholderMap(&PlaceholderMapOps, sizeof(PlaceholderMapEntry)) + , mUndisplayedMap(nullptr) + , mDisplayContentsMap(nullptr) + , mIsDestroyingFrames(false) +{ +} + +//---------------------------------------------------------------------- + +// XXXldb This seems too complicated for what I think it's doing, and it +// should also be using PLDHashTable rather than plhash to use less memory. + +class nsFrameManagerBase::UndisplayedMap { +public: + explicit UndisplayedMap(uint32_t aNumBuckets = 16); + ~UndisplayedMap(void); + + UndisplayedNode* GetFirstNode(nsIContent* aParentContent); + + nsresult AddNodeFor(nsIContent* aParentContent, + nsIContent* aChild, nsStyleContext* aStyle); + + void RemoveNodeFor(nsIContent* aParentContent, + UndisplayedNode* aNode); + + void RemoveNodesFor(nsIContent* aParentContent); + UndisplayedNode* UnlinkNodesFor(nsIContent* aParentContent); + + // Removes all entries from the hash table + void Clear(void); + +protected: + /** + * Gets the entry for the provided parent content. If the content + * is a <xbl:children> element, |**aParentContent| is set to + * the parent of the children element. + */ + PLHashEntry** GetEntryFor(nsIContent** aParentContent); + void AppendNodeFor(UndisplayedNode* aNode, + nsIContent* aParentContent); + + PLHashTable* mTable; + PLHashEntry** mLastLookup; +}; + +//---------------------------------------------------------------------- + +nsFrameManager::~nsFrameManager() +{ + NS_ASSERTION(!mPresShell, "nsFrameManager::Destroy never called"); +} + +void +nsFrameManager::Destroy() +{ + NS_ASSERTION(mPresShell, "Frame manager already shut down."); + + // Destroy the frame hierarchy. + mPresShell->SetIgnoreFrameDestruction(true); + + // Unregister all placeholders before tearing down the frame tree + nsFrameManager::ClearPlaceholderFrameMap(); + + if (mRootFrame) { + mRootFrame->Destroy(); + mRootFrame = nullptr; + } + + delete mUndisplayedMap; + mUndisplayedMap = nullptr; + delete mDisplayContentsMap; + mDisplayContentsMap = nullptr; + + mPresShell = nullptr; +} + +//---------------------------------------------------------------------- + +// Placeholder frame functions +nsPlaceholderFrame* +nsFrameManager::GetPlaceholderFrameFor(const nsIFrame* aFrame) +{ + NS_PRECONDITION(aFrame, "null param unexpected"); + + auto entry = static_cast<PlaceholderMapEntry*> + (const_cast<PLDHashTable*>(&mPlaceholderMap)->Search(aFrame)); + if (entry) { + return entry->placeholderFrame; + } + + return nullptr; +} + +void +nsFrameManager::RegisterPlaceholderFrame(nsPlaceholderFrame* aPlaceholderFrame) +{ + MOZ_ASSERT(aPlaceholderFrame, "null param unexpected"); + MOZ_ASSERT(nsGkAtoms::placeholderFrame == aPlaceholderFrame->GetType(), + "unexpected frame type"); + auto entry = static_cast<PlaceholderMapEntry*> + (mPlaceholderMap.Add(aPlaceholderFrame->GetOutOfFlowFrame())); + MOZ_ASSERT(!entry->placeholderFrame, + "Registering a placeholder for a frame that already has a placeholder!"); + entry->placeholderFrame = aPlaceholderFrame; +} + +void +nsFrameManager::UnregisterPlaceholderFrame(nsPlaceholderFrame* aPlaceholderFrame) +{ + NS_PRECONDITION(aPlaceholderFrame, "null param unexpected"); + NS_PRECONDITION(nsGkAtoms::placeholderFrame == aPlaceholderFrame->GetType(), + "unexpected frame type"); + + mPlaceholderMap.Remove(aPlaceholderFrame->GetOutOfFlowFrame()); +} + +void +nsFrameManager::ClearPlaceholderFrameMap() +{ + for (auto iter = mPlaceholderMap.Iter(); !iter.Done(); iter.Next()) { + auto entry = static_cast<PlaceholderMapEntry*>(iter.Get()); + entry->placeholderFrame->SetOutOfFlowFrame(nullptr); + } + mPlaceholderMap.Clear(); +} + +//---------------------------------------------------------------------- + +/* static */ nsStyleContext* +nsFrameManager::GetStyleContextInMap(UndisplayedMap* aMap, nsIContent* aContent) +{ + if (!aContent) { + return nullptr; + } + nsIContent* parent = aContent->GetParentElementCrossingShadowRoot(); + MOZ_ASSERT(parent || !aContent->GetParent(), "no non-elements"); + for (UndisplayedNode* node = aMap->GetFirstNode(parent); + node; node = node->mNext) { + if (node->mContent == aContent) + return node->mStyle; + } + + return nullptr; +} + +/* static */ UndisplayedNode* +nsFrameManager::GetAllUndisplayedNodesInMapFor(UndisplayedMap* aMap, + nsIContent* aParentContent) +{ + return aMap ? aMap->GetFirstNode(aParentContent) : nullptr; +} + +UndisplayedNode* +nsFrameManager::GetAllUndisplayedContentIn(nsIContent* aParentContent) +{ + return GetAllUndisplayedNodesInMapFor(mUndisplayedMap, aParentContent); +} + +/* static */ void +nsFrameManager::SetStyleContextInMap(UndisplayedMap* aMap, + nsIContent* aContent, + nsStyleContext* aStyleContext) +{ + NS_PRECONDITION(!aStyleContext->GetPseudo(), + "Should only have actual elements here"); + +#if defined(DEBUG_UNDISPLAYED_MAP) || defined(DEBUG_DISPLAY_BOX_CONTENTS_MAP) + static int i = 0; + printf("SetStyleContextInMap(%d): p=%p \n", i++, (void *)aContent); +#endif + + NS_ASSERTION(!GetStyleContextInMap(aMap, aContent), + "Already have an entry for aContent"); + + nsIContent* parent = aContent->GetParentElementCrossingShadowRoot(); + MOZ_ASSERT(parent || !aContent->GetParent(), "no non-elements"); +#ifdef DEBUG + nsIPresShell* shell = aStyleContext->PresContext()->PresShell(); + NS_ASSERTION(parent || (shell && shell->GetDocument() && + shell->GetDocument()->GetRootElement() == aContent), + "undisplayed content must have a parent, unless it's the root " + "element"); +#endif + aMap->AddNodeFor(parent, aContent, aStyleContext); +} + +void +nsFrameManager::SetUndisplayedContent(nsIContent* aContent, + nsStyleContext* aStyleContext) +{ + if (!mUndisplayedMap) { + mUndisplayedMap = new UndisplayedMap; + } + SetStyleContextInMap(mUndisplayedMap, aContent, aStyleContext); +} + +/* static */ void +nsFrameManager::ChangeStyleContextInMap(UndisplayedMap* aMap, + nsIContent* aContent, + nsStyleContext* aStyleContext) +{ + MOZ_ASSERT(aMap, "expecting a map"); + +#if defined(DEBUG_UNDISPLAYED_MAP) || defined(DEBUG_DISPLAY_BOX_CONTENTS_MAP) + static int i = 0; + printf("ChangeStyleContextInMap(%d): p=%p \n", i++, (void *)aContent); +#endif + + for (UndisplayedNode* node = aMap->GetFirstNode(aContent->GetParent()); + node; node = node->mNext) { + if (node->mContent == aContent) { + node->mStyle = aStyleContext; + return; + } + } + + MOZ_CRASH("couldn't find the entry to change"); +} + +void +nsFrameManager::ClearUndisplayedContentIn(nsIContent* aContent, + nsIContent* aParentContent) +{ +#ifdef DEBUG_UNDISPLAYED_MAP + static int i = 0; + printf("ClearUndisplayedContent(%d): content=%p parent=%p --> ", i++, (void *)aContent, (void*)aParentContent); +#endif + + if (mUndisplayedMap) { + UndisplayedNode* node = mUndisplayedMap->GetFirstNode(aParentContent); + while (node) { + if (node->mContent == aContent) { + mUndisplayedMap->RemoveNodeFor(aParentContent, node); + +#ifdef DEBUG_UNDISPLAYED_MAP + printf( "REMOVED!\n"); +#endif +#ifdef DEBUG + // make sure that there are no more entries for the same content + nsStyleContext *context = GetUndisplayedContent(aContent); + NS_ASSERTION(context == nullptr, "Found more undisplayed content data after removal"); +#endif + return; + } + node = node->mNext; + } + } +#ifdef DEBUG_UNDISPLAYED_MAP + printf( "not found.\n"); +#endif +} + +void +nsFrameManager::ClearAllUndisplayedContentIn(nsIContent* aParentContent) +{ +#ifdef DEBUG_UNDISPLAYED_MAP + static int i = 0; + printf("ClearAllUndisplayedContentIn(%d): parent=%p \n", i++, (void*)aParentContent); +#endif + + if (mUndisplayedMap) { + mUndisplayedMap->RemoveNodesFor(aParentContent); + } + + // Need to look at aParentContent's content list due to XBL insertions. + // Nodes in aParentContent's content list do not have aParentContent as a + // parent, but are treated as children of aParentContent. We iterate over + // the flattened content list and just ignore any nodes we don't care about. + FlattenedChildIterator iter(aParentContent); + for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) { + if (child->GetParent() != aParentContent) { + ClearUndisplayedContentIn(child, child->GetParent()); + } + } +} + +//---------------------------------------------------------------------- + +void +nsFrameManager::SetDisplayContents(nsIContent* aContent, + nsStyleContext* aStyleContext) +{ + if (!mDisplayContentsMap) { + mDisplayContentsMap = new UndisplayedMap; + } + SetStyleContextInMap(mDisplayContentsMap, aContent, aStyleContext); +} + +UndisplayedNode* +nsFrameManager::GetAllDisplayContentsIn(nsIContent* aParentContent) +{ + return GetAllUndisplayedNodesInMapFor(mDisplayContentsMap, aParentContent); +} + +void +nsFrameManager::ClearDisplayContentsIn(nsIContent* aContent, + nsIContent* aParentContent) +{ +#ifdef DEBUG_DISPLAY_CONTENTS_MAP + static int i = 0; + printf("ClearDisplayContents(%d): content=%p parent=%p --> ", i++, (void *)aContent, (void*)aParentContent); +#endif + + if (mDisplayContentsMap) { + UndisplayedNode* node = mDisplayContentsMap->GetFirstNode(aParentContent); + while (node) { + if (node->mContent == aContent) { + mDisplayContentsMap->RemoveNodeFor(aParentContent, node); + +#ifdef DEBUG_DISPLAY_CONTENTS_MAP + printf( "REMOVED!\n"); +#endif +#ifdef DEBUG + // make sure that there are no more entries for the same content + nsStyleContext* context = GetDisplayContentsStyleFor(aContent); + NS_ASSERTION(context == nullptr, "Found more entries for aContent after removal"); +#endif + ClearAllDisplayContentsIn(aContent); + ClearAllUndisplayedContentIn(aContent); + return; + } + node = node->mNext; + } + } +#ifdef DEBUG_DISPLAY_CONTENTS_MAP + printf( "not found.\n"); +#endif +} + +void +nsFrameManager::ClearAllDisplayContentsIn(nsIContent* aParentContent) +{ +#ifdef DEBUG_DISPLAY_CONTENTS_MAP + static int i = 0; + printf("ClearAllDisplayContentsIn(%d): parent=%p \n", i++, (void*)aParentContent); +#endif + + if (mDisplayContentsMap) { + UndisplayedNode* cur = mDisplayContentsMap->UnlinkNodesFor(aParentContent); + while (cur) { + UndisplayedNode* next = cur->mNext; + cur->mNext = nullptr; + ClearAllDisplayContentsIn(cur->mContent); + ClearAllUndisplayedContentIn(cur->mContent); + delete cur; + cur = next; + } + } + + // Need to look at aParentContent's content list due to XBL insertions. + // Nodes in aParentContent's content list do not have aParentContent as a + // parent, but are treated as children of aParentContent. We iterate over + // the flattened content list and just ignore any nodes we don't care about. + FlattenedChildIterator iter(aParentContent); + for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) { + if (child->GetParent() != aParentContent) { + ClearDisplayContentsIn(child, child->GetParent()); + ClearUndisplayedContentIn(child, child->GetParent()); + } + } +} + +//---------------------------------------------------------------------- +void +nsFrameManager::AppendFrames(nsContainerFrame* aParentFrame, + ChildListID aListID, + nsFrameList& aFrameList) +{ + if (aParentFrame->IsAbsoluteContainer() && + aListID == aParentFrame->GetAbsoluteListID()) { + aParentFrame->GetAbsoluteContainingBlock()-> + AppendFrames(aParentFrame, aListID, aFrameList); + } else { + aParentFrame->AppendFrames(aListID, aFrameList); + } +} + +void +nsFrameManager::InsertFrames(nsContainerFrame* aParentFrame, + ChildListID aListID, + nsIFrame* aPrevFrame, + nsFrameList& aFrameList) +{ + NS_PRECONDITION(!aPrevFrame || (!aPrevFrame->GetNextContinuation() + || (((aPrevFrame->GetNextContinuation()->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) + && !(aPrevFrame->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER))), + "aPrevFrame must be the last continuation in its chain!"); + + if (aParentFrame->IsAbsoluteContainer() && + aListID == aParentFrame->GetAbsoluteListID()) { + aParentFrame->GetAbsoluteContainingBlock()-> + InsertFrames(aParentFrame, aListID, aPrevFrame, aFrameList); + } else { + aParentFrame->InsertFrames(aListID, aPrevFrame, aFrameList); + } +} + +void +nsFrameManager::RemoveFrame(ChildListID aListID, + nsIFrame* aOldFrame) +{ + bool wasDestroyingFrames = mIsDestroyingFrames; + mIsDestroyingFrames = true; + + // In case the reflow doesn't invalidate anything since it just leaves + // a gap where the old frame was, we invalidate it here. (This is + // reasonably likely to happen when removing a last child in a way + // that doesn't change the size of the parent.) + // This has to sure to invalidate the entire overflow rect; this + // is important in the presence of absolute positioning + aOldFrame->InvalidateFrameForRemoval(); + + NS_ASSERTION(!aOldFrame->GetPrevContinuation() || + // exception for nsCSSFrameConstructor::RemoveFloatingFirstLetterFrames + aOldFrame->GetType() == nsGkAtoms::textFrame, + "Must remove first continuation."); + NS_ASSERTION(!(aOldFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW && + GetPlaceholderFrameFor(aOldFrame)), + "Must call RemoveFrame on placeholder for out-of-flows."); + nsContainerFrame* parentFrame = aOldFrame->GetParent(); + if (parentFrame->IsAbsoluteContainer() && + aListID == parentFrame->GetAbsoluteListID()) { + parentFrame->GetAbsoluteContainingBlock()-> + RemoveFrame(parentFrame, aListID, aOldFrame); + } else { + parentFrame->RemoveFrame(aListID, aOldFrame); + } + + mIsDestroyingFrames = wasDestroyingFrames; +} + +//---------------------------------------------------------------------- + +void +nsFrameManager::NotifyDestroyingFrame(nsIFrame* aFrame) +{ + nsIContent* content = aFrame->GetContent(); + if (content && content->GetPrimaryFrame() == aFrame) { + ClearAllUndisplayedContentIn(content); + ClearAllDisplayContentsIn(content); + } +} + +// Capture state for a given frame. +// Accept a content id here, in some cases we may not have content (scroll position) +void +nsFrameManager::CaptureFrameStateFor(nsIFrame* aFrame, + nsILayoutHistoryState* aState) +{ + if (!aFrame || !aState) { + NS_WARNING("null frame, or state"); + return; + } + + // Only capture state for stateful frames + nsIStatefulFrame* statefulFrame = do_QueryFrame(aFrame); + if (!statefulFrame) { + return; + } + + // Capture the state, exit early if we get null (nothing to save) + nsAutoPtr<nsPresState> frameState; + nsresult rv = statefulFrame->SaveState(getter_Transfers(frameState)); + if (!frameState) { + return; + } + + // Generate the hash key to store the state under + // Exit early if we get empty key + nsAutoCString stateKey; + nsIContent* content = aFrame->GetContent(); + nsIDocument* doc = content ? content->GetUncomposedDoc() : nullptr; + rv = statefulFrame->GenerateStateKey(content, doc, stateKey); + if(NS_FAILED(rv) || stateKey.IsEmpty()) { + return; + } + + // Store the state. aState owns frameState now. + aState->AddState(stateKey, frameState.forget()); +} + +void +nsFrameManager::CaptureFrameState(nsIFrame* aFrame, + nsILayoutHistoryState* aState) +{ + NS_PRECONDITION(nullptr != aFrame && nullptr != aState, "null parameters passed in"); + + CaptureFrameStateFor(aFrame, aState); + + // Now capture state recursively for the frame hierarchy rooted at aFrame + nsIFrame::ChildListIterator lists(aFrame); + for (; !lists.IsDone(); lists.Next()) { + nsFrameList::Enumerator childFrames(lists.CurrentList()); + for (; !childFrames.AtEnd(); childFrames.Next()) { + nsIFrame* child = childFrames.get(); + if (child->GetStateBits() & NS_FRAME_OUT_OF_FLOW) { + // We'll pick it up when we get to its placeholder + continue; + } + // Make sure to walk through placeholders as needed, so that we + // save state for out-of-flows which may not be our descendants + // themselves but whose placeholders are our descendants. + CaptureFrameState(nsPlaceholderFrame::GetRealFrameFor(child), aState); + } + } +} + +// Restore state for a given frame. +// Accept a content id here, in some cases we may not have content (scroll position) +void +nsFrameManager::RestoreFrameStateFor(nsIFrame* aFrame, + nsILayoutHistoryState* aState) +{ + if (!aFrame || !aState) { + NS_WARNING("null frame or state"); + return; + } + + // Only restore state for stateful frames + nsIStatefulFrame* statefulFrame = do_QueryFrame(aFrame); + if (!statefulFrame) { + return; + } + + // Generate the hash key the state was stored under + // Exit early if we get empty key + nsIContent* content = aFrame->GetContent(); + // If we don't have content, we can't generate a hash + // key and there's probably no state information for us. + if (!content) { + return; + } + + nsAutoCString stateKey; + nsIDocument* doc = content->GetUncomposedDoc(); + nsresult rv = statefulFrame->GenerateStateKey(content, doc, stateKey); + if (NS_FAILED(rv) || stateKey.IsEmpty()) { + return; + } + + // Get the state from the hash + nsPresState* frameState = aState->GetState(stateKey); + if (!frameState) { + return; + } + + // Restore it + rv = statefulFrame->RestoreState(frameState); + if (NS_FAILED(rv)) { + return; + } + + // If we restore ok, remove the state from the state table + aState->RemoveState(stateKey); +} + +void +nsFrameManager::RestoreFrameState(nsIFrame* aFrame, + nsILayoutHistoryState* aState) +{ + NS_PRECONDITION(nullptr != aFrame && nullptr != aState, "null parameters passed in"); + + RestoreFrameStateFor(aFrame, aState); + + // Now restore state recursively for the frame hierarchy rooted at aFrame + nsIFrame::ChildListIterator lists(aFrame); + for (; !lists.IsDone(); lists.Next()) { + nsFrameList::Enumerator childFrames(lists.CurrentList()); + for (; !childFrames.AtEnd(); childFrames.Next()) { + RestoreFrameState(childFrames.get(), aState); + } + } +} + +//---------------------------------------------------------------------- + +static PLHashNumber +HashKey(void* key) +{ + return NS_PTR_TO_INT32(key); +} + +static int +CompareKeys(void* key1, void* key2) +{ + return key1 == key2; +} + +//---------------------------------------------------------------------- + +nsFrameManagerBase::UndisplayedMap::UndisplayedMap(uint32_t aNumBuckets) +{ + MOZ_COUNT_CTOR(nsFrameManagerBase::UndisplayedMap); + mTable = PL_NewHashTable(aNumBuckets, (PLHashFunction)HashKey, + (PLHashComparator)CompareKeys, + (PLHashComparator)nullptr, + nullptr, nullptr); + mLastLookup = nullptr; +} + +nsFrameManagerBase::UndisplayedMap::~UndisplayedMap(void) +{ + MOZ_COUNT_DTOR(nsFrameManagerBase::UndisplayedMap); + Clear(); + PL_HashTableDestroy(mTable); +} + +PLHashEntry** +nsFrameManagerBase::UndisplayedMap::GetEntryFor(nsIContent** aParentContent) +{ + nsIContent* parentContent = *aParentContent; + + if (mLastLookup && (parentContent == (*mLastLookup)->key)) { + return mLastLookup; + } + + // In the case of XBL default content, <xbl:children> elements do not get a + // frame causing a mismatch between the content tree and the frame tree. + // |GetEntryFor| is sometimes called with the content tree parent (which may + // be a <xbl:children> element) but the parent in the frame tree would be the + // insertion parent (parent of the <xbl:children> element). Here the children + // elements are normalized to the insertion parent to correct for the mismatch. + if (parentContent && nsContentUtils::IsContentInsertionPoint(parentContent)) { + parentContent = parentContent->GetParent(); + // Change the caller's pointer for the parent content to be the insertion parent. + *aParentContent = parentContent; + } + + PLHashNumber hashCode = NS_PTR_TO_INT32(parentContent); + PLHashEntry** entry = PL_HashTableRawLookup(mTable, hashCode, parentContent); + if (*entry) { + mLastLookup = entry; + } + return entry; +} + +UndisplayedNode* +nsFrameManagerBase::UndisplayedMap::GetFirstNode(nsIContent* aParentContent) +{ + PLHashEntry** entry = GetEntryFor(&aParentContent); + if (*entry) { + return (UndisplayedNode*)((*entry)->value); + } + return nullptr; +} + +void +nsFrameManagerBase::UndisplayedMap::AppendNodeFor(UndisplayedNode* aNode, + nsIContent* aParentContent) +{ + PLHashEntry** entry = GetEntryFor(&aParentContent); + if (*entry) { + UndisplayedNode* node = (UndisplayedNode*)((*entry)->value); + while (node->mNext) { + if (node->mContent == aNode->mContent) { + // We actually need to check this in optimized builds because + // there are some callers that do this. See bug 118014, bug + // 136704, etc. + NS_NOTREACHED("node in map twice"); + delete aNode; + return; + } + node = node->mNext; + } + node->mNext = aNode; + } + else { + PLHashNumber hashCode = NS_PTR_TO_INT32(aParentContent); + PL_HashTableRawAdd(mTable, entry, hashCode, aParentContent, aNode); + mLastLookup = nullptr; // hashtable may have shifted bucket out from under us + } +} + +nsresult +nsFrameManagerBase::UndisplayedMap::AddNodeFor(nsIContent* aParentContent, + nsIContent* aChild, + nsStyleContext* aStyle) +{ + UndisplayedNode* node = new UndisplayedNode(aChild, aStyle); + + AppendNodeFor(node, aParentContent); + return NS_OK; +} + +void +nsFrameManagerBase::UndisplayedMap::RemoveNodeFor(nsIContent* aParentContent, + UndisplayedNode* aNode) +{ + PLHashEntry** entry = GetEntryFor(&aParentContent); + NS_ASSERTION(*entry, "content not in map"); + if (*entry) { + if ((UndisplayedNode*)((*entry)->value) == aNode) { // first node + if (aNode->mNext) { + (*entry)->value = aNode->mNext; + aNode->mNext = nullptr; + } + else { + PL_HashTableRawRemove(mTable, entry, *entry); + mLastLookup = nullptr; // hashtable may have shifted bucket out from under us + } + } + else { + UndisplayedNode* node = (UndisplayedNode*)((*entry)->value); + while (node->mNext) { + if (node->mNext == aNode) { + node->mNext = aNode->mNext; + aNode->mNext = nullptr; + break; + } + node = node->mNext; + } + } + } + delete aNode; +} + + +UndisplayedNode* +nsFrameManagerBase::UndisplayedMap::UnlinkNodesFor(nsIContent* aParentContent) +{ + PLHashEntry** entry = GetEntryFor(&aParentContent); + NS_ASSERTION(entry, "content not in map"); + if (*entry) { + UndisplayedNode* node = (UndisplayedNode*)((*entry)->value); + NS_ASSERTION(node, "null node for non-null entry in UndisplayedMap"); + PL_HashTableRawRemove(mTable, entry, *entry); + mLastLookup = nullptr; // hashtable may have shifted bucket out from under us + return node; + } + return nullptr; +} + +void +nsFrameManagerBase::UndisplayedMap::RemoveNodesFor(nsIContent* aParentContent) +{ + delete UnlinkNodesFor(aParentContent); +} + +static int +RemoveUndisplayedEntry(PLHashEntry* he, int i, void* arg) +{ + UndisplayedNode* node = (UndisplayedNode*)(he->value); + delete node; + // Remove and free this entry and continue enumerating + return HT_ENUMERATE_REMOVE | HT_ENUMERATE_NEXT; +} + +void +nsFrameManagerBase::UndisplayedMap::Clear(void) +{ + mLastLookup = nullptr; + PL_HashTableEnumerateEntries(mTable, RemoveUndisplayedEntry, 0); +} + +uint32_t nsFrameManagerBase::sGlobalGenerationNumber; |