layout/base/nsFrameManager.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
michael@0 2 * vim:cindent:ts=2:et:sw=2:
michael@0 3 *
michael@0 4 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 5 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 6 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
michael@0 7 *
michael@0 8 * This Original Code has been modified by IBM Corporation. Modifications made by IBM
michael@0 9 * described herein are Copyright (c) International Business Machines Corporation, 2000.
michael@0 10 * Modifications to Mozilla code or documentation identified per MPL Section 3.3
michael@0 11 *
michael@0 12 * Date Modified by Description of modification
michael@0 13 * 04/20/2000 IBM Corp. OS/2 VisualAge build.
michael@0 14 */
michael@0 15
michael@0 16 /* storage of the frame tree and information about it */
michael@0 17
michael@0 18 #include "nscore.h"
michael@0 19 #include "nsIPresShell.h"
michael@0 20 #include "nsStyleContext.h"
michael@0 21 #include "nsCOMPtr.h"
michael@0 22 #include "plhash.h"
michael@0 23 #include "nsPlaceholderFrame.h"
michael@0 24 #include "nsGkAtoms.h"
michael@0 25 #include "nsILayoutHistoryState.h"
michael@0 26 #include "nsPresState.h"
michael@0 27 #include "mozilla/dom/Element.h"
michael@0 28 #include "nsIDocument.h"
michael@0 29
michael@0 30 #include "nsContentUtils.h"
michael@0 31 #include "nsError.h"
michael@0 32 #include "nsAutoPtr.h"
michael@0 33 #include "nsAbsoluteContainingBlock.h"
michael@0 34 #include "ChildIterator.h"
michael@0 35
michael@0 36 #include "nsFrameManager.h"
michael@0 37 #include "GeckoProfiler.h"
michael@0 38 #include "nsIStatefulFrame.h"
michael@0 39
michael@0 40 #ifdef DEBUG
michael@0 41 //#define DEBUG_UNDISPLAYED_MAP
michael@0 42 #else
michael@0 43 #undef DEBUG_UNDISPLAYED_MAP
michael@0 44 #endif
michael@0 45
michael@0 46 using namespace mozilla;
michael@0 47 using namespace mozilla::dom;
michael@0 48
michael@0 49 //----------------------------------------------------------------------
michael@0 50
michael@0 51 struct PlaceholderMapEntry : public PLDHashEntryHdr {
michael@0 52 // key (the out of flow frame) can be obtained through placeholder frame
michael@0 53 nsPlaceholderFrame *placeholderFrame;
michael@0 54 };
michael@0 55
michael@0 56 static bool
michael@0 57 PlaceholderMapMatchEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr,
michael@0 58 const void *key)
michael@0 59 {
michael@0 60 const PlaceholderMapEntry *entry =
michael@0 61 static_cast<const PlaceholderMapEntry*>(hdr);
michael@0 62 NS_ASSERTION(entry->placeholderFrame->GetOutOfFlowFrame() !=
michael@0 63 (void*)0xdddddddd,
michael@0 64 "Dead placeholder in placeholder map");
michael@0 65 return entry->placeholderFrame->GetOutOfFlowFrame() == key;
michael@0 66 }
michael@0 67
michael@0 68 static const PLDHashTableOps PlaceholderMapOps = {
michael@0 69 PL_DHashAllocTable,
michael@0 70 PL_DHashFreeTable,
michael@0 71 PL_DHashVoidPtrKeyStub,
michael@0 72 PlaceholderMapMatchEntry,
michael@0 73 PL_DHashMoveEntryStub,
michael@0 74 PL_DHashClearEntryStub,
michael@0 75 PL_DHashFinalizeStub,
michael@0 76 nullptr
michael@0 77 };
michael@0 78
michael@0 79 //----------------------------------------------------------------------
michael@0 80
michael@0 81 // XXXldb This seems too complicated for what I think it's doing, and it
michael@0 82 // should also be using pldhash rather than plhash to use less memory.
michael@0 83
michael@0 84 class nsFrameManagerBase::UndisplayedMap {
michael@0 85 public:
michael@0 86 UndisplayedMap(uint32_t aNumBuckets = 16) NS_HIDDEN;
michael@0 87 ~UndisplayedMap(void) NS_HIDDEN;
michael@0 88
michael@0 89 NS_HIDDEN_(UndisplayedNode*) GetFirstNode(nsIContent* aParentContent);
michael@0 90
michael@0 91 NS_HIDDEN_(nsresult) AddNodeFor(nsIContent* aParentContent,
michael@0 92 nsIContent* aChild, nsStyleContext* aStyle);
michael@0 93
michael@0 94 NS_HIDDEN_(void) RemoveNodeFor(nsIContent* aParentContent,
michael@0 95 UndisplayedNode* aNode);
michael@0 96
michael@0 97 NS_HIDDEN_(void) RemoveNodesFor(nsIContent* aParentContent);
michael@0 98
michael@0 99 // Removes all entries from the hash table
michael@0 100 NS_HIDDEN_(void) Clear(void);
michael@0 101
michael@0 102 protected:
michael@0 103 /**
michael@0 104 * Gets the entry for the provided parent content. If the content
michael@0 105 * is a <xbl:children> element, |**aParentContent| is set to
michael@0 106 * the parent of the children element.
michael@0 107 */
michael@0 108 NS_HIDDEN_(PLHashEntry**) GetEntryFor(nsIContent** aParentContent);
michael@0 109 NS_HIDDEN_(void) AppendNodeFor(UndisplayedNode* aNode,
michael@0 110 nsIContent* aParentContent);
michael@0 111
michael@0 112 PLHashTable* mTable;
michael@0 113 PLHashEntry** mLastLookup;
michael@0 114 };
michael@0 115
michael@0 116 //----------------------------------------------------------------------
michael@0 117
michael@0 118 nsFrameManager::~nsFrameManager()
michael@0 119 {
michael@0 120 NS_ASSERTION(!mPresShell, "nsFrameManager::Destroy never called");
michael@0 121 }
michael@0 122
michael@0 123 void
michael@0 124 nsFrameManager::Destroy()
michael@0 125 {
michael@0 126 NS_ASSERTION(mPresShell, "Frame manager already shut down.");
michael@0 127
michael@0 128 // Destroy the frame hierarchy.
michael@0 129 mPresShell->SetIgnoreFrameDestruction(true);
michael@0 130
michael@0 131 // Unregister all placeholders before tearing down the frame tree
michael@0 132 nsFrameManager::ClearPlaceholderFrameMap();
michael@0 133
michael@0 134 if (mRootFrame) {
michael@0 135 mRootFrame->Destroy();
michael@0 136 mRootFrame = nullptr;
michael@0 137 }
michael@0 138
michael@0 139 delete mUndisplayedMap;
michael@0 140 mUndisplayedMap = nullptr;
michael@0 141
michael@0 142 mPresShell = nullptr;
michael@0 143 }
michael@0 144
michael@0 145 //----------------------------------------------------------------------
michael@0 146
michael@0 147 // Placeholder frame functions
michael@0 148 nsPlaceholderFrame*
michael@0 149 nsFrameManager::GetPlaceholderFrameFor(const nsIFrame* aFrame)
michael@0 150 {
michael@0 151 NS_PRECONDITION(aFrame, "null param unexpected");
michael@0 152
michael@0 153 if (mPlaceholderMap.ops) {
michael@0 154 PlaceholderMapEntry *entry = static_cast<PlaceholderMapEntry*>
michael@0 155 (PL_DHashTableOperate(const_cast<PLDHashTable*>(&mPlaceholderMap),
michael@0 156 aFrame, PL_DHASH_LOOKUP));
michael@0 157 if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
michael@0 158 return entry->placeholderFrame;
michael@0 159 }
michael@0 160 }
michael@0 161
michael@0 162 return nullptr;
michael@0 163 }
michael@0 164
michael@0 165 nsresult
michael@0 166 nsFrameManager::RegisterPlaceholderFrame(nsPlaceholderFrame* aPlaceholderFrame)
michael@0 167 {
michael@0 168 NS_PRECONDITION(aPlaceholderFrame, "null param unexpected");
michael@0 169 NS_PRECONDITION(nsGkAtoms::placeholderFrame == aPlaceholderFrame->GetType(),
michael@0 170 "unexpected frame type");
michael@0 171 if (!mPlaceholderMap.ops) {
michael@0 172 PL_DHashTableInit(&mPlaceholderMap, &PlaceholderMapOps, nullptr,
michael@0 173 sizeof(PlaceholderMapEntry), 16);
michael@0 174 }
michael@0 175 PlaceholderMapEntry *entry = static_cast<PlaceholderMapEntry*>(PL_DHashTableOperate(&mPlaceholderMap,
michael@0 176 aPlaceholderFrame->GetOutOfFlowFrame(),
michael@0 177 PL_DHASH_ADD));
michael@0 178 if (!entry)
michael@0 179 return NS_ERROR_OUT_OF_MEMORY;
michael@0 180
michael@0 181 NS_ASSERTION(!entry->placeholderFrame, "Registering a placeholder for a frame that already has a placeholder!");
michael@0 182 entry->placeholderFrame = aPlaceholderFrame;
michael@0 183
michael@0 184 return NS_OK;
michael@0 185 }
michael@0 186
michael@0 187 void
michael@0 188 nsFrameManager::UnregisterPlaceholderFrame(nsPlaceholderFrame* aPlaceholderFrame)
michael@0 189 {
michael@0 190 NS_PRECONDITION(aPlaceholderFrame, "null param unexpected");
michael@0 191 NS_PRECONDITION(nsGkAtoms::placeholderFrame == aPlaceholderFrame->GetType(),
michael@0 192 "unexpected frame type");
michael@0 193
michael@0 194 if (mPlaceholderMap.ops) {
michael@0 195 PL_DHashTableOperate(&mPlaceholderMap,
michael@0 196 aPlaceholderFrame->GetOutOfFlowFrame(),
michael@0 197 PL_DHASH_REMOVE);
michael@0 198 }
michael@0 199 }
michael@0 200
michael@0 201 static PLDHashOperator
michael@0 202 UnregisterPlaceholders(PLDHashTable* table, PLDHashEntryHdr* hdr,
michael@0 203 uint32_t number, void* arg)
michael@0 204 {
michael@0 205 PlaceholderMapEntry* entry = static_cast<PlaceholderMapEntry*>(hdr);
michael@0 206 entry->placeholderFrame->SetOutOfFlowFrame(nullptr);
michael@0 207 return PL_DHASH_NEXT;
michael@0 208 }
michael@0 209
michael@0 210 void
michael@0 211 nsFrameManager::ClearPlaceholderFrameMap()
michael@0 212 {
michael@0 213 if (mPlaceholderMap.ops) {
michael@0 214 PL_DHashTableEnumerate(&mPlaceholderMap, UnregisterPlaceholders, nullptr);
michael@0 215 PL_DHashTableFinish(&mPlaceholderMap);
michael@0 216 mPlaceholderMap.ops = nullptr;
michael@0 217 }
michael@0 218 }
michael@0 219
michael@0 220 //----------------------------------------------------------------------
michael@0 221
michael@0 222 nsStyleContext*
michael@0 223 nsFrameManager::GetUndisplayedContent(nsIContent* aContent)
michael@0 224 {
michael@0 225 if (!aContent || !mUndisplayedMap)
michael@0 226 return nullptr;
michael@0 227
michael@0 228 nsIContent* parent = aContent->GetParent();
michael@0 229 for (UndisplayedNode* node = mUndisplayedMap->GetFirstNode(parent);
michael@0 230 node; node = node->mNext) {
michael@0 231 if (node->mContent == aContent)
michael@0 232 return node->mStyle;
michael@0 233 }
michael@0 234
michael@0 235 return nullptr;
michael@0 236 }
michael@0 237
michael@0 238 UndisplayedNode*
michael@0 239 nsFrameManager::GetAllUndisplayedContentIn(nsIContent* aParentContent)
michael@0 240 {
michael@0 241 if (!mUndisplayedMap)
michael@0 242 return nullptr;
michael@0 243
michael@0 244 return mUndisplayedMap->GetFirstNode(aParentContent);
michael@0 245 }
michael@0 246
michael@0 247 void
michael@0 248 nsFrameManager::SetUndisplayedContent(nsIContent* aContent,
michael@0 249 nsStyleContext* aStyleContext)
michael@0 250 {
michael@0 251 NS_PRECONDITION(!aStyleContext->GetPseudo(),
michael@0 252 "Should only have actual elements here");
michael@0 253
michael@0 254 #ifdef DEBUG_UNDISPLAYED_MAP
michael@0 255 static int i = 0;
michael@0 256 printf("SetUndisplayedContent(%d): p=%p \n", i++, (void *)aContent);
michael@0 257 #endif
michael@0 258
michael@0 259 NS_ASSERTION(!GetUndisplayedContent(aContent),
michael@0 260 "Already have an undisplayed context entry for aContent");
michael@0 261
michael@0 262 if (! mUndisplayedMap) {
michael@0 263 mUndisplayedMap = new UndisplayedMap;
michael@0 264 }
michael@0 265 nsIContent* parent = aContent->GetParent();
michael@0 266 NS_ASSERTION(parent || (mPresShell && mPresShell->GetDocument() &&
michael@0 267 mPresShell->GetDocument()->GetRootElement() == aContent),
michael@0 268 "undisplayed content must have a parent, unless it's the root "
michael@0 269 "element");
michael@0 270 mUndisplayedMap->AddNodeFor(parent, aContent, aStyleContext);
michael@0 271 }
michael@0 272
michael@0 273 void
michael@0 274 nsFrameManager::ChangeUndisplayedContent(nsIContent* aContent,
michael@0 275 nsStyleContext* aStyleContext)
michael@0 276 {
michael@0 277 NS_ASSERTION(mUndisplayedMap, "no existing undisplayed content");
michael@0 278
michael@0 279 #ifdef DEBUG_UNDISPLAYED_MAP
michael@0 280 static int i = 0;
michael@0 281 printf("ChangeUndisplayedContent(%d): p=%p \n", i++, (void *)aContent);
michael@0 282 #endif
michael@0 283
michael@0 284 for (UndisplayedNode* node = mUndisplayedMap->GetFirstNode(aContent->GetParent());
michael@0 285 node; node = node->mNext) {
michael@0 286 if (node->mContent == aContent) {
michael@0 287 node->mStyle = aStyleContext;
michael@0 288 return;
michael@0 289 }
michael@0 290 }
michael@0 291
michael@0 292 NS_NOTREACHED("no existing undisplayed content");
michael@0 293 }
michael@0 294
michael@0 295 void
michael@0 296 nsFrameManager::ClearUndisplayedContentIn(nsIContent* aContent,
michael@0 297 nsIContent* aParentContent)
michael@0 298 {
michael@0 299 #ifdef DEBUG_UNDISPLAYED_MAP
michael@0 300 static int i = 0;
michael@0 301 printf("ClearUndisplayedContent(%d): content=%p parent=%p --> ", i++, (void *)aContent, (void*)aParentContent);
michael@0 302 #endif
michael@0 303
michael@0 304 if (mUndisplayedMap) {
michael@0 305 UndisplayedNode* node = mUndisplayedMap->GetFirstNode(aParentContent);
michael@0 306 while (node) {
michael@0 307 if (node->mContent == aContent) {
michael@0 308 mUndisplayedMap->RemoveNodeFor(aParentContent, node);
michael@0 309
michael@0 310 #ifdef DEBUG_UNDISPLAYED_MAP
michael@0 311 printf( "REMOVED!\n");
michael@0 312 #endif
michael@0 313 #ifdef DEBUG
michael@0 314 // make sure that there are no more entries for the same content
michael@0 315 nsStyleContext *context = GetUndisplayedContent(aContent);
michael@0 316 NS_ASSERTION(context == nullptr, "Found more undisplayed content data after removal");
michael@0 317 #endif
michael@0 318 return;
michael@0 319 }
michael@0 320 node = node->mNext;
michael@0 321 }
michael@0 322 }
michael@0 323 }
michael@0 324
michael@0 325 void
michael@0 326 nsFrameManager::ClearAllUndisplayedContentIn(nsIContent* aParentContent)
michael@0 327 {
michael@0 328 #ifdef DEBUG_UNDISPLAYED_MAP
michael@0 329 static int i = 0;
michael@0 330 printf("ClearAllUndisplayedContentIn(%d): parent=%p \n", i++, (void*)aParentContent);
michael@0 331 #endif
michael@0 332
michael@0 333 if (mUndisplayedMap) {
michael@0 334 mUndisplayedMap->RemoveNodesFor(aParentContent);
michael@0 335 }
michael@0 336
michael@0 337 // Need to look at aParentContent's content list due to XBL insertions.
michael@0 338 // Nodes in aParentContent's content list do not have aParentContent as a
michael@0 339 // parent, but are treated as children of aParentContent. We iterate over
michael@0 340 // the flattened content list and just ignore any nodes we don't care about.
michael@0 341 FlattenedChildIterator iter(aParentContent);
michael@0 342 for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
michael@0 343 if (child->GetParent() != aParentContent) {
michael@0 344 ClearUndisplayedContentIn(child, child->GetParent());
michael@0 345 }
michael@0 346 }
michael@0 347 }
michael@0 348
michael@0 349 //----------------------------------------------------------------------
michael@0 350 nsresult
michael@0 351 nsFrameManager::AppendFrames(nsIFrame* aParentFrame,
michael@0 352 ChildListID aListID,
michael@0 353 nsFrameList& aFrameList)
michael@0 354 {
michael@0 355 if (aParentFrame->IsAbsoluteContainer() &&
michael@0 356 aListID == aParentFrame->GetAbsoluteListID()) {
michael@0 357 return aParentFrame->GetAbsoluteContainingBlock()->
michael@0 358 AppendFrames(aParentFrame, aListID, aFrameList);
michael@0 359 } else {
michael@0 360 return aParentFrame->AppendFrames(aListID, aFrameList);
michael@0 361 }
michael@0 362 }
michael@0 363
michael@0 364 nsresult
michael@0 365 nsFrameManager::InsertFrames(nsIFrame* aParentFrame,
michael@0 366 ChildListID aListID,
michael@0 367 nsIFrame* aPrevFrame,
michael@0 368 nsFrameList& aFrameList)
michael@0 369 {
michael@0 370 NS_PRECONDITION(!aPrevFrame || (!aPrevFrame->GetNextContinuation()
michael@0 371 || (((aPrevFrame->GetNextContinuation()->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER))
michael@0 372 && !(aPrevFrame->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER))),
michael@0 373 "aPrevFrame must be the last continuation in its chain!");
michael@0 374
michael@0 375 if (aParentFrame->IsAbsoluteContainer() &&
michael@0 376 aListID == aParentFrame->GetAbsoluteListID()) {
michael@0 377 return aParentFrame->GetAbsoluteContainingBlock()->
michael@0 378 InsertFrames(aParentFrame, aListID, aPrevFrame, aFrameList);
michael@0 379 } else {
michael@0 380 return aParentFrame->InsertFrames(aListID, aPrevFrame, aFrameList);
michael@0 381 }
michael@0 382 }
michael@0 383
michael@0 384 nsresult
michael@0 385 nsFrameManager::RemoveFrame(ChildListID aListID,
michael@0 386 nsIFrame* aOldFrame)
michael@0 387 {
michael@0 388 bool wasDestroyingFrames = mIsDestroyingFrames;
michael@0 389 mIsDestroyingFrames = true;
michael@0 390
michael@0 391 // In case the reflow doesn't invalidate anything since it just leaves
michael@0 392 // a gap where the old frame was, we invalidate it here. (This is
michael@0 393 // reasonably likely to happen when removing a last child in a way
michael@0 394 // that doesn't change the size of the parent.)
michael@0 395 // This has to sure to invalidate the entire overflow rect; this
michael@0 396 // is important in the presence of absolute positioning
michael@0 397 aOldFrame->InvalidateFrameForRemoval();
michael@0 398
michael@0 399 NS_ASSERTION(!aOldFrame->GetPrevContinuation() ||
michael@0 400 // exception for nsCSSFrameConstructor::RemoveFloatingFirstLetterFrames
michael@0 401 aOldFrame->GetType() == nsGkAtoms::textFrame,
michael@0 402 "Must remove first continuation.");
michael@0 403 NS_ASSERTION(!(aOldFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW &&
michael@0 404 GetPlaceholderFrameFor(aOldFrame)),
michael@0 405 "Must call RemoveFrame on placeholder for out-of-flows.");
michael@0 406 nsresult rv = NS_OK;
michael@0 407 nsIFrame* parentFrame = aOldFrame->GetParent();
michael@0 408 if (parentFrame->IsAbsoluteContainer() &&
michael@0 409 aListID == parentFrame->GetAbsoluteListID()) {
michael@0 410 parentFrame->GetAbsoluteContainingBlock()->
michael@0 411 RemoveFrame(parentFrame, aListID, aOldFrame);
michael@0 412 } else {
michael@0 413 rv = parentFrame->RemoveFrame(aListID, aOldFrame);
michael@0 414 }
michael@0 415
michael@0 416 mIsDestroyingFrames = wasDestroyingFrames;
michael@0 417
michael@0 418 return rv;
michael@0 419 }
michael@0 420
michael@0 421 //----------------------------------------------------------------------
michael@0 422
michael@0 423 void
michael@0 424 nsFrameManager::NotifyDestroyingFrame(nsIFrame* aFrame)
michael@0 425 {
michael@0 426 nsIContent* content = aFrame->GetContent();
michael@0 427 if (content && content->GetPrimaryFrame() == aFrame) {
michael@0 428 ClearAllUndisplayedContentIn(content);
michael@0 429 }
michael@0 430 }
michael@0 431
michael@0 432 // Capture state for a given frame.
michael@0 433 // Accept a content id here, in some cases we may not have content (scroll position)
michael@0 434 void
michael@0 435 nsFrameManager::CaptureFrameStateFor(nsIFrame* aFrame,
michael@0 436 nsILayoutHistoryState* aState)
michael@0 437 {
michael@0 438 if (!aFrame || !aState) {
michael@0 439 NS_WARNING("null frame, or state");
michael@0 440 return;
michael@0 441 }
michael@0 442
michael@0 443 // Only capture state for stateful frames
michael@0 444 nsIStatefulFrame* statefulFrame = do_QueryFrame(aFrame);
michael@0 445 if (!statefulFrame) {
michael@0 446 return;
michael@0 447 }
michael@0 448
michael@0 449 // Capture the state, exit early if we get null (nothing to save)
michael@0 450 nsAutoPtr<nsPresState> frameState;
michael@0 451 nsresult rv = statefulFrame->SaveState(getter_Transfers(frameState));
michael@0 452 if (!frameState) {
michael@0 453 return;
michael@0 454 }
michael@0 455
michael@0 456 // Generate the hash key to store the state under
michael@0 457 // Exit early if we get empty key
michael@0 458 nsAutoCString stateKey;
michael@0 459 nsIContent* content = aFrame->GetContent();
michael@0 460 nsIDocument* doc = content ? content->GetCurrentDoc() : nullptr;
michael@0 461 rv = nsContentUtils::GenerateStateKey(content, doc, stateKey);
michael@0 462 if(NS_FAILED(rv) || stateKey.IsEmpty()) {
michael@0 463 return;
michael@0 464 }
michael@0 465
michael@0 466 // Store the state. aState owns frameState now.
michael@0 467 aState->AddState(stateKey, frameState.forget());
michael@0 468 }
michael@0 469
michael@0 470 void
michael@0 471 nsFrameManager::CaptureFrameState(nsIFrame* aFrame,
michael@0 472 nsILayoutHistoryState* aState)
michael@0 473 {
michael@0 474 NS_PRECONDITION(nullptr != aFrame && nullptr != aState, "null parameters passed in");
michael@0 475
michael@0 476 CaptureFrameStateFor(aFrame, aState);
michael@0 477
michael@0 478 // Now capture state recursively for the frame hierarchy rooted at aFrame
michael@0 479 nsIFrame::ChildListIterator lists(aFrame);
michael@0 480 for (; !lists.IsDone(); lists.Next()) {
michael@0 481 nsFrameList::Enumerator childFrames(lists.CurrentList());
michael@0 482 for (; !childFrames.AtEnd(); childFrames.Next()) {
michael@0 483 nsIFrame* child = childFrames.get();
michael@0 484 if (child->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
michael@0 485 // We'll pick it up when we get to its placeholder
michael@0 486 continue;
michael@0 487 }
michael@0 488 // Make sure to walk through placeholders as needed, so that we
michael@0 489 // save state for out-of-flows which may not be our descendants
michael@0 490 // themselves but whose placeholders are our descendants.
michael@0 491 CaptureFrameState(nsPlaceholderFrame::GetRealFrameFor(child), aState);
michael@0 492 }
michael@0 493 }
michael@0 494 }
michael@0 495
michael@0 496 // Restore state for a given frame.
michael@0 497 // Accept a content id here, in some cases we may not have content (scroll position)
michael@0 498 void
michael@0 499 nsFrameManager::RestoreFrameStateFor(nsIFrame* aFrame,
michael@0 500 nsILayoutHistoryState* aState)
michael@0 501 {
michael@0 502 if (!aFrame || !aState) {
michael@0 503 NS_WARNING("null frame or state");
michael@0 504 return;
michael@0 505 }
michael@0 506
michael@0 507 // Only restore state for stateful frames
michael@0 508 nsIStatefulFrame* statefulFrame = do_QueryFrame(aFrame);
michael@0 509 if (!statefulFrame) {
michael@0 510 return;
michael@0 511 }
michael@0 512
michael@0 513 // Generate the hash key the state was stored under
michael@0 514 // Exit early if we get empty key
michael@0 515 nsIContent* content = aFrame->GetContent();
michael@0 516 // If we don't have content, we can't generate a hash
michael@0 517 // key and there's probably no state information for us.
michael@0 518 if (!content) {
michael@0 519 return;
michael@0 520 }
michael@0 521
michael@0 522 nsAutoCString stateKey;
michael@0 523 nsIDocument* doc = content->GetCurrentDoc();
michael@0 524 nsresult rv = nsContentUtils::GenerateStateKey(content, doc, stateKey);
michael@0 525 if (NS_FAILED(rv) || stateKey.IsEmpty()) {
michael@0 526 return;
michael@0 527 }
michael@0 528
michael@0 529 // Get the state from the hash
michael@0 530 nsPresState* frameState = aState->GetState(stateKey);
michael@0 531 if (!frameState) {
michael@0 532 return;
michael@0 533 }
michael@0 534
michael@0 535 // Restore it
michael@0 536 rv = statefulFrame->RestoreState(frameState);
michael@0 537 if (NS_FAILED(rv)) {
michael@0 538 return;
michael@0 539 }
michael@0 540
michael@0 541 // If we restore ok, remove the state from the state table
michael@0 542 aState->RemoveState(stateKey);
michael@0 543 }
michael@0 544
michael@0 545 void
michael@0 546 nsFrameManager::RestoreFrameState(nsIFrame* aFrame,
michael@0 547 nsILayoutHistoryState* aState)
michael@0 548 {
michael@0 549 NS_PRECONDITION(nullptr != aFrame && nullptr != aState, "null parameters passed in");
michael@0 550
michael@0 551 RestoreFrameStateFor(aFrame, aState);
michael@0 552
michael@0 553 // Now restore state recursively for the frame hierarchy rooted at aFrame
michael@0 554 nsIFrame::ChildListIterator lists(aFrame);
michael@0 555 for (; !lists.IsDone(); lists.Next()) {
michael@0 556 nsFrameList::Enumerator childFrames(lists.CurrentList());
michael@0 557 for (; !childFrames.AtEnd(); childFrames.Next()) {
michael@0 558 RestoreFrameState(childFrames.get(), aState);
michael@0 559 }
michael@0 560 }
michael@0 561 }
michael@0 562
michael@0 563 //----------------------------------------------------------------------
michael@0 564
michael@0 565 static PLHashNumber
michael@0 566 HashKey(void* key)
michael@0 567 {
michael@0 568 return NS_PTR_TO_INT32(key);
michael@0 569 }
michael@0 570
michael@0 571 static int
michael@0 572 CompareKeys(void* key1, void* key2)
michael@0 573 {
michael@0 574 return key1 == key2;
michael@0 575 }
michael@0 576
michael@0 577 //----------------------------------------------------------------------
michael@0 578
michael@0 579 nsFrameManagerBase::UndisplayedMap::UndisplayedMap(uint32_t aNumBuckets)
michael@0 580 {
michael@0 581 MOZ_COUNT_CTOR(nsFrameManagerBase::UndisplayedMap);
michael@0 582 mTable = PL_NewHashTable(aNumBuckets, (PLHashFunction)HashKey,
michael@0 583 (PLHashComparator)CompareKeys,
michael@0 584 (PLHashComparator)nullptr,
michael@0 585 nullptr, nullptr);
michael@0 586 mLastLookup = nullptr;
michael@0 587 }
michael@0 588
michael@0 589 nsFrameManagerBase::UndisplayedMap::~UndisplayedMap(void)
michael@0 590 {
michael@0 591 MOZ_COUNT_DTOR(nsFrameManagerBase::UndisplayedMap);
michael@0 592 Clear();
michael@0 593 PL_HashTableDestroy(mTable);
michael@0 594 }
michael@0 595
michael@0 596 PLHashEntry**
michael@0 597 nsFrameManagerBase::UndisplayedMap::GetEntryFor(nsIContent** aParentContent)
michael@0 598 {
michael@0 599 nsIContent* parentContent = *aParentContent;
michael@0 600
michael@0 601 if (mLastLookup && (parentContent == (*mLastLookup)->key)) {
michael@0 602 return mLastLookup;
michael@0 603 }
michael@0 604
michael@0 605 // In the case of XBL default content, <xbl:children> elements do not get a
michael@0 606 // frame causing a mismatch between the content tree and the frame tree.
michael@0 607 // |GetEntryFor| is sometimes called with the content tree parent (which may
michael@0 608 // be a <xbl:children> element) but the parent in the frame tree would be the
michael@0 609 // insertion parent (parent of the <xbl:children> element). Here the children
michael@0 610 // elements are normalized to the insertion parent to correct for the mismatch.
michael@0 611 if (parentContent && nsContentUtils::IsContentInsertionPoint(parentContent)) {
michael@0 612 parentContent = parentContent->GetParent();
michael@0 613 // Change the caller's pointer for the parent content to be the insertion parent.
michael@0 614 *aParentContent = parentContent;
michael@0 615 }
michael@0 616
michael@0 617 PLHashNumber hashCode = NS_PTR_TO_INT32(parentContent);
michael@0 618 PLHashEntry** entry = PL_HashTableRawLookup(mTable, hashCode, parentContent);
michael@0 619 if (*entry) {
michael@0 620 mLastLookup = entry;
michael@0 621 }
michael@0 622 return entry;
michael@0 623 }
michael@0 624
michael@0 625 UndisplayedNode*
michael@0 626 nsFrameManagerBase::UndisplayedMap::GetFirstNode(nsIContent* aParentContent)
michael@0 627 {
michael@0 628 PLHashEntry** entry = GetEntryFor(&aParentContent);
michael@0 629 if (*entry) {
michael@0 630 return (UndisplayedNode*)((*entry)->value);
michael@0 631 }
michael@0 632 return nullptr;
michael@0 633 }
michael@0 634
michael@0 635 void
michael@0 636 nsFrameManagerBase::UndisplayedMap::AppendNodeFor(UndisplayedNode* aNode,
michael@0 637 nsIContent* aParentContent)
michael@0 638 {
michael@0 639 PLHashEntry** entry = GetEntryFor(&aParentContent);
michael@0 640 if (*entry) {
michael@0 641 UndisplayedNode* node = (UndisplayedNode*)((*entry)->value);
michael@0 642 while (node->mNext) {
michael@0 643 if (node->mContent == aNode->mContent) {
michael@0 644 // We actually need to check this in optimized builds because
michael@0 645 // there are some callers that do this. See bug 118014, bug
michael@0 646 // 136704, etc.
michael@0 647 NS_NOTREACHED("node in map twice");
michael@0 648 delete aNode;
michael@0 649 return;
michael@0 650 }
michael@0 651 node = node->mNext;
michael@0 652 }
michael@0 653 node->mNext = aNode;
michael@0 654 }
michael@0 655 else {
michael@0 656 PLHashNumber hashCode = NS_PTR_TO_INT32(aParentContent);
michael@0 657 PL_HashTableRawAdd(mTable, entry, hashCode, aParentContent, aNode);
michael@0 658 mLastLookup = nullptr; // hashtable may have shifted bucket out from under us
michael@0 659 }
michael@0 660 }
michael@0 661
michael@0 662 nsresult
michael@0 663 nsFrameManagerBase::UndisplayedMap::AddNodeFor(nsIContent* aParentContent,
michael@0 664 nsIContent* aChild,
michael@0 665 nsStyleContext* aStyle)
michael@0 666 {
michael@0 667 UndisplayedNode* node = new UndisplayedNode(aChild, aStyle);
michael@0 668
michael@0 669 AppendNodeFor(node, aParentContent);
michael@0 670 return NS_OK;
michael@0 671 }
michael@0 672
michael@0 673 void
michael@0 674 nsFrameManagerBase::UndisplayedMap::RemoveNodeFor(nsIContent* aParentContent,
michael@0 675 UndisplayedNode* aNode)
michael@0 676 {
michael@0 677 PLHashEntry** entry = GetEntryFor(&aParentContent);
michael@0 678 NS_ASSERTION(*entry, "content not in map");
michael@0 679 if (*entry) {
michael@0 680 if ((UndisplayedNode*)((*entry)->value) == aNode) { // first node
michael@0 681 if (aNode->mNext) {
michael@0 682 (*entry)->value = aNode->mNext;
michael@0 683 aNode->mNext = nullptr;
michael@0 684 }
michael@0 685 else {
michael@0 686 PL_HashTableRawRemove(mTable, entry, *entry);
michael@0 687 mLastLookup = nullptr; // hashtable may have shifted bucket out from under us
michael@0 688 }
michael@0 689 }
michael@0 690 else {
michael@0 691 UndisplayedNode* node = (UndisplayedNode*)((*entry)->value);
michael@0 692 while (node->mNext) {
michael@0 693 if (node->mNext == aNode) {
michael@0 694 node->mNext = aNode->mNext;
michael@0 695 aNode->mNext = nullptr;
michael@0 696 break;
michael@0 697 }
michael@0 698 node = node->mNext;
michael@0 699 }
michael@0 700 }
michael@0 701 }
michael@0 702 delete aNode;
michael@0 703 }
michael@0 704
michael@0 705 void
michael@0 706 nsFrameManagerBase::UndisplayedMap::RemoveNodesFor(nsIContent* aParentContent)
michael@0 707 {
michael@0 708 PLHashEntry** entry = GetEntryFor(&aParentContent);
michael@0 709 NS_ASSERTION(entry, "content not in map");
michael@0 710 if (*entry) {
michael@0 711 UndisplayedNode* node = (UndisplayedNode*)((*entry)->value);
michael@0 712 NS_ASSERTION(node, "null node for non-null entry in UndisplayedMap");
michael@0 713 delete node;
michael@0 714 PL_HashTableRawRemove(mTable, entry, *entry);
michael@0 715 mLastLookup = nullptr; // hashtable may have shifted bucket out from under us
michael@0 716 }
michael@0 717 }
michael@0 718
michael@0 719 static int
michael@0 720 RemoveUndisplayedEntry(PLHashEntry* he, int i, void* arg)
michael@0 721 {
michael@0 722 UndisplayedNode* node = (UndisplayedNode*)(he->value);
michael@0 723 delete node;
michael@0 724 // Remove and free this entry and continue enumerating
michael@0 725 return HT_ENUMERATE_REMOVE | HT_ENUMERATE_NEXT;
michael@0 726 }
michael@0 727
michael@0 728 void
michael@0 729 nsFrameManagerBase::UndisplayedMap::Clear(void)
michael@0 730 {
michael@0 731 mLastLookup = nullptr;
michael@0 732 PL_HashTableEnumerateEntries(mTable, RemoveUndisplayedEntry, 0);
michael@0 733 }
michael@0 734
michael@0 735 uint32_t nsFrameManagerBase::sGlobalGenerationNumber;

mercurial