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