1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 : * vim:cindent:ts=2:et:sw=2:
3 : *
4 : * ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
7 : * The contents of this file are subject to the Mozilla Public License Version
8 : * 1.1 (the "License"); you may not use this file except in compliance with
9 : * the License. You may obtain a copy of the License at
10 : * http://www.mozilla.org/MPL/
11 : *
12 : * Software distributed under the License is distributed on an "AS IS" basis,
13 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 : * for the specific language governing rights and limitations under the
15 : * License.
16 : *
17 : * The Original Code is mozilla.org code.
18 : *
19 : * The Initial Developer of the Original Code is
20 : * Netscape Communications Corporation.
21 : * Portions created by the Initial Developer are Copyright (C) 1998
22 : * the Initial Developer. All Rights Reserved.
23 : *
24 : * Contributor(s):
25 : * Mats Palmgren <matspal@gmail.com>
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either of the GNU General Public License Version 2 or later (the "GPL"),
29 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK *****
40 : *
41 : * This Original Code has been modified by IBM Corporation. Modifications made by IBM
42 : * described herein are Copyright (c) International Business Machines Corporation, 2000.
43 : * Modifications to Mozilla code or documentation identified per MPL Section 3.3
44 : *
45 : * Date Modified by Description of modification
46 : * 04/20/2000 IBM Corp. OS/2 VisualAge build.
47 : */
48 :
49 : /* storage of the frame tree and information about it */
50 :
51 : #include "nscore.h"
52 : #include "nsPresContext.h"
53 : #include "nsIPresShell.h"
54 : #include "nsStyleSet.h"
55 : #include "nsCSSFrameConstructor.h"
56 : #include "nsStyleContext.h"
57 : #include "nsStyleChangeList.h"
58 : #include "nsIServiceManager.h"
59 : #include "nsCOMPtr.h"
60 : #include "prthread.h"
61 : #include "plhash.h"
62 : #include "nsPlaceholderFrame.h"
63 : #include "nsContainerFrame.h"
64 : #include "nsBlockFrame.h"
65 : #include "nsGkAtoms.h"
66 : #include "nsCSSAnonBoxes.h"
67 : #include "nsCSSPseudoElements.h"
68 : #ifdef NS_DEBUG
69 : #include "nsIStyleRule.h"
70 : #endif
71 : #include "nsILayoutHistoryState.h"
72 : #include "nsPresState.h"
73 : #include "nsIContent.h"
74 : #include "nsINameSpaceManager.h"
75 : #include "nsIDocument.h"
76 : #include "nsIScrollableFrame.h"
77 :
78 : #include "nsIHTMLDocument.h"
79 : #include "nsIDOMHTMLDocument.h"
80 : #include "nsIDOMNodeList.h"
81 : #include "nsIDOMHTMLCollection.h"
82 : #include "nsIFormControl.h"
83 : #include "nsIDOMElement.h"
84 : #include "nsIDOMHTMLFormElement.h"
85 : #include "nsIForm.h"
86 : #include "nsContentUtils.h"
87 : #include "nsReadableUtils.h"
88 : #include "nsUnicharUtils.h"
89 : #include "nsLayoutErrors.h"
90 : #include "nsLayoutUtils.h"
91 : #include "nsAutoPtr.h"
92 : #include "imgIRequest.h"
93 : #include "nsTransitionManager.h"
94 : #include "RestyleTracker.h"
95 : #include "nsAbsoluteContainingBlock.h"
96 :
97 : #include "nsFrameManager.h"
98 : #include "nsRuleProcessorData.h"
99 :
100 : #ifdef ACCESSIBILITY
101 : #include "nsAccessibilityService.h"
102 : #endif
103 :
104 : #ifdef DEBUG
105 : //#define NOISY_DEBUG
106 : //#define DEBUG_UNDISPLAYED_MAP
107 : #else
108 : #undef NOISY_DEBUG
109 : #undef DEBUG_UNDISPLAYED_MAP
110 : #endif
111 :
112 : #ifdef NOISY_DEBUG
113 : #define NOISY_TRACE(_msg) \
114 : printf("%s",_msg);
115 : #define NOISY_TRACE_FRAME(_msg,_frame) \
116 : printf("%s ",_msg); nsFrame::ListTag(stdout,_frame); printf("\n");
117 : #else
118 : #define NOISY_TRACE(_msg);
119 : #define NOISY_TRACE_FRAME(_msg,_frame);
120 : #endif
121 :
122 : using namespace mozilla;
123 : using namespace mozilla::dom;
124 :
125 : //----------------------------------------------------------------------
126 :
127 : struct PlaceholderMapEntry : public PLDHashEntryHdr {
128 : // key (the out of flow frame) can be obtained through placeholder frame
129 : nsPlaceholderFrame *placeholderFrame;
130 : };
131 :
132 : static bool
133 0 : PlaceholderMapMatchEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr,
134 : const void *key)
135 : {
136 : const PlaceholderMapEntry *entry =
137 0 : static_cast<const PlaceholderMapEntry*>(hdr);
138 0 : NS_ASSERTION(entry->placeholderFrame->GetOutOfFlowFrame() !=
139 : (void*)0xdddddddd,
140 : "Dead placeholder in placeholder map");
141 0 : return entry->placeholderFrame->GetOutOfFlowFrame() == key;
142 : }
143 :
144 : static PLDHashTableOps PlaceholderMapOps = {
145 : PL_DHashAllocTable,
146 : PL_DHashFreeTable,
147 : PL_DHashVoidPtrKeyStub,
148 : PlaceholderMapMatchEntry,
149 : PL_DHashMoveEntryStub,
150 : PL_DHashClearEntryStub,
151 : PL_DHashFinalizeStub,
152 : NULL
153 : };
154 :
155 : //----------------------------------------------------------------------
156 :
157 : // XXXldb This seems too complicated for what I think it's doing, and it
158 : // should also be using pldhash rather than plhash to use less memory.
159 :
160 : class UndisplayedNode {
161 : public:
162 0 : UndisplayedNode(nsIContent* aContent, nsStyleContext* aStyle)
163 : : mContent(aContent),
164 : mStyle(aStyle),
165 0 : mNext(nsnull)
166 : {
167 0 : MOZ_COUNT_CTOR(UndisplayedNode);
168 0 : }
169 :
170 0 : NS_HIDDEN ~UndisplayedNode()
171 0 : {
172 0 : MOZ_COUNT_DTOR(UndisplayedNode);
173 :
174 : // Delete mNext iteratively to avoid blowing up the stack (bug 460461).
175 0 : UndisplayedNode *cur = mNext;
176 0 : while (cur) {
177 0 : UndisplayedNode *next = cur->mNext;
178 0 : cur->mNext = nsnull;
179 0 : delete cur;
180 0 : cur = next;
181 : }
182 0 : }
183 :
184 : nsCOMPtr<nsIContent> mContent;
185 : nsRefPtr<nsStyleContext> mStyle;
186 : UndisplayedNode* mNext;
187 : };
188 :
189 : class nsFrameManagerBase::UndisplayedMap {
190 : public:
191 : UndisplayedMap(PRUint32 aNumBuckets = 16) NS_HIDDEN;
192 : ~UndisplayedMap(void) NS_HIDDEN;
193 :
194 : NS_HIDDEN_(UndisplayedNode*) GetFirstNode(nsIContent* aParentContent);
195 :
196 : NS_HIDDEN_(nsresult) AddNodeFor(nsIContent* aParentContent,
197 : nsIContent* aChild, nsStyleContext* aStyle);
198 :
199 : NS_HIDDEN_(void) RemoveNodeFor(nsIContent* aParentContent,
200 : UndisplayedNode* aNode);
201 :
202 : NS_HIDDEN_(void) RemoveNodesFor(nsIContent* aParentContent);
203 :
204 : // Removes all entries from the hash table
205 : NS_HIDDEN_(void) Clear(void);
206 :
207 : protected:
208 : NS_HIDDEN_(PLHashEntry**) GetEntryFor(nsIContent* aParentContent);
209 : NS_HIDDEN_(void) AppendNodeFor(UndisplayedNode* aNode,
210 : nsIContent* aParentContent);
211 :
212 : PLHashTable* mTable;
213 : PLHashEntry** mLastLookup;
214 : };
215 :
216 : //----------------------------------------------------------------------
217 :
218 0 : nsFrameManager::~nsFrameManager()
219 : {
220 0 : NS_ASSERTION(!mPresShell, "nsFrameManager::Destroy never called");
221 0 : }
222 :
223 : nsresult
224 0 : nsFrameManager::Init(nsStyleSet* aStyleSet)
225 : {
226 0 : if (!mPresShell) {
227 0 : NS_ERROR("null pres shell");
228 0 : return NS_ERROR_FAILURE;
229 : }
230 :
231 0 : if (!aStyleSet) {
232 0 : NS_ERROR("null style set");
233 0 : return NS_ERROR_FAILURE;
234 : }
235 :
236 0 : mStyleSet = aStyleSet;
237 0 : return NS_OK;
238 : }
239 :
240 : void
241 0 : nsFrameManager::Destroy()
242 : {
243 0 : NS_ASSERTION(mPresShell, "Frame manager already shut down.");
244 :
245 : // Destroy the frame hierarchy.
246 0 : mPresShell->SetIgnoreFrameDestruction(true);
247 :
248 : // Unregister all placeholders before tearing down the frame tree
249 0 : nsFrameManager::ClearPlaceholderFrameMap();
250 :
251 0 : if (mRootFrame) {
252 0 : mRootFrame->Destroy();
253 0 : mRootFrame = nsnull;
254 : }
255 :
256 0 : delete mUndisplayedMap;
257 0 : mUndisplayedMap = nsnull;
258 :
259 0 : mPresShell = nsnull;
260 0 : }
261 :
262 : //----------------------------------------------------------------------
263 :
264 : // Placeholder frame functions
265 : nsPlaceholderFrame*
266 0 : nsFrameManager::GetPlaceholderFrameFor(const nsIFrame* aFrame)
267 : {
268 0 : NS_PRECONDITION(aFrame, "null param unexpected");
269 :
270 0 : if (mPlaceholderMap.ops) {
271 : PlaceholderMapEntry *entry = static_cast<PlaceholderMapEntry*>
272 : (PL_DHashTableOperate(const_cast<PLDHashTable*>(&mPlaceholderMap),
273 0 : aFrame, PL_DHASH_LOOKUP));
274 0 : if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
275 0 : return entry->placeholderFrame;
276 : }
277 : }
278 :
279 0 : return nsnull;
280 : }
281 :
282 : nsresult
283 0 : nsFrameManager::RegisterPlaceholderFrame(nsPlaceholderFrame* aPlaceholderFrame)
284 : {
285 0 : NS_PRECONDITION(aPlaceholderFrame, "null param unexpected");
286 0 : NS_PRECONDITION(nsGkAtoms::placeholderFrame == aPlaceholderFrame->GetType(),
287 : "unexpected frame type");
288 0 : if (!mPlaceholderMap.ops) {
289 0 : if (!PL_DHashTableInit(&mPlaceholderMap, &PlaceholderMapOps, nsnull,
290 0 : sizeof(PlaceholderMapEntry), 16)) {
291 0 : mPlaceholderMap.ops = nsnull;
292 0 : return NS_ERROR_OUT_OF_MEMORY;
293 : }
294 : }
295 : PlaceholderMapEntry *entry = static_cast<PlaceholderMapEntry*>(PL_DHashTableOperate(&mPlaceholderMap,
296 0 : aPlaceholderFrame->GetOutOfFlowFrame(),
297 0 : PL_DHASH_ADD));
298 0 : if (!entry)
299 0 : return NS_ERROR_OUT_OF_MEMORY;
300 :
301 0 : NS_ASSERTION(!entry->placeholderFrame, "Registering a placeholder for a frame that already has a placeholder!");
302 0 : entry->placeholderFrame = aPlaceholderFrame;
303 :
304 0 : return NS_OK;
305 : }
306 :
307 : void
308 0 : nsFrameManager::UnregisterPlaceholderFrame(nsPlaceholderFrame* aPlaceholderFrame)
309 : {
310 0 : NS_PRECONDITION(aPlaceholderFrame, "null param unexpected");
311 0 : NS_PRECONDITION(nsGkAtoms::placeholderFrame == aPlaceholderFrame->GetType(),
312 : "unexpected frame type");
313 :
314 0 : if (mPlaceholderMap.ops) {
315 : PL_DHashTableOperate(&mPlaceholderMap,
316 0 : aPlaceholderFrame->GetOutOfFlowFrame(),
317 0 : PL_DHASH_REMOVE);
318 : }
319 0 : }
320 :
321 : static PLDHashOperator
322 0 : UnregisterPlaceholders(PLDHashTable* table, PLDHashEntryHdr* hdr,
323 : PRUint32 number, void* arg)
324 : {
325 0 : PlaceholderMapEntry* entry = static_cast<PlaceholderMapEntry*>(hdr);
326 0 : entry->placeholderFrame->SetOutOfFlowFrame(nsnull);
327 0 : return PL_DHASH_NEXT;
328 : }
329 :
330 : void
331 0 : nsFrameManager::ClearPlaceholderFrameMap()
332 : {
333 0 : if (mPlaceholderMap.ops) {
334 0 : PL_DHashTableEnumerate(&mPlaceholderMap, UnregisterPlaceholders, nsnull);
335 0 : PL_DHashTableFinish(&mPlaceholderMap);
336 0 : mPlaceholderMap.ops = nsnull;
337 : }
338 0 : }
339 :
340 : //----------------------------------------------------------------------
341 :
342 : nsStyleContext*
343 0 : nsFrameManager::GetUndisplayedContent(nsIContent* aContent)
344 : {
345 0 : if (!aContent || !mUndisplayedMap)
346 0 : return nsnull;
347 :
348 0 : nsIContent* parent = aContent->GetParent();
349 0 : for (UndisplayedNode* node = mUndisplayedMap->GetFirstNode(parent);
350 : node; node = node->mNext) {
351 0 : if (node->mContent == aContent)
352 0 : return node->mStyle;
353 : }
354 :
355 0 : return nsnull;
356 : }
357 :
358 : void
359 0 : nsFrameManager::SetUndisplayedContent(nsIContent* aContent,
360 : nsStyleContext* aStyleContext)
361 : {
362 0 : NS_PRECONDITION(!aStyleContext->GetPseudo(),
363 : "Should only have actual elements here");
364 :
365 : #ifdef DEBUG_UNDISPLAYED_MAP
366 : static int i = 0;
367 : printf("SetUndisplayedContent(%d): p=%p \n", i++, (void *)aContent);
368 : #endif
369 :
370 0 : NS_ASSERTION(!GetUndisplayedContent(aContent),
371 : "Already have an undisplayed context entry for aContent");
372 :
373 0 : if (! mUndisplayedMap) {
374 0 : mUndisplayedMap = new UndisplayedMap;
375 : }
376 0 : nsIContent* parent = aContent->GetParent();
377 0 : NS_ASSERTION(parent || (mPresShell && mPresShell->GetDocument() &&
378 : mPresShell->GetDocument()->GetRootElement() == aContent),
379 : "undisplayed content must have a parent, unless it's the root "
380 : "element");
381 0 : mUndisplayedMap->AddNodeFor(parent, aContent, aStyleContext);
382 0 : }
383 :
384 : void
385 0 : nsFrameManager::ChangeUndisplayedContent(nsIContent* aContent,
386 : nsStyleContext* aStyleContext)
387 : {
388 0 : NS_ASSERTION(mUndisplayedMap, "no existing undisplayed content");
389 :
390 : #ifdef DEBUG_UNDISPLAYED_MAP
391 : static int i = 0;
392 : printf("ChangeUndisplayedContent(%d): p=%p \n", i++, (void *)aContent);
393 : #endif
394 :
395 0 : for (UndisplayedNode* node = mUndisplayedMap->GetFirstNode(aContent->GetParent());
396 : node; node = node->mNext) {
397 0 : if (node->mContent == aContent) {
398 0 : node->mStyle = aStyleContext;
399 0 : return;
400 : }
401 : }
402 :
403 0 : NS_NOTREACHED("no existing undisplayed content");
404 : }
405 :
406 : void
407 0 : nsFrameManager::ClearUndisplayedContentIn(nsIContent* aContent,
408 : nsIContent* aParentContent)
409 : {
410 : #ifdef DEBUG_UNDISPLAYED_MAP
411 : static int i = 0;
412 : printf("ClearUndisplayedContent(%d): content=%p parent=%p --> ", i++, (void *)aContent, (void*)aParentContent);
413 : #endif
414 :
415 0 : if (mUndisplayedMap) {
416 0 : UndisplayedNode* node = mUndisplayedMap->GetFirstNode(aParentContent);
417 0 : while (node) {
418 0 : if (node->mContent == aContent) {
419 0 : mUndisplayedMap->RemoveNodeFor(aParentContent, node);
420 :
421 : #ifdef DEBUG_UNDISPLAYED_MAP
422 : printf( "REMOVED!\n");
423 : #endif
424 : #ifdef DEBUG
425 : // make sure that there are no more entries for the same content
426 0 : nsStyleContext *context = GetUndisplayedContent(aContent);
427 0 : NS_ASSERTION(context == nsnull, "Found more undisplayed content data after removal");
428 : #endif
429 0 : return;
430 : }
431 0 : node = node->mNext;
432 : }
433 : }
434 : }
435 :
436 : void
437 0 : nsFrameManager::ClearAllUndisplayedContentIn(nsIContent* aParentContent)
438 : {
439 : #ifdef DEBUG_UNDISPLAYED_MAP
440 : static int i = 0;
441 : printf("ClearAllUndisplayedContentIn(%d): parent=%p \n", i++, (void*)aParentContent);
442 : #endif
443 :
444 0 : if (mUndisplayedMap) {
445 0 : mUndisplayedMap->RemoveNodesFor(aParentContent);
446 : }
447 :
448 : // Need to look at aParentContent's content list due to XBL insertions.
449 : // Nodes in aParentContent's content list do not have aParentContent as a
450 : // parent, but are treated as children of aParentContent. We get access to
451 : // the content list via GetXBLChildNodesFor and just ignore any nodes we
452 : // don't care about.
453 : nsINodeList* list =
454 0 : aParentContent->OwnerDoc()->BindingManager()->GetXBLChildNodesFor(aParentContent);
455 0 : if (list) {
456 : PRUint32 length;
457 0 : list->GetLength(&length);
458 0 : for (PRUint32 i = 0; i < length; ++i) {
459 0 : nsIContent* child = list->GetNodeAt(i);
460 0 : if (child->GetParent() != aParentContent) {
461 0 : ClearUndisplayedContentIn(child, child->GetParent());
462 : }
463 : }
464 : }
465 0 : }
466 :
467 : //----------------------------------------------------------------------
468 : nsresult
469 0 : nsFrameManager::AppendFrames(nsIFrame* aParentFrame,
470 : ChildListID aListID,
471 : nsFrameList& aFrameList)
472 : {
473 0 : if (aParentFrame->IsAbsoluteContainer() &&
474 0 : aListID == aParentFrame->GetAbsoluteListID()) {
475 : return aParentFrame->GetAbsoluteContainingBlock()->
476 0 : AppendFrames(aParentFrame, aListID, aFrameList);
477 : } else {
478 0 : return aParentFrame->AppendFrames(aListID, aFrameList);
479 : }
480 : }
481 :
482 : nsresult
483 0 : nsFrameManager::InsertFrames(nsIFrame* aParentFrame,
484 : ChildListID aListID,
485 : nsIFrame* aPrevFrame,
486 : nsFrameList& aFrameList)
487 : {
488 0 : NS_PRECONDITION(!aPrevFrame || (!aPrevFrame->GetNextContinuation()
489 : || (aPrevFrame->GetNextContinuation()->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER))
490 : && !(aPrevFrame->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER),
491 : "aPrevFrame must be the last continuation in its chain!");
492 :
493 0 : if (aParentFrame->IsAbsoluteContainer() &&
494 0 : aListID == aParentFrame->GetAbsoluteListID()) {
495 : return aParentFrame->GetAbsoluteContainingBlock()->
496 0 : InsertFrames(aParentFrame, aListID, aPrevFrame, aFrameList);
497 : } else {
498 0 : return aParentFrame->InsertFrames(aListID, aPrevFrame, aFrameList);
499 : }
500 : }
501 :
502 : nsresult
503 0 : nsFrameManager::RemoveFrame(ChildListID aListID,
504 : nsIFrame* aOldFrame)
505 : {
506 0 : bool wasDestroyingFrames = mIsDestroyingFrames;
507 0 : mIsDestroyingFrames = true;
508 :
509 : // In case the reflow doesn't invalidate anything since it just leaves
510 : // a gap where the old frame was, we invalidate it here. (This is
511 : // reasonably likely to happen when removing a last child in a way
512 : // that doesn't change the size of the parent.)
513 : // This has to sure to invalidate the entire overflow rect; this
514 : // is important in the presence of absolute positioning
515 0 : aOldFrame->InvalidateFrameSubtree();
516 :
517 0 : NS_ASSERTION(!aOldFrame->GetPrevContinuation() ||
518 : // exception for nsCSSFrameConstructor::RemoveFloatingFirstLetterFrames
519 : aOldFrame->GetType() == nsGkAtoms::textFrame,
520 : "Must remove first continuation.");
521 0 : NS_ASSERTION(!(aOldFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW &&
522 : GetPlaceholderFrameFor(aOldFrame)),
523 : "Must call RemoveFrame on placeholder for out-of-flows.");
524 0 : nsresult rv = NS_OK;
525 0 : nsIFrame* parentFrame = aOldFrame->GetParent();
526 0 : if (parentFrame->IsAbsoluteContainer() &&
527 0 : aListID == parentFrame->GetAbsoluteListID()) {
528 : parentFrame->GetAbsoluteContainingBlock()->
529 0 : RemoveFrame(parentFrame, aListID, aOldFrame);
530 : } else {
531 0 : rv = parentFrame->RemoveFrame(aListID, aOldFrame);
532 : }
533 :
534 0 : mIsDestroyingFrames = wasDestroyingFrames;
535 :
536 0 : return rv;
537 : }
538 :
539 : //----------------------------------------------------------------------
540 :
541 : void
542 0 : nsFrameManager::NotifyDestroyingFrame(nsIFrame* aFrame)
543 : {
544 0 : nsIContent* content = aFrame->GetContent();
545 0 : if (content && content->GetPrimaryFrame() == aFrame) {
546 0 : ClearAllUndisplayedContentIn(content);
547 : }
548 0 : }
549 :
550 : #ifdef NS_DEBUG
551 : static void
552 0 : DumpContext(nsIFrame* aFrame, nsStyleContext* aContext)
553 : {
554 0 : if (aFrame) {
555 0 : fputs("frame: ", stdout);
556 0 : nsAutoString name;
557 0 : aFrame->GetFrameName(name);
558 0 : fputs(NS_LossyConvertUTF16toASCII(name).get(), stdout);
559 0 : fprintf(stdout, " (%p)", static_cast<void*>(aFrame));
560 : }
561 0 : if (aContext) {
562 0 : fprintf(stdout, " style: %p ", static_cast<void*>(aContext));
563 :
564 0 : nsIAtom* pseudoTag = aContext->GetPseudo();
565 0 : if (pseudoTag) {
566 0 : nsAutoString buffer;
567 0 : pseudoTag->ToString(buffer);
568 0 : fputs(NS_LossyConvertUTF16toASCII(buffer).get(), stdout);
569 0 : fputs(" ", stdout);
570 : }
571 0 : fputs("{}\n", stdout);
572 : }
573 0 : }
574 :
575 : static void
576 0 : VerifySameTree(nsStyleContext* aContext1, nsStyleContext* aContext2)
577 : {
578 0 : nsStyleContext* top1 = aContext1;
579 0 : nsStyleContext* top2 = aContext2;
580 : nsStyleContext* parent;
581 0 : for (;;) {
582 0 : parent = top1->GetParent();
583 0 : if (!parent)
584 0 : break;
585 0 : top1 = parent;
586 : }
587 0 : for (;;) {
588 0 : parent = top2->GetParent();
589 0 : if (!parent)
590 : break;
591 0 : top2 = parent;
592 : }
593 0 : NS_ASSERTION(top1 == top2,
594 : "Style contexts are not in the same style context tree");
595 0 : }
596 :
597 : static void
598 0 : VerifyContextParent(nsPresContext* aPresContext, nsIFrame* aFrame,
599 : nsStyleContext* aContext, nsStyleContext* aParentContext)
600 : {
601 : // get the contexts not provided
602 0 : if (!aContext) {
603 0 : aContext = aFrame->GetStyleContext();
604 : }
605 :
606 0 : if (!aParentContext) {
607 : // Get the correct parent context from the frame
608 : // - if the frame is a placeholder, we get the out of flow frame's context
609 : // as the parent context instead of asking the frame
610 :
611 : // get the parent context from the frame (indirectly)
612 0 : nsIFrame* providerFrame = aFrame->GetParentStyleContextFrame();
613 0 : if (providerFrame)
614 0 : aParentContext = providerFrame->GetStyleContext();
615 : // aParentContext could still be null
616 : }
617 :
618 0 : NS_ASSERTION(aContext, "Failure to get required contexts");
619 0 : nsStyleContext* actualParentContext = aContext->GetParent();
620 :
621 0 : if (aParentContext) {
622 0 : if (aParentContext != actualParentContext) {
623 0 : DumpContext(aFrame, aContext);
624 0 : if (aContext == aParentContext) {
625 0 : NS_ERROR("Using parent's style context");
626 : }
627 : else {
628 0 : NS_ERROR("Wrong parent style context");
629 0 : fputs("Wrong parent style context: ", stdout);
630 0 : DumpContext(nsnull, actualParentContext);
631 0 : fputs("should be using: ", stdout);
632 0 : DumpContext(nsnull, aParentContext);
633 0 : VerifySameTree(actualParentContext, aParentContext);
634 0 : fputs("\n", stdout);
635 : }
636 : }
637 :
638 : }
639 : else {
640 0 : if (actualParentContext) {
641 0 : NS_ERROR("Have parent context and shouldn't");
642 0 : DumpContext(aFrame, aContext);
643 0 : fputs("Has parent context: ", stdout);
644 0 : DumpContext(nsnull, actualParentContext);
645 0 : fputs("Should be null\n\n", stdout);
646 : }
647 : }
648 :
649 0 : nsStyleContext* childStyleIfVisited = aContext->GetStyleIfVisited();
650 : // Either childStyleIfVisited has aContext->GetParent()->GetStyleIfVisited()
651 : // as the parent or it has a different rulenode from aContext _and_ has
652 : // aContext->GetParent() as the parent.
653 0 : if (childStyleIfVisited &&
654 0 : !((childStyleIfVisited->GetRuleNode() != aContext->GetRuleNode() &&
655 0 : childStyleIfVisited->GetParent() == aContext->GetParent()) ||
656 0 : childStyleIfVisited->GetParent() ==
657 0 : aContext->GetParent()->GetStyleIfVisited())) {
658 0 : NS_ERROR("Visited style has wrong parent");
659 0 : DumpContext(aFrame, aContext);
660 0 : fputs("\n", stdout);
661 : }
662 0 : }
663 :
664 : static void
665 0 : VerifyStyleTree(nsPresContext* aPresContext, nsIFrame* aFrame,
666 : nsStyleContext* aParentContext)
667 : {
668 0 : nsStyleContext* context = aFrame->GetStyleContext();
669 0 : VerifyContextParent(aPresContext, aFrame, context, nsnull);
670 :
671 0 : nsIFrame::ChildListIterator lists(aFrame);
672 0 : for (; !lists.IsDone(); lists.Next()) {
673 0 : nsFrameList::Enumerator childFrames(lists.CurrentList());
674 0 : for (; !childFrames.AtEnd(); childFrames.Next()) {
675 0 : nsIFrame* child = childFrames.get();
676 0 : if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
677 : // only do frames that are in flow
678 0 : if (nsGkAtoms::placeholderFrame == child->GetType()) {
679 : // placeholder: first recurse and verify the out of flow frame,
680 : // then verify the placeholder's context
681 : nsIFrame* outOfFlowFrame =
682 0 : nsPlaceholderFrame::GetRealFrameForPlaceholder(child);
683 :
684 : // recurse to out of flow frame, letting the parent context get resolved
685 0 : do {
686 0 : VerifyStyleTree(aPresContext, outOfFlowFrame, nsnull);
687 0 : } while ((outOfFlowFrame = outOfFlowFrame->GetNextContinuation()));
688 :
689 : // verify placeholder using the parent frame's context as
690 : // parent context
691 0 : VerifyContextParent(aPresContext, child, nsnull, nsnull);
692 : }
693 : else { // regular frame
694 0 : VerifyStyleTree(aPresContext, child, nsnull);
695 : }
696 : }
697 : }
698 : }
699 :
700 : // do additional contexts
701 0 : PRInt32 contextIndex = -1;
702 0 : while (1) {
703 0 : nsStyleContext* extraContext = aFrame->GetAdditionalStyleContext(++contextIndex);
704 0 : if (extraContext) {
705 0 : VerifyContextParent(aPresContext, aFrame, extraContext, context);
706 : }
707 : else {
708 : break;
709 : }
710 : }
711 0 : }
712 :
713 : void
714 0 : nsFrameManager::DebugVerifyStyleTree(nsIFrame* aFrame)
715 : {
716 0 : if (aFrame) {
717 0 : nsStyleContext* context = aFrame->GetStyleContext();
718 0 : nsStyleContext* parentContext = context->GetParent();
719 0 : VerifyStyleTree(GetPresContext(), aFrame, parentContext);
720 : }
721 0 : }
722 :
723 : #endif // DEBUG
724 :
725 : // aContent must be the content for the frame in question, which may be
726 : // :before/:after content
727 : static void
728 0 : TryStartingTransition(nsPresContext *aPresContext, nsIContent *aContent,
729 : nsStyleContext *aOldStyleContext,
730 : nsRefPtr<nsStyleContext> *aNewStyleContext /* inout */)
731 : {
732 0 : if (!aContent || !aContent->IsElement()) {
733 0 : return;
734 : }
735 :
736 : // Notify the transition manager, and if it starts a transition,
737 : // it will give us back a transition-covering style rule which
738 : // we'll use to get *another* style context. We want to ignore
739 : // any already-running transitions, but cover up any that we're
740 : // currently starting with their start value so we don't start
741 : // them again for descendants that inherit that value.
742 : nsCOMPtr<nsIStyleRule> coverRule =
743 : aPresContext->TransitionManager()->StyleContextChanged(
744 0 : aContent->AsElement(), aOldStyleContext, *aNewStyleContext);
745 0 : if (coverRule) {
746 0 : nsCOMArray<nsIStyleRule> rules;
747 0 : rules.AppendObject(coverRule);
748 : *aNewStyleContext = aPresContext->StyleSet()->
749 0 : ResolveStyleByAddingRules(*aNewStyleContext, rules);
750 : }
751 : }
752 :
753 : static inline Element*
754 0 : ElementForStyleContext(nsIContent* aParentContent,
755 : nsIFrame* aFrame,
756 : nsCSSPseudoElements::Type aPseudoType)
757 : {
758 : // We don't expect XUL tree stuff here.
759 0 : NS_PRECONDITION(aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement ||
760 : aPseudoType == nsCSSPseudoElements::ePseudo_AnonBox ||
761 : aPseudoType < nsCSSPseudoElements::ePseudo_PseudoElementCount,
762 : "Unexpected pseudo");
763 : // XXX see the comments about the various element confusion in
764 : // ReResolveStyleContext.
765 0 : if (aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement) {
766 0 : return aFrame->GetContent()->AsElement();
767 : }
768 :
769 0 : if (aPseudoType == nsCSSPseudoElements::ePseudo_AnonBox) {
770 0 : return nsnull;
771 : }
772 :
773 0 : if (aPseudoType == nsCSSPseudoElements::ePseudo_firstLetter) {
774 0 : NS_ASSERTION(aFrame->GetType() == nsGkAtoms::letterFrame,
775 : "firstLetter pseudoTag without a nsFirstLetterFrame");
776 0 : nsBlockFrame* block = nsBlockFrame::GetNearestAncestorBlock(aFrame);
777 0 : return block->GetContent()->AsElement();
778 : }
779 :
780 0 : nsIContent* content = aParentContent ? aParentContent : aFrame->GetContent();
781 0 : return content->AsElement();
782 : }
783 :
784 : static nsIFrame*
785 0 : GetPrevContinuationWithPossiblySameStyle(nsIFrame* aFrame)
786 : {
787 : // Account for {ib} splits when looking for "prevContinuation". In
788 : // particular, for the first-continuation of a part of an {ib} split we
789 : // want to use the special prevsibling of the special prevsibling of
790 : // aFrame, which should have the same style context as aFrame itself.
791 : // In particular, if aFrame is the first continuation of an inline part
792 : // of an {ib} split then its special prevsibling is a block, and the
793 : // special prevsibling of _that_ is an inline, just like aFrame.
794 : // Similarly, if aFrame is the first continuation of a block part of an
795 : // {ib} split (an {ib} wrapper block), then its special prevsibling is
796 : // an inline and the special prevsibling of that is either another {ib}
797 : // wrapper block block or null.
798 0 : nsIFrame *prevContinuation = aFrame->GetPrevContinuation();
799 0 : if (!prevContinuation && (aFrame->GetStateBits() & NS_FRAME_IS_SPECIAL)) {
800 : // We're the first continuation, so we can just get the frame
801 : // property directly
802 : prevContinuation = static_cast<nsIFrame*>(
803 0 : aFrame->Properties().Get(nsIFrame::IBSplitSpecialPrevSibling()));
804 0 : if (prevContinuation) {
805 : prevContinuation = static_cast<nsIFrame*>(
806 0 : prevContinuation->Properties().Get(nsIFrame::IBSplitSpecialPrevSibling()));
807 : }
808 : }
809 0 : return prevContinuation;
810 : }
811 :
812 : nsresult
813 0 : nsFrameManager::ReparentStyleContext(nsIFrame* aFrame)
814 : {
815 0 : if (nsGkAtoms::placeholderFrame == aFrame->GetType()) {
816 : // Also reparent the out-of-flow and all its continuations.
817 : nsIFrame* outOfFlow =
818 0 : nsPlaceholderFrame::GetRealFrameForPlaceholder(aFrame);
819 0 : NS_ASSERTION(outOfFlow, "no out-of-flow frame");
820 0 : do {
821 0 : ReparentStyleContext(outOfFlow);
822 0 : } while ((outOfFlow = outOfFlow->GetNextContinuation()));
823 : }
824 :
825 : // DO NOT verify the style tree before reparenting. The frame
826 : // tree has already been changed, so this check would just fail.
827 0 : nsStyleContext* oldContext = aFrame->GetStyleContext();
828 : // XXXbz can oldContext really ever be null?
829 0 : if (oldContext) {
830 0 : nsRefPtr<nsStyleContext> newContext;
831 0 : nsIFrame* providerFrame = aFrame->GetParentStyleContextFrame();
832 0 : bool isChild = providerFrame && providerFrame->GetParent() == aFrame;
833 0 : nsStyleContext* newParentContext = nsnull;
834 0 : nsIFrame* providerChild = nsnull;
835 0 : if (isChild) {
836 0 : ReparentStyleContext(providerFrame);
837 0 : newParentContext = providerFrame->GetStyleContext();
838 0 : providerChild = providerFrame;
839 0 : } else if (providerFrame) {
840 0 : newParentContext = providerFrame->GetStyleContext();
841 : } else {
842 : NS_NOTREACHED("Reparenting something that has no usable parent? "
843 0 : "Shouldn't happen!");
844 : }
845 : // XXX need to do something here to produce the correct style context for
846 : // an IB split whose first inline part is inside a first-line frame.
847 : // Currently the first IB anonymous block's style context takes the first
848 : // part's style context as parent, which is wrong since first-line style
849 : // should not apply to the anonymous block.
850 :
851 : #ifdef DEBUG
852 : {
853 : // Check that our assumption that continuations of the same
854 : // pseudo-type and with the same style context parent have the
855 : // same style context is valid before the reresolution. (We need
856 : // to check the pseudo-type and style context parent because of
857 : // :first-letter and :first-line, where we create styled and
858 : // unstyled letter/line frames distinguished by pseudo-type, and
859 : // then need to distinguish their descendants based on having
860 : // different parents.)
861 0 : nsIFrame *nextContinuation = aFrame->GetNextContinuation();
862 0 : if (nextContinuation) {
863 : nsStyleContext *nextContinuationContext =
864 0 : nextContinuation->GetStyleContext();
865 0 : NS_ASSERTION(oldContext == nextContinuationContext ||
866 : oldContext->GetPseudo() !=
867 : nextContinuationContext->GetPseudo() ||
868 : oldContext->GetParent() !=
869 : nextContinuationContext->GetParent(),
870 : "continuations should have the same style context");
871 : }
872 : }
873 : #endif
874 :
875 : nsIFrame *prevContinuation =
876 0 : GetPrevContinuationWithPossiblySameStyle(aFrame);
877 : nsStyleContext *prevContinuationContext;
878 : bool copyFromContinuation =
879 : prevContinuation &&
880 : (prevContinuationContext = prevContinuation->GetStyleContext())
881 0 : ->GetPseudo() == oldContext->GetPseudo() &&
882 0 : prevContinuationContext->GetParent() == newParentContext;
883 0 : if (copyFromContinuation) {
884 : // Just use the style context from the frame's previous
885 : // continuation (see assertion about aFrame->GetNextContinuation()
886 : // above, which we would have previously hit for aFrame's previous
887 : // continuation).
888 0 : newContext = prevContinuationContext;
889 : } else {
890 0 : nsIFrame* parentFrame = aFrame->GetParent();
891 : Element* element =
892 : ElementForStyleContext(parentFrame ? parentFrame->GetContent() : nsnull,
893 : aFrame,
894 0 : oldContext->GetPseudoType());
895 : newContext = mStyleSet->ReparentStyleContext(oldContext,
896 : newParentContext,
897 0 : element);
898 : }
899 :
900 0 : if (newContext) {
901 0 : if (newContext != oldContext) {
902 : // We probably don't want to initiate transitions from
903 : // ReparentStyleContext, since we call it during frame
904 : // construction rather than in response to dynamic changes.
905 : // Also see the comment at the start of
906 : // nsTransitionManager::ConsiderStartingTransition.
907 : #if 0
908 : if (!copyFromContinuation) {
909 : TryStartingTransition(GetPresContext(), aFrame->GetContent(),
910 : oldContext, &newContext);
911 : }
912 : #endif
913 :
914 : // Make sure to call CalcStyleDifference so that the new context ends
915 : // up resolving all the structs the old context resolved.
916 0 : nsChangeHint styleChange = oldContext->CalcStyleDifference(newContext);
917 : // The style change is always 0 because we have the same rulenode and
918 : // CalcStyleDifference optimizes us away. That's OK, though:
919 : // reparenting should never trigger a frame reconstruct, and whenever
920 : // it's happening we already plan to reflow and repaint the frames.
921 0 : NS_ASSERTION(!(styleChange & nsChangeHint_ReconstructFrame),
922 : "Our frame tree is likely to be bogus!");
923 :
924 0 : aFrame->SetStyleContext(newContext);
925 :
926 0 : nsIFrame::ChildListIterator lists(aFrame);
927 0 : for (; !lists.IsDone(); lists.Next()) {
928 0 : nsFrameList::Enumerator childFrames(lists.CurrentList());
929 0 : for (; !childFrames.AtEnd(); childFrames.Next()) {
930 0 : nsIFrame* child = childFrames.get();
931 : // only do frames that are in flow
932 0 : if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
933 : child != providerChild) {
934 : #ifdef DEBUG
935 0 : if (nsGkAtoms::placeholderFrame == child->GetType()) {
936 : nsIFrame* outOfFlowFrame =
937 0 : nsPlaceholderFrame::GetRealFrameForPlaceholder(child);
938 0 : NS_ASSERTION(outOfFlowFrame, "no out-of-flow frame");
939 :
940 0 : NS_ASSERTION(outOfFlowFrame != providerChild,
941 : "Out of flow provider?");
942 : }
943 : #endif
944 0 : ReparentStyleContext(child);
945 : }
946 : }
947 : }
948 :
949 : // If this frame is part of an IB split, then the style context of
950 : // the next part of the split might be a child of our style context.
951 : // Reparent its style context just in case one of our ancestors
952 : // (split or not) hasn't done so already). It's not a problem to
953 : // reparent the same frame twice because the "if (newContext !=
954 : // oldContext)" check will prevent us from redoing work.
955 0 : if ((aFrame->GetStateBits() & NS_FRAME_IS_SPECIAL) &&
956 0 : !aFrame->GetPrevContinuation()) {
957 : nsIFrame* sib = static_cast<nsIFrame*>
958 0 : (aFrame->Properties().Get(nsIFrame::IBSplitSpecialSibling()));
959 0 : if (sib) {
960 0 : ReparentStyleContext(sib);
961 : }
962 : }
963 :
964 : // do additional contexts
965 0 : PRInt32 contextIndex = -1;
966 0 : while (1) {
967 : nsStyleContext* oldExtraContext =
968 0 : aFrame->GetAdditionalStyleContext(++contextIndex);
969 0 : if (oldExtraContext) {
970 0 : nsRefPtr<nsStyleContext> newExtraContext;
971 : newExtraContext = mStyleSet->ReparentStyleContext(oldExtraContext,
972 : newContext,
973 0 : nsnull);
974 0 : if (newExtraContext) {
975 0 : if (newExtraContext != oldExtraContext) {
976 : // Make sure to call CalcStyleDifference so that the new
977 : // context ends up resolving all the structs the old context
978 : // resolved.
979 : styleChange =
980 0 : oldExtraContext->CalcStyleDifference(newExtraContext);
981 : // The style change is always 0 because we have the same
982 : // rulenode and CalcStyleDifference optimizes us away. That's
983 : // OK, though: reparenting should never trigger a frame
984 : // reconstruct, and whenever it's happening we already plan to
985 : // reflow and repaint the frames.
986 0 : NS_ASSERTION(!(styleChange & nsChangeHint_ReconstructFrame),
987 : "Our frame tree is likely to be bogus!");
988 : }
989 :
990 0 : aFrame->SetAdditionalStyleContext(contextIndex, newExtraContext);
991 : }
992 : }
993 : else {
994 : break;
995 : }
996 : }
997 : #ifdef DEBUG
998 0 : VerifyStyleTree(GetPresContext(), aFrame, newParentContext);
999 : #endif
1000 : }
1001 : }
1002 : }
1003 0 : return NS_OK;
1004 : }
1005 :
1006 : static nsChangeHint
1007 0 : CaptureChange(nsStyleContext* aOldContext, nsStyleContext* aNewContext,
1008 : nsIFrame* aFrame, nsIContent* aContent,
1009 : nsStyleChangeList* aChangeList, nsChangeHint aMinChange,
1010 : nsChangeHint aChangeToAssume)
1011 : {
1012 0 : nsChangeHint ourChange = aOldContext->CalcStyleDifference(aNewContext);
1013 0 : NS_ASSERTION(!(ourChange & nsChangeHint_ReflowFrame) ||
1014 : (ourChange & nsChangeHint_NeedReflow),
1015 : "Reflow hint bits set without actually asking for a reflow");
1016 :
1017 0 : NS_UpdateHint(ourChange, aChangeToAssume);
1018 0 : if (NS_UpdateHint(aMinChange, ourChange)) {
1019 0 : if (!(ourChange & nsChangeHint_ReconstructFrame) || aContent) {
1020 0 : aChangeList->AppendChange(aFrame, aContent, ourChange);
1021 : }
1022 : }
1023 0 : return aMinChange;
1024 : }
1025 :
1026 : /**
1027 : * Recompute style for aFrame and accumulate changes into aChangeList
1028 : * given that aMinChange is already accumulated for an ancestor.
1029 : * aParentContent is the content node used to resolve the parent style
1030 : * context. This means that, for pseudo-elements, it is the content
1031 : * that should be used for selector matching (rather than the fake
1032 : * content node attached to the frame).
1033 : */
1034 : nsChangeHint
1035 0 : nsFrameManager::ReResolveStyleContext(nsPresContext *aPresContext,
1036 : nsIFrame *aFrame,
1037 : nsIContent *aParentContent,
1038 : nsStyleChangeList *aChangeList,
1039 : nsChangeHint aMinChange,
1040 : nsRestyleHint aRestyleHint,
1041 : RestyleTracker& aRestyleTracker,
1042 : DesiredA11yNotifications aDesiredA11yNotifications,
1043 : nsTArray<nsIContent*>& aVisibleKidsOfHiddenElement,
1044 : TreeMatchContext &aTreeMatchContext)
1045 : {
1046 0 : if (!NS_IsHintSubset(nsChangeHint_NeedDirtyReflow, aMinChange)) {
1047 : // If aMinChange doesn't include nsChangeHint_NeedDirtyReflow, clear out
1048 : // all the reflow change bits from it, so that we'll make sure to append a
1049 : // change to the list for ourselves if we need a reflow. We need this
1050 : // because the parent may or may not actually end up reflowing us
1051 : // otherwise.
1052 0 : aMinChange = NS_SubtractHint(aMinChange, nsChangeHint_ReflowFrame);
1053 0 : } else if (!NS_IsHintSubset(nsChangeHint_ClearDescendantIntrinsics,
1054 0 : aMinChange)) {
1055 : // If aMinChange doesn't include nsChangeHint_ClearDescendantIntrinsics,
1056 : // clear out the nsChangeHint_ClearAncestorIntrinsics flag, since it's
1057 : // possible that we had some random ancestor that cleared ancestor
1058 : // intrinsic widths, but we still need to clear intrinsic widths on frames
1059 : // that are our ancestors but its descendants.
1060 : aMinChange =
1061 0 : NS_SubtractHint(aMinChange, nsChangeHint_ClearAncestorIntrinsics);
1062 : }
1063 :
1064 : // We need to generate a new change list entry for every frame whose style
1065 : // comparision returns one of these hints. These hints don't automatically
1066 : // update all their descendant frames.
1067 0 : aMinChange = NS_SubtractHint(aMinChange, nsChangeHint_UpdateTransformLayer);
1068 0 : aMinChange = NS_SubtractHint(aMinChange, nsChangeHint_UpdateOpacityLayer);
1069 0 : aMinChange = NS_SubtractHint(aMinChange, nsChangeHint_UpdateOverflow);
1070 :
1071 : // It would be nice if we could make stronger assertions here; they
1072 : // would let us simplify the ?: expressions below setting |content|
1073 : // and |pseudoContent| in sensible ways as well as making what
1074 : // |localContent|, |content|, and |pseudoContent| mean make more
1075 : // sense. However, we can't, because of frame trees like the one in
1076 : // https://bugzilla.mozilla.org/show_bug.cgi?id=472353#c14 . Once we
1077 : // fix bug 242277 we should be able to make this make more sense.
1078 0 : NS_ASSERTION(aFrame->GetContent() || !aParentContent ||
1079 : !aParentContent->GetParent(),
1080 : "frame must have content (unless at the top of the tree)");
1081 : // XXXldb get new context from prev-in-flow if possible, to avoid
1082 : // duplication. (Or should we just let |GetContext| handle that?)
1083 : // Getting the hint would be nice too, but that's harder.
1084 :
1085 : // XXXbryner we may be able to avoid some of the refcounting goop here.
1086 : // We do need a reference to oldContext for the lifetime of this function, and it's possible
1087 : // that the frame has the last reference to it, so AddRef it here.
1088 :
1089 0 : nsChangeHint assumeDifferenceHint = NS_STYLE_HINT_NONE;
1090 : // XXXbz oldContext should just be an nsRefPtr
1091 0 : nsStyleContext* oldContext = aFrame->GetStyleContext();
1092 0 : nsStyleSet* styleSet = aPresContext->StyleSet();
1093 :
1094 : // XXXbz the nsIFrame constructor takes an nsStyleContext, so how
1095 : // could oldContext be null?
1096 0 : if (oldContext) {
1097 0 : oldContext->AddRef();
1098 :
1099 : #ifdef ACCESSIBILITY
1100 0 : bool wasFrameVisible = nsIPresShell::IsAccessibilityActive() ?
1101 0 : oldContext->GetStyleVisibility()->IsVisible() : false;
1102 : #endif
1103 :
1104 0 : nsIAtom* const pseudoTag = oldContext->GetPseudo();
1105 0 : const nsCSSPseudoElements::Type pseudoType = oldContext->GetPseudoType();
1106 0 : nsIContent* localContent = aFrame->GetContent();
1107 : // |content| is the node that we used for rule matching of
1108 : // normal elements (not pseudo-elements) and for which we generate
1109 : // framechange hints if we need them.
1110 : // XXXldb Why does it make sense to use aParentContent? (See
1111 : // comment above assertion at start of function.)
1112 0 : nsIContent* content = localContent ? localContent : aParentContent;
1113 :
1114 0 : if (content && content->IsElement()) {
1115 0 : content->OwnerDoc()->FlushPendingLinkUpdates();
1116 : RestyleTracker::RestyleData restyleData;
1117 0 : if (aRestyleTracker.GetRestyleData(content->AsElement(), &restyleData)) {
1118 0 : if (NS_UpdateHint(aMinChange, restyleData.mChangeHint)) {
1119 0 : aChangeList->AppendChange(aFrame, content, restyleData.mChangeHint);
1120 : }
1121 0 : aRestyleHint = nsRestyleHint(aRestyleHint | restyleData.mRestyleHint);
1122 : }
1123 : }
1124 :
1125 0 : nsRestyleHint childRestyleHint = aRestyleHint;
1126 :
1127 0 : if (childRestyleHint == eRestyle_Self) {
1128 0 : childRestyleHint = nsRestyleHint(0);
1129 : }
1130 :
1131 : nsStyleContext* parentContext;
1132 0 : nsIFrame* resolvedChild = nsnull;
1133 : // Get the frame providing the parent style context. If it is a
1134 : // child, then resolve the provider first.
1135 0 : nsIFrame* providerFrame = aFrame->GetParentStyleContextFrame();
1136 0 : bool isChild = providerFrame && providerFrame->GetParent() == aFrame;
1137 0 : if (!isChild) {
1138 0 : if (providerFrame)
1139 0 : parentContext = providerFrame->GetStyleContext();
1140 : else
1141 0 : parentContext = nsnull;
1142 : }
1143 : else {
1144 0 : MOZ_ASSERT(providerFrame->GetContent() == aFrame->GetContent(),
1145 : "Postcondition for GetParentStyleContextFrame() violated. "
1146 : "That means we need to add the current element to the "
1147 0 : "ancestor filter.");
1148 :
1149 : // resolve the provider here (before aFrame below).
1150 :
1151 : // assumeDifferenceHint forces the parent's change to be also
1152 : // applied to this frame, no matter what
1153 : // nsStyleContext::CalcStyleDifference says. CalcStyleDifference
1154 : // can't be trusted because it assumes any changes to the parent
1155 : // style context provider will be automatically propagated to
1156 : // the frame(s) with child style contexts.
1157 :
1158 : assumeDifferenceHint = ReResolveStyleContext(aPresContext, providerFrame,
1159 : aParentContent, aChangeList,
1160 : aMinChange, aRestyleHint,
1161 : aRestyleTracker,
1162 : aDesiredA11yNotifications,
1163 : aVisibleKidsOfHiddenElement,
1164 0 : aTreeMatchContext);
1165 :
1166 : // The provider's new context becomes the parent context of
1167 : // aFrame's context.
1168 0 : parentContext = providerFrame->GetStyleContext();
1169 : // Set |resolvedChild| so we don't bother resolving the
1170 : // provider again.
1171 0 : resolvedChild = providerFrame;
1172 : }
1173 :
1174 : #ifdef DEBUG
1175 : {
1176 : // Check that our assumption that continuations of the same
1177 : // pseudo-type and with the same style context parent have the
1178 : // same style context is valid before the reresolution. (We need
1179 : // to check the pseudo-type and style context parent because of
1180 : // :first-letter and :first-line, where we create styled and
1181 : // unstyled letter/line frames distinguished by pseudo-type, and
1182 : // then need to distinguish their descendants based on having
1183 : // different parents.)
1184 0 : nsIFrame *nextContinuation = aFrame->GetNextContinuation();
1185 0 : if (nextContinuation) {
1186 : nsStyleContext *nextContinuationContext =
1187 0 : nextContinuation->GetStyleContext();
1188 0 : NS_ASSERTION(oldContext == nextContinuationContext ||
1189 : oldContext->GetPseudo() !=
1190 : nextContinuationContext->GetPseudo() ||
1191 : oldContext->GetParent() !=
1192 : nextContinuationContext->GetParent(),
1193 : "continuations should have the same style context");
1194 : }
1195 : // And assert the same thing for {ib} splits. See the comments in
1196 : // GetPrevContinuationWithPossiblySameStyle for an explanation of
1197 : // why we step two forward in the special sibling chain.
1198 0 : if ((aFrame->GetStateBits() & NS_FRAME_IS_SPECIAL) &&
1199 0 : !aFrame->GetPrevContinuation()) {
1200 : nsIFrame *nextIBSibling = static_cast<nsIFrame*>(
1201 0 : aFrame->Properties().Get(nsIFrame::IBSplitSpecialSibling()));
1202 0 : if (nextIBSibling) {
1203 : nextIBSibling = static_cast<nsIFrame*>(
1204 0 : nextIBSibling->Properties().Get(nsIFrame::IBSplitSpecialSibling()));
1205 : }
1206 0 : if (nextIBSibling) {
1207 : nsStyleContext *nextIBSiblingContext =
1208 0 : nextIBSibling->GetStyleContext();
1209 0 : NS_ASSERTION(oldContext == nextIBSiblingContext ||
1210 : oldContext->GetPseudo() !=
1211 : nextIBSiblingContext->GetPseudo() ||
1212 : oldContext->GetParent() !=
1213 : nextIBSiblingContext->GetParent(),
1214 : "continuations should have the same style context");
1215 : }
1216 : }
1217 : }
1218 : #endif
1219 :
1220 : // do primary context
1221 0 : nsRefPtr<nsStyleContext> newContext;
1222 : nsIFrame *prevContinuation =
1223 0 : GetPrevContinuationWithPossiblySameStyle(aFrame);
1224 : nsStyleContext *prevContinuationContext;
1225 : bool copyFromContinuation =
1226 : prevContinuation &&
1227 : (prevContinuationContext = prevContinuation->GetStyleContext())
1228 0 : ->GetPseudo() == oldContext->GetPseudo() &&
1229 0 : prevContinuationContext->GetParent() == parentContext;
1230 0 : if (copyFromContinuation) {
1231 : // Just use the style context from the frame's previous
1232 : // continuation (see assertion about aFrame->GetNextContinuation()
1233 : // above, which we would have previously hit for aFrame's previous
1234 : // continuation).
1235 0 : newContext = prevContinuationContext;
1236 : }
1237 0 : else if (pseudoTag == nsCSSAnonBoxes::mozNonElement) {
1238 0 : NS_ASSERTION(localContent,
1239 : "non pseudo-element frame without content node");
1240 0 : newContext = styleSet->ResolveStyleForNonElement(parentContext);
1241 : }
1242 0 : else if (!aRestyleHint && !prevContinuation) {
1243 : // Unfortunately, if prevContinuation is non-null then we may have
1244 : // already stolen the restyle tracker entry for this element while
1245 : // processing prevContinuation. So we don't know whether aRestyleHint
1246 : // should really be 0 here or whether it should be eRestyle_Self. Be
1247 : // pessimistic and force an actual reresolve in that situation. The good
1248 : // news is that in the common case when prevContinuation is non-null we
1249 : // just used prevContinuationContext anyway and aren't reaching this code
1250 : // to start with.
1251 : newContext =
1252 : styleSet->ReparentStyleContext(oldContext, parentContext,
1253 : ElementForStyleContext(aParentContent,
1254 : aFrame,
1255 0 : pseudoType));
1256 0 : } else if (pseudoType == nsCSSPseudoElements::ePseudo_AnonBox) {
1257 : newContext = styleSet->ResolveAnonymousBoxStyle(pseudoTag,
1258 0 : parentContext);
1259 : }
1260 : else {
1261 : Element* element = ElementForStyleContext(aParentContent,
1262 : aFrame,
1263 0 : pseudoType);
1264 0 : if (pseudoTag) {
1265 0 : if (pseudoTag == nsCSSPseudoElements::before ||
1266 : pseudoTag == nsCSSPseudoElements::after) {
1267 : // XXX what other pseudos do we need to treat like this?
1268 : newContext = styleSet->ProbePseudoElementStyle(element,
1269 : pseudoType,
1270 : parentContext,
1271 0 : aTreeMatchContext);
1272 0 : if (!newContext) {
1273 : // This pseudo should no longer exist; gotta reframe
1274 0 : NS_UpdateHint(aMinChange, nsChangeHint_ReconstructFrame);
1275 : aChangeList->AppendChange(aFrame, element,
1276 0 : nsChangeHint_ReconstructFrame);
1277 : // We're reframing anyway; just keep the same context
1278 0 : newContext = oldContext;
1279 : }
1280 : } else {
1281 : // Don't expect XUL tree stuff here, since it needs a comparator and
1282 : // all.
1283 0 : NS_ASSERTION(pseudoType <
1284 : nsCSSPseudoElements::ePseudo_PseudoElementCount,
1285 : "Unexpected pseudo type");
1286 : newContext = styleSet->ResolvePseudoElementStyle(element,
1287 : pseudoType,
1288 0 : parentContext);
1289 : }
1290 : }
1291 : else {
1292 0 : NS_ASSERTION(localContent,
1293 : "non pseudo-element frame without content node");
1294 : newContext = styleSet->ResolveStyleFor(element, parentContext,
1295 0 : aTreeMatchContext);
1296 : }
1297 : }
1298 :
1299 0 : NS_ASSERTION(newContext, "failed to get new style context");
1300 0 : if (newContext) {
1301 0 : if (!parentContext) {
1302 0 : if (oldContext->GetRuleNode() == newContext->GetRuleNode() &&
1303 0 : oldContext->IsLinkContext() == newContext->IsLinkContext() &&
1304 0 : oldContext->RelevantLinkVisited() ==
1305 0 : newContext->RelevantLinkVisited()) {
1306 : // We're the root of the style context tree and the new style
1307 : // context returned has the same rule node. This means that
1308 : // we can use FindChildWithRules to keep a lot of the old
1309 : // style contexts around. However, we need to start from the
1310 : // same root.
1311 0 : newContext = oldContext;
1312 : }
1313 : }
1314 :
1315 0 : if (newContext != oldContext) {
1316 0 : if (!copyFromContinuation) {
1317 : TryStartingTransition(aPresContext, aFrame->GetContent(),
1318 0 : oldContext, &newContext);
1319 : }
1320 :
1321 : aMinChange = CaptureChange(oldContext, newContext, aFrame,
1322 : content, aChangeList, aMinChange,
1323 0 : assumeDifferenceHint);
1324 0 : if (!(aMinChange & nsChangeHint_ReconstructFrame)) {
1325 : // if frame gets regenerated, let it keep old context
1326 0 : aFrame->SetStyleContext(newContext);
1327 : }
1328 : }
1329 0 : oldContext->Release();
1330 : }
1331 : else {
1332 0 : NS_ERROR("resolve style context failed");
1333 0 : newContext = oldContext; // new context failed, recover...
1334 : }
1335 :
1336 : // do additional contexts
1337 : // XXXbz might be able to avoid selector matching here in some
1338 : // cases; won't worry about it for now.
1339 0 : PRInt32 contextIndex = -1;
1340 0 : while (1 == 1) {
1341 0 : nsStyleContext* oldExtraContext = nsnull;
1342 0 : oldExtraContext = aFrame->GetAdditionalStyleContext(++contextIndex);
1343 0 : if (oldExtraContext) {
1344 0 : nsRefPtr<nsStyleContext> newExtraContext;
1345 0 : nsIAtom* const extraPseudoTag = oldExtraContext->GetPseudo();
1346 : const nsCSSPseudoElements::Type extraPseudoType =
1347 0 : oldExtraContext->GetPseudoType();
1348 0 : NS_ASSERTION(extraPseudoTag &&
1349 : extraPseudoTag != nsCSSAnonBoxes::mozNonElement,
1350 : "extra style context is not pseudo element");
1351 0 : if (extraPseudoType == nsCSSPseudoElements::ePseudo_AnonBox) {
1352 : newExtraContext = styleSet->ResolveAnonymousBoxStyle(extraPseudoTag,
1353 0 : newContext);
1354 : }
1355 : else {
1356 : // Don't expect XUL tree stuff here, since it needs a comparator and
1357 : // all.
1358 0 : NS_ASSERTION(extraPseudoType <
1359 : nsCSSPseudoElements::ePseudo_PseudoElementCount,
1360 : "Unexpected type");
1361 : newExtraContext = styleSet->ResolvePseudoElementStyle(content->AsElement(),
1362 : extraPseudoType,
1363 0 : newContext);
1364 : }
1365 0 : if (newExtraContext) {
1366 0 : if (oldExtraContext != newExtraContext) {
1367 : aMinChange = CaptureChange(oldExtraContext, newExtraContext,
1368 : aFrame, content, aChangeList,
1369 0 : aMinChange, assumeDifferenceHint);
1370 0 : if (!(aMinChange & nsChangeHint_ReconstructFrame)) {
1371 0 : aFrame->SetAdditionalStyleContext(contextIndex, newExtraContext);
1372 : }
1373 : }
1374 : }
1375 : }
1376 : else {
1377 : break;
1378 : }
1379 : }
1380 :
1381 : // now look for undisplayed child content and pseudos
1382 :
1383 : // When the root element is display:none, we still construct *some*
1384 : // frames that have the root element as their mContent, down to the
1385 : // DocElementContainingBlock.
1386 : bool checkUndisplayed;
1387 : nsIContent *undisplayedParent;
1388 0 : if (pseudoTag) {
1389 : checkUndisplayed = aFrame == mPresShell->FrameConstructor()->
1390 0 : GetDocElementContainingBlock();
1391 0 : undisplayedParent = nsnull;
1392 : } else {
1393 0 : checkUndisplayed = !!localContent;
1394 0 : undisplayedParent = localContent;
1395 : }
1396 0 : if (checkUndisplayed && mUndisplayedMap) {
1397 : UndisplayedNode* undisplayed =
1398 0 : mUndisplayedMap->GetFirstNode(undisplayedParent);
1399 0 : for (AncestorFilter::AutoAncestorPusher
1400 : pushAncestor(undisplayed, aTreeMatchContext.mAncestorFilter,
1401 0 : undisplayedParent ? undisplayedParent->AsElement()
1402 0 : : nsnull);
1403 : undisplayed; undisplayed = undisplayed->mNext) {
1404 0 : NS_ASSERTION(undisplayedParent ||
1405 : undisplayed->mContent ==
1406 : mPresShell->GetDocument()->GetRootElement(),
1407 : "undisplayed node child of null must be root");
1408 0 : NS_ASSERTION(!undisplayed->mStyle->GetPseudo(),
1409 : "Shouldn't have random pseudo style contexts in the "
1410 : "undisplayed map");
1411 0 : nsRestyleHint thisChildHint = childRestyleHint;
1412 : RestyleTracker::RestyleData undisplayedRestyleData;
1413 0 : if (aRestyleTracker.GetRestyleData(undisplayed->mContent->AsElement(),
1414 0 : &undisplayedRestyleData)) {
1415 : thisChildHint =
1416 0 : nsRestyleHint(thisChildHint | undisplayedRestyleData.mRestyleHint);
1417 : }
1418 0 : nsRefPtr<nsStyleContext> undisplayedContext;
1419 0 : if (thisChildHint) {
1420 : undisplayedContext =
1421 0 : styleSet->ResolveStyleFor(undisplayed->mContent->AsElement(),
1422 : newContext,
1423 0 : aTreeMatchContext);
1424 : } else {
1425 : undisplayedContext =
1426 : styleSet->ReparentStyleContext(undisplayed->mStyle, newContext,
1427 0 : undisplayed->mContent->AsElement());
1428 : }
1429 0 : if (undisplayedContext) {
1430 0 : const nsStyleDisplay* display = undisplayedContext->GetStyleDisplay();
1431 0 : if (display->mDisplay != NS_STYLE_DISPLAY_NONE) {
1432 0 : NS_ASSERTION(undisplayed->mContent,
1433 : "Must have undisplayed content");
1434 : aChangeList->AppendChange(nsnull, undisplayed->mContent,
1435 0 : NS_STYLE_HINT_FRAMECHANGE);
1436 : // The node should be removed from the undisplayed map when
1437 : // we reframe it.
1438 : } else {
1439 : // update the undisplayed node with the new context
1440 0 : undisplayed->mStyle = undisplayedContext;
1441 : }
1442 : }
1443 : }
1444 : }
1445 :
1446 : // Check whether we might need to create a new ::before frame.
1447 : // There's no need to do this if we're planning to reframe already
1448 : // or if we're not forcing restyles on kids.
1449 0 : if (!(aMinChange & nsChangeHint_ReconstructFrame) &&
1450 : childRestyleHint) {
1451 : // Make sure not to do this for pseudo-frames -- those can't have :before
1452 : // or :after content. Neither can non-elements or leaf frames.
1453 0 : if (!pseudoTag && localContent && localContent->IsElement() &&
1454 0 : !aFrame->IsLeaf()) {
1455 : // Check for a new :before pseudo and an existing :before
1456 : // frame, but only if the frame is the first continuation.
1457 0 : nsIFrame* prevContinuation = aFrame->GetPrevContinuation();
1458 0 : if (!prevContinuation) {
1459 : // Checking for a :before frame is cheaper than getting the
1460 : // :before style context.
1461 0 : if (!nsLayoutUtils::GetBeforeFrame(aFrame) &&
1462 : nsLayoutUtils::HasPseudoStyle(localContent, newContext,
1463 : nsCSSPseudoElements::ePseudo_before,
1464 0 : aPresContext)) {
1465 : // Have to create the new :before frame
1466 0 : NS_UpdateHint(aMinChange, nsChangeHint_ReconstructFrame);
1467 : aChangeList->AppendChange(aFrame, content,
1468 0 : nsChangeHint_ReconstructFrame);
1469 : }
1470 : }
1471 : }
1472 : }
1473 :
1474 : // Check whether we might need to create a new ::after frame.
1475 : // There's no need to do this if we're planning to reframe already
1476 : // or if we're not forcing restyles on kids.
1477 0 : if (!(aMinChange & nsChangeHint_ReconstructFrame) &&
1478 : childRestyleHint) {
1479 : // Make sure not to do this for pseudo-frames -- those can't have :before
1480 : // or :after content. Neither can non-elements or leaf frames.
1481 0 : if (!pseudoTag && localContent && localContent->IsElement() &&
1482 0 : !aFrame->IsLeaf()) {
1483 : // Check for new :after content, but only if the frame is the
1484 : // last continuation.
1485 0 : nsIFrame* nextContinuation = aFrame->GetNextContinuation();
1486 :
1487 0 : if (!nextContinuation) {
1488 : // Getting the :after frame is more expensive than getting the pseudo
1489 : // context, so get the pseudo context first.
1490 0 : if (nsLayoutUtils::HasPseudoStyle(localContent, newContext,
1491 : nsCSSPseudoElements::ePseudo_after,
1492 0 : aPresContext) &&
1493 0 : !nsLayoutUtils::GetAfterFrame(aFrame)) {
1494 : // have to create the new :after frame
1495 0 : NS_UpdateHint(aMinChange, nsChangeHint_ReconstructFrame);
1496 : aChangeList->AppendChange(aFrame, content,
1497 0 : nsChangeHint_ReconstructFrame);
1498 : }
1499 : }
1500 : }
1501 : }
1502 :
1503 0 : if (!(aMinChange & nsChangeHint_ReconstructFrame)) {
1504 : DesiredA11yNotifications kidsDesiredA11yNotification =
1505 0 : aDesiredA11yNotifications;
1506 : #ifdef ACCESSIBILITY
1507 0 : A11yNotificationType ourA11yNotification = eDontNotify;
1508 : // Notify a11y for primary frame only if it's a root frame of visibility
1509 : // changes or its parent frame was hidden while it stays visible and
1510 : // it is not inside a {ib} split or is the first frame of {ib} split.
1511 0 : if (nsIPresShell::IsAccessibilityActive() &&
1512 0 : !aFrame->GetPrevContinuation() &&
1513 0 : !nsLayoutUtils::FrameIsNonFirstInIBSplit(aFrame)) {
1514 0 : if (aDesiredA11yNotifications == eSendAllNotifications) {
1515 0 : bool isFrameVisible = newContext->GetStyleVisibility()->IsVisible();
1516 0 : if (isFrameVisible != wasFrameVisible) {
1517 0 : if (isFrameVisible) {
1518 : // Notify a11y the element (perhaps with its children) was shown.
1519 : // We don't fall into this case if this element gets or stays shown
1520 : // while its parent becomes hidden.
1521 0 : kidsDesiredA11yNotification = eSkipNotifications;
1522 0 : ourA11yNotification = eNotifyShown;
1523 : } else {
1524 : // The element is being hidden; its children may stay visible, or
1525 : // become visible after being hidden previously. If we'll find
1526 : // visible children then we should notify a11y about that as if
1527 : // they were inserted into tree. Notify a11y this element was
1528 : // hidden.
1529 0 : kidsDesiredA11yNotification = eNotifyIfShown;
1530 0 : ourA11yNotification = eNotifyHidden;
1531 : }
1532 : }
1533 0 : } else if (aDesiredA11yNotifications == eNotifyIfShown &&
1534 0 : newContext->GetStyleVisibility()->IsVisible()) {
1535 : // Notify a11y that element stayed visible while its parent was
1536 : // hidden.
1537 0 : aVisibleKidsOfHiddenElement.AppendElement(aFrame->GetContent());
1538 0 : kidsDesiredA11yNotification = eSkipNotifications;
1539 : }
1540 : }
1541 : #endif
1542 :
1543 : // There is no need to waste time crawling into a frame's children on a frame change.
1544 : // The act of reconstructing frames will force new style contexts to be resolved on all
1545 : // of this frame's descendants anyway, so we want to avoid wasting time processing
1546 : // style contexts that we're just going to throw away anyway. - dwh
1547 :
1548 : // now do children
1549 0 : nsIFrame::ChildListIterator lists(aFrame);
1550 0 : for (AncestorFilter::AutoAncestorPusher
1551 0 : pushAncestor(!lists.IsDone(),
1552 : aTreeMatchContext.mAncestorFilter,
1553 0 : content && content->IsElement() ? content->AsElement()
1554 0 : : nsnull);
1555 0 : !lists.IsDone(); lists.Next()) {
1556 0 : nsFrameList::Enumerator childFrames(lists.CurrentList());
1557 0 : for (; !childFrames.AtEnd(); childFrames.Next()) {
1558 0 : nsIFrame* child = childFrames.get();
1559 0 : if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
1560 : // only do frames that are in flow
1561 0 : if (nsGkAtoms::placeholderFrame == child->GetType()) { // placeholder
1562 : // get out of flow frame and recur there
1563 : nsIFrame* outOfFlowFrame =
1564 0 : nsPlaceholderFrame::GetRealFrameForPlaceholder(child);
1565 0 : NS_ASSERTION(outOfFlowFrame, "no out-of-flow frame");
1566 0 : NS_ASSERTION(outOfFlowFrame != resolvedChild,
1567 : "out-of-flow frame not a true descendant");
1568 :
1569 : // Note that the out-of-flow may not be a geometric descendant of
1570 : // the frame where we started the reresolve. Therefore, even if
1571 : // aMinChange already includes nsChangeHint_ReflowFrame we don't
1572 : // want to pass that on to the out-of-flow reresolve, since that
1573 : // can lead to the out-of-flow not getting reflowed when it should
1574 : // be (eg a reresolve starting at <body> that involves reflowing
1575 : // the <body> would miss reflowing fixed-pos nodes that also need
1576 : // reflow). In the cases when the out-of-flow _is_ a geometric
1577 : // descendant of a frame we already have a reflow hint for,
1578 : // reflow coalescing should keep us from doing the work twice.
1579 :
1580 : // |nsFrame::GetParentStyleContextFrame| checks being out
1581 : // of flow so that this works correctly.
1582 0 : do {
1583 : ReResolveStyleContext(aPresContext, outOfFlowFrame,
1584 : content, aChangeList,
1585 : NS_SubtractHint(aMinChange,
1586 : nsChangeHint_ReflowFrame),
1587 : childRestyleHint,
1588 : aRestyleTracker,
1589 : kidsDesiredA11yNotification,
1590 : aVisibleKidsOfHiddenElement,
1591 0 : aTreeMatchContext);
1592 0 : } while ((outOfFlowFrame = outOfFlowFrame->GetNextContinuation()));
1593 :
1594 : // reresolve placeholder's context under the same parent
1595 : // as the out-of-flow frame
1596 : ReResolveStyleContext(aPresContext, child, content,
1597 : aChangeList, aMinChange,
1598 : childRestyleHint,
1599 : aRestyleTracker,
1600 : kidsDesiredA11yNotification,
1601 : aVisibleKidsOfHiddenElement,
1602 0 : aTreeMatchContext);
1603 : }
1604 : else { // regular child frame
1605 0 : if (child != resolvedChild) {
1606 : ReResolveStyleContext(aPresContext, child, content,
1607 : aChangeList, aMinChange,
1608 : childRestyleHint,
1609 : aRestyleTracker,
1610 : kidsDesiredA11yNotification,
1611 : aVisibleKidsOfHiddenElement,
1612 0 : aTreeMatchContext);
1613 : } else {
1614 : NOISY_TRACE_FRAME("child frame already resolved as descendant, skipping",aFrame);
1615 : }
1616 : }
1617 : }
1618 : }
1619 : }
1620 : // XXX need to do overflow frames???
1621 :
1622 : #ifdef ACCESSIBILITY
1623 : // Send notifications about visibility changes.
1624 0 : if (ourA11yNotification == eNotifyShown) {
1625 0 : nsAccessibilityService* accService = nsIPresShell::AccService();
1626 0 : if (accService) {
1627 0 : nsIPresShell* presShell = aFrame->PresContext()->GetPresShell();
1628 0 : nsIContent* content = aFrame->GetContent();
1629 :
1630 : accService->ContentRangeInserted(presShell, content->GetParent(),
1631 : content,
1632 0 : content->GetNextSibling());
1633 : }
1634 0 : } else if (ourA11yNotification == eNotifyHidden) {
1635 0 : nsAccessibilityService* accService = nsIPresShell::AccService();
1636 0 : if (accService) {
1637 0 : nsIPresShell* presShell = aFrame->PresContext()->GetPresShell();
1638 0 : nsIContent* content = aFrame->GetContent();
1639 0 : accService->ContentRemoved(presShell, content->GetParent(), content);
1640 :
1641 : // Process children staying shown.
1642 0 : PRUint32 visibleContentCount = aVisibleKidsOfHiddenElement.Length();
1643 0 : for (PRUint32 idx = 0; idx < visibleContentCount; idx++) {
1644 0 : nsIContent* content = aVisibleKidsOfHiddenElement[idx];
1645 : accService->ContentRangeInserted(presShell, content->GetParent(),
1646 0 : content, content->GetNextSibling());
1647 : }
1648 0 : aVisibleKidsOfHiddenElement.Clear();
1649 : }
1650 : }
1651 : #endif
1652 : }
1653 : }
1654 :
1655 0 : return aMinChange;
1656 : }
1657 :
1658 : void
1659 0 : nsFrameManager::ComputeStyleChangeFor(nsIFrame *aFrame,
1660 : nsStyleChangeList *aChangeList,
1661 : nsChangeHint aMinChange,
1662 : RestyleTracker& aRestyleTracker,
1663 : bool aRestyleDescendants)
1664 : {
1665 0 : nsIContent *content = aFrame->GetContent();
1666 0 : if (aMinChange) {
1667 0 : aChangeList->AppendChange(aFrame, content, aMinChange);
1668 : }
1669 :
1670 0 : nsChangeHint topLevelChange = aMinChange;
1671 :
1672 0 : nsIFrame* frame = aFrame;
1673 0 : nsIFrame* frame2 = aFrame;
1674 :
1675 0 : NS_ASSERTION(!frame->GetPrevContinuation(), "must start with the first in flow");
1676 :
1677 : // We want to start with this frame and walk all its next-in-flows,
1678 : // as well as all its special siblings and their next-in-flows,
1679 : // reresolving style on all the frames we encounter in this walk.
1680 :
1681 0 : FramePropertyTable *propTable = GetPresContext()->PropertyTable();
1682 :
1683 : TreeMatchContext treeMatchContext(true,
1684 : nsRuleWalker::eRelevantLinkUnvisited,
1685 0 : mPresShell->GetDocument());
1686 0 : nsIContent *parent = content ? content->GetParent() : nsnull;
1687 : Element *parentElement =
1688 0 : parent && parent->IsElement() ? parent->AsElement() : nsnull;
1689 0 : treeMatchContext.mAncestorFilter.Init(parentElement);
1690 0 : nsTArray<nsIContent*> visibleKidsOfHiddenElement;
1691 0 : do {
1692 : // Outer loop over special siblings
1693 0 : do {
1694 : // Inner loop over next-in-flows of the current frame
1695 : nsChangeHint frameChange =
1696 : ReResolveStyleContext(GetPresContext(), frame, nsnull,
1697 : aChangeList, topLevelChange,
1698 : aRestyleDescendants ?
1699 : eRestyle_Subtree : eRestyle_Self,
1700 : aRestyleTracker,
1701 : eSendAllNotifications,
1702 : visibleKidsOfHiddenElement,
1703 0 : treeMatchContext);
1704 0 : NS_UpdateHint(topLevelChange, frameChange);
1705 :
1706 0 : if (topLevelChange & nsChangeHint_ReconstructFrame) {
1707 : // If it's going to cause a framechange, then don't bother
1708 : // with the continuations or special siblings since they'll be
1709 : // clobbered by the frame reconstruct anyway.
1710 0 : NS_ASSERTION(!frame->GetPrevContinuation(),
1711 : "continuing frame had more severe impact than first-in-flow");
1712 : return;
1713 : }
1714 :
1715 0 : frame = frame->GetNextContinuation();
1716 : } while (frame);
1717 :
1718 : // Might we have special siblings?
1719 0 : if (!(frame2->GetStateBits() & NS_FRAME_IS_SPECIAL)) {
1720 : // nothing more to do here
1721 : return;
1722 : }
1723 :
1724 : frame2 = static_cast<nsIFrame*>
1725 0 : (propTable->Get(frame2, nsIFrame::IBSplitSpecialSibling()));
1726 0 : frame = frame2;
1727 : } while (frame2);
1728 : }
1729 :
1730 : // Capture state for a given frame.
1731 : // Accept a content id here, in some cases we may not have content (scroll position)
1732 : void
1733 0 : nsFrameManager::CaptureFrameStateFor(nsIFrame* aFrame,
1734 : nsILayoutHistoryState* aState,
1735 : nsIStatefulFrame::SpecialStateID aID)
1736 : {
1737 0 : if (!aFrame || !aState) {
1738 0 : NS_WARNING("null frame, or state");
1739 0 : return;
1740 : }
1741 :
1742 : // Only capture state for stateful frames
1743 0 : nsIStatefulFrame* statefulFrame = do_QueryFrame(aFrame);
1744 0 : if (!statefulFrame) {
1745 0 : return;
1746 : }
1747 :
1748 : // Capture the state, exit early if we get null (nothing to save)
1749 0 : nsAutoPtr<nsPresState> frameState;
1750 0 : nsresult rv = statefulFrame->SaveState(aID, getter_Transfers(frameState));
1751 0 : if (!frameState) {
1752 : return;
1753 : }
1754 :
1755 : // Generate the hash key to store the state under
1756 : // Exit early if we get empty key
1757 0 : nsCAutoString stateKey;
1758 0 : nsIContent* content = aFrame->GetContent();
1759 0 : nsIDocument* doc = content ? content->GetCurrentDoc() : nsnull;
1760 0 : rv = nsContentUtils::GenerateStateKey(content, doc, aID, stateKey);
1761 0 : if(NS_FAILED(rv) || stateKey.IsEmpty()) {
1762 : return;
1763 : }
1764 :
1765 : // Store the state
1766 0 : rv = aState->AddState(stateKey, frameState);
1767 0 : if (NS_SUCCEEDED(rv)) {
1768 : // aState owns frameState now.
1769 0 : frameState.forget();
1770 : }
1771 : }
1772 :
1773 : void
1774 0 : nsFrameManager::CaptureFrameState(nsIFrame* aFrame,
1775 : nsILayoutHistoryState* aState)
1776 : {
1777 0 : NS_PRECONDITION(nsnull != aFrame && nsnull != aState, "null parameters passed in");
1778 :
1779 0 : CaptureFrameStateFor(aFrame, aState);
1780 :
1781 : // Now capture state recursively for the frame hierarchy rooted at aFrame
1782 0 : nsIFrame::ChildListIterator lists(aFrame);
1783 0 : for (; !lists.IsDone(); lists.Next()) {
1784 0 : nsFrameList::Enumerator childFrames(lists.CurrentList());
1785 0 : for (; !childFrames.AtEnd(); childFrames.Next()) {
1786 0 : CaptureFrameState(childFrames.get(), aState);
1787 : }
1788 : }
1789 0 : }
1790 :
1791 : // Restore state for a given frame.
1792 : // Accept a content id here, in some cases we may not have content (scroll position)
1793 : void
1794 0 : nsFrameManager::RestoreFrameStateFor(nsIFrame* aFrame,
1795 : nsILayoutHistoryState* aState,
1796 : nsIStatefulFrame::SpecialStateID aID)
1797 : {
1798 0 : if (!aFrame || !aState) {
1799 0 : NS_WARNING("null frame or state");
1800 0 : return;
1801 : }
1802 :
1803 : // Only restore state for stateful frames
1804 0 : nsIStatefulFrame* statefulFrame = do_QueryFrame(aFrame);
1805 0 : if (!statefulFrame) {
1806 0 : return;
1807 : }
1808 :
1809 : // Generate the hash key the state was stored under
1810 : // Exit early if we get empty key
1811 0 : nsIContent* content = aFrame->GetContent();
1812 : // If we don't have content, we can't generate a hash
1813 : // key and there's probably no state information for us.
1814 0 : if (!content) {
1815 0 : return;
1816 : }
1817 :
1818 0 : nsCAutoString stateKey;
1819 0 : nsIDocument* doc = content->GetCurrentDoc();
1820 0 : nsresult rv = nsContentUtils::GenerateStateKey(content, doc, aID, stateKey);
1821 0 : if (NS_FAILED(rv) || stateKey.IsEmpty()) {
1822 : return;
1823 : }
1824 :
1825 : // Get the state from the hash
1826 : nsPresState *frameState;
1827 0 : rv = aState->GetState(stateKey, &frameState);
1828 0 : if (!frameState) {
1829 : return;
1830 : }
1831 :
1832 : // Restore it
1833 0 : rv = statefulFrame->RestoreState(frameState);
1834 0 : if (NS_FAILED(rv)) {
1835 : return;
1836 : }
1837 :
1838 : // If we restore ok, remove the state from the state table
1839 0 : aState->RemoveState(stateKey);
1840 : }
1841 :
1842 : void
1843 0 : nsFrameManager::RestoreFrameState(nsIFrame* aFrame,
1844 : nsILayoutHistoryState* aState)
1845 : {
1846 0 : NS_PRECONDITION(nsnull != aFrame && nsnull != aState, "null parameters passed in");
1847 :
1848 0 : RestoreFrameStateFor(aFrame, aState);
1849 :
1850 : // Now restore state recursively for the frame hierarchy rooted at aFrame
1851 0 : nsIFrame::ChildListIterator lists(aFrame);
1852 0 : for (; !lists.IsDone(); lists.Next()) {
1853 0 : nsFrameList::Enumerator childFrames(lists.CurrentList());
1854 0 : for (; !childFrames.AtEnd(); childFrames.Next()) {
1855 0 : RestoreFrameState(childFrames.get(), aState);
1856 : }
1857 : }
1858 0 : }
1859 :
1860 : //----------------------------------------------------------------------
1861 :
1862 : static PLHashNumber
1863 0 : HashKey(void* key)
1864 : {
1865 0 : return NS_PTR_TO_INT32(key);
1866 : }
1867 :
1868 : static PRIntn
1869 0 : CompareKeys(void* key1, void* key2)
1870 : {
1871 0 : return key1 == key2;
1872 : }
1873 :
1874 : //----------------------------------------------------------------------
1875 :
1876 0 : nsFrameManagerBase::UndisplayedMap::UndisplayedMap(PRUint32 aNumBuckets)
1877 : {
1878 0 : MOZ_COUNT_CTOR(nsFrameManagerBase::UndisplayedMap);
1879 : mTable = PL_NewHashTable(aNumBuckets, (PLHashFunction)HashKey,
1880 : (PLHashComparator)CompareKeys,
1881 : (PLHashComparator)nsnull,
1882 0 : nsnull, nsnull);
1883 0 : mLastLookup = nsnull;
1884 0 : }
1885 :
1886 0 : nsFrameManagerBase::UndisplayedMap::~UndisplayedMap(void)
1887 : {
1888 0 : MOZ_COUNT_DTOR(nsFrameManagerBase::UndisplayedMap);
1889 0 : Clear();
1890 0 : PL_HashTableDestroy(mTable);
1891 0 : }
1892 :
1893 : PLHashEntry**
1894 0 : nsFrameManagerBase::UndisplayedMap::GetEntryFor(nsIContent* aParentContent)
1895 : {
1896 0 : if (mLastLookup && (aParentContent == (*mLastLookup)->key)) {
1897 0 : return mLastLookup;
1898 : }
1899 0 : PLHashNumber hashCode = NS_PTR_TO_INT32(aParentContent);
1900 0 : PLHashEntry** entry = PL_HashTableRawLookup(mTable, hashCode, aParentContent);
1901 0 : if (*entry) {
1902 0 : mLastLookup = entry;
1903 : }
1904 0 : return entry;
1905 : }
1906 :
1907 : UndisplayedNode*
1908 0 : nsFrameManagerBase::UndisplayedMap::GetFirstNode(nsIContent* aParentContent)
1909 : {
1910 0 : PLHashEntry** entry = GetEntryFor(aParentContent);
1911 0 : if (*entry) {
1912 0 : return (UndisplayedNode*)((*entry)->value);
1913 : }
1914 0 : return nsnull;
1915 : }
1916 :
1917 : void
1918 0 : nsFrameManagerBase::UndisplayedMap::AppendNodeFor(UndisplayedNode* aNode,
1919 : nsIContent* aParentContent)
1920 : {
1921 0 : PLHashEntry** entry = GetEntryFor(aParentContent);
1922 0 : if (*entry) {
1923 0 : UndisplayedNode* node = (UndisplayedNode*)((*entry)->value);
1924 0 : while (node->mNext) {
1925 0 : if (node->mContent == aNode->mContent) {
1926 : // We actually need to check this in optimized builds because
1927 : // there are some callers that do this. See bug 118014, bug
1928 : // 136704, etc.
1929 0 : NS_NOTREACHED("node in map twice");
1930 0 : delete aNode;
1931 0 : return;
1932 : }
1933 0 : node = node->mNext;
1934 : }
1935 0 : node->mNext = aNode;
1936 : }
1937 : else {
1938 0 : PLHashNumber hashCode = NS_PTR_TO_INT32(aParentContent);
1939 0 : PL_HashTableRawAdd(mTable, entry, hashCode, aParentContent, aNode);
1940 0 : mLastLookup = nsnull; // hashtable may have shifted bucket out from under us
1941 : }
1942 : }
1943 :
1944 : nsresult
1945 0 : nsFrameManagerBase::UndisplayedMap::AddNodeFor(nsIContent* aParentContent,
1946 : nsIContent* aChild,
1947 : nsStyleContext* aStyle)
1948 : {
1949 0 : UndisplayedNode* node = new UndisplayedNode(aChild, aStyle);
1950 :
1951 0 : AppendNodeFor(node, aParentContent);
1952 0 : return NS_OK;
1953 : }
1954 :
1955 : void
1956 0 : nsFrameManagerBase::UndisplayedMap::RemoveNodeFor(nsIContent* aParentContent,
1957 : UndisplayedNode* aNode)
1958 : {
1959 0 : PLHashEntry** entry = GetEntryFor(aParentContent);
1960 0 : NS_ASSERTION(*entry, "content not in map");
1961 0 : if (*entry) {
1962 0 : if ((UndisplayedNode*)((*entry)->value) == aNode) { // first node
1963 0 : if (aNode->mNext) {
1964 0 : (*entry)->value = aNode->mNext;
1965 0 : aNode->mNext = nsnull;
1966 : }
1967 : else {
1968 0 : PL_HashTableRawRemove(mTable, entry, *entry);
1969 0 : mLastLookup = nsnull; // hashtable may have shifted bucket out from under us
1970 : }
1971 : }
1972 : else {
1973 0 : UndisplayedNode* node = (UndisplayedNode*)((*entry)->value);
1974 0 : while (node->mNext) {
1975 0 : if (node->mNext == aNode) {
1976 0 : node->mNext = aNode->mNext;
1977 0 : aNode->mNext = nsnull;
1978 0 : break;
1979 : }
1980 0 : node = node->mNext;
1981 : }
1982 : }
1983 : }
1984 0 : delete aNode;
1985 0 : }
1986 :
1987 : void
1988 0 : nsFrameManagerBase::UndisplayedMap::RemoveNodesFor(nsIContent* aParentContent)
1989 : {
1990 0 : PLHashEntry** entry = GetEntryFor(aParentContent);
1991 0 : NS_ASSERTION(entry, "content not in map");
1992 0 : if (*entry) {
1993 0 : UndisplayedNode* node = (UndisplayedNode*)((*entry)->value);
1994 0 : NS_ASSERTION(node, "null node for non-null entry in UndisplayedMap");
1995 0 : delete node;
1996 0 : PL_HashTableRawRemove(mTable, entry, *entry);
1997 0 : mLastLookup = nsnull; // hashtable may have shifted bucket out from under us
1998 : }
1999 0 : }
2000 :
2001 : static PRIntn
2002 0 : RemoveUndisplayedEntry(PLHashEntry* he, PRIntn i, void* arg)
2003 : {
2004 0 : UndisplayedNode* node = (UndisplayedNode*)(he->value);
2005 0 : delete node;
2006 : // Remove and free this entry and continue enumerating
2007 0 : return HT_ENUMERATE_REMOVE | HT_ENUMERATE_NEXT;
2008 : }
2009 :
2010 : void
2011 0 : nsFrameManagerBase::UndisplayedMap::Clear(void)
2012 : {
2013 0 : mLastLookup = nsnull;
2014 0 : PL_HashTableEnumerateEntries(mTable, RemoveUndisplayedEntry, 0);
2015 0 : }
|