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