1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/base/nsFrameManager.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,735 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + * vim:cindent:ts=2:et:sw=2: 1.6 + * 1.7 + * This Source Code Form is subject to the terms of the Mozilla Public 1.8 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.9 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. 1.10 + * 1.11 + * This Original Code has been modified by IBM Corporation. Modifications made by IBM 1.12 + * described herein are Copyright (c) International Business Machines Corporation, 2000. 1.13 + * Modifications to Mozilla code or documentation identified per MPL Section 3.3 1.14 + * 1.15 + * Date Modified by Description of modification 1.16 + * 04/20/2000 IBM Corp. OS/2 VisualAge build. 1.17 + */ 1.18 + 1.19 +/* storage of the frame tree and information about it */ 1.20 + 1.21 +#include "nscore.h" 1.22 +#include "nsIPresShell.h" 1.23 +#include "nsStyleContext.h" 1.24 +#include "nsCOMPtr.h" 1.25 +#include "plhash.h" 1.26 +#include "nsPlaceholderFrame.h" 1.27 +#include "nsGkAtoms.h" 1.28 +#include "nsILayoutHistoryState.h" 1.29 +#include "nsPresState.h" 1.30 +#include "mozilla/dom/Element.h" 1.31 +#include "nsIDocument.h" 1.32 + 1.33 +#include "nsContentUtils.h" 1.34 +#include "nsError.h" 1.35 +#include "nsAutoPtr.h" 1.36 +#include "nsAbsoluteContainingBlock.h" 1.37 +#include "ChildIterator.h" 1.38 + 1.39 +#include "nsFrameManager.h" 1.40 +#include "GeckoProfiler.h" 1.41 +#include "nsIStatefulFrame.h" 1.42 + 1.43 + #ifdef DEBUG 1.44 + //#define DEBUG_UNDISPLAYED_MAP 1.45 + #else 1.46 + #undef DEBUG_UNDISPLAYED_MAP 1.47 + #endif 1.48 + 1.49 +using namespace mozilla; 1.50 +using namespace mozilla::dom; 1.51 + 1.52 +//---------------------------------------------------------------------- 1.53 + 1.54 +struct PlaceholderMapEntry : public PLDHashEntryHdr { 1.55 + // key (the out of flow frame) can be obtained through placeholder frame 1.56 + nsPlaceholderFrame *placeholderFrame; 1.57 +}; 1.58 + 1.59 +static bool 1.60 +PlaceholderMapMatchEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr, 1.61 + const void *key) 1.62 +{ 1.63 + const PlaceholderMapEntry *entry = 1.64 + static_cast<const PlaceholderMapEntry*>(hdr); 1.65 + NS_ASSERTION(entry->placeholderFrame->GetOutOfFlowFrame() != 1.66 + (void*)0xdddddddd, 1.67 + "Dead placeholder in placeholder map"); 1.68 + return entry->placeholderFrame->GetOutOfFlowFrame() == key; 1.69 +} 1.70 + 1.71 +static const PLDHashTableOps PlaceholderMapOps = { 1.72 + PL_DHashAllocTable, 1.73 + PL_DHashFreeTable, 1.74 + PL_DHashVoidPtrKeyStub, 1.75 + PlaceholderMapMatchEntry, 1.76 + PL_DHashMoveEntryStub, 1.77 + PL_DHashClearEntryStub, 1.78 + PL_DHashFinalizeStub, 1.79 + nullptr 1.80 +}; 1.81 + 1.82 +//---------------------------------------------------------------------- 1.83 + 1.84 +// XXXldb This seems too complicated for what I think it's doing, and it 1.85 +// should also be using pldhash rather than plhash to use less memory. 1.86 + 1.87 +class nsFrameManagerBase::UndisplayedMap { 1.88 +public: 1.89 + UndisplayedMap(uint32_t aNumBuckets = 16) NS_HIDDEN; 1.90 + ~UndisplayedMap(void) NS_HIDDEN; 1.91 + 1.92 + NS_HIDDEN_(UndisplayedNode*) GetFirstNode(nsIContent* aParentContent); 1.93 + 1.94 + NS_HIDDEN_(nsresult) AddNodeFor(nsIContent* aParentContent, 1.95 + nsIContent* aChild, nsStyleContext* aStyle); 1.96 + 1.97 + NS_HIDDEN_(void) RemoveNodeFor(nsIContent* aParentContent, 1.98 + UndisplayedNode* aNode); 1.99 + 1.100 + NS_HIDDEN_(void) RemoveNodesFor(nsIContent* aParentContent); 1.101 + 1.102 + // Removes all entries from the hash table 1.103 + NS_HIDDEN_(void) Clear(void); 1.104 + 1.105 +protected: 1.106 + /** 1.107 + * Gets the entry for the provided parent content. If the content 1.108 + * is a <xbl:children> element, |**aParentContent| is set to 1.109 + * the parent of the children element. 1.110 + */ 1.111 + NS_HIDDEN_(PLHashEntry**) GetEntryFor(nsIContent** aParentContent); 1.112 + NS_HIDDEN_(void) AppendNodeFor(UndisplayedNode* aNode, 1.113 + nsIContent* aParentContent); 1.114 + 1.115 + PLHashTable* mTable; 1.116 + PLHashEntry** mLastLookup; 1.117 +}; 1.118 + 1.119 +//---------------------------------------------------------------------- 1.120 + 1.121 +nsFrameManager::~nsFrameManager() 1.122 +{ 1.123 + NS_ASSERTION(!mPresShell, "nsFrameManager::Destroy never called"); 1.124 +} 1.125 + 1.126 +void 1.127 +nsFrameManager::Destroy() 1.128 +{ 1.129 + NS_ASSERTION(mPresShell, "Frame manager already shut down."); 1.130 + 1.131 + // Destroy the frame hierarchy. 1.132 + mPresShell->SetIgnoreFrameDestruction(true); 1.133 + 1.134 + // Unregister all placeholders before tearing down the frame tree 1.135 + nsFrameManager::ClearPlaceholderFrameMap(); 1.136 + 1.137 + if (mRootFrame) { 1.138 + mRootFrame->Destroy(); 1.139 + mRootFrame = nullptr; 1.140 + } 1.141 + 1.142 + delete mUndisplayedMap; 1.143 + mUndisplayedMap = nullptr; 1.144 + 1.145 + mPresShell = nullptr; 1.146 +} 1.147 + 1.148 +//---------------------------------------------------------------------- 1.149 + 1.150 +// Placeholder frame functions 1.151 +nsPlaceholderFrame* 1.152 +nsFrameManager::GetPlaceholderFrameFor(const nsIFrame* aFrame) 1.153 +{ 1.154 + NS_PRECONDITION(aFrame, "null param unexpected"); 1.155 + 1.156 + if (mPlaceholderMap.ops) { 1.157 + PlaceholderMapEntry *entry = static_cast<PlaceholderMapEntry*> 1.158 + (PL_DHashTableOperate(const_cast<PLDHashTable*>(&mPlaceholderMap), 1.159 + aFrame, PL_DHASH_LOOKUP)); 1.160 + if (PL_DHASH_ENTRY_IS_BUSY(entry)) { 1.161 + return entry->placeholderFrame; 1.162 + } 1.163 + } 1.164 + 1.165 + return nullptr; 1.166 +} 1.167 + 1.168 +nsresult 1.169 +nsFrameManager::RegisterPlaceholderFrame(nsPlaceholderFrame* aPlaceholderFrame) 1.170 +{ 1.171 + NS_PRECONDITION(aPlaceholderFrame, "null param unexpected"); 1.172 + NS_PRECONDITION(nsGkAtoms::placeholderFrame == aPlaceholderFrame->GetType(), 1.173 + "unexpected frame type"); 1.174 + if (!mPlaceholderMap.ops) { 1.175 + PL_DHashTableInit(&mPlaceholderMap, &PlaceholderMapOps, nullptr, 1.176 + sizeof(PlaceholderMapEntry), 16); 1.177 + } 1.178 + PlaceholderMapEntry *entry = static_cast<PlaceholderMapEntry*>(PL_DHashTableOperate(&mPlaceholderMap, 1.179 + aPlaceholderFrame->GetOutOfFlowFrame(), 1.180 + PL_DHASH_ADD)); 1.181 + if (!entry) 1.182 + return NS_ERROR_OUT_OF_MEMORY; 1.183 + 1.184 + NS_ASSERTION(!entry->placeholderFrame, "Registering a placeholder for a frame that already has a placeholder!"); 1.185 + entry->placeholderFrame = aPlaceholderFrame; 1.186 + 1.187 + return NS_OK; 1.188 +} 1.189 + 1.190 +void 1.191 +nsFrameManager::UnregisterPlaceholderFrame(nsPlaceholderFrame* aPlaceholderFrame) 1.192 +{ 1.193 + NS_PRECONDITION(aPlaceholderFrame, "null param unexpected"); 1.194 + NS_PRECONDITION(nsGkAtoms::placeholderFrame == aPlaceholderFrame->GetType(), 1.195 + "unexpected frame type"); 1.196 + 1.197 + if (mPlaceholderMap.ops) { 1.198 + PL_DHashTableOperate(&mPlaceholderMap, 1.199 + aPlaceholderFrame->GetOutOfFlowFrame(), 1.200 + PL_DHASH_REMOVE); 1.201 + } 1.202 +} 1.203 + 1.204 +static PLDHashOperator 1.205 +UnregisterPlaceholders(PLDHashTable* table, PLDHashEntryHdr* hdr, 1.206 + uint32_t number, void* arg) 1.207 +{ 1.208 + PlaceholderMapEntry* entry = static_cast<PlaceholderMapEntry*>(hdr); 1.209 + entry->placeholderFrame->SetOutOfFlowFrame(nullptr); 1.210 + return PL_DHASH_NEXT; 1.211 +} 1.212 + 1.213 +void 1.214 +nsFrameManager::ClearPlaceholderFrameMap() 1.215 +{ 1.216 + if (mPlaceholderMap.ops) { 1.217 + PL_DHashTableEnumerate(&mPlaceholderMap, UnregisterPlaceholders, nullptr); 1.218 + PL_DHashTableFinish(&mPlaceholderMap); 1.219 + mPlaceholderMap.ops = nullptr; 1.220 + } 1.221 +} 1.222 + 1.223 +//---------------------------------------------------------------------- 1.224 + 1.225 +nsStyleContext* 1.226 +nsFrameManager::GetUndisplayedContent(nsIContent* aContent) 1.227 +{ 1.228 + if (!aContent || !mUndisplayedMap) 1.229 + return nullptr; 1.230 + 1.231 + nsIContent* parent = aContent->GetParent(); 1.232 + for (UndisplayedNode* node = mUndisplayedMap->GetFirstNode(parent); 1.233 + node; node = node->mNext) { 1.234 + if (node->mContent == aContent) 1.235 + return node->mStyle; 1.236 + } 1.237 + 1.238 + return nullptr; 1.239 +} 1.240 + 1.241 +UndisplayedNode* 1.242 +nsFrameManager::GetAllUndisplayedContentIn(nsIContent* aParentContent) 1.243 +{ 1.244 + if (!mUndisplayedMap) 1.245 + return nullptr; 1.246 + 1.247 + return mUndisplayedMap->GetFirstNode(aParentContent); 1.248 +} 1.249 + 1.250 +void 1.251 +nsFrameManager::SetUndisplayedContent(nsIContent* aContent, 1.252 + nsStyleContext* aStyleContext) 1.253 +{ 1.254 + NS_PRECONDITION(!aStyleContext->GetPseudo(), 1.255 + "Should only have actual elements here"); 1.256 + 1.257 +#ifdef DEBUG_UNDISPLAYED_MAP 1.258 + static int i = 0; 1.259 + printf("SetUndisplayedContent(%d): p=%p \n", i++, (void *)aContent); 1.260 +#endif 1.261 + 1.262 + NS_ASSERTION(!GetUndisplayedContent(aContent), 1.263 + "Already have an undisplayed context entry for aContent"); 1.264 + 1.265 + if (! mUndisplayedMap) { 1.266 + mUndisplayedMap = new UndisplayedMap; 1.267 + } 1.268 + nsIContent* parent = aContent->GetParent(); 1.269 + NS_ASSERTION(parent || (mPresShell && mPresShell->GetDocument() && 1.270 + mPresShell->GetDocument()->GetRootElement() == aContent), 1.271 + "undisplayed content must have a parent, unless it's the root " 1.272 + "element"); 1.273 + mUndisplayedMap->AddNodeFor(parent, aContent, aStyleContext); 1.274 +} 1.275 + 1.276 +void 1.277 +nsFrameManager::ChangeUndisplayedContent(nsIContent* aContent, 1.278 + nsStyleContext* aStyleContext) 1.279 +{ 1.280 + NS_ASSERTION(mUndisplayedMap, "no existing undisplayed content"); 1.281 + 1.282 +#ifdef DEBUG_UNDISPLAYED_MAP 1.283 + static int i = 0; 1.284 + printf("ChangeUndisplayedContent(%d): p=%p \n", i++, (void *)aContent); 1.285 +#endif 1.286 + 1.287 + for (UndisplayedNode* node = mUndisplayedMap->GetFirstNode(aContent->GetParent()); 1.288 + node; node = node->mNext) { 1.289 + if (node->mContent == aContent) { 1.290 + node->mStyle = aStyleContext; 1.291 + return; 1.292 + } 1.293 + } 1.294 + 1.295 + NS_NOTREACHED("no existing undisplayed content"); 1.296 +} 1.297 + 1.298 +void 1.299 +nsFrameManager::ClearUndisplayedContentIn(nsIContent* aContent, 1.300 + nsIContent* aParentContent) 1.301 +{ 1.302 +#ifdef DEBUG_UNDISPLAYED_MAP 1.303 + static int i = 0; 1.304 + printf("ClearUndisplayedContent(%d): content=%p parent=%p --> ", i++, (void *)aContent, (void*)aParentContent); 1.305 +#endif 1.306 + 1.307 + if (mUndisplayedMap) { 1.308 + UndisplayedNode* node = mUndisplayedMap->GetFirstNode(aParentContent); 1.309 + while (node) { 1.310 + if (node->mContent == aContent) { 1.311 + mUndisplayedMap->RemoveNodeFor(aParentContent, node); 1.312 + 1.313 +#ifdef DEBUG_UNDISPLAYED_MAP 1.314 + printf( "REMOVED!\n"); 1.315 +#endif 1.316 +#ifdef DEBUG 1.317 + // make sure that there are no more entries for the same content 1.318 + nsStyleContext *context = GetUndisplayedContent(aContent); 1.319 + NS_ASSERTION(context == nullptr, "Found more undisplayed content data after removal"); 1.320 +#endif 1.321 + return; 1.322 + } 1.323 + node = node->mNext; 1.324 + } 1.325 + } 1.326 +} 1.327 + 1.328 +void 1.329 +nsFrameManager::ClearAllUndisplayedContentIn(nsIContent* aParentContent) 1.330 +{ 1.331 +#ifdef DEBUG_UNDISPLAYED_MAP 1.332 + static int i = 0; 1.333 + printf("ClearAllUndisplayedContentIn(%d): parent=%p \n", i++, (void*)aParentContent); 1.334 +#endif 1.335 + 1.336 + if (mUndisplayedMap) { 1.337 + mUndisplayedMap->RemoveNodesFor(aParentContent); 1.338 + } 1.339 + 1.340 + // Need to look at aParentContent's content list due to XBL insertions. 1.341 + // Nodes in aParentContent's content list do not have aParentContent as a 1.342 + // parent, but are treated as children of aParentContent. We iterate over 1.343 + // the flattened content list and just ignore any nodes we don't care about. 1.344 + FlattenedChildIterator iter(aParentContent); 1.345 + for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) { 1.346 + if (child->GetParent() != aParentContent) { 1.347 + ClearUndisplayedContentIn(child, child->GetParent()); 1.348 + } 1.349 + } 1.350 +} 1.351 + 1.352 +//---------------------------------------------------------------------- 1.353 +nsresult 1.354 +nsFrameManager::AppendFrames(nsIFrame* aParentFrame, 1.355 + ChildListID aListID, 1.356 + nsFrameList& aFrameList) 1.357 +{ 1.358 + if (aParentFrame->IsAbsoluteContainer() && 1.359 + aListID == aParentFrame->GetAbsoluteListID()) { 1.360 + return aParentFrame->GetAbsoluteContainingBlock()-> 1.361 + AppendFrames(aParentFrame, aListID, aFrameList); 1.362 + } else { 1.363 + return aParentFrame->AppendFrames(aListID, aFrameList); 1.364 + } 1.365 +} 1.366 + 1.367 +nsresult 1.368 +nsFrameManager::InsertFrames(nsIFrame* aParentFrame, 1.369 + ChildListID aListID, 1.370 + nsIFrame* aPrevFrame, 1.371 + nsFrameList& aFrameList) 1.372 +{ 1.373 + NS_PRECONDITION(!aPrevFrame || (!aPrevFrame->GetNextContinuation() 1.374 + || (((aPrevFrame->GetNextContinuation()->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) 1.375 + && !(aPrevFrame->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER))), 1.376 + "aPrevFrame must be the last continuation in its chain!"); 1.377 + 1.378 + if (aParentFrame->IsAbsoluteContainer() && 1.379 + aListID == aParentFrame->GetAbsoluteListID()) { 1.380 + return aParentFrame->GetAbsoluteContainingBlock()-> 1.381 + InsertFrames(aParentFrame, aListID, aPrevFrame, aFrameList); 1.382 + } else { 1.383 + return aParentFrame->InsertFrames(aListID, aPrevFrame, aFrameList); 1.384 + } 1.385 +} 1.386 + 1.387 +nsresult 1.388 +nsFrameManager::RemoveFrame(ChildListID aListID, 1.389 + nsIFrame* aOldFrame) 1.390 +{ 1.391 + bool wasDestroyingFrames = mIsDestroyingFrames; 1.392 + mIsDestroyingFrames = true; 1.393 + 1.394 + // In case the reflow doesn't invalidate anything since it just leaves 1.395 + // a gap where the old frame was, we invalidate it here. (This is 1.396 + // reasonably likely to happen when removing a last child in a way 1.397 + // that doesn't change the size of the parent.) 1.398 + // This has to sure to invalidate the entire overflow rect; this 1.399 + // is important in the presence of absolute positioning 1.400 + aOldFrame->InvalidateFrameForRemoval(); 1.401 + 1.402 + NS_ASSERTION(!aOldFrame->GetPrevContinuation() || 1.403 + // exception for nsCSSFrameConstructor::RemoveFloatingFirstLetterFrames 1.404 + aOldFrame->GetType() == nsGkAtoms::textFrame, 1.405 + "Must remove first continuation."); 1.406 + NS_ASSERTION(!(aOldFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW && 1.407 + GetPlaceholderFrameFor(aOldFrame)), 1.408 + "Must call RemoveFrame on placeholder for out-of-flows."); 1.409 + nsresult rv = NS_OK; 1.410 + nsIFrame* parentFrame = aOldFrame->GetParent(); 1.411 + if (parentFrame->IsAbsoluteContainer() && 1.412 + aListID == parentFrame->GetAbsoluteListID()) { 1.413 + parentFrame->GetAbsoluteContainingBlock()-> 1.414 + RemoveFrame(parentFrame, aListID, aOldFrame); 1.415 + } else { 1.416 + rv = parentFrame->RemoveFrame(aListID, aOldFrame); 1.417 + } 1.418 + 1.419 + mIsDestroyingFrames = wasDestroyingFrames; 1.420 + 1.421 + return rv; 1.422 +} 1.423 + 1.424 +//---------------------------------------------------------------------- 1.425 + 1.426 +void 1.427 +nsFrameManager::NotifyDestroyingFrame(nsIFrame* aFrame) 1.428 +{ 1.429 + nsIContent* content = aFrame->GetContent(); 1.430 + if (content && content->GetPrimaryFrame() == aFrame) { 1.431 + ClearAllUndisplayedContentIn(content); 1.432 + } 1.433 +} 1.434 + 1.435 +// Capture state for a given frame. 1.436 +// Accept a content id here, in some cases we may not have content (scroll position) 1.437 +void 1.438 +nsFrameManager::CaptureFrameStateFor(nsIFrame* aFrame, 1.439 + nsILayoutHistoryState* aState) 1.440 +{ 1.441 + if (!aFrame || !aState) { 1.442 + NS_WARNING("null frame, or state"); 1.443 + return; 1.444 + } 1.445 + 1.446 + // Only capture state for stateful frames 1.447 + nsIStatefulFrame* statefulFrame = do_QueryFrame(aFrame); 1.448 + if (!statefulFrame) { 1.449 + return; 1.450 + } 1.451 + 1.452 + // Capture the state, exit early if we get null (nothing to save) 1.453 + nsAutoPtr<nsPresState> frameState; 1.454 + nsresult rv = statefulFrame->SaveState(getter_Transfers(frameState)); 1.455 + if (!frameState) { 1.456 + return; 1.457 + } 1.458 + 1.459 + // Generate the hash key to store the state under 1.460 + // Exit early if we get empty key 1.461 + nsAutoCString stateKey; 1.462 + nsIContent* content = aFrame->GetContent(); 1.463 + nsIDocument* doc = content ? content->GetCurrentDoc() : nullptr; 1.464 + rv = nsContentUtils::GenerateStateKey(content, doc, stateKey); 1.465 + if(NS_FAILED(rv) || stateKey.IsEmpty()) { 1.466 + return; 1.467 + } 1.468 + 1.469 + // Store the state. aState owns frameState now. 1.470 + aState->AddState(stateKey, frameState.forget()); 1.471 +} 1.472 + 1.473 +void 1.474 +nsFrameManager::CaptureFrameState(nsIFrame* aFrame, 1.475 + nsILayoutHistoryState* aState) 1.476 +{ 1.477 + NS_PRECONDITION(nullptr != aFrame && nullptr != aState, "null parameters passed in"); 1.478 + 1.479 + CaptureFrameStateFor(aFrame, aState); 1.480 + 1.481 + // Now capture state recursively for the frame hierarchy rooted at aFrame 1.482 + nsIFrame::ChildListIterator lists(aFrame); 1.483 + for (; !lists.IsDone(); lists.Next()) { 1.484 + nsFrameList::Enumerator childFrames(lists.CurrentList()); 1.485 + for (; !childFrames.AtEnd(); childFrames.Next()) { 1.486 + nsIFrame* child = childFrames.get(); 1.487 + if (child->GetStateBits() & NS_FRAME_OUT_OF_FLOW) { 1.488 + // We'll pick it up when we get to its placeholder 1.489 + continue; 1.490 + } 1.491 + // Make sure to walk through placeholders as needed, so that we 1.492 + // save state for out-of-flows which may not be our descendants 1.493 + // themselves but whose placeholders are our descendants. 1.494 + CaptureFrameState(nsPlaceholderFrame::GetRealFrameFor(child), aState); 1.495 + } 1.496 + } 1.497 +} 1.498 + 1.499 +// Restore state for a given frame. 1.500 +// Accept a content id here, in some cases we may not have content (scroll position) 1.501 +void 1.502 +nsFrameManager::RestoreFrameStateFor(nsIFrame* aFrame, 1.503 + nsILayoutHistoryState* aState) 1.504 +{ 1.505 + if (!aFrame || !aState) { 1.506 + NS_WARNING("null frame or state"); 1.507 + return; 1.508 + } 1.509 + 1.510 + // Only restore state for stateful frames 1.511 + nsIStatefulFrame* statefulFrame = do_QueryFrame(aFrame); 1.512 + if (!statefulFrame) { 1.513 + return; 1.514 + } 1.515 + 1.516 + // Generate the hash key the state was stored under 1.517 + // Exit early if we get empty key 1.518 + nsIContent* content = aFrame->GetContent(); 1.519 + // If we don't have content, we can't generate a hash 1.520 + // key and there's probably no state information for us. 1.521 + if (!content) { 1.522 + return; 1.523 + } 1.524 + 1.525 + nsAutoCString stateKey; 1.526 + nsIDocument* doc = content->GetCurrentDoc(); 1.527 + nsresult rv = nsContentUtils::GenerateStateKey(content, doc, stateKey); 1.528 + if (NS_FAILED(rv) || stateKey.IsEmpty()) { 1.529 + return; 1.530 + } 1.531 + 1.532 + // Get the state from the hash 1.533 + nsPresState* frameState = aState->GetState(stateKey); 1.534 + if (!frameState) { 1.535 + return; 1.536 + } 1.537 + 1.538 + // Restore it 1.539 + rv = statefulFrame->RestoreState(frameState); 1.540 + if (NS_FAILED(rv)) { 1.541 + return; 1.542 + } 1.543 + 1.544 + // If we restore ok, remove the state from the state table 1.545 + aState->RemoveState(stateKey); 1.546 +} 1.547 + 1.548 +void 1.549 +nsFrameManager::RestoreFrameState(nsIFrame* aFrame, 1.550 + nsILayoutHistoryState* aState) 1.551 +{ 1.552 + NS_PRECONDITION(nullptr != aFrame && nullptr != aState, "null parameters passed in"); 1.553 + 1.554 + RestoreFrameStateFor(aFrame, aState); 1.555 + 1.556 + // Now restore state recursively for the frame hierarchy rooted at aFrame 1.557 + nsIFrame::ChildListIterator lists(aFrame); 1.558 + for (; !lists.IsDone(); lists.Next()) { 1.559 + nsFrameList::Enumerator childFrames(lists.CurrentList()); 1.560 + for (; !childFrames.AtEnd(); childFrames.Next()) { 1.561 + RestoreFrameState(childFrames.get(), aState); 1.562 + } 1.563 + } 1.564 +} 1.565 + 1.566 +//---------------------------------------------------------------------- 1.567 + 1.568 +static PLHashNumber 1.569 +HashKey(void* key) 1.570 +{ 1.571 + return NS_PTR_TO_INT32(key); 1.572 +} 1.573 + 1.574 +static int 1.575 +CompareKeys(void* key1, void* key2) 1.576 +{ 1.577 + return key1 == key2; 1.578 +} 1.579 + 1.580 +//---------------------------------------------------------------------- 1.581 + 1.582 +nsFrameManagerBase::UndisplayedMap::UndisplayedMap(uint32_t aNumBuckets) 1.583 +{ 1.584 + MOZ_COUNT_CTOR(nsFrameManagerBase::UndisplayedMap); 1.585 + mTable = PL_NewHashTable(aNumBuckets, (PLHashFunction)HashKey, 1.586 + (PLHashComparator)CompareKeys, 1.587 + (PLHashComparator)nullptr, 1.588 + nullptr, nullptr); 1.589 + mLastLookup = nullptr; 1.590 +} 1.591 + 1.592 +nsFrameManagerBase::UndisplayedMap::~UndisplayedMap(void) 1.593 +{ 1.594 + MOZ_COUNT_DTOR(nsFrameManagerBase::UndisplayedMap); 1.595 + Clear(); 1.596 + PL_HashTableDestroy(mTable); 1.597 +} 1.598 + 1.599 +PLHashEntry** 1.600 +nsFrameManagerBase::UndisplayedMap::GetEntryFor(nsIContent** aParentContent) 1.601 +{ 1.602 + nsIContent* parentContent = *aParentContent; 1.603 + 1.604 + if (mLastLookup && (parentContent == (*mLastLookup)->key)) { 1.605 + return mLastLookup; 1.606 + } 1.607 + 1.608 + // In the case of XBL default content, <xbl:children> elements do not get a 1.609 + // frame causing a mismatch between the content tree and the frame tree. 1.610 + // |GetEntryFor| is sometimes called with the content tree parent (which may 1.611 + // be a <xbl:children> element) but the parent in the frame tree would be the 1.612 + // insertion parent (parent of the <xbl:children> element). Here the children 1.613 + // elements are normalized to the insertion parent to correct for the mismatch. 1.614 + if (parentContent && nsContentUtils::IsContentInsertionPoint(parentContent)) { 1.615 + parentContent = parentContent->GetParent(); 1.616 + // Change the caller's pointer for the parent content to be the insertion parent. 1.617 + *aParentContent = parentContent; 1.618 + } 1.619 + 1.620 + PLHashNumber hashCode = NS_PTR_TO_INT32(parentContent); 1.621 + PLHashEntry** entry = PL_HashTableRawLookup(mTable, hashCode, parentContent); 1.622 + if (*entry) { 1.623 + mLastLookup = entry; 1.624 + } 1.625 + return entry; 1.626 +} 1.627 + 1.628 +UndisplayedNode* 1.629 +nsFrameManagerBase::UndisplayedMap::GetFirstNode(nsIContent* aParentContent) 1.630 +{ 1.631 + PLHashEntry** entry = GetEntryFor(&aParentContent); 1.632 + if (*entry) { 1.633 + return (UndisplayedNode*)((*entry)->value); 1.634 + } 1.635 + return nullptr; 1.636 +} 1.637 + 1.638 +void 1.639 +nsFrameManagerBase::UndisplayedMap::AppendNodeFor(UndisplayedNode* aNode, 1.640 + nsIContent* aParentContent) 1.641 +{ 1.642 + PLHashEntry** entry = GetEntryFor(&aParentContent); 1.643 + if (*entry) { 1.644 + UndisplayedNode* node = (UndisplayedNode*)((*entry)->value); 1.645 + while (node->mNext) { 1.646 + if (node->mContent == aNode->mContent) { 1.647 + // We actually need to check this in optimized builds because 1.648 + // there are some callers that do this. See bug 118014, bug 1.649 + // 136704, etc. 1.650 + NS_NOTREACHED("node in map twice"); 1.651 + delete aNode; 1.652 + return; 1.653 + } 1.654 + node = node->mNext; 1.655 + } 1.656 + node->mNext = aNode; 1.657 + } 1.658 + else { 1.659 + PLHashNumber hashCode = NS_PTR_TO_INT32(aParentContent); 1.660 + PL_HashTableRawAdd(mTable, entry, hashCode, aParentContent, aNode); 1.661 + mLastLookup = nullptr; // hashtable may have shifted bucket out from under us 1.662 + } 1.663 +} 1.664 + 1.665 +nsresult 1.666 +nsFrameManagerBase::UndisplayedMap::AddNodeFor(nsIContent* aParentContent, 1.667 + nsIContent* aChild, 1.668 + nsStyleContext* aStyle) 1.669 +{ 1.670 + UndisplayedNode* node = new UndisplayedNode(aChild, aStyle); 1.671 + 1.672 + AppendNodeFor(node, aParentContent); 1.673 + return NS_OK; 1.674 +} 1.675 + 1.676 +void 1.677 +nsFrameManagerBase::UndisplayedMap::RemoveNodeFor(nsIContent* aParentContent, 1.678 + UndisplayedNode* aNode) 1.679 +{ 1.680 + PLHashEntry** entry = GetEntryFor(&aParentContent); 1.681 + NS_ASSERTION(*entry, "content not in map"); 1.682 + if (*entry) { 1.683 + if ((UndisplayedNode*)((*entry)->value) == aNode) { // first node 1.684 + if (aNode->mNext) { 1.685 + (*entry)->value = aNode->mNext; 1.686 + aNode->mNext = nullptr; 1.687 + } 1.688 + else { 1.689 + PL_HashTableRawRemove(mTable, entry, *entry); 1.690 + mLastLookup = nullptr; // hashtable may have shifted bucket out from under us 1.691 + } 1.692 + } 1.693 + else { 1.694 + UndisplayedNode* node = (UndisplayedNode*)((*entry)->value); 1.695 + while (node->mNext) { 1.696 + if (node->mNext == aNode) { 1.697 + node->mNext = aNode->mNext; 1.698 + aNode->mNext = nullptr; 1.699 + break; 1.700 + } 1.701 + node = node->mNext; 1.702 + } 1.703 + } 1.704 + } 1.705 + delete aNode; 1.706 +} 1.707 + 1.708 +void 1.709 +nsFrameManagerBase::UndisplayedMap::RemoveNodesFor(nsIContent* aParentContent) 1.710 +{ 1.711 + PLHashEntry** entry = GetEntryFor(&aParentContent); 1.712 + NS_ASSERTION(entry, "content not in map"); 1.713 + if (*entry) { 1.714 + UndisplayedNode* node = (UndisplayedNode*)((*entry)->value); 1.715 + NS_ASSERTION(node, "null node for non-null entry in UndisplayedMap"); 1.716 + delete node; 1.717 + PL_HashTableRawRemove(mTable, entry, *entry); 1.718 + mLastLookup = nullptr; // hashtable may have shifted bucket out from under us 1.719 + } 1.720 +} 1.721 + 1.722 +static int 1.723 +RemoveUndisplayedEntry(PLHashEntry* he, int i, void* arg) 1.724 +{ 1.725 + UndisplayedNode* node = (UndisplayedNode*)(he->value); 1.726 + delete node; 1.727 + // Remove and free this entry and continue enumerating 1.728 + return HT_ENUMERATE_REMOVE | HT_ENUMERATE_NEXT; 1.729 +} 1.730 + 1.731 +void 1.732 +nsFrameManagerBase::UndisplayedMap::Clear(void) 1.733 +{ 1.734 + mLastLookup = nullptr; 1.735 + PL_HashTableEnumerateEntries(mTable, RemoveUndisplayedEntry, 0); 1.736 +} 1.737 + 1.738 +uint32_t nsFrameManagerBase::sGlobalGenerationNumber;