Tracer build fixes. (b=588021, r=dvander)
[mozilla-central.git] / layout / base / nsCSSFrameConstructor.cpp
blobf6ed4e4f3ee18e6f8b10ce051932dfd03a9d0d33
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 // vim:cindent:ts=2:et:sw=2:
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998
21 * the Initial Developer. All Rights Reserved.
23 * Contributor(s):
24 * Dan Rosen <dr@netscape.com>
25 * Mats Palmgren <matspal@gmail.com>
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.
39 * ***** END LICENSE BLOCK ***** */
42 * construction of a frame tree that is nearly isomorphic to the content
43 * tree and updating of that tree in response to dynamic changes
46 #include "nsCSSFrameConstructor.h"
47 #include "nsCRT.h"
48 #include "nsIAtom.h"
49 #include "nsIURL.h"
50 #include "nsHashtable.h"
51 #include "nsIHTMLDocument.h"
52 #include "nsIStyleRule.h"
53 #include "nsIFrame.h"
54 #include "nsGkAtoms.h"
55 #include "nsPresContext.h"
56 #include "nsILinkHandler.h"
57 #include "nsIDocument.h"
58 #include "nsTableFrame.h"
59 #include "nsTableColGroupFrame.h"
60 #include "nsTableColFrame.h"
61 #include "nsIDOMHTMLDocument.h"
62 #include "nsIDOMHTMLTableColElement.h"
63 #include "nsIDOMHTMLTableCaptionElem.h"
64 #include "nsHTMLParts.h"
65 #include "nsIPresShell.h"
66 #include "nsUnicharUtils.h"
67 #include "nsStyleSet.h"
68 #include "nsIViewManager.h"
69 #include "nsIEventStateManager.h"
70 #include "nsStyleConsts.h"
71 #include "nsTableOuterFrame.h"
72 #include "nsIDOMXULElement.h"
73 #include "nsHTMLContainerFrame.h"
74 #include "nsINameSpaceManager.h"
75 #include "nsIDOMHTMLSelectElement.h"
76 #include "nsIDOMHTMLLegendElement.h"
77 #include "nsIComboboxControlFrame.h"
78 #include "nsIListControlFrame.h"
79 #include "nsISelectControlFrame.h"
80 #include "nsIDOMCharacterData.h"
81 #include "nsIDOMHTMLImageElement.h"
82 #include "nsPlaceholderFrame.h"
83 #include "nsTableRowGroupFrame.h"
84 #include "nsStyleChangeList.h"
85 #include "nsIFormControl.h"
86 #include "nsCSSAnonBoxes.h"
87 #include "nsIDeviceContext.h"
88 #include "nsTextFragment.h"
89 #include "nsIAnonymousContentCreator.h"
90 #include "nsFrameManager.h"
91 #include "nsLegendFrame.h"
92 #include "nsIContentIterator.h"
93 #include "nsBoxLayoutState.h"
94 #include "nsBindingManager.h"
95 #include "nsXBLBinding.h"
96 #include "nsITheme.h"
97 #include "nsContentCID.h"
98 #include "nsContentUtils.h"
99 #include "nsIScriptError.h"
100 #include "nsIDocShell.h"
101 #include "nsIDocShellTreeItem.h"
102 #include "nsObjectFrame.h"
103 #include "nsRuleNode.h"
104 #include "nsIDOMMutationEvent.h"
105 #include "nsChildIterator.h"
106 #include "nsCSSRendering.h"
107 #include "nsISelectElement.h"
108 #include "nsLayoutErrors.h"
109 #include "nsLayoutUtils.h"
110 #include "nsAutoPtr.h"
111 #include "nsBoxFrame.h"
112 #include "nsIBoxLayout.h"
113 #include "nsImageFrame.h"
114 #include "nsIObjectLoadingContent.h"
115 #include "nsContentErrors.h"
116 #include "nsIPrincipal.h"
117 #include "nsIDOMWindowInternal.h"
118 #include "nsStyleUtil.h"
119 #include "nsBox.h"
120 #include "nsTArray.h"
121 #include "nsGenericDOMDataNode.h"
122 #include "mozilla/dom/Element.h"
123 #include "FrameLayerBuilder.h"
125 #ifdef MOZ_XUL
126 #include "nsIRootBox.h"
127 #include "nsIDOMXULCommandDispatcher.h"
128 #include "nsIDOMXULDocument.h"
129 #include "nsIXULDocument.h"
130 #endif
131 #ifdef ACCESSIBILITY
132 #include "nsIAccessibilityService.h"
133 #endif
135 #include "nsInlineFrame.h"
136 #include "nsBlockFrame.h"
138 #include "nsIScrollableFrame.h"
140 #include "nsIXBLService.h"
142 #undef NOISY_FIRST_LETTER
144 #ifdef MOZ_MATHML
145 #include "nsMathMLParts.h"
146 #endif
147 #ifdef MOZ_SVG
148 #include "nsSVGFeatures.h"
149 #include "nsSVGEffects.h"
150 #include "nsSVGUtils.h"
151 #include "nsSVGOuterSVGFrame.h"
152 #endif
154 #include "nsRefreshDriver.h"
156 using namespace mozilla;
157 using namespace mozilla::dom;
159 nsIFrame*
160 NS_NewHTMLCanvasFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
162 #if defined(MOZ_MEDIA)
163 nsIFrame*
164 NS_NewHTMLVideoFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
165 #endif
167 #ifdef MOZ_SVG
168 #include "nsSVGTextContainerFrame.h"
170 PRBool
171 NS_SVGEnabled();
172 nsIFrame*
173 NS_NewSVGOuterSVGFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
174 nsIFrame*
175 NS_NewSVGInnerSVGFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
176 nsIFrame*
177 NS_NewSVGPathGeometryFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
178 nsIFrame*
179 NS_NewSVGGFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
180 nsIFrame*
181 NS_NewSVGGenericContainerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
182 nsIFrame*
183 NS_NewSVGForeignObjectFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
184 nsIFrame*
185 NS_NewSVGAFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
186 nsIFrame*
187 NS_NewSVGGlyphFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
188 nsIFrame*
189 NS_NewSVGSwitchFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
190 nsIFrame*
191 NS_NewSVGTextFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
192 nsIFrame*
193 NS_NewSVGTSpanFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
194 nsIFrame*
195 NS_NewSVGContainerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
196 nsIFrame*
197 NS_NewSVGUseFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
198 extern nsIFrame*
199 NS_NewSVGLinearGradientFrame(nsIPresShell *aPresShell, nsStyleContext* aContext);
200 extern nsIFrame*
201 NS_NewSVGRadialGradientFrame(nsIPresShell *aPresShell, nsStyleContext* aContext);
202 extern nsIFrame*
203 NS_NewSVGStopFrame(nsIPresShell *aPresShell, nsStyleContext* aContext);
204 nsIFrame*
205 NS_NewSVGMarkerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
206 extern nsIFrame*
207 NS_NewSVGImageFrame(nsIPresShell *aPresShell, nsStyleContext* aContext);
208 nsIFrame*
209 NS_NewSVGClipPathFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
210 nsIFrame*
211 NS_NewSVGTextPathFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
212 nsIFrame*
213 NS_NewSVGFilterFrame(nsIPresShell *aPresShell, nsStyleContext* aContext);
214 nsIFrame*
215 NS_NewSVGPatternFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
216 nsIFrame*
217 NS_NewSVGMaskFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
218 nsIFrame*
219 NS_NewSVGLeafFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
220 #endif
222 #include "nsIDocument.h"
223 #include "nsIDOMElement.h"
224 #include "nsIDOMNodeList.h"
225 #include "nsIDOMDocument.h"
226 #include "nsIDOMDocumentXBL.h"
227 #include "nsIScrollable.h"
228 #include "nsINodeInfo.h"
229 #include "prenv.h"
230 #include "nsWidgetsCID.h"
231 #include "nsNodeInfoManager.h"
232 #include "nsContentCreatorFunctions.h"
233 #include "nsIServiceManager.h"
235 // Global object maintenance
236 nsIXBLService * nsCSSFrameConstructor::gXBLService = nsnull;
238 #ifdef DEBUG
239 // Set the environment variable GECKO_FRAMECTOR_DEBUG_FLAGS to one or
240 // more of the following flags (comma separated) for handy debug
241 // output.
242 static PRBool gNoisyContentUpdates = PR_FALSE;
243 static PRBool gReallyNoisyContentUpdates = PR_FALSE;
244 static PRBool gNoisyInlineConstruction = PR_FALSE;
246 struct FrameCtorDebugFlags {
247 const char* name;
248 PRBool* on;
251 static FrameCtorDebugFlags gFlags[] = {
252 { "content-updates", &gNoisyContentUpdates },
253 { "really-noisy-content-updates", &gReallyNoisyContentUpdates },
254 { "noisy-inline", &gNoisyInlineConstruction }
257 #define NUM_DEBUG_FLAGS (sizeof(gFlags) / sizeof(gFlags[0]))
258 #endif
261 #ifdef MOZ_XUL
262 #include "nsMenuFrame.h"
263 #include "nsMenuPopupFrame.h"
264 #include "nsPopupSetFrame.h"
265 #include "nsTreeColFrame.h"
266 #include "nsIBoxObject.h"
267 #include "nsPIListBoxObject.h"
268 #include "nsListBoxBodyFrame.h"
269 #include "nsListItemFrame.h"
270 #include "nsXULLabelFrame.h"
272 //------------------------------------------------------------------
274 nsIFrame*
275 NS_NewAutoRepeatBoxFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
277 nsIFrame*
278 NS_NewRootBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
280 nsIFrame*
281 NS_NewDocElementBoxFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
283 nsIFrame*
284 NS_NewThumbFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
286 nsIFrame*
287 NS_NewDeckFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
289 nsIFrame*
290 NS_NewLeafBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
292 nsIFrame*
293 NS_NewStackFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
295 nsIFrame*
296 NS_NewProgressMeterFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
298 nsIFrame*
299 NS_NewImageBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
301 nsIFrame*
302 NS_NewTextBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
304 nsIFrame*
305 NS_NewGroupBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
307 nsIFrame*
308 NS_NewButtonBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
310 nsIFrame*
311 NS_NewSplitterFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
313 nsIFrame*
314 NS_NewMenuPopupFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
316 nsIFrame*
317 NS_NewPopupSetFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
319 nsIFrame*
320 NS_NewMenuFrame (nsIPresShell* aPresShell, nsStyleContext* aContext, PRUint32 aFlags);
322 nsIFrame*
323 NS_NewMenuBarFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
325 nsIFrame*
326 NS_NewTreeBodyFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
328 // grid
329 nsresult
330 NS_NewGridLayout2 ( nsIPresShell* aPresShell, nsIBoxLayout** aNewLayout );
331 nsIFrame*
332 NS_NewGridRowLeafFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
333 nsIFrame*
334 NS_NewGridRowGroupFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
336 // end grid
338 nsIFrame*
339 NS_NewTitleBarFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
341 nsIFrame*
342 NS_NewResizerFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
345 #endif
347 nsIFrame*
348 NS_NewHTMLScrollFrame (nsIPresShell* aPresShell, nsStyleContext* aContext, PRBool aIsRoot);
350 nsIFrame*
351 NS_NewXULScrollFrame (nsIPresShell* aPresShell, nsStyleContext* aContext, PRBool aIsRoot);
353 nsIFrame*
354 NS_NewSliderFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
356 nsIFrame*
357 NS_NewScrollbarFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
359 nsIFrame*
360 NS_NewScrollbarButtonFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
363 #ifdef NOISY_FINDFRAME
364 static PRInt32 FFWC_totalCount=0;
365 static PRInt32 FFWC_doLoop=0;
366 static PRInt32 FFWC_doSibling=0;
367 static PRInt32 FFWC_recursions=0;
368 static PRInt32 FFWC_nextInFlows=0;
369 #endif
371 static inline nsIFrame*
372 GetFieldSetBlockFrame(nsIFrame* aFieldsetFrame)
374 // Depends on the fieldset child frame order - see ConstructFieldSetFrame() below.
375 nsIFrame* firstChild = aFieldsetFrame->GetFirstChild(nsnull);
376 return firstChild && firstChild->GetNextSibling() ? firstChild->GetNextSibling() : firstChild;
379 #define FCDATA_DECL(_flags, _func) \
380 { _flags, { (FrameCreationFunc)_func } }
382 //----------------------------------------------------------------------
384 static PRBool
385 IsInlineOutside(nsIFrame* aFrame)
387 return aFrame->GetStyleDisplay()->IsInlineOutside();
391 * True if aFrame is an actual inline frame in the sense of non-replaced
392 * display:inline CSS boxes. In other words, it can be affected by {ib}
393 * splitting and can contain first-letter frames. Basically, this is either an
394 * inline frame (positioned or otherwise) or an line frame (this last because
395 * it can contain first-letter and because inserting blocks in the middle of it
396 * needs to terminate it).
398 static PRBool
399 IsInlineFrame(const nsIFrame* aFrame)
401 return aFrame->IsFrameOfType(nsIFrame::eLineParticipant);
405 * If any children require a block parent, return the first such child.
406 * Otherwise return null.
408 static nsIContent*
409 AnyKidsNeedBlockParent(nsIFrame *aFrameList)
411 for (nsIFrame *k = aFrameList; k; k = k->GetNextSibling()) {
412 // Line participants, such as text and inline frames, can't be
413 // directly inside a XUL box; they must be wrapped in an
414 // intermediate block.
415 if (k->IsFrameOfType(nsIFrame::eLineParticipant)) {
416 return k->GetContent();
419 return nsnull;
422 // Reparent a frame into a wrapper frame that is a child of its old parent.
423 static void
424 ReparentFrame(nsFrameManager* aFrameManager,
425 nsIFrame* aNewParentFrame,
426 nsIFrame* aFrame)
428 aFrame->SetParent(aNewParentFrame);
429 aFrameManager->ReparentStyleContext(aFrame);
432 static void
433 ReparentFrames(nsFrameManager* aFrameManager,
434 nsIFrame* aNewParentFrame,
435 const nsFrameList& aFrameList)
437 for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
438 ReparentFrame(aFrameManager, aNewParentFrame, e.get());
442 //----------------------------------------------------------------------
444 // When inline frames get weird and have block frames in them, we
445 // annotate them to help us respond to incremental content changes
446 // more easily.
448 static inline PRBool
449 IsFrameSpecial(nsIFrame* aFrame)
451 return (aFrame->GetStateBits() & NS_FRAME_IS_SPECIAL) != 0;
454 static nsIFrame* GetSpecialSibling(nsIFrame* aFrame)
456 NS_PRECONDITION(IsFrameSpecial(aFrame), "Shouldn't call this");
458 // We only store the "special sibling" annotation with the first
459 // frame in the continuation chain. Walk back to find that frame now.
460 return static_cast<nsIFrame*>
461 (aFrame->GetFirstContinuation()->
462 Properties().Get(nsIFrame::IBSplitSpecialSibling()));
465 static nsIFrame* GetSpecialPrevSibling(nsIFrame* aFrame)
467 NS_PRECONDITION(IsFrameSpecial(aFrame), "Shouldn't call this");
469 // We only store the "special sibling" annotation with the first
470 // frame in the continuation chain. Walk back to find that frame now.
471 return static_cast<nsIFrame*>
472 (aFrame->GetFirstContinuation()->
473 Properties().Get(nsIFrame::IBSplitSpecialPrevSibling()));
476 static nsIFrame*
477 GetLastSpecialSibling(nsIFrame* aFrame, PRBool aReturnEmptyTrailingInline)
479 for (nsIFrame *frame = aFrame, *next; ; frame = next) {
480 next = GetSpecialSibling(frame);
481 if (!next ||
482 (!aReturnEmptyTrailingInline && !next->GetFirstChild(nsnull) &&
483 !GetSpecialSibling(next))) {
484 NS_ASSERTION(!next || !IsInlineOutside(frame),
485 "Should have a block here!");
486 return frame;
489 NS_NOTREACHED("unreachable code");
490 return nsnull;
493 static void
494 SetFrameIsSpecial(nsIFrame* aFrame, nsIFrame* aSpecialSibling)
496 NS_PRECONDITION(aFrame, "bad args!");
498 // We should be the only continuation
499 NS_ASSERTION(!aFrame->GetPrevContinuation(),
500 "assigning special sibling to other than first continuation!");
501 NS_ASSERTION(!aFrame->GetNextContinuation() ||
502 IsFrameSpecial(aFrame->GetNextContinuation()),
503 "should have no non-special continuations here");
505 // Mark the frame as "special".
506 aFrame->AddStateBits(NS_FRAME_IS_SPECIAL);
508 if (aSpecialSibling) {
509 NS_ASSERTION(!aSpecialSibling->GetPrevContinuation(),
510 "assigning something other than the first continuation as the "
511 "special sibling");
513 // Store the "special sibling" (if we were given one) with the
514 // first frame in the flow.
515 FramePropertyTable* props = aFrame->PresContext()->PropertyTable();
516 props->Set(aFrame, nsIFrame::IBSplitSpecialSibling(), aSpecialSibling);
517 props->Set(aSpecialSibling, nsIFrame::IBSplitSpecialPrevSibling(), aFrame);
521 static nsIFrame*
522 GetIBContainingBlockFor(nsIFrame* aFrame)
524 NS_PRECONDITION(IsFrameSpecial(aFrame),
525 "GetIBContainingBlockFor() should only be called on known IB frames");
527 // Get the first "normal" ancestor of the target frame.
528 nsIFrame* parentFrame;
529 do {
530 parentFrame = aFrame->GetParent();
532 if (! parentFrame) {
533 NS_ERROR("no unsplit block frame in IB hierarchy");
534 return aFrame;
537 // Note that we ignore non-special frames which have a pseudo on their
538 // style context -- they're not the frames we're looking for! In
539 // particular, they may be hiding a real parent that _is_ special.
540 if (!IsFrameSpecial(parentFrame) &&
541 !parentFrame->GetStyleContext()->GetPseudo())
542 break;
544 aFrame = parentFrame;
545 } while (1);
547 // post-conditions
548 NS_ASSERTION(parentFrame, "no normal ancestor found for special frame in GetIBContainingBlockFor");
549 NS_ASSERTION(parentFrame != aFrame, "parentFrame is actually the child frame - bogus reslt");
551 return parentFrame;
554 //----------------------------------------------------------------------
556 // Block/inline frame construction logic. We maintain a few invariants here:
558 // 1. Block frames contain block and inline frames.
560 // 2. Inline frames only contain inline frames. If an inline parent has a block
561 // child then the block child is migrated upward until it lands in a block
562 // parent (the inline frames containing block is where it will end up).
564 // After this function returns, aLink is pointing to the first link at or
565 // after its starting position for which the next frame is a block. If there
566 // is no such link, it points to the end of the list.
567 static void
568 FindFirstBlock(nsFrameList::FrameLinkEnumerator& aLink)
570 for ( ; !aLink.AtEnd(); aLink.Next()) {
571 if (!IsInlineOutside(aLink.NextFrame())) {
572 return;
577 // This function returns a frame link enumerator pointing to the first link in
578 // the list for which the next frame is not block. If there is no such link,
579 // it points to the end of the list.
580 static nsFrameList::FrameLinkEnumerator
581 FindFirstNonBlock(const nsFrameList& aList)
583 nsFrameList::FrameLinkEnumerator link(aList);
584 for (; !link.AtEnd(); link.Next()) {
585 if (IsInlineOutside(link.NextFrame())) {
586 break;
589 return link;
592 inline void
593 SetInitialSingleChild(nsIFrame* aParent, nsIFrame* aFrame)
595 NS_PRECONDITION(!aFrame->GetNextSibling(), "Should be using a frame list");
596 nsFrameList temp(aFrame, aFrame);
597 aParent->SetInitialChildList(nsnull, temp);
600 // -----------------------------------------------------------
602 // Structure used when constructing formatting object trees.
603 struct nsFrameItems : public nsFrameList
605 // Appends the frame to the end of the list
606 void AddChild(nsIFrame* aChild);
609 void
610 nsFrameItems::AddChild(nsIFrame* aChild)
612 NS_PRECONDITION(aChild, "nsFrameItems::AddChild");
614 // It'd be really nice if we could just AppendFrames(nsnull, aChild) here,
615 // but some of our callers put frames that have different
616 // parents (caption, I'm looking at you) on the same framelist, and
617 // nsFrameList asserts if you try to do that.
618 if (IsEmpty()) {
619 SetFrames(aChild);
621 else {
622 NS_ASSERTION(aChild != mLastChild,
623 "Same frame being added to frame list twice?");
624 mLastChild->SetNextSibling(aChild);
625 mLastChild = nsLayoutUtils::GetLastSibling(aChild);
629 // -----------------------------------------------------------
631 // Structure used when constructing formatting object trees. Contains
632 // state information needed for absolutely positioned elements
633 struct nsAbsoluteItems : nsFrameItems {
634 // containing block for absolutely positioned elements
635 nsIFrame* containingBlock;
637 nsAbsoluteItems(nsIFrame* aContainingBlock);
638 #ifdef DEBUG
639 // XXXbz Does this need a debug-only assignment operator that nulls out the
640 // childList in the nsAbsoluteItems we're copying? Introducing a difference
641 // between debug and non-debug behavior seems bad, so I guess not...
642 ~nsAbsoluteItems() {
643 NS_ASSERTION(!FirstChild(),
644 "Dangling child list. Someone forgot to insert it?");
646 #endif
648 // Appends the frame to the end of the list
649 void AddChild(nsIFrame* aChild);
652 nsAbsoluteItems::nsAbsoluteItems(nsIFrame* aContainingBlock)
653 : containingBlock(aContainingBlock)
657 // Additional behavior is that it sets the frame's NS_FRAME_OUT_OF_FLOW flag
658 void
659 nsAbsoluteItems::AddChild(nsIFrame* aChild)
661 NS_ASSERTION(aChild->PresContext()->FrameManager()->
662 GetPlaceholderFrameFor(aChild),
663 "Child without placeholder being added to nsAbsoluteItems?");
664 aChild->AddStateBits(NS_FRAME_OUT_OF_FLOW);
665 nsFrameItems::AddChild(aChild);
668 // -----------------------------------------------------------
670 // Structure for saving the existing state when pushing/poping containing
671 // blocks. The destructor restores the state to its previous state
672 class NS_STACK_CLASS nsFrameConstructorSaveState {
673 public:
674 nsFrameConstructorSaveState();
675 ~nsFrameConstructorSaveState();
677 private:
678 nsAbsoluteItems* mItems; // pointer to struct whose data we save/restore
679 PRPackedBool* mFixedPosIsAbsPos;
681 nsAbsoluteItems mSavedItems; // copy of original data
682 PRPackedBool mSavedFixedPosIsAbsPos;
684 // The name of the child list in which our frames would belong
685 nsIAtom* mChildListName;
686 nsFrameConstructorState* mState;
688 friend class nsFrameConstructorState;
691 // Structure used to keep track of a list of bindings we need to call
692 // AddToAttachedQueue on. These should be in post-order depth-first
693 // flattened tree traversal order.
694 struct PendingBinding : public PRCList
696 #ifdef NS_BUILD_REFCNT_LOGGING
697 PendingBinding() {
698 MOZ_COUNT_CTOR(PendingBinding);
700 ~PendingBinding() {
701 MOZ_COUNT_DTOR(PendingBinding);
703 #endif
705 nsRefPtr<nsXBLBinding> mBinding;
708 // Structure used for maintaining state information during the
709 // frame construction process
710 class NS_STACK_CLASS nsFrameConstructorState {
711 public:
712 nsPresContext *mPresContext;
713 nsIPresShell *mPresShell;
714 nsFrameManager *mFrameManager;
716 #ifdef MOZ_XUL
717 // Frames destined for the nsGkAtoms::popupList.
718 nsAbsoluteItems mPopupItems;
719 #endif
721 // Containing block information for out-of-flow frames.
722 nsAbsoluteItems mFixedItems;
723 nsAbsoluteItems mAbsoluteItems;
724 nsAbsoluteItems mFloatedItems;
726 nsCOMPtr<nsILayoutHistoryState> mFrameState;
727 // These bits will be added to the state bits of any frame we construct
728 // using this state.
729 nsFrameState mAdditionalStateBits;
731 // When working with the -moz-transform property, we want to hook
732 // the abs-pos and fixed-pos lists together, since transformed
733 // elements are fixed-pos containing blocks. This flag determines
734 // whether or not we want to wire the fixed-pos and abs-pos lists
735 // together.
736 PRPackedBool mFixedPosIsAbsPos;
738 // A boolean to indicate whether we have a "pending" popupgroup. That is, we
739 // have already created the FrameConstructionItem for the root popupgroup but
740 // we have not yet created the relevant frame.
741 PRPackedBool mHavePendingPopupgroup;
743 // If false (which is the default) then call SetPrimaryFrame() as needed
744 // during frame construction. If true, don't make any SetPrimaryFrame()
745 // calls. The mCreatingExtraFrames == PR_TRUE mode is meant to be used for
746 // construction of random "extra" frames for elements via normal frame
747 // construction APIs (e.g. replication of things across pages in paginated
748 // mode).
749 PRPackedBool mCreatingExtraFrames;
751 nsCOMArray<nsIContent> mGeneratedTextNodesWithInitializer;
753 // Constructor
754 // Use the passed-in history state.
755 nsFrameConstructorState(nsIPresShell* aPresShell,
756 nsIFrame* aFixedContainingBlock,
757 nsIFrame* aAbsoluteContainingBlock,
758 nsIFrame* aFloatContainingBlock,
759 nsILayoutHistoryState* aHistoryState);
760 // Get the history state from the pres context's pres shell.
761 nsFrameConstructorState(nsIPresShell* aPresShell,
762 nsIFrame* aFixedContainingBlock,
763 nsIFrame* aAbsoluteContainingBlock,
764 nsIFrame* aFloatContainingBlock);
766 ~nsFrameConstructorState();
768 // Function to push the existing absolute containing block state and
769 // create a new scope. Code that uses this function should get matching
770 // logic in GetAbsoluteContainingBlock.
771 void PushAbsoluteContainingBlock(nsIFrame* aNewAbsoluteContainingBlock,
772 nsFrameConstructorSaveState& aSaveState);
774 // Function to push the existing float containing block state and
775 // create a new scope. Code that uses this function should get matching
776 // logic in GetFloatContainingBlock.
777 // Pushing a null float containing block forbids any frames from being
778 // floated until a new float containing block is pushed.
779 // XXX we should get rid of null float containing blocks and teach the
780 // various frame classes to deal with floats instead.
781 void PushFloatContainingBlock(nsIFrame* aNewFloatContainingBlock,
782 nsFrameConstructorSaveState& aSaveState);
784 // Function to return the proper geometric parent for a frame with display
785 // struct given by aStyleDisplay and parent's frame given by
786 // aContentParentFrame.
787 nsIFrame* GetGeometricParent(const nsStyleDisplay* aStyleDisplay,
788 nsIFrame* aContentParentFrame);
791 * Function to add a new frame to the right frame list. This MUST be called
792 * on frames before their children have been processed if the frames might
793 * conceivably be out-of-flow; otherwise cleanup in error cases won't work
794 * right. Also, this MUST be called on frames after they have been
795 * initialized.
796 * @param aNewFrame the frame to add
797 * @param aFrameItems the list to add in-flow frames to
798 * @param aContent the content pointer for aNewFrame
799 * @param aStyleContext the style context resolved for aContent
800 * @param aParentFrame the parent frame for the content if it were in-flow
801 * @param aCanBePositioned pass false if the frame isn't allowed to be
802 * positioned
803 * @param aCanBeFloated pass false if the frame isn't allowed to be
804 * floated
805 * @param aIsOutOfFlowPopup pass true if the frame is an out-of-flow popup
806 * (XUL-only)
807 * @throws NS_ERROR_OUT_OF_MEMORY if it happens.
808 * @note If this method throws, that means that aNewFrame was not inserted
809 * into any frame lists. Furthermore, this method will handle cleanup
810 * of aNewFrame (via calling Destroy() on it).
812 nsresult AddChild(nsIFrame* aNewFrame,
813 nsFrameItems& aFrameItems,
814 nsIContent* aContent,
815 nsStyleContext* aStyleContext,
816 nsIFrame* aParentFrame,
817 PRBool aCanBePositioned = PR_TRUE,
818 PRBool aCanBeFloated = PR_TRUE,
819 PRBool aIsOutOfFlowPopup = PR_FALSE,
820 PRBool aInsertAfter = PR_FALSE,
821 nsIFrame* aInsertAfterFrame = nsnull);
824 * Function to return the fixed-pos element list. Normally this will just hand back the
825 * fixed-pos element list, but in case we're dealing with a transformed element that's
826 * acting as an abs-pos and fixed-pos container, we'll hand back the abs-pos list. Callers should
827 * use this function if they want to get the list acting as the fixed-pos item parent.
829 nsAbsoluteItems& GetFixedItems()
831 return mFixedPosIsAbsPos ? mAbsoluteItems : mFixedItems;
833 const nsAbsoluteItems& GetFixedItems() const
835 return mFixedPosIsAbsPos ? mAbsoluteItems : mFixedItems;
840 * class to automatically push and pop a pending binding in the frame
841 * constructor state. See nsCSSFrameConstructor::FrameConstructionItem
842 * mPendingBinding documentation.
844 class PendingBindingAutoPusher;
845 friend class PendingBindingAutoPusher;
846 class NS_STACK_CLASS PendingBindingAutoPusher {
847 public:
848 PendingBindingAutoPusher(nsFrameConstructorState& aState,
849 PendingBinding* aPendingBinding) :
850 mState(aState),
851 mPendingBinding(aState.mCurrentPendingBindingInsertionPoint)
853 NS_PRECONDITION(mPendingBinding, "how did that happen?");
854 if (aPendingBinding) {
855 aState.mCurrentPendingBindingInsertionPoint = aPendingBinding;
859 ~PendingBindingAutoPusher()
861 mState.mCurrentPendingBindingInsertionPoint = mPendingBinding;
864 private:
865 nsFrameConstructorState& mState;
866 PRCList* mPendingBinding;
870 * Add a new pending binding to the list
872 void AddPendingBinding(PendingBinding* aPendingBinding) {
873 PR_INSERT_BEFORE(aPendingBinding, mCurrentPendingBindingInsertionPoint);
876 protected:
877 friend class nsFrameConstructorSaveState;
880 * ProcessFrameInsertions takes the frames in aFrameItems and adds them as
881 * kids to the aChildListName child list of |aFrameItems.containingBlock|.
883 void ProcessFrameInsertions(nsAbsoluteItems& aFrameItems,
884 nsIAtom* aChildListName);
886 // Our list of all pending bindings. When we're done, we need to call
887 // AddToAttachedQueue on all of them, in order.
888 PRCList mPendingBindings;
890 PRCList* mCurrentPendingBindingInsertionPoint;
893 nsFrameConstructorState::nsFrameConstructorState(nsIPresShell* aPresShell,
894 nsIFrame* aFixedContainingBlock,
895 nsIFrame* aAbsoluteContainingBlock,
896 nsIFrame* aFloatContainingBlock,
897 nsILayoutHistoryState* aHistoryState)
898 : mPresContext(aPresShell->GetPresContext()),
899 mPresShell(aPresShell),
900 mFrameManager(aPresShell->FrameManager()),
901 #ifdef MOZ_XUL
902 mPopupItems(nsnull),
903 #endif
904 mFixedItems(aFixedContainingBlock),
905 mAbsoluteItems(aAbsoluteContainingBlock),
906 mFloatedItems(aFloatContainingBlock),
907 // See PushAbsoluteContaningBlock below
908 mFrameState(aHistoryState),
909 mAdditionalStateBits(0),
910 mFixedPosIsAbsPos(aAbsoluteContainingBlock &&
911 aAbsoluteContainingBlock->GetStyleDisplay()->
912 HasTransform()),
913 mHavePendingPopupgroup(PR_FALSE),
914 mCreatingExtraFrames(PR_FALSE),
915 mCurrentPendingBindingInsertionPoint(&mPendingBindings)
917 #ifdef MOZ_XUL
918 nsIRootBox* rootBox = nsIRootBox::GetRootBox(aPresShell);
919 if (rootBox) {
920 mPopupItems.containingBlock = rootBox->GetPopupSetFrame();
922 #endif
923 MOZ_COUNT_CTOR(nsFrameConstructorState);
924 PR_INIT_CLIST(&mPendingBindings);
927 nsFrameConstructorState::nsFrameConstructorState(nsIPresShell* aPresShell,
928 nsIFrame* aFixedContainingBlock,
929 nsIFrame* aAbsoluteContainingBlock,
930 nsIFrame* aFloatContainingBlock)
931 : mPresContext(aPresShell->GetPresContext()),
932 mPresShell(aPresShell),
933 mFrameManager(aPresShell->FrameManager()),
934 #ifdef MOZ_XUL
935 mPopupItems(nsnull),
936 #endif
937 mFixedItems(aFixedContainingBlock),
938 mAbsoluteItems(aAbsoluteContainingBlock),
939 mFloatedItems(aFloatContainingBlock),
940 // See PushAbsoluteContaningBlock below
941 mAdditionalStateBits(0),
942 mFixedPosIsAbsPos(aAbsoluteContainingBlock &&
943 aAbsoluteContainingBlock->GetStyleDisplay()->
944 HasTransform()),
945 mHavePendingPopupgroup(PR_FALSE),
946 mCreatingExtraFrames(PR_FALSE),
947 mCurrentPendingBindingInsertionPoint(&mPendingBindings)
949 #ifdef MOZ_XUL
950 nsIRootBox* rootBox = nsIRootBox::GetRootBox(aPresShell);
951 if (rootBox) {
952 mPopupItems.containingBlock = rootBox->GetPopupSetFrame();
954 #endif
955 MOZ_COUNT_CTOR(nsFrameConstructorState);
956 mFrameState = aPresShell->GetDocument()->GetLayoutHistoryState();
957 PR_INIT_CLIST(&mPendingBindings);
960 nsFrameConstructorState::~nsFrameConstructorState()
962 // Frame order comparison functions only work properly when the placeholders
963 // have been inserted into the frame tree. So for example if we have a new float
964 // containing the placeholder for a new abs-pos frame, and we process the abs-pos
965 // insertion first, then we won't be able to find the right place to insert in
966 // in the abs-pos list. So put floats in first, because they can contain placeholders
967 // for abs-pos and fixed-pos items whose containing blocks are outside the floats.
968 // Then put abs-pos frames in, because they can contain placeholders for fixed-pos
969 // items whose containing block is outside the abs-pos frames.
970 MOZ_COUNT_DTOR(nsFrameConstructorState);
971 ProcessFrameInsertions(mFloatedItems, nsGkAtoms::floatList);
972 ProcessFrameInsertions(mAbsoluteItems, nsGkAtoms::absoluteList);
973 ProcessFrameInsertions(mFixedItems, nsGkAtoms::fixedList);
974 #ifdef MOZ_XUL
975 ProcessFrameInsertions(mPopupItems, nsGkAtoms::popupList);
976 #endif
977 for (PRInt32 i = mGeneratedTextNodesWithInitializer.Count() - 1; i >= 0; --i) {
978 mGeneratedTextNodesWithInitializer[i]->
979 DeleteProperty(nsGkAtoms::genConInitializerProperty);
981 if (!PR_CLIST_IS_EMPTY(&mPendingBindings)) {
982 nsBindingManager* bindingManager = mPresShell->GetDocument()->BindingManager();
983 do {
984 PendingBinding* pendingBinding =
985 static_cast<PendingBinding*>(PR_NEXT_LINK(&mPendingBindings));
986 PR_REMOVE_LINK(pendingBinding);
987 bindingManager->AddToAttachedQueue(pendingBinding->mBinding);
988 delete pendingBinding;
989 } while (!PR_CLIST_IS_EMPTY(&mPendingBindings));
993 static nsIFrame*
994 AdjustAbsoluteContainingBlock(nsIFrame* aContainingBlockIn)
996 if (!aContainingBlockIn) {
997 return nsnull;
1000 // Always use the container's first continuation. (Inline frames can have
1001 // non-fluid bidi continuations...)
1002 return aContainingBlockIn->GetFirstContinuation();
1005 void
1006 nsFrameConstructorState::PushAbsoluteContainingBlock(nsIFrame* aNewAbsoluteContainingBlock,
1007 nsFrameConstructorSaveState& aSaveState)
1009 aSaveState.mItems = &mAbsoluteItems;
1010 aSaveState.mSavedItems = mAbsoluteItems;
1011 aSaveState.mChildListName = nsGkAtoms::absoluteList;
1012 aSaveState.mState = this;
1014 /* Store whether we're wiring the abs-pos and fixed-pos lists together. */
1015 aSaveState.mFixedPosIsAbsPos = &mFixedPosIsAbsPos;
1016 aSaveState.mSavedFixedPosIsAbsPos = mFixedPosIsAbsPos;
1018 mAbsoluteItems =
1019 nsAbsoluteItems(AdjustAbsoluteContainingBlock(aNewAbsoluteContainingBlock));
1021 /* See if we're wiring the fixed-pos and abs-pos lists together. This happens iff
1022 * we're a transformed element.
1024 mFixedPosIsAbsPos = (aNewAbsoluteContainingBlock &&
1025 aNewAbsoluteContainingBlock->GetStyleDisplay()->HasTransform());
1028 void
1029 nsFrameConstructorState::PushFloatContainingBlock(nsIFrame* aNewFloatContainingBlock,
1030 nsFrameConstructorSaveState& aSaveState)
1032 NS_PRECONDITION(!aNewFloatContainingBlock ||
1033 aNewFloatContainingBlock->IsFloatContainingBlock(),
1034 "Please push a real float containing block!");
1035 aSaveState.mItems = &mFloatedItems;
1036 aSaveState.mSavedItems = mFloatedItems;
1037 aSaveState.mChildListName = nsGkAtoms::floatList;
1038 aSaveState.mState = this;
1039 mFloatedItems = nsAbsoluteItems(aNewFloatContainingBlock);
1042 nsIFrame*
1043 nsFrameConstructorState::GetGeometricParent(const nsStyleDisplay* aStyleDisplay,
1044 nsIFrame* aContentParentFrame)
1046 NS_PRECONDITION(aStyleDisplay, "Must have display struct!");
1048 // If there is no container for a fixed, absolute, or floating root
1049 // frame, we will ignore the positioning. This hack is originally
1050 // brought to you by the letter T: tables, since other roots don't
1051 // even call into this code. See bug 178855.
1053 // XXX Disabling positioning in this case is a hack. If one was so inclined,
1054 // one could support this either by (1) inserting a dummy block between the
1055 // table and the canvas or (2) teaching the canvas how to reflow positioned
1056 // elements. (1) has the usual problems when multiple frames share the same
1057 // content (notice all the special cases in this file dealing with inner
1058 // tables and outer tables which share the same content). (2) requires some
1059 // work and possible factoring.
1061 // XXXbz couldn't we just force position to "static" on roots and
1062 // float to "none"? That's OK per CSS 2.1, as far as I can tell.
1064 if (aStyleDisplay->IsFloating() && mFloatedItems.containingBlock) {
1065 NS_ASSERTION(!aStyleDisplay->IsAbsolutelyPositioned(),
1066 "Absolutely positioned _and_ floating?");
1067 return mFloatedItems.containingBlock;
1070 if (aStyleDisplay->mPosition == NS_STYLE_POSITION_ABSOLUTE &&
1071 mAbsoluteItems.containingBlock) {
1072 return mAbsoluteItems.containingBlock;
1075 if (aStyleDisplay->mPosition == NS_STYLE_POSITION_FIXED &&
1076 GetFixedItems().containingBlock) {
1077 return GetFixedItems().containingBlock;
1080 return aContentParentFrame;
1083 nsresult
1084 nsFrameConstructorState::AddChild(nsIFrame* aNewFrame,
1085 nsFrameItems& aFrameItems,
1086 nsIContent* aContent,
1087 nsStyleContext* aStyleContext,
1088 nsIFrame* aParentFrame,
1089 PRBool aCanBePositioned,
1090 PRBool aCanBeFloated,
1091 PRBool aIsOutOfFlowPopup,
1092 PRBool aInsertAfter,
1093 nsIFrame* aInsertAfterFrame)
1095 NS_PRECONDITION(!aNewFrame->GetNextSibling(), "Shouldn't happen");
1097 const nsStyleDisplay* disp = aNewFrame->GetStyleDisplay();
1099 // The comments in GetGeometricParent regarding root table frames
1100 // all apply here, unfortunately.
1102 PRBool needPlaceholder = PR_FALSE;
1103 nsFrameState placeholderType;
1104 nsFrameItems* frameItems = &aFrameItems;
1105 #ifdef MOZ_XUL
1106 if (NS_UNLIKELY(aIsOutOfFlowPopup)) {
1107 NS_ASSERTION(aNewFrame->GetParent() == mPopupItems.containingBlock,
1108 "Popup whose parent is not the popup containing block?");
1109 NS_ASSERTION(mPopupItems.containingBlock, "Must have a popup set frame!");
1110 needPlaceholder = PR_TRUE;
1111 frameItems = &mPopupItems;
1112 placeholderType = PLACEHOLDER_FOR_POPUP;
1114 else
1115 #endif // MOZ_XUL
1116 if (aCanBeFloated && disp->IsFloating() &&
1117 mFloatedItems.containingBlock) {
1118 NS_ASSERTION(aNewFrame->GetParent() == mFloatedItems.containingBlock,
1119 "Float whose parent is not the float containing block?");
1120 needPlaceholder = PR_TRUE;
1121 frameItems = &mFloatedItems;
1122 placeholderType = PLACEHOLDER_FOR_FLOAT;
1124 else if (aCanBePositioned) {
1125 if (disp->mPosition == NS_STYLE_POSITION_ABSOLUTE &&
1126 mAbsoluteItems.containingBlock) {
1127 NS_ASSERTION(aNewFrame->GetParent() == mAbsoluteItems.containingBlock,
1128 "Abs pos whose parent is not the abs pos containing block?");
1129 needPlaceholder = PR_TRUE;
1130 frameItems = &mAbsoluteItems;
1131 placeholderType = PLACEHOLDER_FOR_ABSPOS;
1133 if (disp->mPosition == NS_STYLE_POSITION_FIXED &&
1134 GetFixedItems().containingBlock) {
1135 NS_ASSERTION(aNewFrame->GetParent() == GetFixedItems().containingBlock,
1136 "Fixed pos whose parent is not the fixed pos containing block?");
1137 needPlaceholder = PR_TRUE;
1138 frameItems = &GetFixedItems();
1139 placeholderType = PLACEHOLDER_FOR_FIXEDPOS;
1143 if (needPlaceholder) {
1144 NS_ASSERTION(frameItems != &aFrameItems,
1145 "Putting frame in-flow _and_ want a placeholder?");
1146 nsIFrame* placeholderFrame;
1147 nsresult rv =
1148 nsCSSFrameConstructor::CreatePlaceholderFrameFor(mPresShell,
1149 aContent,
1150 aNewFrame,
1151 aStyleContext,
1152 aParentFrame,
1153 nsnull,
1154 placeholderType,
1155 &placeholderFrame);
1156 if (NS_FAILED(rv)) {
1157 // Note that aNewFrame could be the top frame for a scrollframe setup,
1158 // hence already set as the primary frame. So we have to clean up here.
1159 // But it shouldn't have any out-of-flow kids.
1160 // XXXbz Maybe add a utility function to assert that?
1161 aNewFrame->Destroy();
1162 return rv;
1165 placeholderFrame->AddStateBits(mAdditionalStateBits);
1166 // Add the placeholder frame to the flow
1167 aFrameItems.AddChild(placeholderFrame);
1169 #ifdef DEBUG
1170 else {
1171 NS_ASSERTION(aNewFrame->GetParent() == aParentFrame,
1172 "In-flow frame has wrong parent");
1174 #endif
1176 if (aInsertAfter) {
1177 frameItems->InsertFrame(nsnull, aInsertAfterFrame, aNewFrame);
1178 } else {
1179 frameItems->AddChild(aNewFrame);
1182 return NS_OK;
1185 void
1186 nsFrameConstructorState::ProcessFrameInsertions(nsAbsoluteItems& aFrameItems,
1187 nsIAtom* aChildListName)
1189 #define NS_NONXUL_LIST_TEST (&aFrameItems == &mFloatedItems && \
1190 aChildListName == nsGkAtoms::floatList) || \
1191 (&aFrameItems == &mAbsoluteItems && \
1192 aChildListName == nsGkAtoms::absoluteList) || \
1193 (&aFrameItems == &mFixedItems && \
1194 aChildListName == nsGkAtoms::fixedList)
1195 #ifdef MOZ_XUL
1196 NS_PRECONDITION(NS_NONXUL_LIST_TEST ||
1197 (&aFrameItems == &mPopupItems &&
1198 aChildListName == nsGkAtoms::popupList),
1199 "Unexpected aFrameItems/aChildListName combination");
1200 #else
1201 NS_PRECONDITION(NS_NONXUL_LIST_TEST,
1202 "Unexpected aFrameItems/aChildListName combination");
1203 #endif
1205 if (aFrameItems.IsEmpty()) {
1206 return;
1209 nsIFrame* containingBlock = aFrameItems.containingBlock;
1211 NS_ASSERTION(containingBlock,
1212 "Child list without containing block?");
1214 // Insert the frames hanging out in aItems. We can use SetInitialChildList()
1215 // if the containing block hasn't been reflowed yet (so NS_FRAME_FIRST_REFLOW
1216 // is set) and doesn't have any frames in the aChildListName child list yet.
1217 const nsFrameList& childList = containingBlock->GetChildList(aChildListName);
1218 nsresult rv = NS_OK;
1219 if (childList.IsEmpty() &&
1220 (containingBlock->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
1221 rv = containingBlock->SetInitialChildList(aChildListName, aFrameItems);
1222 } else {
1223 // Note that whether the frame construction context is doing an append or
1224 // not is not helpful here, since it could be appending to some frame in
1225 // the middle of the document, which means we're not necessarily
1226 // appending to the children of the containing block.
1228 // We need to make sure the 'append to the end of document' case is fast.
1229 // So first test the last child of the containing block
1230 nsIFrame* lastChild = childList.LastChild();
1232 // CompareTreePosition uses placeholder hierarchy for out of flow frames,
1233 // so this will make out-of-flows respect the ordering of placeholders,
1234 // which is great because it takes care of anonymous content.
1235 nsIFrame* firstNewFrame = aFrameItems.FirstChild();
1236 if (!lastChild ||
1237 nsLayoutUtils::CompareTreePosition(lastChild, firstNewFrame, containingBlock) < 0) {
1238 // no lastChild, or lastChild comes before the new children, so just append
1239 rv = containingBlock->AppendFrames(aChildListName, aFrameItems);
1240 } else {
1241 // try the other children
1242 nsIFrame* insertionPoint = nsnull;
1243 for (nsIFrame* f = childList.FirstChild(); f != lastChild;
1244 f = f->GetNextSibling()) {
1245 PRInt32 compare =
1246 nsLayoutUtils::CompareTreePosition(f, firstNewFrame, containingBlock);
1247 if (compare > 0) {
1248 // f comes after the new children, so stop here and insert after
1249 // the previous frame
1250 break;
1252 insertionPoint = f;
1254 rv = containingBlock->InsertFrames(aChildListName, insertionPoint,
1255 aFrameItems);
1259 NS_POSTCONDITION(aFrameItems.IsEmpty(), "How did that happen?");
1261 // XXXbz And if NS_FAILED(rv), what? I guess we need to clean up the list
1262 // and deal with all the placeholders... but what if the placeholders aren't
1263 // in the document yet? Could that happen?
1264 NS_ASSERTION(NS_SUCCEEDED(rv), "Frames getting lost!");
1268 nsFrameConstructorSaveState::nsFrameConstructorSaveState()
1269 : mItems(nsnull),
1270 mFixedPosIsAbsPos(nsnull),
1271 mSavedItems(nsnull),
1272 mSavedFixedPosIsAbsPos(PR_FALSE),
1273 mChildListName(nsnull),
1274 mState(nsnull)
1278 nsFrameConstructorSaveState::~nsFrameConstructorSaveState()
1280 // Restore the state
1281 if (mItems) {
1282 NS_ASSERTION(mState, "Can't have mItems set without having a state!");
1283 mState->ProcessFrameInsertions(*mItems, mChildListName);
1284 *mItems = mSavedItems;
1285 #ifdef DEBUG
1286 // We've transferred the child list, so drop the pointer we held to it.
1287 // Note that this only matters for the assert in ~nsAbsoluteItems.
1288 mSavedItems.Clear();
1289 #endif
1291 if (mFixedPosIsAbsPos) {
1292 *mFixedPosIsAbsPos = mSavedFixedPosIsAbsPos;
1296 static
1297 PRBool IsBorderCollapse(nsIFrame* aFrame)
1299 for (nsIFrame* frame = aFrame; frame; frame = frame->GetParent()) {
1300 if (nsGkAtoms::tableFrame == frame->GetType()) {
1301 return ((nsTableFrame*)frame)->IsBorderCollapse();
1304 NS_ASSERTION(PR_FALSE, "program error");
1305 return PR_FALSE;
1309 * Moves aFrameList from aOldParent to aNewParent. This updates the parent
1310 * pointer of the frames in the list, and reparents their views as needed.
1311 * nsFrame::SetParent sets the NS_FRAME_HAS_VIEW bit on aNewParent and its
1312 * ancestors as needed. Then it sets the list as the initial child list
1313 * on aNewParent, unless aNewParent either already has kids or has been
1314 * reflowed; in that case it appends the new frames. Note that this
1315 * method differs from ReparentFrames in that it doesn't change the kids'
1316 * style contexts.
1318 // XXXbz Since this is only used for {ib} splits, could we just copy the view
1319 // bits from aOldParent to aNewParent and then use the
1320 // nsFrameList::ApplySetParent? That would still leave us doing two passes
1321 // over the list, of course; if we really wanted to we could factor out the
1322 // relevant part of ReparentFrameViewList, I suppose... Or just get rid of
1323 // views, which would make most of this function go away.
1324 static void
1325 MoveChildrenTo(nsPresContext* aPresContext,
1326 nsIFrame* aOldParent,
1327 nsIFrame* aNewParent,
1328 nsFrameList& aFrameList)
1330 PRBool sameGrandParent = aOldParent->GetParent() == aNewParent->GetParent();
1332 if (aNewParent->HasView() || aOldParent->HasView() || !sameGrandParent) {
1333 // Move the frames into the new view
1334 nsHTMLContainerFrame::ReparentFrameViewList(aPresContext, aFrameList,
1335 aOldParent, aNewParent);
1338 for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
1339 e.get()->SetParent(aNewParent);
1342 if (aNewParent->GetChildList(nsnull).IsEmpty() &&
1343 (aNewParent->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
1344 aNewParent->SetInitialChildList(nsnull, aFrameList);
1345 } else {
1346 aNewParent->AppendFrames(nsnull, aFrameList);
1350 //----------------------------------------------------------------------
1352 nsCSSFrameConstructor::nsCSSFrameConstructor(nsIDocument *aDocument,
1353 nsIPresShell *aPresShell)
1354 : mDocument(aDocument)
1355 , mPresShell(aPresShell)
1356 , mRootElementFrame(nsnull)
1357 , mRootElementStyleFrame(nsnull)
1358 , mFixedContainingBlock(nsnull)
1359 , mDocElementContainingBlock(nsnull)
1360 , mGfxScrollFrame(nsnull)
1361 , mPageSequenceFrame(nsnull)
1362 , mUpdateCount(0)
1363 , mQuotesDirty(PR_FALSE)
1364 , mCountersDirty(PR_FALSE)
1365 , mIsDestroyingFrameTree(PR_FALSE)
1366 , mRebuildAllStyleData(PR_FALSE)
1367 , mHasRootAbsPosContainingBlock(PR_FALSE)
1368 , mObservingRefreshDriver(PR_FALSE)
1369 , mInStyleRefresh(PR_FALSE)
1370 , mHoverGeneration(0)
1371 , mRebuildAllExtraHint(nsChangeHint(0))
1372 , mPendingRestyles(ELEMENT_HAS_PENDING_RESTYLE |
1373 ELEMENT_IS_POTENTIAL_RESTYLE_ROOT, this)
1374 , mPendingAnimationRestyles(ELEMENT_HAS_PENDING_ANIMATION_RESTYLE |
1375 ELEMENT_IS_POTENTIAL_ANIMATION_RESTYLE_ROOT, this)
1377 // XXXbz this should be in Init() or something!
1378 if (!mPendingRestyles.Init() || !mPendingAnimationRestyles.Init()) {
1379 // now what?
1382 #ifdef DEBUG
1383 static PRBool gFirstTime = PR_TRUE;
1384 if (gFirstTime) {
1385 gFirstTime = PR_FALSE;
1386 char* flags = PR_GetEnv("GECKO_FRAMECTOR_DEBUG_FLAGS");
1387 if (flags) {
1388 PRBool error = PR_FALSE;
1389 for (;;) {
1390 char* comma = PL_strchr(flags, ',');
1391 if (comma)
1392 *comma = '\0';
1394 PRBool found = PR_FALSE;
1395 FrameCtorDebugFlags* flag = gFlags;
1396 FrameCtorDebugFlags* limit = gFlags + NUM_DEBUG_FLAGS;
1397 while (flag < limit) {
1398 if (PL_strcasecmp(flag->name, flags) == 0) {
1399 *(flag->on) = PR_TRUE;
1400 printf("nsCSSFrameConstructor: setting %s debug flag on\n", flag->name);
1401 found = PR_TRUE;
1402 break;
1404 ++flag;
1407 if (! found)
1408 error = PR_TRUE;
1410 if (! comma)
1411 break;
1413 *comma = ',';
1414 flags = comma + 1;
1417 if (error) {
1418 printf("Here are the available GECKO_FRAMECTOR_DEBUG_FLAGS:\n");
1419 FrameCtorDebugFlags* flag = gFlags;
1420 FrameCtorDebugFlags* limit = gFlags + NUM_DEBUG_FLAGS;
1421 while (flag < limit) {
1422 printf(" %s\n", flag->name);
1423 ++flag;
1425 printf("Note: GECKO_FRAMECTOR_DEBUG_FLAGS is a comma separated list of flag\n");
1426 printf("names (no whitespace)\n");
1430 #endif
1433 nsIXBLService * nsCSSFrameConstructor::GetXBLService()
1435 if (!gXBLService) {
1436 nsresult rv = CallGetService("@mozilla.org/xbl;1", &gXBLService);
1437 if (NS_FAILED(rv))
1438 gXBLService = nsnull;
1441 return gXBLService;
1444 void
1445 nsCSSFrameConstructor::NotifyDestroyingFrame(nsIFrame* aFrame)
1447 NS_PRECONDITION(mUpdateCount != 0,
1448 "Should be in an update while destroying frames");
1450 if (aFrame->GetStateBits() & NS_FRAME_GENERATED_CONTENT) {
1451 if (mQuoteList.DestroyNodesFor(aFrame))
1452 QuotesDirty();
1455 if (mCounterManager.DestroyNodesFor(aFrame)) {
1456 // Technically we don't need to update anything if we destroyed only
1457 // USE nodes. However, this is unlikely to happen in the real world
1458 // since USE nodes generally go along with INCREMENT nodes.
1459 CountersDirty();
1463 struct nsGenConInitializer {
1464 nsAutoPtr<nsGenConNode> mNode;
1465 nsGenConList* mList;
1466 void (nsCSSFrameConstructor::*mDirtyAll)();
1468 nsGenConInitializer(nsGenConNode* aNode, nsGenConList* aList,
1469 void (nsCSSFrameConstructor::*aDirtyAll)())
1470 : mNode(aNode), mList(aList), mDirtyAll(aDirtyAll) {}
1473 static void
1474 DestroyGenConInitializer(void* aFrame,
1475 nsIAtom* aPropertyName,
1476 void* aPropertyValue,
1477 void* aDtorData)
1479 delete static_cast<nsGenConInitializer*>(aPropertyValue);
1482 already_AddRefed<nsIContent>
1483 nsCSSFrameConstructor::CreateGenConTextNode(nsFrameConstructorState& aState,
1484 const nsString& aString,
1485 nsCOMPtr<nsIDOMCharacterData>* aText,
1486 nsGenConInitializer* aInitializer)
1488 nsCOMPtr<nsIContent> content;
1489 NS_NewTextNode(getter_AddRefs(content), mDocument->NodeInfoManager());
1490 if (!content) {
1491 // XXX The quotes/counters code doesn't like the text pointer
1492 // being null in case of dynamic changes!
1493 NS_ASSERTION(!aText, "this OOM case isn't handled very well");
1494 return nsnull;
1496 content->SetText(aString, PR_FALSE);
1497 if (aText) {
1498 *aText = do_QueryInterface(content);
1500 if (aInitializer) {
1501 content->SetProperty(nsGkAtoms::genConInitializerProperty, aInitializer,
1502 DestroyGenConInitializer);
1503 aState.mGeneratedTextNodesWithInitializer.AppendObject(content);
1505 return content.forget();
1508 already_AddRefed<nsIContent>
1509 nsCSSFrameConstructor::CreateGeneratedContent(nsFrameConstructorState& aState,
1510 nsIContent* aParentContent,
1511 nsStyleContext* aStyleContext,
1512 PRUint32 aContentIndex)
1514 // Get the content value
1515 const nsStyleContentData &data =
1516 aStyleContext->GetStyleContent()->ContentAt(aContentIndex);
1517 nsStyleContentType type = data.mType;
1519 if (eStyleContentType_Image == type) {
1520 if (!data.mContent.mImage) {
1521 // CSS had something specified that couldn't be converted to an
1522 // image object
1523 return nsnull;
1526 // Create an image content object and pass it the image request.
1527 // XXX Check if it's an image type we can handle...
1529 nsCOMPtr<nsINodeInfo> nodeInfo;
1530 nodeInfo = mDocument->NodeInfoManager()->GetNodeInfo(nsGkAtoms::mozgeneratedcontentimage, nsnull,
1531 kNameSpaceID_XHTML);
1533 nsCOMPtr<nsIContent> content;
1534 NS_NewGenConImageContent(getter_AddRefs(content), nodeInfo.forget(),
1535 data.mContent.mImage);
1536 return content.forget();
1539 switch (type) {
1540 case eStyleContentType_String:
1541 return CreateGenConTextNode(aState,
1542 nsDependentString(data.mContent.mString),
1543 nsnull, nsnull);
1545 case eStyleContentType_Attr:
1547 nsCOMPtr<nsIAtom> attrName;
1548 PRInt32 attrNameSpace = kNameSpaceID_None;
1549 nsAutoString contentString(data.mContent.mString);
1551 PRInt32 barIndex = contentString.FindChar('|'); // CSS namespace delimiter
1552 if (-1 != barIndex) {
1553 nsAutoString nameSpaceVal;
1554 contentString.Left(nameSpaceVal, barIndex);
1555 PRInt32 error;
1556 attrNameSpace = nameSpaceVal.ToInteger(&error, 10);
1557 contentString.Cut(0, barIndex + 1);
1558 if (contentString.Length()) {
1559 if (mDocument->IsHTML() && aParentContent->IsHTML()) {
1560 ToLowerCase(contentString);
1562 attrName = do_GetAtom(contentString);
1565 else {
1566 if (mDocument->IsHTML() && aParentContent->IsHTML()) {
1567 ToLowerCase(contentString);
1569 attrName = do_GetAtom(contentString);
1572 if (!attrName) {
1573 return nsnull;
1576 nsCOMPtr<nsIContent> content;
1577 NS_NewAttributeContent(mDocument->NodeInfoManager(),
1578 attrNameSpace, attrName, getter_AddRefs(content));
1579 return content.forget();
1582 case eStyleContentType_Counter:
1583 case eStyleContentType_Counters:
1585 nsCSSValue::Array* counters = data.mContent.mCounters;
1586 nsCounterList* counterList = mCounterManager.CounterListFor(
1587 nsDependentString(counters->Item(0).GetStringBufferValue()));
1588 if (!counterList)
1589 return nsnull;
1591 nsCounterUseNode* node =
1592 new nsCounterUseNode(counters, aContentIndex,
1593 type == eStyleContentType_Counters);
1594 if (!node)
1595 return nsnull;
1597 nsGenConInitializer* initializer =
1598 new nsGenConInitializer(node, counterList,
1599 &nsCSSFrameConstructor::CountersDirty);
1600 return CreateGenConTextNode(aState, EmptyString(), &node->mText,
1601 initializer);
1604 case eStyleContentType_Image:
1605 NS_NOTREACHED("handled by if above");
1606 return nsnull;
1608 case eStyleContentType_OpenQuote:
1609 case eStyleContentType_CloseQuote:
1610 case eStyleContentType_NoOpenQuote:
1611 case eStyleContentType_NoCloseQuote:
1613 nsQuoteNode* node =
1614 new nsQuoteNode(type, aContentIndex);
1615 if (!node)
1616 return nsnull;
1618 nsGenConInitializer* initializer =
1619 new nsGenConInitializer(node, &mQuoteList,
1620 &nsCSSFrameConstructor::QuotesDirty);
1621 return CreateGenConTextNode(aState, EmptyString(), &node->mText,
1622 initializer);
1625 case eStyleContentType_AltContent:
1627 // Use the "alt" attribute; if that fails and the node is an HTML
1628 // <input>, try the value attribute and then fall back to some default
1629 // localized text we have.
1630 // XXX what if the 'alt' attribute is added later, how will we
1631 // detect that and do the right thing here?
1632 if (aParentContent->HasAttr(kNameSpaceID_None, nsGkAtoms::alt)) {
1633 nsCOMPtr<nsIContent> content;
1634 NS_NewAttributeContent(mDocument->NodeInfoManager(),
1635 kNameSpaceID_None, nsGkAtoms::alt, getter_AddRefs(content));
1636 return content.forget();
1639 if (aParentContent->IsHTML() &&
1640 aParentContent->NodeInfo()->Equals(nsGkAtoms::input)) {
1641 if (aParentContent->HasAttr(kNameSpaceID_None, nsGkAtoms::value)) {
1642 nsCOMPtr<nsIContent> content;
1643 NS_NewAttributeContent(mDocument->NodeInfoManager(),
1644 kNameSpaceID_None, nsGkAtoms::value, getter_AddRefs(content));
1645 return content.forget();
1648 nsXPIDLString temp;
1649 nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
1650 "Submit", temp);
1651 return CreateGenConTextNode(aState, temp, nsnull, nsnull);
1654 break;
1656 } // switch
1658 return nsnull;
1662 * aParentFrame - the frame that should be the parent of the generated
1663 * content. This is the frame for the corresponding content node,
1664 * which must not be a leaf frame.
1666 * Any items created are added to aItems.
1668 * We create an XML element (tag _moz_generated_content_before or
1669 * _moz_generated_content_after) representing the pseudoelement. We
1670 * create a DOM node for each 'content' item and make those nodes the
1671 * children of the XML element. Then we create a frame subtree for
1672 * the XML element as if it were a regular child of
1673 * aParentFrame/aParentContent, giving the XML element the ::before or
1674 * ::after style.
1676 void
1677 nsCSSFrameConstructor::CreateGeneratedContentItem(nsFrameConstructorState& aState,
1678 nsIFrame* aParentFrame,
1679 nsIContent* aParentContent,
1680 nsStyleContext* aStyleContext,
1681 nsCSSPseudoElements::Type aPseudoElement,
1682 FrameConstructionItemList& aItems)
1684 // XXXbz is this ever true?
1685 if (!aParentContent->IsElement()) {
1686 NS_ERROR("Bogus generated content parent");
1687 return;
1690 nsStyleSet *styleSet = mPresShell->StyleSet();
1692 // Probe for the existence of the pseudo-element
1693 nsRefPtr<nsStyleContext> pseudoStyleContext;
1694 pseudoStyleContext =
1695 styleSet->ProbePseudoElementStyle(aParentContent->AsElement(),
1696 aPseudoElement,
1697 aStyleContext);
1698 if (!pseudoStyleContext)
1699 return;
1700 // |ProbePseudoStyleFor| checked the 'display' property and the
1701 // |ContentCount()| of the 'content' property for us.
1702 nsCOMPtr<nsINodeInfo> nodeInfo;
1703 nsIAtom* elemName = aPseudoElement == nsCSSPseudoElements::ePseudo_before ?
1704 nsGkAtoms::mozgeneratedcontentbefore : nsGkAtoms::mozgeneratedcontentafter;
1705 nodeInfo = mDocument->NodeInfoManager()->GetNodeInfo(elemName, nsnull,
1706 kNameSpaceID_None);
1707 nsCOMPtr<nsIContent> container;
1708 nsresult rv = NS_NewXMLElement(getter_AddRefs(container), nodeInfo.forget());
1709 if (NS_FAILED(rv))
1710 return;
1711 container->SetNativeAnonymous();
1713 rv = container->BindToTree(mDocument, aParentContent, aParentContent, PR_TRUE);
1714 if (NS_FAILED(rv)) {
1715 container->UnbindFromTree();
1716 return;
1719 PRUint32 contentCount = pseudoStyleContext->GetStyleContent()->ContentCount();
1720 for (PRUint32 contentIndex = 0; contentIndex < contentCount; contentIndex++) {
1721 nsCOMPtr<nsIContent> content =
1722 CreateGeneratedContent(aState, aParentContent, pseudoStyleContext,
1723 contentIndex);
1724 if (content) {
1725 container->AppendChildTo(content, PR_FALSE);
1729 AddFrameConstructionItemsInternal(aState, container, aParentFrame, elemName,
1730 kNameSpaceID_None, PR_TRUE,
1731 pseudoStyleContext,
1732 ITEM_IS_GENERATED_CONTENT, aItems);
1735 /****************************************************
1736 ** BEGIN TABLE SECTION
1737 ****************************************************/
1739 // The term pseudo frame is being used instead of anonymous frame, since anonymous
1740 // frame has been used elsewhere to refer to frames that have generated content
1742 static PRBool
1743 IsTableRelated(nsIAtom* aParentType)
1745 return
1746 nsGkAtoms::tableOuterFrame == aParentType ||
1747 nsGkAtoms::tableFrame == aParentType ||
1748 nsGkAtoms::tableRowGroupFrame == aParentType ||
1749 nsGkAtoms::tableRowFrame == aParentType ||
1750 nsGkAtoms::tableCaptionFrame == aParentType ||
1751 nsGkAtoms::tableColGroupFrame == aParentType ||
1752 nsGkAtoms::tableColFrame == aParentType ||
1753 IS_TABLE_CELL(aParentType);
1756 // Return whether the given frame is a table pseudo-frame. Note that
1757 // cell-content and table-outer frames have pseudo-types, but are always
1758 // created, even for non-anonymous cells and tables respectively. So for those
1759 // we have to examine the cell or table frame to see whether it's a pseudo
1760 // frame. In particular, a lone table caption will have an outer table as its
1761 // parent, but will also trigger construction of an empty inner table, which
1762 // will be the one we can examine to see whether the outer was a pseudo-frame.
1763 static PRBool
1764 IsTablePseudo(nsIFrame* aFrame)
1766 nsIAtom* pseudoType = aFrame->GetStyleContext()->GetPseudo();
1767 return pseudoType &&
1768 (pseudoType == nsCSSAnonBoxes::table ||
1769 pseudoType == nsCSSAnonBoxes::inlineTable ||
1770 pseudoType == nsCSSAnonBoxes::tableColGroup ||
1771 pseudoType == nsCSSAnonBoxes::tableRowGroup ||
1772 pseudoType == nsCSSAnonBoxes::tableRow ||
1773 pseudoType == nsCSSAnonBoxes::tableCell ||
1774 (pseudoType == nsCSSAnonBoxes::cellContent &&
1775 aFrame->GetParent()->GetStyleContext()->GetPseudo() ==
1776 nsCSSAnonBoxes::tableCell) ||
1777 (pseudoType == nsCSSAnonBoxes::tableOuter &&
1778 (aFrame->GetFirstChild(nsnull)->GetStyleContext()->GetPseudo() ==
1779 nsCSSAnonBoxes::table ||
1780 aFrame->GetFirstChild(nsnull)->GetStyleContext()->GetPseudo() ==
1781 nsCSSAnonBoxes::inlineTable)));
1784 /* static */
1785 nsCSSFrameConstructor::ParentType
1786 nsCSSFrameConstructor::GetParentType(nsIAtom* aFrameType)
1788 if (aFrameType == nsGkAtoms::tableFrame) {
1789 return eTypeTable;
1791 if (aFrameType == nsGkAtoms::tableRowGroupFrame) {
1792 return eTypeRowGroup;
1794 if (aFrameType == nsGkAtoms::tableRowFrame) {
1795 return eTypeRow;
1797 if (aFrameType == nsGkAtoms::tableColGroupFrame) {
1798 return eTypeColGroup;
1801 return eTypeBlock;
1804 static nsIFrame*
1805 AdjustCaptionParentFrame(nsIFrame* aParentFrame)
1807 if (nsGkAtoms::tableFrame == aParentFrame->GetType()) {
1808 return aParentFrame->GetParent();;
1810 return aParentFrame;
1814 * If the parent frame is a |tableFrame| and the child is a
1815 * |captionFrame|, then we want to insert the frames beneath the
1816 * |tableFrame|'s parent frame. Returns |PR_TRUE| if the parent frame
1817 * needed to be fixed up.
1819 static PRBool
1820 GetCaptionAdjustedParent(nsIFrame* aParentFrame,
1821 const nsIFrame* aChildFrame,
1822 nsIFrame** aAdjParentFrame)
1824 *aAdjParentFrame = aParentFrame;
1825 PRBool haveCaption = PR_FALSE;
1827 if (nsGkAtoms::tableCaptionFrame == aChildFrame->GetType()) {
1828 haveCaption = PR_TRUE;
1829 *aAdjParentFrame = AdjustCaptionParentFrame(aParentFrame);
1831 return haveCaption;
1834 void
1835 nsCSSFrameConstructor::AdjustParentFrame(nsIFrame* & aParentFrame,
1836 const FrameConstructionData* aFCData,
1837 nsStyleContext* aStyleContext)
1839 NS_PRECONDITION(aStyleContext, "Must have child's style context");
1840 NS_PRECONDITION(aFCData, "Must have frame construction data");
1842 PRBool tablePart = ((aFCData->mBits & FCDATA_IS_TABLE_PART) != 0);
1844 if (tablePart && aStyleContext->GetStyleDisplay()->mDisplay ==
1845 NS_STYLE_DISPLAY_TABLE_CAPTION) {
1846 aParentFrame = AdjustCaptionParentFrame(aParentFrame);
1850 // Pull all the captions present in aItems out into aCaptions
1851 static void
1852 PullOutCaptionFrames(nsFrameItems& aItems, nsFrameItems& aCaptions)
1854 nsIFrame *child = aItems.FirstChild();
1855 while (child) {
1856 nsIFrame *nextSibling = child->GetNextSibling();
1857 if (nsGkAtoms::tableCaptionFrame == child->GetType()) {
1858 aItems.RemoveFrame(child);
1859 aCaptions.AddChild(child);
1861 child = nextSibling;
1866 // Construct the outer, inner table frames and the children frames for the table.
1867 // XXX Page break frames for pseudo table frames are not constructed to avoid the risk
1868 // associated with revising the pseudo frame mechanism. The long term solution
1869 // of having frames handle page-break-before/after will solve the problem.
1870 nsresult
1871 nsCSSFrameConstructor::ConstructTable(nsFrameConstructorState& aState,
1872 FrameConstructionItem& aItem,
1873 nsIFrame* aParentFrame,
1874 const nsStyleDisplay* aDisplay,
1875 nsFrameItems& aFrameItems,
1876 nsIFrame** aNewFrame)
1878 NS_PRECONDITION(aDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE ||
1879 aDisplay->mDisplay == NS_STYLE_DISPLAY_INLINE_TABLE,
1880 "Unexpected call");
1882 nsIContent* const content = aItem.mContent;
1883 nsStyleContext* const styleContext = aItem.mStyleContext;
1884 const PRUint32 nameSpaceID = aItem.mNameSpaceID;
1886 nsresult rv = NS_OK;
1888 // create the pseudo SC for the outer table as a child of the inner SC
1889 nsRefPtr<nsStyleContext> outerStyleContext;
1890 outerStyleContext = mPresShell->StyleSet()->
1891 ResolveAnonymousBoxStyle(nsCSSAnonBoxes::tableOuter, styleContext);
1893 // Create the outer table frame which holds the caption and inner table frame
1894 nsIFrame* newFrame;
1895 #ifdef MOZ_MATHML
1896 if (kNameSpaceID_MathML == nameSpaceID)
1897 newFrame = NS_NewMathMLmtableOuterFrame(mPresShell, outerStyleContext);
1898 else
1899 #endif
1900 newFrame = NS_NewTableOuterFrame(mPresShell, outerStyleContext);
1902 nsIFrame* geometricParent =
1903 aState.GetGeometricParent(outerStyleContext->GetStyleDisplay(),
1904 aParentFrame);
1906 // Init the table outer frame and see if we need to create a view, e.g.
1907 // the frame is absolutely positioned
1908 InitAndRestoreFrame(aState, content, geometricParent, nsnull, newFrame);
1909 nsHTMLContainerFrame::CreateViewForFrame(newFrame, PR_FALSE);
1911 // Create the inner table frame
1912 nsIFrame* innerFrame;
1913 #ifdef MOZ_MATHML
1914 if (kNameSpaceID_MathML == nameSpaceID)
1915 innerFrame = NS_NewMathMLmtableFrame(mPresShell, styleContext);
1916 else
1917 #endif
1918 innerFrame = NS_NewTableFrame(mPresShell, styleContext);
1920 InitAndRestoreFrame(aState, content, newFrame, nsnull, innerFrame);
1922 // Put the newly created frames into the right child list
1923 SetInitialSingleChild(newFrame, innerFrame);
1925 rv = aState.AddChild(newFrame, aFrameItems, content, styleContext,
1926 aParentFrame);
1927 if (NS_FAILED(rv)) {
1928 return rv;
1931 if (!mRootElementFrame) {
1932 // The frame we're constructing will be the root element frame.
1933 // Set mRootElementFrame before processing children.
1934 mRootElementFrame = newFrame;
1937 nsFrameItems childItems;
1938 if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) {
1939 rv = ConstructFramesFromItemList(aState, aItem.mChildItems,
1940 innerFrame, childItems);
1941 } else {
1942 rv = ProcessChildren(aState, content, styleContext, innerFrame,
1943 PR_TRUE, childItems, PR_FALSE, aItem.mPendingBinding);
1945 // XXXbz what about cleaning up?
1946 if (NS_FAILED(rv)) return rv;
1948 nsFrameItems captionItems;
1949 PullOutCaptionFrames(childItems, captionItems);
1951 // Set the inner table frame's initial primary list
1952 innerFrame->SetInitialChildList(nsnull, childItems);
1954 // Set the outer table frame's secondary childlist lists
1955 if (captionItems.NotEmpty()) {
1956 newFrame->SetInitialChildList(nsGkAtoms::captionList, captionItems);
1959 *aNewFrame = newFrame;
1960 return rv;
1963 nsresult
1964 nsCSSFrameConstructor::ConstructTableRow(nsFrameConstructorState& aState,
1965 FrameConstructionItem& aItem,
1966 nsIFrame* aParentFrame,
1967 const nsStyleDisplay* aDisplay,
1968 nsFrameItems& aFrameItems,
1969 nsIFrame** aNewFrame)
1971 NS_PRECONDITION(aDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_ROW,
1972 "Unexpected call");
1973 nsIContent* const content = aItem.mContent;
1974 nsStyleContext* const styleContext = aItem.mStyleContext;
1975 const PRUint32 nameSpaceID = aItem.mNameSpaceID;
1977 nsIFrame* newFrame;
1978 #ifdef MOZ_MATHML
1979 if (kNameSpaceID_MathML == nameSpaceID)
1980 newFrame = NS_NewMathMLmtrFrame(mPresShell, styleContext);
1981 else
1982 #endif
1983 newFrame = NS_NewTableRowFrame(mPresShell, styleContext);
1985 if (NS_UNLIKELY(!newFrame)) {
1986 return NS_ERROR_OUT_OF_MEMORY;
1988 InitAndRestoreFrame(aState, content, aParentFrame, nsnull, newFrame);
1989 nsHTMLContainerFrame::CreateViewForFrame(newFrame, PR_FALSE);
1991 nsFrameItems childItems;
1992 nsresult rv;
1993 if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) {
1994 rv = ConstructFramesFromItemList(aState, aItem.mChildItems, newFrame,
1995 childItems);
1996 } else {
1997 rv = ProcessChildren(aState, content, styleContext, newFrame,
1998 PR_TRUE, childItems, PR_FALSE, aItem.mPendingBinding);
2000 if (NS_FAILED(rv)) return rv;
2002 newFrame->SetInitialChildList(nsnull, childItems);
2003 aFrameItems.AddChild(newFrame);
2004 *aNewFrame = newFrame;
2006 return NS_OK;
2009 nsresult
2010 nsCSSFrameConstructor::ConstructTableCol(nsFrameConstructorState& aState,
2011 FrameConstructionItem& aItem,
2012 nsIFrame* aParentFrame,
2013 const nsStyleDisplay* aStyleDisplay,
2014 nsFrameItems& aFrameItems,
2015 nsIFrame** aNewFrame)
2017 nsIContent* const content = aItem.mContent;
2018 nsStyleContext* const styleContext = aItem.mStyleContext;
2020 nsTableColFrame* colFrame = NS_NewTableColFrame(mPresShell, styleContext);
2021 if (NS_UNLIKELY(!colFrame)) {
2022 return NS_ERROR_OUT_OF_MEMORY;
2024 InitAndRestoreFrame(aState, content, aParentFrame, nsnull, colFrame);
2026 NS_ASSERTION(colFrame->GetStyleContext() == styleContext,
2027 "Unexpected style context");
2029 aFrameItems.AddChild(colFrame);
2030 *aNewFrame = colFrame;
2032 // construct additional col frames if the col frame has a span > 1
2033 PRInt32 span = colFrame->GetSpan();
2034 for (PRInt32 spanX = 1; spanX < span; spanX++) {
2035 nsTableColFrame* newCol = NS_NewTableColFrame(mPresShell, styleContext);
2036 if (NS_UNLIKELY(!newCol)) {
2037 return NS_ERROR_OUT_OF_MEMORY;
2039 InitAndRestoreFrame(aState, content, aParentFrame, nsnull, newCol,
2040 PR_FALSE);
2041 aFrameItems.LastChild()->SetNextContinuation(newCol);
2042 newCol->SetPrevContinuation(aFrameItems.LastChild());
2043 aFrameItems.AddChild(newCol);
2044 newCol->SetColType(eColAnonymousCol);
2047 return NS_OK;
2050 nsresult
2051 nsCSSFrameConstructor::ConstructTableCell(nsFrameConstructorState& aState,
2052 FrameConstructionItem& aItem,
2053 nsIFrame* aParentFrame,
2054 const nsStyleDisplay* aDisplay,
2055 nsFrameItems& aFrameItems,
2056 nsIFrame** aNewFrame)
2058 NS_PRECONDITION(aDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_CELL,
2059 "Unexpected call");
2061 nsIContent* const content = aItem.mContent;
2062 nsStyleContext* const styleContext = aItem.mStyleContext;
2063 const PRUint32 nameSpaceID = aItem.mNameSpaceID;
2065 PRBool borderCollapse = IsBorderCollapse(aParentFrame);
2066 nsIFrame* newFrame;
2067 #ifdef MOZ_MATHML
2068 // <mtable> is border separate in mathml.css and the MathML code doesn't implement
2069 // border collapse. For those users who style <mtable> with border collapse,
2070 // give them the default non-MathML table frames that understand border collapse.
2071 // This won't break us because MathML table frames are all subclasses of the default
2072 // table code, and so we can freely mix <mtable> with <mtr> or <tr>, <mtd> or <td>.
2073 // What will happen is just that non-MathML frames won't understand MathML attributes
2074 // and will therefore miss the special handling that the MathML code does.
2075 if (kNameSpaceID_MathML == nameSpaceID && !borderCollapse)
2076 newFrame = NS_NewMathMLmtdFrame(mPresShell, styleContext);
2077 else
2078 #endif
2079 // Warning: If you change this and add a wrapper frame around table cell
2080 // frames, make sure Bug 368554 doesn't regress!
2081 // See IsInAutoWidthTableCellForQuirk() in nsImageFrame.cpp.
2082 newFrame = NS_NewTableCellFrame(mPresShell, styleContext, borderCollapse);
2084 if (NS_UNLIKELY(!newFrame)) {
2085 return NS_ERROR_OUT_OF_MEMORY;
2088 // Initialize the table cell frame
2089 InitAndRestoreFrame(aState, content, aParentFrame, nsnull, newFrame);
2090 nsHTMLContainerFrame::CreateViewForFrame(newFrame, PR_FALSE);
2092 // Resolve pseudo style and initialize the body cell frame
2093 nsRefPtr<nsStyleContext> innerPseudoStyle;
2094 innerPseudoStyle = mPresShell->StyleSet()->
2095 ResolveAnonymousBoxStyle(nsCSSAnonBoxes::cellContent, styleContext);
2097 // Create a block frame that will format the cell's content
2098 PRBool isBlock;
2099 nsIFrame* cellInnerFrame;
2100 #ifdef MOZ_MATHML
2101 if (kNameSpaceID_MathML == nameSpaceID) {
2102 cellInnerFrame = NS_NewMathMLmtdInnerFrame(mPresShell, innerPseudoStyle);
2103 isBlock = PR_FALSE;
2105 else
2106 #endif
2108 cellInnerFrame = NS_NewBlockFormattingContext(mPresShell, innerPseudoStyle);
2109 isBlock = PR_TRUE;
2112 if (NS_UNLIKELY(!cellInnerFrame)) {
2113 newFrame->Destroy();
2114 return NS_ERROR_OUT_OF_MEMORY;
2117 InitAndRestoreFrame(aState, content, newFrame, nsnull, cellInnerFrame);
2119 nsFrameItems childItems;
2120 nsresult rv;
2121 if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) {
2122 // Need to push ourselves as a float containing block.
2123 // XXXbz it might be nice to work on getting the parent
2124 // FrameConstructionItem down into ProcessChildren and just making use of
2125 // the push there, but that's a bit of work.
2126 nsFrameConstructorSaveState floatSaveState;
2127 if (!isBlock) { /* MathML case */
2128 aState.PushFloatContainingBlock(nsnull, floatSaveState);
2129 } else {
2130 aState.PushFloatContainingBlock(cellInnerFrame, floatSaveState);
2133 rv = ConstructFramesFromItemList(aState, aItem.mChildItems, cellInnerFrame,
2134 childItems);
2135 } else {
2136 // Process the child content
2137 rv = ProcessChildren(aState, content, styleContext, cellInnerFrame,
2138 PR_TRUE, childItems, isBlock, aItem.mPendingBinding);
2141 if (NS_FAILED(rv)) {
2142 // Clean up
2143 // XXXbz kids of this stuff need to be cleaned up too!
2144 cellInnerFrame->Destroy();
2145 newFrame->Destroy();
2146 return rv;
2149 cellInnerFrame->SetInitialChildList(nsnull, childItems);
2150 SetInitialSingleChild(newFrame, cellInnerFrame);
2151 aFrameItems.AddChild(newFrame);
2152 *aNewFrame = newFrame;
2154 return NS_OK;
2157 static inline PRBool
2158 NeedFrameFor(const nsFrameConstructorState& aState,
2159 nsIFrame* aParentFrame,
2160 nsIContent* aChildContent)
2162 // XXX the GetContent() != aChildContent check is needed due to bug 135040.
2163 // Remove it once that's fixed.
2164 NS_PRECONDITION(!aChildContent->GetPrimaryFrame() ||
2165 aState.mCreatingExtraFrames ||
2166 aChildContent->GetPrimaryFrame()->GetContent() != aChildContent,
2167 "Why did we get called?");
2169 // don't create a whitespace frame if aParentFrame doesn't want it.
2170 // always create frames for children in generated content. counter(),
2171 // quotes, and attr() content can easily change dynamically and we don't
2172 // want to be reconstructing frames. It's not even clear that these
2173 // should be considered ignorable just because they evaluate to
2174 // whitespace.
2176 // We could handle all this in CreateNeededTablePseudos or some other place
2177 // after we build our frame construction items, but that would involve
2178 // creating frame construction items for whitespace kids of
2179 // eExcludesIgnorableWhitespace frames, where we know we'll be dropping them
2180 // all anyway, and involve an extra walk down the frame construction item
2181 // list.
2182 if (!aParentFrame->IsFrameOfType(nsIFrame::eExcludesIgnorableWhitespace) ||
2183 aParentFrame->IsGeneratedContentFrame() ||
2184 !aChildContent->IsNodeOfType(nsINode::eTEXT)) {
2185 return PR_TRUE;
2188 aChildContent->SetFlags(NS_CREATE_FRAME_IF_NON_WHITESPACE |
2189 NS_REFRAME_IF_WHITESPACE);
2190 return !aChildContent->TextIsOnlyWhitespace();
2193 /***********************************************
2194 * END TABLE SECTION
2195 ***********************************************/
2197 static PRBool CheckOverflow(nsPresContext* aPresContext,
2198 const nsStyleDisplay* aDisplay)
2200 if (aDisplay->mOverflowX == NS_STYLE_OVERFLOW_VISIBLE)
2201 return PR_FALSE;
2203 if (aDisplay->mOverflowX == NS_STYLE_OVERFLOW_CLIP)
2204 aPresContext->SetViewportOverflowOverride(NS_STYLE_OVERFLOW_HIDDEN,
2205 NS_STYLE_OVERFLOW_HIDDEN);
2206 else
2207 aPresContext->SetViewportOverflowOverride(aDisplay->mOverflowX,
2208 aDisplay->mOverflowY);
2209 return PR_TRUE;
2213 * This checks the root element and the HTML BODY, if any, for an "overflow" property
2214 * that should be applied to the viewport. If one is found then we return the
2215 * element that we took the overflow from (which should then be treated as
2216 * "overflow:visible"), and we store the overflow style in the prescontext.
2217 * @return if scroll was propagated from some content node, the content node it
2218 * was propagated from.
2220 nsIContent*
2221 nsCSSFrameConstructor::PropagateScrollToViewport()
2223 // Set default
2224 nsPresContext* presContext = mPresShell->GetPresContext();
2225 presContext->SetViewportOverflowOverride(NS_STYLE_OVERFLOW_AUTO,
2226 NS_STYLE_OVERFLOW_AUTO);
2228 // We never mess with the viewport scroll state
2229 // when printing or in print preview
2230 if (presContext->IsPaginated()) {
2231 return nsnull;
2234 Element* docElement = mDocument->GetRootElement();
2236 // Check the style on the document root element
2237 nsStyleSet *styleSet = mPresShell->StyleSet();
2238 nsRefPtr<nsStyleContext> rootStyle;
2239 rootStyle = styleSet->ResolveStyleFor(docElement, nsnull);
2240 if (!rootStyle) {
2241 return nsnull;
2243 if (CheckOverflow(presContext, rootStyle->GetStyleDisplay())) {
2244 // tell caller we stole the overflow style from the root element
2245 return docElement;
2248 // Don't look in the BODY for non-HTML documents or HTML documents
2249 // with non-HTML roots
2250 // XXX this should be earlier; we shouldn't even look at the document root
2251 // for non-HTML documents. Fix this once we support explicit CSS styling
2252 // of the viewport
2253 // XXX what about XHTML?
2254 nsCOMPtr<nsIDOMHTMLDocument> htmlDoc(do_QueryInterface(mDocument));
2255 if (!htmlDoc || !docElement->IsHTML()) {
2256 return nsnull;
2259 nsCOMPtr<nsIDOMHTMLElement> body;
2260 htmlDoc->GetBody(getter_AddRefs(body));
2261 nsCOMPtr<nsIContent> bodyElement = do_QueryInterface(body);
2263 if (!bodyElement ||
2264 !bodyElement->NodeInfo()->Equals(nsGkAtoms::body)) {
2265 // The body is not a <body> tag, it's a <frameset>.
2266 return nsnull;
2269 nsRefPtr<nsStyleContext> bodyStyle;
2270 bodyStyle = styleSet->ResolveStyleFor(bodyElement->AsElement(), rootStyle);
2271 if (!bodyStyle) {
2272 return nsnull;
2275 if (CheckOverflow(presContext, bodyStyle->GetStyleDisplay())) {
2276 // tell caller we stole the overflow style from the body element
2277 return bodyElement;
2280 return nsnull;
2283 nsresult
2284 nsCSSFrameConstructor::ConstructDocElementFrame(Element* aDocElement,
2285 nsILayoutHistoryState* aFrameState,
2286 nsIFrame** aNewFrame)
2288 NS_PRECONDITION(mFixedContainingBlock,
2289 "No viewport? Someone forgot to call ConstructRootFrame!");
2290 NS_PRECONDITION(mFixedContainingBlock == mPresShell->FrameManager()->GetRootFrame(),
2291 "Unexpected mFixedContainingBlock");
2292 NS_PRECONDITION(!mDocElementContainingBlock,
2293 "Shouldn't have a doc element containing block here");
2295 *aNewFrame = nsnull;
2297 // Make sure to call PropagateScrollToViewport before
2298 // SetUpDocElementContainingBlock, since it sets up our scrollbar state
2299 // properly.
2300 nsIContent* propagatedScrollFrom = PropagateScrollToViewport();
2302 SetUpDocElementContainingBlock(aDocElement);
2304 NS_ASSERTION(mDocElementContainingBlock, "Should have parent by now");
2306 nsFrameConstructorState state(mPresShell, mFixedContainingBlock, nsnull,
2307 nsnull, aFrameState);
2309 // XXXbz why, exactly?
2310 if (!mTempFrameTreeState)
2311 state.mPresShell->CaptureHistoryState(getter_AddRefs(mTempFrameTreeState));
2313 // Make sure that we'll handle restyles for this document element in
2314 // the future. We need this, because the document element might
2315 // have stale restyle bits from a previous frame constructor for
2316 // this document. Unlike in AddFrameConstructionItems, it's safe to
2317 // unset all element restyle flags, since we don't have any
2318 // siblings.
2319 aDocElement->UnsetFlags(ELEMENT_ALL_RESTYLE_FLAGS);
2321 // --------- CREATE AREA OR BOX FRAME -------
2322 nsRefPtr<nsStyleContext> styleContext;
2323 styleContext = mPresShell->StyleSet()->ResolveStyleFor(aDocElement,
2324 nsnull);
2326 const nsStyleDisplay* display = styleContext->GetStyleDisplay();
2328 // Ensure that our XBL bindings are installed.
2329 if (display->mBinding) {
2330 // Get the XBL loader.
2331 nsresult rv;
2332 PRBool resolveStyle;
2334 nsIXBLService * xblService = GetXBLService();
2335 if (!xblService)
2336 return NS_ERROR_FAILURE;
2338 nsRefPtr<nsXBLBinding> binding;
2339 rv = xblService->LoadBindings(aDocElement, display->mBinding->mURI,
2340 display->mBinding->mOriginPrincipal,
2341 PR_FALSE, getter_AddRefs(binding),
2342 &resolveStyle);
2343 if (NS_FAILED(rv) && rv != NS_ERROR_XBL_BLOCKED)
2344 return NS_OK; // Binding will load asynchronously.
2346 if (binding) {
2347 // For backwards compat, keep firing the root's constructor
2348 // after all of its kids' constructors. So tell the binding
2349 // manager about it right now.
2350 mDocument->BindingManager()->AddToAttachedQueue(binding);
2353 if (resolveStyle) {
2354 styleContext = mPresShell->StyleSet()->ResolveStyleFor(aDocElement,
2355 nsnull);
2356 display = styleContext->GetStyleDisplay();
2360 // --------- IF SCROLLABLE WRAP IN SCROLLFRAME --------
2362 #ifdef DEBUG
2363 NS_ASSERTION(!display->IsScrollableOverflow() ||
2364 state.mPresContext->IsPaginated() ||
2365 propagatedScrollFrom == aDocElement,
2366 "Scrollbars should have been propagated to the viewport");
2367 #endif
2369 if (NS_UNLIKELY(display->mDisplay == NS_STYLE_DISPLAY_NONE)) {
2370 state.mFrameManager->SetUndisplayedContent(aDocElement, styleContext);
2371 return NS_OK;
2374 nsFrameConstructorSaveState absoluteSaveState;
2375 if (mHasRootAbsPosContainingBlock) {
2376 // Push the absolute containing block now so we can absolutely position
2377 // the root element
2378 state.PushAbsoluteContainingBlock(mDocElementContainingBlock,
2379 absoluteSaveState);
2382 nsresult rv;
2384 // The rules from CSS 2.1, section 9.2.4, have already been applied
2385 // by the style system, so we can assume that display->mDisplay is
2386 // either NONE, BLOCK, or TABLE.
2388 // contentFrame is the primary frame for the root element. *aNewFrame
2389 // is the frame that will be the child of the initial containing block.
2390 // These are usually the same frame but they can be different, in
2391 // particular if the root frame is positioned, in which case
2392 // contentFrame is the out-of-flow frame and *aNewFrame is the
2393 // placeholder.
2394 nsIFrame* contentFrame;
2395 PRBool processChildren = PR_FALSE;
2397 // Check whether we need to build a XUL box or SVG root frame
2398 #ifdef MOZ_XUL
2399 if (aDocElement->IsXUL()) {
2400 contentFrame = NS_NewDocElementBoxFrame(mPresShell, styleContext);
2401 if (NS_UNLIKELY(!contentFrame)) {
2402 return NS_ERROR_OUT_OF_MEMORY;
2404 InitAndRestoreFrame(state, aDocElement, mDocElementContainingBlock, nsnull,
2405 contentFrame);
2406 *aNewFrame = contentFrame;
2407 processChildren = PR_TRUE;
2409 else
2410 #endif
2411 #ifdef MOZ_SVG
2412 if (aDocElement->GetNameSpaceID() == kNameSpaceID_SVG) {
2413 if (aDocElement->Tag() == nsGkAtoms::svg && NS_SVGEnabled()) {
2414 contentFrame = NS_NewSVGOuterSVGFrame(mPresShell, styleContext);
2415 if (NS_UNLIKELY(!contentFrame)) {
2416 return NS_ERROR_OUT_OF_MEMORY;
2418 InitAndRestoreFrame(state, aDocElement,
2419 state.GetGeometricParent(display,
2420 mDocElementContainingBlock),
2421 nsnull, contentFrame);
2423 // AddChild takes care of transforming the frame tree for fixed-pos
2424 // or abs-pos situations
2425 nsFrameItems frameItems;
2426 rv = state.AddChild(contentFrame, frameItems, aDocElement,
2427 styleContext, mDocElementContainingBlock);
2428 if (NS_FAILED(rv) || frameItems.IsEmpty()) {
2429 return rv;
2431 *aNewFrame = frameItems.FirstChild();
2432 processChildren = PR_TRUE;
2434 // See if we need to create a view
2435 nsHTMLContainerFrame::CreateViewForFrame(contentFrame, PR_FALSE);
2436 } else {
2437 return NS_ERROR_FAILURE;
2440 else
2441 #endif
2443 PRBool docElemIsTable = (display->mDisplay == NS_STYLE_DISPLAY_TABLE);
2444 if (docElemIsTable) {
2445 // We're going to call the right function ourselves, so no need to give a
2446 // function to this FrameConstructionData.
2448 // XXXbz on the other hand, if we converted this whole function to
2449 // FrameConstructionData/Item, then we'd need the right function
2450 // here... but would probably be able to get away with less code in this
2451 // function in general.
2452 // Use a null PendingBinding, since our binding is not in fact pending.
2453 static const FrameConstructionData rootTableData = FCDATA_DECL(0, nsnull);
2454 nsRefPtr<nsStyleContext> extraRef(styleContext);
2455 FrameConstructionItem item(&rootTableData, aDocElement,
2456 aDocElement->Tag(), kNameSpaceID_None,
2457 nsnull, extraRef.forget(), PR_TRUE);
2459 nsFrameItems frameItems;
2460 // if the document is a table then just populate it.
2461 rv = ConstructTable(state, item, mDocElementContainingBlock,
2462 styleContext->GetStyleDisplay(),
2463 frameItems, &contentFrame);
2464 if (NS_FAILED(rv))
2465 return rv;
2466 if (!contentFrame || frameItems.IsEmpty())
2467 return NS_ERROR_FAILURE;
2468 *aNewFrame = frameItems.FirstChild();
2469 NS_ASSERTION(frameItems.OnlyChild(), "multiple root element frames");
2470 } else {
2471 contentFrame = NS_NewBlockFrame(mPresShell, styleContext,
2472 NS_BLOCK_FLOAT_MGR|NS_BLOCK_MARGIN_ROOT);
2473 if (!contentFrame)
2474 return NS_ERROR_OUT_OF_MEMORY;
2475 nsFrameItems frameItems;
2476 // Use a null PendingBinding, since our binding is not in fact pending.
2477 rv = ConstructBlock(state, display, aDocElement,
2478 state.GetGeometricParent(display,
2479 mDocElementContainingBlock),
2480 mDocElementContainingBlock, styleContext,
2481 &contentFrame, frameItems, display->IsPositioned(),
2482 nsnull);
2483 if (NS_FAILED(rv) || frameItems.IsEmpty())
2484 return rv;
2485 *aNewFrame = frameItems.FirstChild();
2486 NS_ASSERTION(frameItems.OnlyChild(), "multiple root element frames");
2490 // set the primary frame
2491 aDocElement->SetPrimaryFrame(contentFrame);
2493 NS_ASSERTION(processChildren ? !mRootElementFrame :
2494 mRootElementFrame == contentFrame,
2495 "unexpected mRootElementFrame");
2496 mRootElementFrame = contentFrame;
2498 // Figure out which frame has the main style for the document element,
2499 // assigning it to mRootElementStyleFrame.
2500 // Backgrounds should be propagated from that frame to the viewport.
2501 PRBool isChild;
2502 contentFrame->GetParentStyleContextFrame(state.mPresContext,
2503 &mRootElementStyleFrame, &isChild);
2504 if (!isChild) {
2505 mRootElementStyleFrame = mRootElementFrame;
2508 if (processChildren) {
2509 // Still need to process the child content
2510 nsFrameItems childItems;
2512 NS_ASSERTION(!nsLayoutUtils::GetAsBlock(contentFrame),
2513 "Only XUL and SVG frames should reach here");
2514 // Use a null PendingBinding, since our binding is not in fact pending.
2515 ProcessChildren(state, aDocElement, styleContext, contentFrame, PR_TRUE,
2516 childItems, PR_FALSE, nsnull);
2518 // Set the initial child lists
2519 contentFrame->SetInitialChildList(nsnull, childItems);
2522 SetInitialSingleChild(mDocElementContainingBlock, *aNewFrame);
2524 return NS_OK;
2528 nsresult
2529 nsCSSFrameConstructor::ConstructRootFrame(nsIFrame** aNewFrame)
2531 AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
2532 NS_PRECONDITION(aNewFrame, "null out param");
2534 nsStyleSet *styleSet = mPresShell->StyleSet();
2536 // Set up our style rule observer.
2537 // XXXbz wouldn't this make more sense as part of presshell init?
2539 styleSet->SetBindingManager(mDocument->BindingManager());
2542 // --------- BUILD VIEWPORT -----------
2543 nsIFrame* viewportFrame = nsnull;
2544 nsRefPtr<nsStyleContext> viewportPseudoStyle;
2546 viewportPseudoStyle =
2547 styleSet->ResolveAnonymousBoxStyle(nsCSSAnonBoxes::viewport, nsnull);
2549 viewportFrame = NS_NewViewportFrame(mPresShell, viewportPseudoStyle);
2551 // XXXbz do we _have_ to pass a null content pointer to that frame?
2552 // Would it really kill us to pass in the root element or something?
2553 // What would that break?
2554 viewportFrame->Init(nsnull, nsnull, nsnull);
2556 // Bind the viewport frame to the root view
2557 nsIView* rootView;
2558 mPresShell->GetViewManager()->GetRootView(rootView);
2559 viewportFrame->SetView(rootView);
2561 nsContainerFrame::SyncFrameViewProperties(mPresShell->GetPresContext(), viewportFrame,
2562 viewportPseudoStyle, rootView);
2563 nsContainerFrame::SyncWindowProperties(mPresShell->GetPresContext(), viewportFrame,
2564 rootView);
2566 // The viewport is the containing block for 'fixed' elements
2567 mFixedContainingBlock = viewportFrame;
2569 *aNewFrame = viewportFrame;
2570 return NS_OK;
2573 nsresult
2574 nsCSSFrameConstructor::SetUpDocElementContainingBlock(nsIContent* aDocElement)
2576 NS_PRECONDITION(aDocElement, "No element?");
2577 NS_PRECONDITION(!aDocElement->GetParent(), "Not root content?");
2578 NS_PRECONDITION(aDocElement->GetCurrentDoc(), "Not in a document?");
2579 NS_PRECONDITION(aDocElement->GetCurrentDoc()->GetRootElement() ==
2580 aDocElement, "Not the root of the document?");
2583 how the root frame hierarchy should look
2585 Galley presentation, non-XUL, with scrolling (i.e. not a frameset):
2587 ViewportFrame [fixed-cb]
2588 nsHTMLScrollFrame
2589 nsCanvasFrame [abs-cb]
2590 root element frame (nsBlockFrame, nsSVGOuterSVGFrame,
2591 nsTableOuterFrame, nsPlaceholderFrame)
2593 Galley presentation, non-XUL, without scrolling (i.e. a frameset):
2595 ViewportFrame [fixed-cb]
2596 nsCanvasFrame [abs-cb]
2597 root element frame (nsBlockFrame)
2599 Galley presentation, XUL
2601 ViewportFrame [fixed-cb]
2602 nsRootBoxFrame
2603 root element frame (nsDocElementBoxFrame)
2605 Print presentation, non-XUL
2607 ViewportFrame
2608 nsSimplePageSequenceFrame
2609 nsPageFrame [fixed-cb]
2610 nsPageContentFrame
2611 nsCanvasFrame [abs-cb]
2612 root element frame (nsBlockFrame, nsSVGOuterSVGFrame,
2613 nsTableOuterFrame, nsPlaceholderFrame)
2615 Print-preview presentation, non-XUL
2617 ViewportFrame
2618 nsHTMLScrollFrame
2619 nsSimplePageSequenceFrame
2620 nsPageFrame [fixed-cb]
2621 nsPageContentFrame
2622 nsCanvasFrame [abs-cb]
2623 root element frame (nsBlockFrame, nsSVGOuterSVGFrame,
2624 nsTableOuterFrame, nsPlaceholderFrame)
2626 Print/print preview of XUL is not supported.
2627 [fixed-cb]: the default containing block for fixed-pos content
2628 [abs-cb]: the default containing block for abs-pos content
2630 Meaning of nsCSSFrameConstructor fields:
2631 mRootElementFrame is "root element frame". This is the primary frame for
2632 the root element.
2633 mDocElementContainingBlock is the parent of mRootElementFrame
2634 (i.e. nsCanvasFrame or nsRootBoxFrame)
2635 mFixedContainingBlock is the [fixed-cb]
2636 mGfxScrollFrame is the nsHTMLScrollFrame mentioned above, or null if there isn't one
2637 mPageSequenceFrame is the nsSimplePageSequenceFrame, or null if there isn't one
2640 // --------- CREATE ROOT FRAME -------
2643 // Create the root frame. The document element's frame is a child of the
2644 // root frame.
2646 // The root frame serves two purposes:
2647 // - reserves space for any margins needed for the document element's frame
2648 // - renders the document element's background. This ensures the background covers
2649 // the entire canvas as specified by the CSS2 spec
2651 nsPresContext* presContext = mPresShell->GetPresContext();
2652 PRBool isPaginated = presContext->IsRootPaginatedDocument();
2653 nsIFrame* viewportFrame = mFixedContainingBlock;
2654 nsStyleContext* viewportPseudoStyle = viewportFrame->GetStyleContext();
2656 nsIFrame* rootFrame = nsnull;
2657 nsIAtom* rootPseudo;
2659 if (!isPaginated) {
2660 #ifdef MOZ_XUL
2661 if (aDocElement->IsXUL())
2663 // pass a temporary stylecontext, the correct one will be set later
2664 rootFrame = NS_NewRootBoxFrame(mPresShell, viewportPseudoStyle);
2665 } else
2666 #endif
2668 // pass a temporary stylecontext, the correct one will be set later
2669 rootFrame = NS_NewCanvasFrame(mPresShell, viewportPseudoStyle);
2670 mHasRootAbsPosContainingBlock = PR_TRUE;
2673 rootPseudo = nsCSSAnonBoxes::canvas;
2674 mDocElementContainingBlock = rootFrame;
2675 } else {
2676 // Create a page sequence frame
2677 rootFrame = NS_NewSimplePageSequenceFrame(mPresShell, viewportPseudoStyle);
2678 mPageSequenceFrame = rootFrame;
2679 rootPseudo = nsCSSAnonBoxes::pageSequence;
2683 // --------- IF SCROLLABLE WRAP IN SCROLLFRAME --------
2685 // If the device supports scrolling (e.g., in galley mode on the screen and
2686 // for print-preview, but not when printing), then create a scroll frame that
2687 // will act as the scrolling mechanism for the viewport.
2688 // XXX Do we even need a viewport when printing to a printer?
2690 // As long as the docshell doesn't prohibit it, and the device supports
2691 // it, create a scroll frame that will act as the scolling mechanism for
2692 // the viewport.
2694 // Threre are three possible values stored in the docshell:
2695 // 1) nsIScrollable::Scrollbar_Never = no scrollbars
2696 // 2) nsIScrollable::Scrollbar_Auto = scrollbars appear if needed
2697 // 3) nsIScrollable::Scrollbar_Always = scrollbars always
2698 // Only need to create a scroll frame/view for cases 2 and 3.
2700 PRBool isHTML = aDocElement->IsHTML();
2701 PRBool isXUL = PR_FALSE;
2703 if (!isHTML) {
2704 isXUL = aDocElement->IsXUL();
2707 // Never create scrollbars for XUL documents
2708 PRBool isScrollable = !isXUL;
2710 // Never create scrollbars for frameset documents.
2711 if (isHTML) {
2712 nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(mDocument);
2713 if (htmlDoc && htmlDoc->GetIsFrameset())
2714 isScrollable = PR_FALSE;
2717 if (isPaginated) {
2718 isScrollable = presContext->HasPaginatedScrolling();
2721 // We no longer need to do overflow propagation here. It's taken care of
2722 // when we construct frames for the element whose overflow might be
2723 // propagated
2724 NS_ASSERTION(!isScrollable || !isXUL,
2725 "XUL documents should never be scrollable - see above");
2727 nsIFrame* newFrame = rootFrame;
2728 nsRefPtr<nsStyleContext> rootPseudoStyle;
2729 // we must create a state because if the scrollbars are GFX it needs the
2730 // state to build the scrollbar frames.
2731 nsFrameConstructorState state(mPresShell, nsnull, nsnull, nsnull);
2733 // Start off with the viewport as parent; we'll adjust it as needed.
2734 nsIFrame* parentFrame = viewportFrame;
2736 nsStyleSet* styleSet = mPresShell->StyleSet();
2737 // If paginated, make sure we don't put scrollbars in
2738 if (!isScrollable) {
2739 rootPseudoStyle = styleSet->ResolveAnonymousBoxStyle(rootPseudo,
2740 viewportPseudoStyle);
2741 } else {
2742 if (rootPseudo == nsCSSAnonBoxes::canvas) {
2743 rootPseudo = nsCSSAnonBoxes::scrolledCanvas;
2744 } else {
2745 NS_ASSERTION(rootPseudo == nsCSSAnonBoxes::pageSequence,
2746 "Unknown root pseudo");
2747 rootPseudo = nsCSSAnonBoxes::scrolledPageSequence;
2750 // Build the frame. We give it the content we are wrapping which is the
2751 // document element, the root frame, the parent view port frame, and we
2752 // should get back the new frame and the scrollable view if one was
2753 // created.
2755 // resolve a context for the scrollframe
2756 nsRefPtr<nsStyleContext> styleContext;
2757 styleContext = styleSet->ResolveAnonymousBoxStyle(nsCSSAnonBoxes::viewportScroll,
2758 viewportPseudoStyle);
2760 // Note that the viewport scrollframe is always built with
2761 // overflow:auto style. This forces the scroll frame to create
2762 // anonymous content for both scrollbars. This is necessary even
2763 // if the HTML or BODY elements are overriding the viewport
2764 // scroll style to 'hidden' --- dynamic style changes might put
2765 // scrollbars back on the viewport and we don't want to have to
2766 // reframe the viewport to create the scrollbar content.
2767 newFrame = nsnull;
2768 rootPseudoStyle = BeginBuildingScrollFrame( state,
2769 aDocElement,
2770 styleContext,
2771 viewportFrame,
2772 rootPseudo,
2773 PR_TRUE,
2774 newFrame);
2775 parentFrame = newFrame;
2776 mGfxScrollFrame = newFrame;
2779 rootFrame->SetStyleContextWithoutNotification(rootPseudoStyle);
2780 rootFrame->Init(aDocElement, parentFrame, nsnull);
2782 if (isScrollable) {
2783 FinishBuildingScrollFrame(parentFrame, rootFrame);
2786 if (isPaginated) { // paginated
2787 // Create the first page
2788 // Set the initial child lists
2789 nsIFrame *pageFrame, *canvasFrame;
2790 ConstructPageFrame(mPresShell, presContext, rootFrame, nsnull,
2791 pageFrame, canvasFrame);
2792 SetInitialSingleChild(rootFrame, pageFrame);
2794 // The eventual parent of the document element frame.
2795 // XXX should this be set for every new page (in ConstructPageFrame)?
2796 mDocElementContainingBlock = canvasFrame;
2797 mHasRootAbsPosContainingBlock = PR_TRUE;
2800 if (viewportFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) {
2801 SetInitialSingleChild(viewportFrame, newFrame);
2802 } else {
2803 nsFrameList newFrameList(newFrame, newFrame);
2804 viewportFrame->AppendFrames(nsnull, newFrameList);
2807 return NS_OK;
2810 nsresult
2811 nsCSSFrameConstructor::ConstructPageFrame(nsIPresShell* aPresShell,
2812 nsPresContext* aPresContext,
2813 nsIFrame* aParentFrame,
2814 nsIFrame* aPrevPageFrame,
2815 nsIFrame*& aPageFrame,
2816 nsIFrame*& aCanvasFrame)
2818 nsStyleContext* parentStyleContext = aParentFrame->GetStyleContext();
2819 nsStyleSet *styleSet = aPresShell->StyleSet();
2821 nsRefPtr<nsStyleContext> pagePseudoStyle;
2822 pagePseudoStyle = styleSet->ResolveAnonymousBoxStyle(nsCSSAnonBoxes::page,
2823 parentStyleContext);
2825 aPageFrame = NS_NewPageFrame(aPresShell, pagePseudoStyle);
2826 if (NS_UNLIKELY(!aPageFrame))
2827 return NS_ERROR_OUT_OF_MEMORY;
2829 // Initialize the page frame and force it to have a view. This makes printing of
2830 // the pages easier and faster.
2831 aPageFrame->Init(nsnull, aParentFrame, aPrevPageFrame);
2833 nsRefPtr<nsStyleContext> pageContentPseudoStyle;
2834 pageContentPseudoStyle =
2835 styleSet->ResolveAnonymousBoxStyle(nsCSSAnonBoxes::pageContent,
2836 pagePseudoStyle);
2838 nsIFrame* pageContentFrame = NS_NewPageContentFrame(aPresShell, pageContentPseudoStyle);
2839 if (NS_UNLIKELY(!pageContentFrame))
2840 return NS_ERROR_OUT_OF_MEMORY;
2842 // Initialize the page content frame and force it to have a view. Also make it the
2843 // containing block for fixed elements which are repeated on every page.
2844 nsIFrame* prevPageContentFrame = nsnull;
2845 if (aPrevPageFrame) {
2846 prevPageContentFrame = aPrevPageFrame->GetFirstChild(nsnull);
2847 NS_ASSERTION(prevPageContentFrame, "missing page content frame");
2849 pageContentFrame->Init(nsnull, aPageFrame, prevPageContentFrame);
2850 SetInitialSingleChild(aPageFrame, pageContentFrame);
2851 mFixedContainingBlock = pageContentFrame;
2853 nsRefPtr<nsStyleContext> canvasPseudoStyle;
2854 canvasPseudoStyle = styleSet->ResolveAnonymousBoxStyle(nsCSSAnonBoxes::canvas,
2855 pageContentPseudoStyle);
2857 aCanvasFrame = NS_NewCanvasFrame(aPresShell, canvasPseudoStyle);
2858 if (NS_UNLIKELY(!aCanvasFrame))
2859 return NS_ERROR_OUT_OF_MEMORY;
2861 nsIFrame* prevCanvasFrame = nsnull;
2862 if (prevPageContentFrame) {
2863 prevCanvasFrame = prevPageContentFrame->GetFirstChild(nsnull);
2864 NS_ASSERTION(prevCanvasFrame, "missing canvas frame");
2866 aCanvasFrame->Init(nsnull, pageContentFrame, prevCanvasFrame);
2867 SetInitialSingleChild(pageContentFrame, aCanvasFrame);
2869 return NS_OK;
2872 /* static */
2873 nsresult
2874 nsCSSFrameConstructor::CreatePlaceholderFrameFor(nsIPresShell* aPresShell,
2875 nsIContent* aContent,
2876 nsIFrame* aFrame,
2877 nsStyleContext* aStyleContext,
2878 nsIFrame* aParentFrame,
2879 nsIFrame* aPrevInFlow,
2880 nsFrameState aTypeBit,
2881 nsIFrame** aPlaceholderFrame)
2883 nsRefPtr<nsStyleContext> placeholderStyle = aPresShell->StyleSet()->
2884 ResolveStyleForNonElement(aStyleContext->GetParent());
2886 // The placeholder frame gets a pseudo style context
2887 nsPlaceholderFrame* placeholderFrame =
2888 (nsPlaceholderFrame*)NS_NewPlaceholderFrame(aPresShell, placeholderStyle,
2889 aTypeBit);
2891 if (placeholderFrame) {
2892 placeholderFrame->Init(aContent, aParentFrame, aPrevInFlow);
2894 // The placeholder frame has a pointer back to the out-of-flow frame
2895 placeholderFrame->SetOutOfFlowFrame(aFrame);
2897 aFrame->AddStateBits(NS_FRAME_OUT_OF_FLOW);
2899 // Add mapping from absolutely positioned frame to its placeholder frame
2900 aPresShell->FrameManager()->RegisterPlaceholderFrame(placeholderFrame);
2902 *aPlaceholderFrame = static_cast<nsIFrame*>(placeholderFrame);
2904 return NS_OK;
2906 else {
2907 return NS_ERROR_OUT_OF_MEMORY;
2911 // Clears any lazy bits set in the range [aStartContent, aEndContent). If
2912 // aEndContent is null, that means to clear bits in all siblings starting with
2913 // aStartContent. aStartContent must not be null unless aEndContent is also
2914 // null. We do this so that when new children are inserted under elements whose
2915 // frame is a leaf the new children don't cause us to try to construct frames
2916 // for the existing children again.
2917 static inline void
2918 ClearLazyBits(nsIContent* aStartContent, nsIContent* aEndContent)
2920 NS_PRECONDITION(aStartContent || !aEndContent,
2921 "Must have start child if we have an end child");
2922 for (nsIContent* cur = aStartContent; cur != aEndContent;
2923 cur = cur->GetNextSibling()) {
2924 cur->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
2928 nsresult
2929 nsCSSFrameConstructor::ConstructButtonFrame(nsFrameConstructorState& aState,
2930 FrameConstructionItem& aItem,
2931 nsIFrame* aParentFrame,
2932 const nsStyleDisplay* aStyleDisplay,
2933 nsFrameItems& aFrameItems,
2934 nsIFrame** aNewFrame)
2936 *aNewFrame = nsnull;
2937 nsIFrame* buttonFrame = nsnull;
2938 nsIContent* const content = aItem.mContent;
2939 nsStyleContext* const styleContext = aItem.mStyleContext;
2941 if (nsGkAtoms::button == aItem.mTag) {
2942 buttonFrame = NS_NewHTMLButtonControlFrame(mPresShell, styleContext);
2944 else {
2945 buttonFrame = NS_NewGfxButtonControlFrame(mPresShell, styleContext);
2947 if (NS_UNLIKELY(!buttonFrame)) {
2948 return NS_ERROR_OUT_OF_MEMORY;
2950 // Initialize the button frame
2951 nsresult rv = InitAndRestoreFrame(aState, content,
2952 aState.GetGeometricParent(aStyleDisplay, aParentFrame),
2953 nsnull, buttonFrame);
2954 if (NS_FAILED(rv)) {
2955 buttonFrame->Destroy();
2956 return rv;
2958 // See if we need to create a view
2959 nsHTMLContainerFrame::CreateViewForFrame(buttonFrame, PR_FALSE);
2961 nsRefPtr<nsStyleContext> innerBlockContext;
2962 innerBlockContext =
2963 mPresShell->StyleSet()->ResolveAnonymousBoxStyle(nsCSSAnonBoxes::buttonContent,
2964 styleContext);
2966 nsIFrame* blockFrame = NS_NewBlockFrame(mPresShell, innerBlockContext,
2967 NS_BLOCK_FLOAT_MGR);
2969 if (NS_UNLIKELY(!blockFrame)) {
2970 buttonFrame->Destroy();
2971 return NS_ERROR_OUT_OF_MEMORY;
2973 rv = InitAndRestoreFrame(aState, content, buttonFrame, nsnull, blockFrame);
2974 if (NS_FAILED(rv)) {
2975 blockFrame->Destroy();
2976 buttonFrame->Destroy();
2977 return rv;
2980 rv = aState.AddChild(buttonFrame, aFrameItems, content, styleContext,
2981 aParentFrame);
2982 if (NS_FAILED(rv)) {
2983 blockFrame->Destroy();
2984 buttonFrame->Destroy();
2985 return rv;
2988 PRBool isLeaf = buttonFrame->IsLeaf();
2989 #ifdef DEBUG
2990 // Make sure that we're an anonymous content creator exactly when we're a
2991 // leaf
2992 nsIAnonymousContentCreator* creator = do_QueryFrame(buttonFrame);
2993 NS_ASSERTION(!creator == !isLeaf,
2994 "Should be creator exactly when we're a leaf");
2995 #endif
2997 if (!isLeaf) {
2998 // Process children
2999 nsFrameConstructorSaveState absoluteSaveState;
3000 nsFrameItems childItems;
3002 if (aStyleDisplay->IsPositioned()) {
3003 // The area frame becomes a container for child frames that are
3004 // absolutely positioned
3005 aState.PushAbsoluteContainingBlock(blockFrame, absoluteSaveState);
3008 #ifdef DEBUG
3009 // Make sure that anonymous child creation will have no effect in this case
3010 nsIAnonymousContentCreator* creator = do_QueryFrame(blockFrame);
3011 NS_ASSERTION(!creator, "Shouldn't be an anonymous content creator!");
3012 #endif
3014 rv = ProcessChildren(aState, content, styleContext, blockFrame, PR_TRUE,
3015 childItems, aStyleDisplay->IsBlockInside(),
3016 aItem.mPendingBinding);
3017 if (NS_FAILED(rv)) return rv;
3019 // Set the areas frame's initial child lists
3020 blockFrame->SetInitialChildList(nsnull, childItems);
3023 SetInitialSingleChild(buttonFrame, blockFrame);
3025 if (isLeaf) {
3026 ClearLazyBits(content->GetFirstChild(), nsnull);
3028 nsFrameItems anonymousChildItems;
3029 // if there are any anonymous children create frames for them. Note that
3030 // we're doing this using a different parent frame from the one we pass to
3031 // ProcessChildren!
3032 CreateAnonymousFrames(aState, content, buttonFrame, aItem.mPendingBinding,
3033 anonymousChildItems);
3034 if (anonymousChildItems.NotEmpty()) {
3035 // the anonymous content is already parented to the area frame
3036 aState.mFrameManager->AppendFrames(blockFrame, nsnull,
3037 anonymousChildItems);
3041 // our new button frame returned is the top frame.
3042 *aNewFrame = buttonFrame;
3044 return NS_OK;
3047 nsresult
3048 nsCSSFrameConstructor::ConstructSelectFrame(nsFrameConstructorState& aState,
3049 FrameConstructionItem& aItem,
3050 nsIFrame* aParentFrame,
3051 const nsStyleDisplay* aStyleDisplay,
3052 nsFrameItems& aFrameItems,
3053 nsIFrame** aNewFrame)
3055 nsresult rv = NS_OK;
3056 const PRInt32 kNoSizeSpecified = -1;
3058 nsIContent* const content = aItem.mContent;
3059 nsStyleContext* const styleContext = aItem.mStyleContext;
3061 // Construct a frame-based listbox or combobox
3062 nsCOMPtr<nsIDOMHTMLSelectElement> sel(do_QueryInterface(content));
3063 PRInt32 size = 1;
3064 if (sel) {
3065 sel->GetSize(&size);
3066 PRBool multipleSelect = PR_FALSE;
3067 sel->GetMultiple(&multipleSelect);
3068 // Construct a combobox if size=1 or no size is specified and its multiple select
3069 if (((1 == size || 0 == size) || (kNoSizeSpecified == size)) && (PR_FALSE == multipleSelect)) {
3070 // Construct a frame-based combo box.
3071 // The frame-based combo box is built out of three parts. A display area, a button and
3072 // a dropdown list. The display area and button are created through anonymous content.
3073 // The drop-down list's frame is created explicitly. The combobox frame shares its content
3074 // with the drop-down list.
3075 PRUint32 flags = NS_BLOCK_FLOAT_MGR;
3076 nsIFrame* comboboxFrame = NS_NewComboboxControlFrame(mPresShell, styleContext, flags);
3078 // Save the history state so we don't restore during construction
3079 // since the complete tree is required before we restore.
3080 nsILayoutHistoryState *historyState = aState.mFrameState;
3081 aState.mFrameState = nsnull;
3082 // Initialize the combobox frame
3083 InitAndRestoreFrame(aState, content,
3084 aState.GetGeometricParent(aStyleDisplay, aParentFrame),
3085 nsnull, comboboxFrame);
3087 nsHTMLContainerFrame::CreateViewForFrame(comboboxFrame, PR_FALSE);
3089 rv = aState.AddChild(comboboxFrame, aFrameItems, content, styleContext,
3090 aParentFrame);
3091 if (NS_FAILED(rv)) {
3092 return rv;
3095 ///////////////////////////////////////////////////////////////////
3096 // Combobox - Old Native Implementation
3097 ///////////////////////////////////////////////////////////////////
3098 nsIComboboxControlFrame* comboBox = do_QueryFrame(comboboxFrame);
3099 NS_ASSERTION(comboBox, "NS_NewComboboxControlFrame returned frame that "
3100 "doesn't implement nsIComboboxControlFrame");
3102 // Resolve pseudo element style for the dropdown list
3103 nsRefPtr<nsStyleContext> listStyle;
3104 listStyle = mPresShell->StyleSet()->
3105 ResolveAnonymousBoxStyle(nsCSSAnonBoxes::dropDownList, styleContext);
3107 // Create a listbox
3108 nsIFrame* listFrame = NS_NewListControlFrame(mPresShell, listStyle);
3110 // Notify the listbox that it is being used as a dropdown list.
3111 nsIListControlFrame * listControlFrame = do_QueryFrame(listFrame);
3112 if (listControlFrame) {
3113 listControlFrame->SetComboboxFrame(comboboxFrame);
3115 // Notify combobox that it should use the listbox as it's popup
3116 comboBox->SetDropDown(listFrame);
3118 NS_ASSERTION(!listStyle->GetStyleDisplay()->IsPositioned(),
3119 "Ended up with positioned dropdown list somehow.");
3120 NS_ASSERTION(!listStyle->GetStyleDisplay()->IsFloating(),
3121 "Ended up with floating dropdown list somehow.");
3123 // Initialize the scroll frame positioned. Note that it is NOT
3124 // initialized as absolutely positioned.
3125 nsIFrame* scrolledFrame = NS_NewSelectsAreaFrame(mPresShell, styleContext, flags);
3127 InitializeSelectFrame(aState, listFrame, scrolledFrame, content,
3128 comboboxFrame, listStyle, PR_TRUE,
3129 aItem.mPendingBinding, aFrameItems);
3131 // Set flag so the events go to the listFrame not child frames.
3132 // XXX: We should replace this with a real widget manager similar
3133 // to how the nsFormControlFrame works. Re-directing events is a temporary Kludge.
3134 NS_ASSERTION(listFrame->GetView(), "ListFrame's view is nsnull");
3135 //listFrame->GetView()->SetViewFlags(NS_VIEW_PUBLIC_FLAG_DONT_CHECK_CHILDREN);
3137 // Create display and button frames from the combobox's anonymous content.
3138 // The anonymous content is appended to existing anonymous content for this
3139 // element (the scrollbars).
3141 nsFrameItems childItems;
3142 CreateAnonymousFrames(aState, content, comboboxFrame,
3143 aItem.mPendingBinding, childItems);
3145 comboboxFrame->SetInitialChildList(nsnull, childItems);
3147 // Initialize the additional popup child list which contains the
3148 // dropdown list frame.
3149 nsFrameItems popupItems;
3150 popupItems.AddChild(listFrame);
3151 comboboxFrame->SetInitialChildList(nsGkAtoms::selectPopupList,
3152 popupItems);
3154 *aNewFrame = comboboxFrame;
3155 aState.mFrameState = historyState;
3156 if (aState.mFrameState && aState.mFrameManager) {
3157 // Restore frame state for the entire subtree of |comboboxFrame|.
3158 aState.mFrameManager->RestoreFrameState(comboboxFrame,
3159 aState.mFrameState);
3161 } else {
3162 ///////////////////////////////////////////////////////////////////
3163 // ListBox - Old Native Implementation
3164 ///////////////////////////////////////////////////////////////////
3165 nsIFrame* listFrame = NS_NewListControlFrame(mPresShell, styleContext);
3166 if (listFrame) {
3167 rv = NS_OK;
3169 else {
3170 rv = NS_ERROR_OUT_OF_MEMORY;
3173 nsIFrame* scrolledFrame = NS_NewSelectsAreaFrame(
3174 mPresShell, styleContext, NS_BLOCK_FLOAT_MGR);
3176 // ******* this code stolen from Initialze ScrollFrame ********
3177 // please adjust this code to use BuildScrollFrame.
3179 InitializeSelectFrame(aState, listFrame, scrolledFrame, content,
3180 aParentFrame, styleContext, PR_FALSE,
3181 aItem.mPendingBinding, aFrameItems);
3183 *aNewFrame = listFrame;
3186 return rv;
3191 * Used to be InitializeScrollFrame but now it's only used for the select tag
3192 * But the select tag should really be fixed to use GFX scrollbars that can
3193 * be create with BuildScrollFrame.
3195 nsresult
3196 nsCSSFrameConstructor::InitializeSelectFrame(nsFrameConstructorState& aState,
3197 nsIFrame* scrollFrame,
3198 nsIFrame* scrolledFrame,
3199 nsIContent* aContent,
3200 nsIFrame* aParentFrame,
3201 nsStyleContext* aStyleContext,
3202 PRBool aBuildCombobox,
3203 PendingBinding* aPendingBinding,
3204 nsFrameItems& aFrameItems)
3206 const nsStyleDisplay* display = aStyleContext->GetStyleDisplay();
3208 // Initialize it
3209 nsIFrame* geometricParent = aState.GetGeometricParent(display, aParentFrame);
3211 // We don't call InitAndRestoreFrame for scrollFrame because we can only
3212 // restore the frame state after its parts have been created (in particular,
3213 // the scrollable view). So we have to split Init and Restore.
3215 // Initialize the frame
3216 scrollFrame->Init(aContent, geometricParent, nsnull);
3218 if (!aBuildCombobox) {
3219 nsresult rv = aState.AddChild(scrollFrame, aFrameItems, aContent,
3220 aStyleContext, aParentFrame);
3221 if (NS_FAILED(rv)) {
3222 return rv;
3226 nsHTMLContainerFrame::CreateViewForFrame(scrollFrame, aBuildCombobox);
3227 if (aBuildCombobox) {
3228 // Give the drop-down list a popup widget
3229 nsIView* view = scrollFrame->GetView();
3230 NS_ASSERTION(view, "We asked for a view but didn't get one");
3231 if (view) {
3232 view->GetViewManager()->SetViewFloating(view, PR_TRUE);
3234 nsWidgetInitData widgetData;
3235 widgetData.mWindowType = eWindowType_popup;
3236 widgetData.mBorderStyle = eBorderStyle_default;
3237 view->CreateWidgetForPopup(&widgetData);
3241 BuildScrollFrame(aState, aContent, aStyleContext, scrolledFrame,
3242 geometricParent, scrollFrame);
3244 if (aState.mFrameState && aState.mFrameManager) {
3245 // Restore frame state for the scroll frame
3246 aState.mFrameManager->RestoreFrameStateFor(scrollFrame, aState.mFrameState);
3249 // Process children
3250 nsFrameConstructorSaveState absoluteSaveState;
3251 nsFrameItems childItems;
3253 if (display->IsPositioned()) {
3254 // The area frame becomes a container for child frames that are
3255 // absolutely positioned
3256 aState.PushAbsoluteContainingBlock(scrolledFrame, absoluteSaveState);
3259 ProcessChildren(aState, aContent, aStyleContext, scrolledFrame, PR_FALSE,
3260 childItems, PR_FALSE, aPendingBinding);
3262 // Set the scrolled frame's initial child lists
3263 scrolledFrame->SetInitialChildList(nsnull, childItems);
3264 return NS_OK;
3267 nsresult
3268 nsCSSFrameConstructor::ConstructFieldSetFrame(nsFrameConstructorState& aState,
3269 FrameConstructionItem& aItem,
3270 nsIFrame* aParentFrame,
3271 const nsStyleDisplay* aStyleDisplay,
3272 nsFrameItems& aFrameItems,
3273 nsIFrame** aNewFrame)
3275 nsIContent* const content = aItem.mContent;
3276 nsStyleContext* const styleContext = aItem.mStyleContext;
3278 nsIFrame* newFrame = NS_NewFieldSetFrame(mPresShell, styleContext);
3279 if (NS_UNLIKELY(!newFrame)) {
3280 return NS_ERROR_OUT_OF_MEMORY;
3283 // Initialize it
3284 InitAndRestoreFrame(aState, content,
3285 aState.GetGeometricParent(aStyleDisplay, aParentFrame),
3286 nsnull, newFrame);
3288 // See if we need to create a view, e.g. the frame is absolutely
3289 // positioned
3290 nsHTMLContainerFrame::CreateViewForFrame(newFrame, PR_FALSE);
3292 // Resolve style and initialize the frame
3293 nsRefPtr<nsStyleContext> fieldsetContentStyle;
3294 fieldsetContentStyle = mPresShell->StyleSet()->
3295 ResolveAnonymousBoxStyle(nsCSSAnonBoxes::fieldsetContent, styleContext);
3297 nsIFrame* blockFrame = NS_NewBlockFrame(mPresShell, fieldsetContentStyle,
3298 NS_BLOCK_FLOAT_MGR |
3299 NS_BLOCK_MARGIN_ROOT);
3300 InitAndRestoreFrame(aState, content, newFrame, nsnull, blockFrame);
3302 nsresult rv = aState.AddChild(newFrame, aFrameItems, content, styleContext,
3303 aParentFrame);
3304 if (NS_FAILED(rv)) {
3305 return rv;
3308 // Process children
3309 nsFrameConstructorSaveState absoluteSaveState;
3310 nsFrameItems childItems;
3312 if (aStyleDisplay->IsPositioned()) {
3313 // The area frame becomes a container for child frames that are
3314 // absolutely positioned
3315 // XXXbz this is probably wrong, and once arbitrary frames can be absolute
3316 // containing blocks we should fix this..
3317 aState.PushAbsoluteContainingBlock(blockFrame, absoluteSaveState);
3320 ProcessChildren(aState, content, styleContext, blockFrame, PR_TRUE,
3321 childItems, PR_TRUE, aItem.mPendingBinding);
3323 nsFrameItems fieldsetKids;
3324 fieldsetKids.AddChild(blockFrame);
3326 for (nsFrameList::Enumerator e(childItems); !e.AtEnd(); e.Next()) {
3327 nsLegendFrame* legendFrame = do_QueryFrame(e.get());
3328 if (legendFrame) {
3329 // We want the legend to be the first frame in the fieldset child list.
3330 // That way the EventStateManager will do the right thing when tabbing
3331 // from a selection point within the legend (bug 236071), which is
3332 // used for implementing legend access keys (bug 81481).
3333 // GetAdjustedParentFrame() below depends on this frame order.
3334 childItems.RemoveFrame(legendFrame);
3335 // Make sure to reparent the legend so it has the fieldset as the parent.
3336 fieldsetKids.InsertFrame(newFrame, nsnull, legendFrame);
3337 break;
3341 // Set the inner frame's initial child lists
3342 blockFrame->SetInitialChildList(nsnull, childItems);
3344 // Set the outer frame's initial child list
3345 newFrame->SetInitialChildList(nsnull, fieldsetKids);
3347 // our new frame returned is the top frame which is the list frame.
3348 *aNewFrame = newFrame;
3350 return NS_OK;
3353 static nsIFrame*
3354 FindAncestorWithGeneratedContentPseudo(nsIFrame* aFrame)
3356 for (nsIFrame* f = aFrame->GetParent(); f; f = f->GetParent()) {
3357 NS_ASSERTION(f->IsGeneratedContentFrame(),
3358 "should not have exited generated content");
3359 nsIAtom* pseudo = f->GetStyleContext()->GetPseudo();
3360 if (pseudo == nsCSSPseudoElements::before ||
3361 pseudo == nsCSSPseudoElements::after)
3362 return f;
3364 return nsnull;
3367 #define SIMPLE_FCDATA(_func) FCDATA_DECL(0, _func)
3368 #define FULL_CTOR_FCDATA(_flags, _func) \
3369 { _flags | FCDATA_FUNC_IS_FULL_CTOR, { nsnull }, _func }
3371 /* static */
3372 const nsCSSFrameConstructor::FrameConstructionData*
3373 nsCSSFrameConstructor::FindTextData(nsIFrame* aParentFrame)
3375 #ifdef MOZ_SVG
3376 if (aParentFrame && aParentFrame->IsFrameOfType(nsIFrame::eSVG)) {
3377 nsIFrame *ancestorFrame =
3378 nsSVGUtils::GetFirstNonAAncestorFrame(aParentFrame);
3379 if (ancestorFrame) {
3380 nsSVGTextContainerFrame* metrics = do_QueryFrame(ancestorFrame);
3381 if (metrics) {
3382 static const FrameConstructionData sSVGGlyphData =
3383 SIMPLE_FCDATA(NS_NewSVGGlyphFrame);
3384 return &sSVGGlyphData;
3387 return nsnull;
3389 #endif
3391 static const FrameConstructionData sTextData =
3392 FCDATA_DECL(FCDATA_IS_LINE_PARTICIPANT, NS_NewTextFrame);
3393 return &sTextData;
3396 nsresult
3397 nsCSSFrameConstructor::ConstructTextFrame(const FrameConstructionData* aData,
3398 nsFrameConstructorState& aState,
3399 nsIContent* aContent,
3400 nsIFrame* aParentFrame,
3401 nsStyleContext* aStyleContext,
3402 nsFrameItems& aFrameItems)
3404 NS_PRECONDITION(aData, "Must have frame construction data");
3406 nsIFrame* newFrame = (*aData->mFunc.mCreationFunc)(mPresShell, aStyleContext);
3408 if (NS_UNLIKELY(!newFrame))
3409 return NS_ERROR_OUT_OF_MEMORY;
3411 nsresult rv = InitAndRestoreFrame(aState, aContent, aParentFrame,
3412 nsnull, newFrame);
3414 if (NS_FAILED(rv)) {
3415 newFrame->Destroy();
3416 return rv;
3419 // We never need to create a view for a text frame.
3421 if (newFrame->IsGeneratedContentFrame()) {
3422 nsAutoPtr<nsGenConInitializer> initializer;
3423 initializer =
3424 static_cast<nsGenConInitializer*>(
3425 aContent->UnsetProperty(nsGkAtoms::genConInitializerProperty));
3426 if (initializer) {
3427 if (initializer->mNode->InitTextFrame(initializer->mList,
3428 FindAncestorWithGeneratedContentPseudo(newFrame), newFrame)) {
3429 (this->*(initializer->mDirtyAll))();
3431 initializer->mNode.forget();
3435 // Add the newly constructed frame to the flow
3436 aFrameItems.AddChild(newFrame);
3438 if (!aState.mCreatingExtraFrames)
3439 aContent->SetPrimaryFrame(newFrame);
3441 return rv;
3444 /* static */
3445 const nsCSSFrameConstructor::FrameConstructionData*
3446 nsCSSFrameConstructor::FindDataByInt(PRInt32 aInt,
3447 nsIContent* aContent,
3448 nsStyleContext* aStyleContext,
3449 const FrameConstructionDataByInt* aDataPtr,
3450 PRUint32 aDataLength)
3452 for (const FrameConstructionDataByInt *curData = aDataPtr,
3453 *endData = aDataPtr + aDataLength;
3454 curData != endData;
3455 ++curData) {
3456 if (curData->mInt == aInt) {
3457 const FrameConstructionData* data = &curData->mData;
3458 if (data->mBits & FCDATA_FUNC_IS_DATA_GETTER) {
3459 return data->mFunc.mDataGetter(aContent, aStyleContext);
3462 return data;
3466 return nsnull;
3469 /* static */
3470 const nsCSSFrameConstructor::FrameConstructionData*
3471 nsCSSFrameConstructor::FindDataByTag(nsIAtom* aTag,
3472 nsIContent* aContent,
3473 nsStyleContext* aStyleContext,
3474 const FrameConstructionDataByTag* aDataPtr,
3475 PRUint32 aDataLength)
3477 for (const FrameConstructionDataByTag *curData = aDataPtr,
3478 *endData = aDataPtr + aDataLength;
3479 curData != endData;
3480 ++curData) {
3481 if (*curData->mTag == aTag) {
3482 const FrameConstructionData* data = &curData->mData;
3483 if (data->mBits & FCDATA_FUNC_IS_DATA_GETTER) {
3484 return data->mFunc.mDataGetter(aContent, aStyleContext);
3487 return data;
3491 return nsnull;
3494 #define SUPPRESS_FCDATA() FCDATA_DECL(FCDATA_SUPPRESS_FRAME, nsnull)
3495 #define SIMPLE_INT_CREATE(_int, _func) { _int, SIMPLE_FCDATA(_func) }
3496 #define SIMPLE_INT_CHAIN(_int, _func) \
3497 { _int, FCDATA_DECL(FCDATA_FUNC_IS_DATA_GETTER, _func) }
3498 #define COMPLEX_INT_CREATE(_int, _func) \
3499 { _int, FULL_CTOR_FCDATA(0, _func) }
3501 #define SIMPLE_TAG_CREATE(_tag, _func) \
3502 { &nsGkAtoms::_tag, SIMPLE_FCDATA(_func) }
3503 #define SIMPLE_TAG_CHAIN(_tag, _func) \
3504 { &nsGkAtoms::_tag, FCDATA_DECL(FCDATA_FUNC_IS_DATA_GETTER, _func) }
3505 #define COMPLEX_TAG_CREATE(_tag, _func) \
3506 { &nsGkAtoms::_tag, FULL_CTOR_FCDATA(0, _func) }
3508 /* static */
3509 const nsCSSFrameConstructor::FrameConstructionData*
3510 nsCSSFrameConstructor::FindHTMLData(nsIContent* aContent,
3511 nsIAtom* aTag,
3512 PRInt32 aNameSpaceID,
3513 nsIFrame* aParentFrame,
3514 nsStyleContext* aStyleContext)
3516 // Ignore the tag if it's not HTML content and if it doesn't extend (via XBL)
3517 // a valid HTML namespace. This check must match the one in
3518 // ShouldHaveFirstLineStyle.
3519 if (aNameSpaceID != kNameSpaceID_XHTML) {
3520 return nsnull;
3523 NS_ASSERTION(!aParentFrame ||
3524 aParentFrame->GetStyleContext()->GetPseudo() !=
3525 nsCSSAnonBoxes::fieldsetContent ||
3526 aParentFrame->GetParent()->GetType() == nsGkAtoms::fieldSetFrame,
3527 "Unexpected parent for fieldset content anon box");
3528 if (aTag == nsGkAtoms::legend &&
3529 (!aParentFrame ||
3530 (aParentFrame->GetType() != nsGkAtoms::fieldSetFrame &&
3531 aParentFrame->GetStyleContext()->GetPseudo() !=
3532 nsCSSAnonBoxes::fieldsetContent) ||
3533 !aContent->GetParent() ||
3534 !aContent->GetParent()->IsHTML() ||
3535 aContent->GetParent()->Tag() != nsGkAtoms::fieldset ||
3536 aStyleContext->GetStyleDisplay()->IsFloating() ||
3537 aStyleContext->GetStyleDisplay()->IsAbsolutelyPositioned())) {
3538 // <legend> is only special inside fieldset, check both the frame tree
3539 // parent and content tree parent due to XBL issues. For floated or
3540 // absolutely positioned legends we want to construct by display type and
3541 // not do special legend stuff.
3542 // XXXbz it would be nice if we could just decide this based on the parent
3543 // tag, and hence just use a SIMPLE_TAG_CHAIN for legend below, but the
3544 // fact that with XBL we could end up with this legend element in some
3545 // totally weird insertion point makes that chancy, I think.
3546 return nsnull;
3549 static const FrameConstructionDataByTag sHTMLData[] = {
3550 SIMPLE_TAG_CHAIN(img, nsCSSFrameConstructor::FindImgData),
3551 SIMPLE_TAG_CHAIN(mozgeneratedcontentimage,
3552 nsCSSFrameConstructor::FindImgData),
3553 { &nsGkAtoms::br,
3554 FCDATA_DECL(FCDATA_IS_LINE_PARTICIPANT | FCDATA_IS_LINE_BREAK,
3555 NS_NewBRFrame) },
3556 SIMPLE_TAG_CREATE(wbr, NS_NewWBRFrame),
3557 SIMPLE_TAG_CHAIN(input, nsCSSFrameConstructor::FindInputData),
3558 SIMPLE_TAG_CREATE(textarea, NS_NewTextControlFrame),
3559 COMPLEX_TAG_CREATE(select, &nsCSSFrameConstructor::ConstructSelectFrame),
3560 SIMPLE_TAG_CHAIN(object, nsCSSFrameConstructor::FindObjectData),
3561 SIMPLE_TAG_CHAIN(applet, nsCSSFrameConstructor::FindObjectData),
3562 SIMPLE_TAG_CHAIN(embed, nsCSSFrameConstructor::FindObjectData),
3563 COMPLEX_TAG_CREATE(fieldset,
3564 &nsCSSFrameConstructor::ConstructFieldSetFrame),
3565 { &nsGkAtoms::legend,
3566 FCDATA_DECL(FCDATA_ALLOW_BLOCK_STYLES, NS_NewLegendFrame) },
3567 SIMPLE_TAG_CREATE(frameset, NS_NewHTMLFramesetFrame),
3568 SIMPLE_TAG_CREATE(iframe, NS_NewSubDocumentFrame),
3569 COMPLEX_TAG_CREATE(button, &nsCSSFrameConstructor::ConstructButtonFrame),
3570 SIMPLE_TAG_CREATE(canvas, NS_NewHTMLCanvasFrame),
3571 #if defined(MOZ_MEDIA)
3572 SIMPLE_TAG_CREATE(video, NS_NewHTMLVideoFrame),
3573 SIMPLE_TAG_CREATE(audio, NS_NewHTMLVideoFrame),
3574 #endif
3575 SIMPLE_TAG_CREATE(isindex, NS_NewIsIndexFrame)
3578 return FindDataByTag(aTag, aContent, aStyleContext, sHTMLData,
3579 NS_ARRAY_LENGTH(sHTMLData));
3582 /* static */
3583 const nsCSSFrameConstructor::FrameConstructionData*
3584 nsCSSFrameConstructor::FindImgData(nsIContent* aContent,
3585 nsStyleContext* aStyleContext)
3587 if (!nsImageFrame::ShouldCreateImageFrameFor(aContent, aStyleContext)) {
3588 return nsnull;
3591 static const FrameConstructionData sImgData = SIMPLE_FCDATA(NS_NewImageFrame);
3592 return &sImgData;
3595 /* static */
3596 const nsCSSFrameConstructor::FrameConstructionData*
3597 nsCSSFrameConstructor::FindImgControlData(nsIContent* aContent,
3598 nsStyleContext* aStyleContext)
3600 if (!nsImageFrame::ShouldCreateImageFrameFor(aContent, aStyleContext)) {
3601 return nsnull;
3604 static const FrameConstructionData sImgControlData =
3605 SIMPLE_FCDATA(NS_NewImageControlFrame);
3606 return &sImgControlData;
3609 /* static */
3610 const nsCSSFrameConstructor::FrameConstructionData*
3611 nsCSSFrameConstructor::FindInputData(nsIContent* aContent,
3612 nsStyleContext* aStyleContext)
3614 static const FrameConstructionDataByInt sInputData[] = {
3615 SIMPLE_INT_CREATE(NS_FORM_INPUT_CHECKBOX, NS_NewGfxCheckboxControlFrame),
3616 SIMPLE_INT_CREATE(NS_FORM_INPUT_RADIO, NS_NewGfxRadioControlFrame),
3617 SIMPLE_INT_CREATE(NS_FORM_INPUT_FILE, NS_NewFileControlFrame),
3618 SIMPLE_INT_CHAIN(NS_FORM_INPUT_IMAGE,
3619 nsCSSFrameConstructor::FindImgControlData),
3620 SIMPLE_INT_CREATE(NS_FORM_INPUT_EMAIL, NS_NewTextControlFrame),
3621 SIMPLE_INT_CREATE(NS_FORM_INPUT_SEARCH, NS_NewTextControlFrame),
3622 SIMPLE_INT_CREATE(NS_FORM_INPUT_TEXT, NS_NewTextControlFrame),
3623 SIMPLE_INT_CREATE(NS_FORM_INPUT_TEL, NS_NewTextControlFrame),
3624 SIMPLE_INT_CREATE(NS_FORM_INPUT_URL, NS_NewTextControlFrame),
3625 SIMPLE_INT_CREATE(NS_FORM_INPUT_PASSWORD, NS_NewTextControlFrame),
3626 COMPLEX_INT_CREATE(NS_FORM_INPUT_SUBMIT,
3627 &nsCSSFrameConstructor::ConstructButtonFrame),
3628 COMPLEX_INT_CREATE(NS_FORM_INPUT_RESET,
3629 &nsCSSFrameConstructor::ConstructButtonFrame),
3630 COMPLEX_INT_CREATE(NS_FORM_INPUT_BUTTON,
3631 &nsCSSFrameConstructor::ConstructButtonFrame)
3632 // Keeping hidden inputs out of here on purpose for so they get frames by
3633 // display (in practice, none).
3636 nsCOMPtr<nsIFormControl> control = do_QueryInterface(aContent);
3637 NS_ASSERTION(control, "input doesn't implement nsIFormControl?");
3639 return FindDataByInt(control->GetType(), aContent, aStyleContext,
3640 sInputData, NS_ARRAY_LENGTH(sInputData));
3643 /* static */
3644 const nsCSSFrameConstructor::FrameConstructionData*
3645 nsCSSFrameConstructor::FindObjectData(nsIContent* aContent,
3646 nsStyleContext* aStyleContext)
3648 // GetDisplayedType isn't necessarily nsIObjectLoadingContent::TYPE_NULL for
3649 // cases when the object is broken/suppressed/etc (e.g. a broken image), but
3650 // we want to treat those cases as TYPE_NULL
3651 PRUint32 type;
3652 if (aContent->IntrinsicState().HasAtLeastOneOfStates(NS_EVENT_STATE_BROKEN |
3653 NS_EVENT_STATE_USERDISABLED |
3654 NS_EVENT_STATE_SUPPRESSED)) {
3655 type = nsIObjectLoadingContent::TYPE_NULL;
3656 } else {
3657 nsCOMPtr<nsIObjectLoadingContent> objContent(do_QueryInterface(aContent));
3658 NS_ASSERTION(objContent,
3659 "applet, embed and object must implement "
3660 "nsIObjectLoadingContent!");
3662 objContent->GetDisplayedType(&type);
3665 static const FrameConstructionDataByInt sObjectData[] = {
3666 SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_LOADING,
3667 NS_NewEmptyFrame),
3668 SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_PLUGIN,
3669 NS_NewObjectFrame),
3670 SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_IMAGE,
3671 NS_NewImageFrame),
3672 SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_DOCUMENT,
3673 NS_NewSubDocumentFrame)
3674 // Nothing for TYPE_NULL so we'll construct frames by display there
3677 return FindDataByInt((PRInt32)type, aContent, aStyleContext,
3678 sObjectData, NS_ARRAY_LENGTH(sObjectData));
3681 nsresult
3682 nsCSSFrameConstructor::ConstructFrameFromItemInternal(FrameConstructionItem& aItem,
3683 nsFrameConstructorState& aState,
3684 nsIFrame* aParentFrame,
3685 nsFrameItems& aFrameItems)
3687 const FrameConstructionData* data = aItem.mFCData;
3688 NS_ASSERTION(data, "Must have frame construction data");
3690 PRUint32 bits = data->mBits;
3692 NS_ASSERTION(!(bits & FCDATA_FUNC_IS_DATA_GETTER),
3693 "Should have dealt with this inside the data finder");
3695 // Some sets of bits are not compatible with each other
3696 #define CHECK_ONLY_ONE_BIT(_bit1, _bit2) \
3697 NS_ASSERTION(!(bits & _bit1) || !(bits & _bit2), \
3698 "Only one of these bits should be set")
3699 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_FORCE_NULL_ABSPOS_CONTAINER);
3700 #ifdef MOZ_MATHML
3701 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_WRAP_KIDS_IN_BLOCKS);
3702 #endif
3703 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_MAY_NEED_SCROLLFRAME);
3704 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_IS_POPUP);
3705 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_SKIP_ABSPOS_PUSH);
3706 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_FORCE_VIEW);
3707 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR,
3708 FCDATA_DISALLOW_GENERATED_CONTENT);
3709 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_ALLOW_BLOCK_STYLES);
3710 CHECK_ONLY_ONE_BIT(FCDATA_MAY_NEED_SCROLLFRAME, FCDATA_FORCE_VIEW);
3711 #undef CHECK_ONLY_ONE_BIT
3713 // Don't create a subdocument frame for iframes if we're creating extra frames
3714 if (aState.mCreatingExtraFrames && aItem.mContent->IsHTML() &&
3715 aItem.mContent->Tag() == nsGkAtoms::iframe)
3717 return NS_OK;
3720 nsStyleContext* const styleContext = aItem.mStyleContext;
3721 const nsStyleDisplay* display = styleContext->GetStyleDisplay();
3723 nsIFrame* newFrame;
3724 nsIFrame* primaryFrame;
3725 if (bits & FCDATA_FUNC_IS_FULL_CTOR) {
3726 nsresult rv =
3727 (this->*(data->mFullConstructor))(aState, aItem, aParentFrame,
3728 display, aFrameItems, &newFrame);
3729 if (NS_FAILED(rv)) {
3730 return rv;
3733 primaryFrame = newFrame;
3734 } else {
3735 nsIContent* const content = aItem.mContent;
3737 newFrame =
3738 (*data->mFunc.mCreationFunc)(mPresShell, styleContext);
3739 if (!newFrame) {
3740 return NS_ERROR_OUT_OF_MEMORY;
3743 PRBool allowOutOfFlow = !(bits & FCDATA_DISALLOW_OUT_OF_FLOW);
3744 PRBool isPopup = aItem.mIsPopup;
3745 NS_ASSERTION(!isPopup ||
3746 (aState.mPopupItems.containingBlock &&
3747 aState.mPopupItems.containingBlock->GetType() ==
3748 nsGkAtoms::popupSetFrame),
3749 "Should have a containing block here!");
3751 nsIFrame* geometricParent =
3752 isPopup ? aState.mPopupItems.containingBlock :
3753 (allowOutOfFlow ? aState.GetGeometricParent(display, aParentFrame)
3754 : aParentFrame);
3756 nsresult rv = NS_OK;
3758 // Must init frameToAddToList to null, since it's inout
3759 nsIFrame* frameToAddToList = nsnull;
3760 if ((bits & FCDATA_MAY_NEED_SCROLLFRAME) &&
3761 display->IsScrollableOverflow()) {
3762 BuildScrollFrame(aState, content, styleContext, newFrame,
3763 geometricParent, frameToAddToList);
3764 } else {
3765 rv = InitAndRestoreFrame(aState, content, geometricParent, nsnull,
3766 newFrame);
3767 NS_ASSERTION(NS_SUCCEEDED(rv), "InitAndRestoreFrame failed");
3768 // See whether we need to create a view
3769 nsHTMLContainerFrame::CreateViewForFrame(newFrame,
3770 (bits & FCDATA_FORCE_VIEW) != 0);
3771 frameToAddToList = newFrame;
3774 // Use frameToAddToList as the primary frame. In the non-scrollframe case
3775 // they're equal, but in the scrollframe case newFrame is the scrolled
3776 // frame, while frameToAddToList is the scrollframe (and should be the
3777 // primary frame).
3778 primaryFrame = frameToAddToList;
3780 rv = aState.AddChild(frameToAddToList, aFrameItems, content, styleContext,
3781 aParentFrame, allowOutOfFlow, allowOutOfFlow, isPopup);
3782 if (NS_FAILED(rv)) {
3783 return rv;
3786 #ifdef MOZ_XUL
3787 // Icky XUL stuff, sadly
3789 if (aItem.mIsRootPopupgroup) {
3790 NS_ASSERTION(nsIRootBox::GetRootBox(mPresShell) &&
3791 nsIRootBox::GetRootBox(mPresShell)->GetPopupSetFrame() ==
3792 newFrame,
3793 "Unexpected PopupSetFrame");
3794 aState.mPopupItems.containingBlock = newFrame;
3795 aState.mHavePendingPopupgroup = PR_FALSE;
3797 #endif /* MOZ_XUL */
3799 // Process the child content if requested
3800 nsFrameItems childItems;
3801 nsFrameConstructorSaveState absoluteSaveState;
3803 if (bits & FCDATA_FORCE_NULL_ABSPOS_CONTAINER) {
3804 aState.PushAbsoluteContainingBlock(nsnull, absoluteSaveState);
3805 } else if (!(bits & FCDATA_SKIP_ABSPOS_PUSH) && display->IsPositioned()) {
3806 aState.PushAbsoluteContainingBlock(newFrame, absoluteSaveState);
3809 if (bits & FCDATA_USE_CHILD_ITEMS) {
3810 rv = ConstructFramesFromItemList(aState, aItem.mChildItems, newFrame,
3811 childItems);
3812 } else {
3813 // Process the child frames.
3814 rv = ProcessChildren(aState, content, styleContext, newFrame,
3815 !(bits & FCDATA_DISALLOW_GENERATED_CONTENT),
3816 childItems,
3817 (bits & FCDATA_ALLOW_BLOCK_STYLES) != 0,
3818 aItem.mPendingBinding);
3821 #ifdef MOZ_XUL
3822 // More icky XUL stuff
3823 if (aItem.mNameSpaceID == kNameSpaceID_XUL &&
3824 (aItem.mTag == nsGkAtoms::treechildren || // trees always need titletips
3825 content->HasAttr(kNameSpaceID_None, nsGkAtoms::tooltiptext) ||
3826 content->HasAttr(kNameSpaceID_None, nsGkAtoms::tooltip))) {
3827 nsIRootBox* rootBox = nsIRootBox::GetRootBox(mPresShell);
3828 if (rootBox) {
3829 rootBox->AddTooltipSupport(content);
3832 #endif
3834 #ifdef MOZ_MATHML
3835 if (NS_SUCCEEDED(rv) && (bits & FCDATA_WRAP_KIDS_IN_BLOCKS)) {
3836 nsFrameItems newItems;
3837 nsFrameItems currentBlock;
3838 nsIFrame* f;
3839 while ((f = childItems.FirstChild()) != nsnull) {
3840 PRBool wrapFrame = IsInlineFrame(f) || IsFrameSpecial(f);
3841 if (!wrapFrame) {
3842 rv = FlushAccumulatedBlock(aState, content, newFrame, &currentBlock, &newItems);
3843 if (NS_FAILED(rv))
3844 break;
3847 childItems.RemoveFrame(f);
3848 if (wrapFrame) {
3849 currentBlock.AddChild(f);
3850 } else {
3851 newItems.AddChild(f);
3854 rv = FlushAccumulatedBlock(aState, content, newFrame, &currentBlock, &newItems);
3856 if (childItems.NotEmpty()) {
3857 // an error must have occurred, delete unprocessed frames
3858 childItems.DestroyFrames();
3861 childItems = newItems;
3863 #endif
3865 // Set the frame's initial child list
3866 // Note that MathML depends on this being called even if
3867 // childItems is empty!
3868 newFrame->SetInitialChildList(nsnull, childItems);
3871 NS_ASSERTION(newFrame->IsFrameOfType(nsIFrame::eLineParticipant) ==
3872 ((bits & FCDATA_IS_LINE_PARTICIPANT) != 0),
3873 "Incorrectly set FCDATA_IS_LINE_PARTICIPANT bits");
3875 if (!aState.mCreatingExtraFrames && !(bits & FCDATA_SKIP_FRAMESET)) {
3876 aItem.mContent->SetPrimaryFrame(primaryFrame);
3879 return NS_OK;
3882 // after the node has been constructed and initialized create any
3883 // anonymous content a node needs.
3884 nsresult
3885 nsCSSFrameConstructor::CreateAnonymousFrames(nsFrameConstructorState& aState,
3886 nsIContent* aParent,
3887 nsIFrame* aParentFrame,
3888 PendingBinding* aPendingBinding,
3889 nsFrameItems& aChildItems)
3891 nsAutoTArray<nsIContent*, 4> newAnonymousItems;
3892 nsresult rv = GetAnonymousContent(aParent, aParentFrame, newAnonymousItems);
3893 NS_ENSURE_SUCCESS(rv, rv);
3895 PRUint32 count = newAnonymousItems.Length();
3896 if (count == 0) {
3897 return NS_OK;
3900 nsFrameConstructorState::PendingBindingAutoPusher pusher(aState,
3901 aPendingBinding);
3903 nsIAnonymousContentCreator* creator = do_QueryFrame(aParentFrame);
3904 NS_ASSERTION(creator,
3905 "How can that happen if we have nodes to construct frames for?");
3907 for (PRUint32 i=0; i < count; i++) {
3908 nsIContent* content = newAnonymousItems[i];
3909 NS_ASSERTION(content, "null anonymous content?");
3911 nsIFrame* newFrame = creator->CreateFrameFor(content);
3912 if (newFrame) {
3913 NS_ASSERTION(content->GetPrimaryFrame(),
3914 "Content must have a primary frame now");
3915 aChildItems.AddChild(newFrame);
3917 else {
3918 // create the frame and attach it to our frame
3919 ConstructFrame(aState, content, aParentFrame, aChildItems);
3923 return NS_OK;
3926 nsresult
3927 nsCSSFrameConstructor::GetAnonymousContent(nsIContent* aParent,
3928 nsIFrame* aParentFrame,
3929 nsTArray<nsIContent*>& aContent)
3931 nsIAnonymousContentCreator* creator = do_QueryFrame(aParentFrame);
3932 if (!creator)
3933 return NS_OK;
3935 nsresult rv = creator->CreateAnonymousContent(aContent);
3936 NS_ENSURE_SUCCESS(rv, rv);
3938 PRUint32 count = aContent.Length();
3939 for (PRUint32 i=0; i < count; i++) {
3940 // get our child's content and set its parent to our content
3941 nsIContent* content = aContent[i];
3942 NS_ASSERTION(content, "null anonymous content?");
3944 #ifdef MOZ_SVG
3945 // least-surprise CSS binding until we do the SVG specified
3946 // cascading rules for <svg:use> - bug 265894
3947 if (aParent &&
3948 aParent->NodeInfo()->Equals(nsGkAtoms::use, kNameSpaceID_SVG)) {
3949 content->SetFlags(NODE_IS_ANONYMOUS);
3950 } else
3951 #endif
3953 content->SetNativeAnonymous();
3956 rv = content->BindToTree(mDocument, aParent, aParent, PR_TRUE);
3957 if (NS_FAILED(rv)) {
3958 content->UnbindFromTree();
3959 return rv;
3963 return NS_OK;
3966 static
3967 PRBool IsXULDisplayType(const nsStyleDisplay* aDisplay)
3969 return (aDisplay->mDisplay == NS_STYLE_DISPLAY_INLINE_BOX ||
3970 #ifdef MOZ_XUL
3971 aDisplay->mDisplay == NS_STYLE_DISPLAY_INLINE_GRID ||
3972 aDisplay->mDisplay == NS_STYLE_DISPLAY_INLINE_STACK ||
3973 #endif
3974 aDisplay->mDisplay == NS_STYLE_DISPLAY_BOX
3975 #ifdef MOZ_XUL
3976 || aDisplay->mDisplay == NS_STYLE_DISPLAY_GRID ||
3977 aDisplay->mDisplay == NS_STYLE_DISPLAY_STACK ||
3978 aDisplay->mDisplay == NS_STYLE_DISPLAY_GRID_GROUP ||
3979 aDisplay->mDisplay == NS_STYLE_DISPLAY_GRID_LINE ||
3980 aDisplay->mDisplay == NS_STYLE_DISPLAY_DECK ||
3981 aDisplay->mDisplay == NS_STYLE_DISPLAY_POPUP ||
3982 aDisplay->mDisplay == NS_STYLE_DISPLAY_GROUPBOX
3983 #endif
3988 // XUL frames are not allowed to be out of flow.
3989 #define SIMPLE_XUL_FCDATA(_func) \
3990 FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_SKIP_ABSPOS_PUSH, \
3991 _func)
3992 #define SCROLLABLE_XUL_FCDATA(_func) \
3993 FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_SKIP_ABSPOS_PUSH | \
3994 FCDATA_MAY_NEED_SCROLLFRAME, _func)
3995 #define SIMPLE_XUL_CREATE(_tag, _func) \
3996 { &nsGkAtoms::_tag, SIMPLE_XUL_FCDATA(_func) }
3997 #define SCROLLABLE_XUL_CREATE(_tag, _func) \
3998 { &nsGkAtoms::_tag, SCROLLABLE_XUL_FCDATA(_func) }
3999 #define SIMPLE_XUL_INT_CREATE(_int, _func) \
4000 { _int, SIMPLE_XUL_FCDATA(_func) }
4001 #define SCROLLABLE_XUL_INT_CREATE(_int, _func) \
4002 { _int, SCROLLABLE_XUL_FCDATA(_func) }
4004 static
4005 nsIFrame* NS_NewGridBoxFrame(nsIPresShell* aPresShell,
4006 nsStyleContext* aStyleContext)
4008 nsCOMPtr<nsIBoxLayout> layout;
4009 NS_NewGridLayout2(aPresShell, getter_AddRefs(layout));
4010 if (!layout) {
4011 return nsnull;
4014 return NS_NewBoxFrame(aPresShell, aStyleContext, PR_FALSE, layout);
4017 /* static */
4018 const nsCSSFrameConstructor::FrameConstructionData*
4019 nsCSSFrameConstructor::FindXULTagData(nsIContent* aContent,
4020 nsIAtom* aTag,
4021 PRInt32 aNameSpaceID,
4022 nsStyleContext* aStyleContext)
4024 if (aNameSpaceID != kNameSpaceID_XUL) {
4025 return nsnull;
4028 static const FrameConstructionDataByTag sXULTagData[] = {
4029 #ifdef MOZ_XUL
4030 SCROLLABLE_XUL_CREATE(button, NS_NewButtonBoxFrame),
4031 SCROLLABLE_XUL_CREATE(checkbox, NS_NewButtonBoxFrame),
4032 SCROLLABLE_XUL_CREATE(radio, NS_NewButtonBoxFrame),
4033 SCROLLABLE_XUL_CREATE(autorepeatbutton, NS_NewAutoRepeatBoxFrame),
4034 SCROLLABLE_XUL_CREATE(titlebar, NS_NewTitleBarFrame),
4035 SCROLLABLE_XUL_CREATE(resizer, NS_NewResizerFrame),
4036 SIMPLE_XUL_CREATE(image, NS_NewImageBoxFrame),
4037 SIMPLE_XUL_CREATE(spring, NS_NewLeafBoxFrame),
4038 SIMPLE_XUL_CREATE(spacer, NS_NewLeafBoxFrame),
4039 SIMPLE_XUL_CREATE(treechildren, NS_NewTreeBodyFrame),
4040 SIMPLE_XUL_CREATE(treecol, NS_NewTreeColFrame),
4041 SIMPLE_XUL_CREATE(text, NS_NewTextBoxFrame),
4042 SIMPLE_TAG_CHAIN(label, nsCSSFrameConstructor::FindXULLabelData),
4043 SIMPLE_TAG_CHAIN(description, nsCSSFrameConstructor::FindXULDescriptionData),
4044 SIMPLE_XUL_CREATE(menu, NS_NewMenuFrame),
4045 SIMPLE_XUL_CREATE(menubutton, NS_NewMenuFrame),
4046 SIMPLE_XUL_CREATE(menuitem, NS_NewMenuItemFrame),
4047 #ifdef XP_MACOSX
4048 SIMPLE_TAG_CHAIN(menubar, nsCSSFrameConstructor::FindXULMenubarData),
4049 #else
4050 SIMPLE_XUL_CREATE(menubar, NS_NewMenuBarFrame),
4051 #endif /* XP_MACOSX */
4052 SIMPLE_TAG_CHAIN(popupgroup, nsCSSFrameConstructor::FindPopupGroupData),
4053 SIMPLE_XUL_CREATE(iframe, NS_NewSubDocumentFrame),
4054 SIMPLE_XUL_CREATE(editor, NS_NewSubDocumentFrame),
4055 SIMPLE_XUL_CREATE(browser, NS_NewSubDocumentFrame),
4056 SIMPLE_XUL_CREATE(progressmeter, NS_NewProgressMeterFrame),
4057 SIMPLE_XUL_CREATE(splitter, NS_NewSplitterFrame),
4058 SIMPLE_TAG_CHAIN(listboxbody,
4059 nsCSSFrameConstructor::FindXULListBoxBodyData),
4060 SIMPLE_TAG_CHAIN(listitem, nsCSSFrameConstructor::FindXULListItemData),
4061 #endif /* MOZ_XUL */
4062 SIMPLE_XUL_CREATE(slider, NS_NewSliderFrame),
4063 SIMPLE_XUL_CREATE(scrollbar, NS_NewScrollbarFrame),
4064 SIMPLE_XUL_CREATE(scrollbarbutton, NS_NewScrollbarButtonFrame)
4067 return FindDataByTag(aTag, aContent, aStyleContext, sXULTagData,
4068 NS_ARRAY_LENGTH(sXULTagData));
4071 #ifdef MOZ_XUL
4072 /* static */
4073 const nsCSSFrameConstructor::FrameConstructionData*
4074 nsCSSFrameConstructor::FindPopupGroupData(nsIContent* aContent,
4075 nsStyleContext* /* unused */)
4077 if (!aContent->IsRootOfNativeAnonymousSubtree()) {
4078 return nsnull;
4081 static const FrameConstructionData sPopupSetData =
4082 SIMPLE_XUL_FCDATA(NS_NewPopupSetFrame);
4083 return &sPopupSetData;
4086 /* static */
4087 const nsCSSFrameConstructor::FrameConstructionData
4088 nsCSSFrameConstructor::sXULTextBoxData = SIMPLE_XUL_FCDATA(NS_NewTextBoxFrame);
4090 /* static */
4091 const nsCSSFrameConstructor::FrameConstructionData*
4092 nsCSSFrameConstructor::FindXULLabelData(nsIContent* aContent,
4093 nsStyleContext* /* unused */)
4095 if (aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::value)) {
4096 return &sXULTextBoxData;
4099 static const FrameConstructionData sLabelData =
4100 SIMPLE_XUL_FCDATA(NS_NewXULLabelFrame);
4101 return &sLabelData;
4104 static nsIFrame*
4105 NS_NewXULDescriptionFrame(nsIPresShell* aPresShell, nsStyleContext *aContext)
4107 // XXXbz do we really need to set those flags? If the parent is not
4108 // a block we'll get them anyway, and if it is, do we want them?
4109 return NS_NewBlockFrame(aPresShell, aContext,
4110 NS_BLOCK_FLOAT_MGR | NS_BLOCK_MARGIN_ROOT);
4113 /* static */
4114 const nsCSSFrameConstructor::FrameConstructionData*
4115 nsCSSFrameConstructor::FindXULDescriptionData(nsIContent* aContent,
4116 nsStyleContext* /* unused */)
4118 if (aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::value)) {
4119 return &sXULTextBoxData;
4122 static const FrameConstructionData sDescriptionData =
4123 SIMPLE_XUL_FCDATA(NS_NewXULDescriptionFrame);
4124 return &sDescriptionData;
4127 #ifdef XP_MACOSX
4128 /* static */
4129 const nsCSSFrameConstructor::FrameConstructionData*
4130 nsCSSFrameConstructor::FindXULMenubarData(nsIContent* aContent,
4131 nsStyleContext* aStyleContext)
4133 nsCOMPtr<nsISupports> container =
4134 aStyleContext->PresContext()->GetContainer();
4135 if (container) {
4136 nsCOMPtr<nsIDocShellTreeItem> treeItem(do_QueryInterface(container));
4137 if (treeItem) {
4138 PRInt32 type;
4139 treeItem->GetItemType(&type);
4140 if (nsIDocShellTreeItem::typeChrome == type) {
4141 nsCOMPtr<nsIDocShellTreeItem> parent;
4142 treeItem->GetParent(getter_AddRefs(parent));
4143 if (!parent) {
4144 // This is the root. Suppress the menubar, since on Mac
4145 // window menus are not attached to the window.
4146 static const FrameConstructionData sSuppressData = SUPPRESS_FCDATA();
4147 return &sSuppressData;
4153 static const FrameConstructionData sMenubarData =
4154 SIMPLE_XUL_FCDATA(NS_NewMenuBarFrame);
4155 return &sMenubarData;
4157 #endif /* XP_MACOSX */
4159 /* static */
4160 const nsCSSFrameConstructor::FrameConstructionData*
4161 nsCSSFrameConstructor::FindXULListBoxBodyData(nsIContent* aContent,
4162 nsStyleContext* aStyleContext)
4164 if (aStyleContext->GetStyleDisplay()->mDisplay !=
4165 NS_STYLE_DISPLAY_GRID_GROUP) {
4166 return nsnull;
4169 static const FrameConstructionData sListBoxBodyData =
4170 SCROLLABLE_XUL_FCDATA(NS_NewListBoxBodyFrame);
4171 return &sListBoxBodyData;
4174 /* static */
4175 const nsCSSFrameConstructor::FrameConstructionData*
4176 nsCSSFrameConstructor::FindXULListItemData(nsIContent* aContent,
4177 nsStyleContext* aStyleContext)
4179 if (aStyleContext->GetStyleDisplay()->mDisplay !=
4180 NS_STYLE_DISPLAY_GRID_LINE) {
4181 return nsnull;
4184 static const FrameConstructionData sListItemData =
4185 SCROLLABLE_XUL_FCDATA(NS_NewListItemFrame);
4186 return &sListItemData;
4189 #endif /* MOZ_XUL */
4191 /* static */
4192 const nsCSSFrameConstructor::FrameConstructionData*
4193 nsCSSFrameConstructor::FindXULDisplayData(const nsStyleDisplay* aDisplay,
4194 nsIContent* aContent,
4195 nsStyleContext* aStyleContext)
4197 static const FrameConstructionDataByInt sXULDisplayData[] = {
4198 SCROLLABLE_XUL_INT_CREATE(NS_STYLE_DISPLAY_INLINE_BOX, NS_NewBoxFrame),
4199 SCROLLABLE_XUL_INT_CREATE(NS_STYLE_DISPLAY_BOX, NS_NewBoxFrame),
4200 #ifdef MOZ_XUL
4201 SCROLLABLE_XUL_INT_CREATE(NS_STYLE_DISPLAY_INLINE_GRID, NS_NewGridBoxFrame),
4202 SCROLLABLE_XUL_INT_CREATE(NS_STYLE_DISPLAY_GRID, NS_NewGridBoxFrame),
4203 SCROLLABLE_XUL_INT_CREATE(NS_STYLE_DISPLAY_GRID_GROUP,
4204 NS_NewGridRowGroupFrame),
4205 SCROLLABLE_XUL_INT_CREATE(NS_STYLE_DISPLAY_GRID_LINE,
4206 NS_NewGridRowLeafFrame),
4207 SIMPLE_XUL_INT_CREATE(NS_STYLE_DISPLAY_DECK, NS_NewDeckFrame),
4208 SCROLLABLE_XUL_INT_CREATE(NS_STYLE_DISPLAY_GROUPBOX, NS_NewGroupBoxFrame),
4209 SCROLLABLE_XUL_INT_CREATE(NS_STYLE_DISPLAY_INLINE_STACK, NS_NewStackFrame),
4210 SCROLLABLE_XUL_INT_CREATE(NS_STYLE_DISPLAY_STACK, NS_NewStackFrame),
4211 { NS_STYLE_DISPLAY_POPUP,
4212 FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_IS_POPUP |
4213 FCDATA_SKIP_ABSPOS_PUSH, NS_NewMenuPopupFrame) }
4214 #endif /* MOZ_XUL */
4217 // Processing by display here:
4218 return FindDataByInt(aDisplay->mDisplay, aContent, aStyleContext,
4219 sXULDisplayData, NS_ARRAY_LENGTH(sXULDisplayData));
4222 already_AddRefed<nsStyleContext>
4223 nsCSSFrameConstructor::BeginBuildingScrollFrame(nsFrameConstructorState& aState,
4224 nsIContent* aContent,
4225 nsStyleContext* aContentStyle,
4226 nsIFrame* aParentFrame,
4227 nsIAtom* aScrolledPseudo,
4228 PRBool aIsRoot,
4229 nsIFrame*& aNewFrame)
4231 nsIFrame* gfxScrollFrame = aNewFrame;
4233 nsFrameItems anonymousItems;
4235 nsRefPtr<nsStyleContext> contentStyle = aContentStyle;
4237 if (!gfxScrollFrame) {
4238 // Build a XULScrollFrame when the child is a box, otherwise an
4239 // HTMLScrollFrame
4240 // XXXbz this is the lone remaining consumer of IsXULDisplayType.
4241 // I wonder whether we can eliminate that somehow.
4242 if (IsXULDisplayType(aContentStyle->GetStyleDisplay())) {
4243 gfxScrollFrame = NS_NewXULScrollFrame(mPresShell, contentStyle, aIsRoot);
4244 } else {
4245 gfxScrollFrame = NS_NewHTMLScrollFrame(mPresShell, contentStyle, aIsRoot);
4248 InitAndRestoreFrame(aState, aContent, aParentFrame, nsnull, gfxScrollFrame);
4250 // Create a view
4251 nsHTMLContainerFrame::CreateViewForFrame(gfxScrollFrame, PR_FALSE);
4254 // if there are any anonymous children for the scroll frame, create
4255 // frames for them.
4256 // Pass a null pending binding: we don't care how constructors for any of
4257 // this anonymous content order with anything else. It's never been
4258 // consistent anyway.
4259 CreateAnonymousFrames(aState, aContent, gfxScrollFrame, nsnull,
4260 anonymousItems);
4262 aNewFrame = gfxScrollFrame;
4264 // we used the style that was passed in. So resolve another one.
4265 nsStyleSet *styleSet = mPresShell->StyleSet();
4266 nsStyleContext* aScrolledChildStyle =
4267 styleSet->ResolveAnonymousBoxStyle(aScrolledPseudo, contentStyle).get();
4269 if (gfxScrollFrame) {
4270 gfxScrollFrame->SetInitialChildList(nsnull, anonymousItems);
4273 return aScrolledChildStyle;
4276 void
4277 nsCSSFrameConstructor::FinishBuildingScrollFrame(nsIFrame* aScrollFrame,
4278 nsIFrame* aScrolledFrame)
4280 nsFrameList scrolled(aScrolledFrame, aScrolledFrame);
4281 aScrollFrame->AppendFrames(nsnull, scrolled);
4286 * Called to wrap a gfx scrollframe around a frame. The hierarchy will look like this
4288 * ------- for gfx scrollbars ------
4291 * ScrollFrame
4294 * Frame (scrolled frame you passed in)
4297 *-----------------------------------
4298 * LEGEND:
4300 * ScrollFrame: This is a frame that manages gfx cross platform frame based scrollbars.
4302 * @param aContent the content node of the child to wrap.
4303 * @param aScrolledFrame The frame of the content to wrap. This should not be
4304 * Initialized. This method will initialize it with a scrolled pseudo
4305 * and no nsIContent. The content will be attached to the scrollframe
4306 * returned.
4307 * @param aContentStyle the style context that has already been resolved for the content being passed in.
4309 * @param aParentFrame The parent to attach the scroll frame to
4311 * @param aNewFrame The new scrollframe or gfx scrollframe that we create. It will contain the
4312 * scrolled frame you passed in. (returned)
4313 * If this is not null, we'll just use it
4314 * @param aScrolledContentStyle the style that was resolved for the scrolled frame. (returned)
4316 nsresult
4317 nsCSSFrameConstructor::BuildScrollFrame(nsFrameConstructorState& aState,
4318 nsIContent* aContent,
4319 nsStyleContext* aContentStyle,
4320 nsIFrame* aScrolledFrame,
4321 nsIFrame* aParentFrame,
4322 nsIFrame*& aNewFrame)
4324 nsRefPtr<nsStyleContext> scrolledContentStyle =
4325 BeginBuildingScrollFrame(aState, aContent, aContentStyle, aParentFrame,
4326 nsCSSAnonBoxes::scrolledContent,
4327 PR_FALSE, aNewFrame);
4329 aScrolledFrame->SetStyleContextWithoutNotification(scrolledContentStyle);
4330 InitAndRestoreFrame(aState, aContent, aNewFrame, nsnull, aScrolledFrame);
4332 FinishBuildingScrollFrame(aNewFrame, aScrolledFrame);
4333 return NS_OK;
4336 const nsCSSFrameConstructor::FrameConstructionData*
4337 nsCSSFrameConstructor::FindDisplayData(const nsStyleDisplay* aDisplay,
4338 nsIContent* aContent,
4339 nsStyleContext* aStyleContext)
4341 PR_STATIC_ASSERT(eParentTypeCount < (1 << (32 - FCDATA_PARENT_TYPE_OFFSET)));
4343 // The style system ensures that floated and positioned frames are
4344 // block-level.
4345 NS_ASSERTION(!(aDisplay->IsFloating() ||
4346 aDisplay->IsAbsolutelyPositioned()) ||
4347 aDisplay->IsBlockOutside(),
4348 "Style system did not apply CSS2.1 section 9.7 fixups");
4350 // If this is "body", try propagating its scroll style to the viewport
4351 // Note that we need to do this even if the body is NOT scrollable;
4352 // it might have dynamically changed from scrollable to not scrollable,
4353 // and that might need to be propagated.
4354 // XXXbz is this the right place to do this? If this code moves,
4355 // make this function static.
4356 PRBool propagatedScrollToViewport = PR_FALSE;
4357 if (aContent->NodeInfo()->Equals(nsGkAtoms::body) &&
4358 aContent->IsHTML()) {
4359 propagatedScrollToViewport =
4360 PropagateScrollToViewport() == aContent;
4363 NS_ASSERTION(!propagatedScrollToViewport ||
4364 !mPresShell->GetPresContext()->IsPaginated(),
4365 "Shouldn't propagate scroll in paginated contexts");
4367 // If the frame is a block-level frame and is scrollable, then wrap it in a
4368 // scroll frame. Except we don't want to do that for paginated contexts for
4369 // frames that are block-outside and aren't frames for native anonymous stuff.
4370 // The condition on skipping scrollframe construction in the
4371 // paginated case needs to match code in ConstructNonScrollableBlock
4372 // and in nsFrame::ApplyPaginatedOverflowClipping.
4373 // XXX Ignore tables for the time being
4374 // XXXbz it would be nice to combine this with the other block
4375 // case... Think about how do do this?
4376 if (aDisplay->IsBlockInside() &&
4377 aDisplay->IsScrollableOverflow() &&
4378 !propagatedScrollToViewport &&
4379 (!mPresShell->GetPresContext()->IsPaginated() ||
4380 !aDisplay->IsBlockOutside() ||
4381 aContent->IsInNativeAnonymousSubtree())) {
4382 static const FrameConstructionData sScrollableBlockData =
4383 FULL_CTOR_FCDATA(0, &nsCSSFrameConstructor::ConstructScrollableBlock);
4384 return &sScrollableBlockData;
4387 // Handle various non-scrollable blocks
4388 if (aDisplay->IsBlockInside() ||
4389 NS_STYLE_DISPLAY_RUN_IN == aDisplay->mDisplay ||
4390 NS_STYLE_DISPLAY_COMPACT == aDisplay->mDisplay) {
4391 static const FrameConstructionData sNonScrollableBlockData =
4392 FULL_CTOR_FCDATA(0, &nsCSSFrameConstructor::ConstructNonScrollableBlock);
4393 return &sNonScrollableBlockData;
4396 static const FrameConstructionDataByInt sDisplayData[] = {
4397 // To keep the hash table small don't add inline frames (they're
4398 // typically things like FONT and B), because we can quickly
4399 // find them if we need to.
4400 // XXXbz the "quickly" part is a bald-faced lie!
4401 { NS_STYLE_DISPLAY_INLINE,
4402 FULL_CTOR_FCDATA(FCDATA_IS_INLINE | FCDATA_IS_LINE_PARTICIPANT,
4403 &nsCSSFrameConstructor::ConstructInline) },
4404 { NS_STYLE_DISPLAY_MARKER,
4405 FULL_CTOR_FCDATA(FCDATA_IS_INLINE | FCDATA_IS_LINE_PARTICIPANT,
4406 &nsCSSFrameConstructor::ConstructInline) },
4407 { NS_STYLE_DISPLAY_TABLE,
4408 FULL_CTOR_FCDATA(0, &nsCSSFrameConstructor::ConstructTable) },
4409 { NS_STYLE_DISPLAY_INLINE_TABLE,
4410 FULL_CTOR_FCDATA(0, &nsCSSFrameConstructor::ConstructTable) },
4411 { NS_STYLE_DISPLAY_TABLE_CAPTION,
4412 FCDATA_DECL(FCDATA_IS_TABLE_PART | FCDATA_ALLOW_BLOCK_STYLES |
4413 FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_SKIP_ABSPOS_PUSH |
4414 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
4415 NS_NewTableCaptionFrame) },
4416 { NS_STYLE_DISPLAY_TABLE_ROW_GROUP,
4417 FCDATA_DECL(FCDATA_IS_TABLE_PART | FCDATA_DISALLOW_OUT_OF_FLOW |
4418 FCDATA_SKIP_ABSPOS_PUSH |
4419 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
4420 NS_NewTableRowGroupFrame) },
4421 { NS_STYLE_DISPLAY_TABLE_HEADER_GROUP,
4422 FCDATA_DECL(FCDATA_IS_TABLE_PART | FCDATA_DISALLOW_OUT_OF_FLOW |
4423 FCDATA_SKIP_ABSPOS_PUSH |
4424 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
4425 NS_NewTableRowGroupFrame) },
4426 { NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP,
4427 FCDATA_DECL(FCDATA_IS_TABLE_PART | FCDATA_DISALLOW_OUT_OF_FLOW |
4428 FCDATA_SKIP_ABSPOS_PUSH |
4429 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
4430 NS_NewTableRowGroupFrame) },
4431 { NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP,
4432 FCDATA_DECL(FCDATA_IS_TABLE_PART | FCDATA_DISALLOW_OUT_OF_FLOW |
4433 FCDATA_SKIP_ABSPOS_PUSH |
4434 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
4435 NS_NewTableColGroupFrame) },
4436 { NS_STYLE_DISPLAY_TABLE_COLUMN,
4437 FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART |
4438 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeColGroup),
4439 &nsCSSFrameConstructor::ConstructTableCol) },
4440 { NS_STYLE_DISPLAY_TABLE_ROW,
4441 FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART |
4442 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRowGroup),
4443 &nsCSSFrameConstructor::ConstructTableRow) },
4444 { NS_STYLE_DISPLAY_TABLE_CELL,
4445 FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART |
4446 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRow),
4447 &nsCSSFrameConstructor::ConstructTableCell) }
4450 return FindDataByInt(aDisplay->mDisplay, aContent, aStyleContext,
4451 sDisplayData, NS_ARRAY_LENGTH(sDisplayData));
4454 nsresult
4455 nsCSSFrameConstructor::ConstructScrollableBlock(nsFrameConstructorState& aState,
4456 FrameConstructionItem& aItem,
4457 nsIFrame* aParentFrame,
4458 const nsStyleDisplay* aDisplay,
4459 nsFrameItems& aFrameItems,
4460 nsIFrame** aNewFrame)
4462 nsIContent* const content = aItem.mContent;
4463 nsStyleContext* const styleContext = aItem.mStyleContext;
4465 *aNewFrame = nsnull;
4466 nsRefPtr<nsStyleContext> scrolledContentStyle
4467 = BeginBuildingScrollFrame(aState, content, styleContext,
4468 aState.GetGeometricParent(aDisplay, aParentFrame),
4469 nsCSSAnonBoxes::scrolledContent,
4470 PR_FALSE, *aNewFrame);
4472 // Create our block frame
4473 // pass a temporary stylecontext, the correct one will be set later
4474 nsIFrame* scrolledFrame =
4475 NS_NewBlockFormattingContext(mPresShell, styleContext);
4477 nsFrameItems blockItem;
4478 nsresult rv = ConstructBlock(aState,
4479 scrolledContentStyle->GetStyleDisplay(), content,
4480 *aNewFrame, *aNewFrame, scrolledContentStyle,
4481 &scrolledFrame, blockItem, aDisplay->IsPositioned(),
4482 aItem.mPendingBinding);
4483 if (NS_UNLIKELY(NS_FAILED(rv))) {
4484 // XXXbz any cleanup needed here?
4485 return rv;
4488 NS_ASSERTION(blockItem.FirstChild() == scrolledFrame,
4489 "Scrollframe's frameItems should be exactly the scrolled frame");
4490 FinishBuildingScrollFrame(*aNewFrame, scrolledFrame);
4492 rv = aState.AddChild(*aNewFrame, aFrameItems, content, styleContext,
4493 aParentFrame);
4494 return rv;
4497 nsresult
4498 nsCSSFrameConstructor::ConstructNonScrollableBlock(nsFrameConstructorState& aState,
4499 FrameConstructionItem& aItem,
4500 nsIFrame* aParentFrame,
4501 const nsStyleDisplay* aDisplay,
4502 nsFrameItems& aFrameItems,
4503 nsIFrame** aNewFrame)
4505 nsStyleContext* const styleContext = aItem.mStyleContext;
4507 if (aDisplay->IsAbsolutelyPositioned() ||
4508 aDisplay->IsFloating() ||
4509 NS_STYLE_DISPLAY_INLINE_BLOCK == aDisplay->mDisplay ||
4510 // This check just needs to be the same as the check for using scrollable
4511 // blocks in FindDisplayData and the check for clipping in
4512 // nsFrame::ApplyPaginatedOverflowClipping; we want a block formatting
4513 // context root in paginated contexts for every block that would be
4514 // scrollable in a non-paginated context. Note that IsPaginated()
4515 // implies that no propagation to viewport has taken place, so we don't
4516 // need to check for propagation here.
4517 (mPresShell->GetPresContext()->IsPaginated() &&
4518 aDisplay->IsBlockInside() &&
4519 aDisplay->IsScrollableOverflow() &&
4520 aDisplay->IsBlockOutside() &&
4521 !aItem.mContent->IsInNativeAnonymousSubtree())) {
4522 *aNewFrame = NS_NewBlockFormattingContext(mPresShell, styleContext);
4523 } else {
4524 *aNewFrame = NS_NewBlockFrame(mPresShell, styleContext);
4527 return ConstructBlock(aState, aDisplay, aItem.mContent,
4528 aState.GetGeometricParent(aDisplay, aParentFrame),
4529 aParentFrame, styleContext, aNewFrame,
4530 aFrameItems, aDisplay->IsPositioned(),
4531 aItem.mPendingBinding);
4535 nsresult
4536 nsCSSFrameConstructor::InitAndRestoreFrame(const nsFrameConstructorState& aState,
4537 nsIContent* aContent,
4538 nsIFrame* aParentFrame,
4539 nsIFrame* aPrevInFlow,
4540 nsIFrame* aNewFrame,
4541 PRBool aAllowCounters)
4543 NS_PRECONDITION(mUpdateCount != 0,
4544 "Should be in an update while creating frames");
4546 nsresult rv = NS_OK;
4548 NS_ASSERTION(aNewFrame, "Null frame cannot be initialized");
4549 if (!aNewFrame)
4550 return NS_ERROR_NULL_POINTER;
4552 // Initialize the frame
4553 rv = aNewFrame->Init(aContent, aParentFrame, aPrevInFlow);
4554 aNewFrame->AddStateBits(aState.mAdditionalStateBits);
4556 if (aState.mFrameState && aState.mFrameManager) {
4557 // Restore frame state for just the newly created frame.
4558 aState.mFrameManager->RestoreFrameStateFor(aNewFrame, aState.mFrameState);
4561 if (aAllowCounters && !aPrevInFlow &&
4562 mCounterManager.AddCounterResetsAndIncrements(aNewFrame)) {
4563 CountersDirty();
4566 return rv;
4569 already_AddRefed<nsStyleContext>
4570 nsCSSFrameConstructor::ResolveStyleContext(nsIFrame* aParentFrame,
4571 nsIContent* aContent)
4573 nsStyleContext* parentStyleContext = nsnull;
4574 NS_ASSERTION(aContent->GetParent(), "Must have parent here");
4576 aParentFrame = nsFrame::CorrectStyleParentFrame(aParentFrame, nsnull);
4578 if (aParentFrame) {
4579 // Resolve the style context based on the content object and the parent
4580 // style context
4581 parentStyleContext = aParentFrame->GetStyleContext();
4582 } else {
4583 // Perhaps aParentFrame is a canvasFrame and we're replicating
4584 // fixed-pos frames.
4585 // XXX should we create a way to tell ConstructFrame which style
4586 // context to use, and pass it the style context for the
4587 // previous page's fixed-pos frame?
4590 return ResolveStyleContext(parentStyleContext, aContent);
4593 already_AddRefed<nsStyleContext>
4594 nsCSSFrameConstructor::ResolveStyleContext(nsStyleContext* aParentStyleContext,
4595 nsIContent* aContent)
4597 nsStyleSet *styleSet = mPresShell->StyleSet();
4599 if (aContent->IsElement()) {
4600 return styleSet->ResolveStyleFor(aContent->AsElement(), aParentStyleContext);
4603 NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
4604 "shouldn't waste time creating style contexts for "
4605 "comments and processing instructions");
4607 return styleSet->ResolveStyleForNonElement(aParentStyleContext);
4610 // MathML Mod - RBS
4611 #ifdef MOZ_MATHML
4612 nsresult
4613 nsCSSFrameConstructor::FlushAccumulatedBlock(nsFrameConstructorState& aState,
4614 nsIContent* aContent,
4615 nsIFrame* aParentFrame,
4616 nsFrameItems* aBlockItems,
4617 nsFrameItems* aNewItems)
4619 if (aBlockItems->IsEmpty()) {
4620 // Nothing to do
4621 return NS_OK;
4624 nsStyleContext* parentContext =
4625 nsFrame::CorrectStyleParentFrame(aParentFrame,
4626 nsCSSAnonBoxes::mozMathMLAnonymousBlock)->GetStyleContext();
4627 nsStyleSet *styleSet = mPresShell->StyleSet();
4628 nsRefPtr<nsStyleContext> blockContext;
4629 blockContext = styleSet->
4630 ResolveAnonymousBoxStyle(nsCSSAnonBoxes::mozMathMLAnonymousBlock,
4631 parentContext);
4633 // then, create a block frame that will wrap the child frames. Make it a
4634 // MathML frame so that Get(Absolute/Float)ContainingBlockFor know that this
4635 // is not a suitable block.
4636 nsIFrame* blockFrame = NS_NewMathMLmathBlockFrame(mPresShell, blockContext,
4637 NS_BLOCK_FLOAT_MGR | NS_BLOCK_MARGIN_ROOT);
4638 if (NS_UNLIKELY(!blockFrame))
4639 return NS_ERROR_OUT_OF_MEMORY;
4641 InitAndRestoreFrame(aState, aContent, aParentFrame, nsnull, blockFrame);
4642 ReparentFrames(aState.mFrameManager, blockFrame, *aBlockItems);
4643 // abs-pos and floats are disabled in MathML children so we don't have to
4644 // worry about messing up those.
4645 blockFrame->SetInitialChildList(nsnull, *aBlockItems);
4646 NS_ASSERTION(aBlockItems->IsEmpty(), "What happened?");
4647 aBlockItems->Clear();
4648 aNewItems->AddChild(blockFrame);
4649 return NS_OK;
4652 // Only <math> elements can be floated or positioned. All other MathML
4653 // should be in-flow.
4654 #define SIMPLE_MATHML_CREATE(_tag, _func) \
4655 { &nsGkAtoms::_tag, \
4656 FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW | \
4657 FCDATA_FORCE_NULL_ABSPOS_CONTAINER | \
4658 FCDATA_WRAP_KIDS_IN_BLOCKS, _func) }
4660 /* static */
4661 const nsCSSFrameConstructor::FrameConstructionData*
4662 nsCSSFrameConstructor::FindMathMLData(nsIContent* aContent,
4663 nsIAtom* aTag,
4664 PRInt32 aNameSpaceID,
4665 nsStyleContext* aStyleContext)
4667 // Make sure that we remain confined in the MathML world
4668 if (aNameSpaceID != kNameSpaceID_MathML)
4669 return nsnull;
4671 // Handle <math> specially, because it sometimes produces inlines
4672 if (aTag == nsGkAtoms::math) {
4673 // This needs to match the test in EnsureBlockDisplay in
4674 // nsRuleNode.cpp. Though the behavior here for the display:table
4675 // case is pretty weird...
4676 if (aStyleContext->GetStyleDisplay()->IsBlockOutside()) {
4677 static const FrameConstructionData sBlockMathData =
4678 FCDATA_DECL(FCDATA_FORCE_NULL_ABSPOS_CONTAINER |
4679 FCDATA_WRAP_KIDS_IN_BLOCKS,
4680 NS_CreateNewMathMLmathBlockFrame);
4681 return &sBlockMathData;
4684 static const FrameConstructionData sInlineMathData =
4685 FCDATA_DECL(FCDATA_FORCE_NULL_ABSPOS_CONTAINER |
4686 FCDATA_IS_LINE_PARTICIPANT |
4687 FCDATA_WRAP_KIDS_IN_BLOCKS,
4688 NS_NewMathMLmathInlineFrame);
4689 return &sInlineMathData;
4693 static const FrameConstructionDataByTag sMathMLData[] = {
4694 SIMPLE_MATHML_CREATE(mi_, NS_NewMathMLTokenFrame),
4695 SIMPLE_MATHML_CREATE(mn_, NS_NewMathMLTokenFrame),
4696 SIMPLE_MATHML_CREATE(ms_, NS_NewMathMLTokenFrame),
4697 SIMPLE_MATHML_CREATE(mtext_, NS_NewMathMLTokenFrame),
4698 SIMPLE_MATHML_CREATE(mo_, NS_NewMathMLmoFrame),
4699 SIMPLE_MATHML_CREATE(mfrac_, NS_NewMathMLmfracFrame),
4700 SIMPLE_MATHML_CREATE(msup_, NS_NewMathMLmsupFrame),
4701 SIMPLE_MATHML_CREATE(msub_, NS_NewMathMLmsubFrame),
4702 SIMPLE_MATHML_CREATE(msubsup_, NS_NewMathMLmsubsupFrame),
4703 SIMPLE_MATHML_CREATE(munder_, NS_NewMathMLmunderFrame),
4704 SIMPLE_MATHML_CREATE(mover_, NS_NewMathMLmoverFrame),
4705 SIMPLE_MATHML_CREATE(munderover_, NS_NewMathMLmunderoverFrame),
4706 SIMPLE_MATHML_CREATE(mphantom_, NS_NewMathMLmphantomFrame),
4707 SIMPLE_MATHML_CREATE(mpadded_, NS_NewMathMLmpaddedFrame),
4708 SIMPLE_MATHML_CREATE(mspace_, NS_NewMathMLmspaceFrame),
4709 SIMPLE_MATHML_CREATE(none, NS_NewMathMLmspaceFrame),
4710 SIMPLE_MATHML_CREATE(mprescripts_, NS_NewMathMLmspaceFrame),
4711 SIMPLE_MATHML_CREATE(mfenced_, NS_NewMathMLmfencedFrame),
4712 SIMPLE_MATHML_CREATE(mmultiscripts_, NS_NewMathMLmmultiscriptsFrame),
4713 SIMPLE_MATHML_CREATE(mstyle_, NS_NewMathMLmstyleFrame),
4714 SIMPLE_MATHML_CREATE(msqrt_, NS_NewMathMLmsqrtFrame),
4715 SIMPLE_MATHML_CREATE(mroot_, NS_NewMathMLmrootFrame),
4716 SIMPLE_MATHML_CREATE(maction_, NS_NewMathMLmactionFrame),
4717 SIMPLE_MATHML_CREATE(mrow_, NS_NewMathMLmrowFrame),
4718 SIMPLE_MATHML_CREATE(merror_, NS_NewMathMLmrowFrame),
4719 SIMPLE_MATHML_CREATE(menclose_, NS_NewMathMLmencloseFrame),
4720 SIMPLE_MATHML_CREATE(semantics_, NS_NewMathMLsemanticsFrame)
4723 return FindDataByTag(aTag, aContent, aStyleContext, sMathMLData,
4724 NS_ARRAY_LENGTH(sMathMLData));
4726 #endif // MOZ_MATHML
4728 #ifdef MOZ_SVG
4729 // Only outer <svg> elements can be floated or positioned. All other SVG
4730 // should be in-flow.
4731 #define SIMPLE_SVG_FCDATA(_func) \
4732 FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW | \
4733 FCDATA_SKIP_ABSPOS_PUSH | \
4734 FCDATA_DISALLOW_GENERATED_CONTENT, _func)
4735 #define SIMPLE_SVG_CREATE(_tag, _func) \
4736 { &nsGkAtoms::_tag, SIMPLE_SVG_FCDATA(_func) }
4738 /* static */
4739 const nsCSSFrameConstructor::FrameConstructionData*
4740 nsCSSFrameConstructor::FindSVGData(nsIContent* aContent,
4741 nsIAtom* aTag,
4742 PRInt32 aNameSpaceID,
4743 nsIFrame* aParentFrame,
4744 nsStyleContext* aStyleContext)
4746 if (aNameSpaceID != kNameSpaceID_SVG || !NS_SVGEnabled()) {
4747 return nsnull;
4750 static const FrameConstructionData sSuppressData = SUPPRESS_FCDATA();
4751 static const FrameConstructionData sGenericContainerData =
4752 SIMPLE_SVG_FCDATA(NS_NewSVGGenericContainerFrame);
4754 PRBool parentIsSVG = PR_FALSE;
4755 nsIContent* parentContent =
4756 aParentFrame ? aParentFrame->GetContent() : nsnull;
4757 // XXXbz should this really be based on the XBL-resolved tag of the parent
4758 // frame's content? Should it not be based on the type of the parent frame
4759 // (e.g. whether it's an SVG frame)?
4760 if (parentContent) {
4761 PRInt32 parentNSID;
4762 nsIAtom* parentTag =
4763 parentContent->GetOwnerDoc()->BindingManager()->
4764 ResolveTag(aParentFrame->GetContent(), &parentNSID);
4766 // It's not clear whether the SVG spec intends to allow any SVG
4767 // content within svg:foreignObject at all (SVG 1.1, section
4768 // 23.2), but if it does, it better be svg:svg. So given that
4769 // we're allowing it, treat it as a non-SVG parent.
4770 parentIsSVG = parentNSID == kNameSpaceID_SVG &&
4771 parentTag != nsGkAtoms::foreignObject;
4774 if ((aTag != nsGkAtoms::svg && !parentIsSVG) ||
4775 (aTag == nsGkAtoms::desc || aTag == nsGkAtoms::title)) {
4776 // Sections 5.1 and G.4 of SVG 1.1 say that SVG elements other than
4777 // svg:svg not contained within svg:svg are incorrect, although they
4778 // don't seem to specify error handling. Ignore them, since many of
4779 // our frame classes can't deal. It *may* be that the document
4780 // should at that point be considered in error according to F.2, but
4781 // it's hard to tell.
4783 // Style mutation can't change this situation, so don't bother
4784 // adding to the undisplayed content map.
4786 // We don't currently handle any UI for desc/title
4787 return &sSuppressData;
4790 // We don't need frames for animation elements
4791 if (aContent->IsNodeOfType(nsINode::eANIMATION)) {
4792 return &sSuppressData;
4795 // Reduce the number of frames we create unnecessarily. Note that this is not
4796 // where we select which frame in a <switch> to render! That happens in
4797 // nsSVGSwitchFrame::PaintSVG.
4798 if (!nsSVGFeatures::PassesConditionalProcessingTests(aContent)) {
4799 // Note that just returning is probably not right. According
4800 // to the spec, <use> is allowed to use an element that fails its
4801 // conditional, but because we never actually create the frame when
4802 // a conditional fails and when we use GetReferencedFrame to find the
4803 // references, things don't work right.
4804 // XXX FIXME XXX
4805 return &sSuppressData;
4808 // Special case for aTag == nsGkAtoms::svg because we don't want to
4809 // have to recompute parentIsSVG for it.
4810 if (aTag == nsGkAtoms::svg) {
4811 if (parentIsSVG) {
4812 static const FrameConstructionData sInnerSVGData =
4813 SIMPLE_SVG_FCDATA(NS_NewSVGInnerSVGFrame);
4814 return &sInnerSVGData;
4817 static const FrameConstructionData sOuterSVGData =
4818 FCDATA_DECL(FCDATA_FORCE_VIEW | FCDATA_SKIP_ABSPOS_PUSH |
4819 FCDATA_DISALLOW_GENERATED_CONTENT,
4820 NS_NewSVGOuterSVGFrame);
4821 return &sOuterSVGData;
4824 // Special cases for text/tspan/textPath, because the kind of frame
4825 // they get depends on the parent frame. We ignore 'a' elements when
4826 // determining the parent, however.
4827 nsIFrame *ancestorFrame =
4828 nsSVGUtils::GetFirstNonAAncestorFrame(aParentFrame);
4829 if (ancestorFrame) {
4830 if (aTag == nsGkAtoms::tspan || aTag == nsGkAtoms::altGlyph) {
4831 // tspan and altGlyph must be children of another text content element.
4832 nsSVGTextContainerFrame* metrics = do_QueryFrame(ancestorFrame);
4833 if (!metrics) {
4834 return &sSuppressData;
4836 } else if (aTag == nsGkAtoms::textPath) {
4837 // textPath must be a child of text.
4838 nsIAtom* ancestorFrameType = ancestorFrame->GetType();
4839 if (ancestorFrameType != nsGkAtoms::svgTextFrame) {
4840 return &sSuppressData;
4842 } else if (aTag != nsGkAtoms::a) {
4843 // Every other element except 'a' must not be a child of a text content
4844 // element.
4845 nsSVGTextContainerFrame* metrics = do_QueryFrame(ancestorFrame);
4846 if (metrics) {
4847 return &sSuppressData;
4852 static const FrameConstructionDataByTag sSVGData[] = {
4853 SIMPLE_SVG_CREATE(g, NS_NewSVGGFrame),
4854 SIMPLE_SVG_CREATE(svgSwitch, NS_NewSVGSwitchFrame),
4855 SIMPLE_SVG_CREATE(polygon, NS_NewSVGPathGeometryFrame),
4856 SIMPLE_SVG_CREATE(polyline, NS_NewSVGPathGeometryFrame),
4857 SIMPLE_SVG_CREATE(circle, NS_NewSVGPathGeometryFrame),
4858 SIMPLE_SVG_CREATE(ellipse, NS_NewSVGPathGeometryFrame),
4859 SIMPLE_SVG_CREATE(line, NS_NewSVGPathGeometryFrame),
4860 SIMPLE_SVG_CREATE(rect, NS_NewSVGPathGeometryFrame),
4861 SIMPLE_SVG_CREATE(path, NS_NewSVGPathGeometryFrame),
4862 SIMPLE_SVG_CREATE(defs, NS_NewSVGContainerFrame),
4863 { &nsGkAtoms::foreignObject,
4864 FULL_CTOR_FCDATA(FCDATA_DISALLOW_OUT_OF_FLOW,
4865 &nsCSSFrameConstructor::ConstructSVGForeignObjectFrame) },
4866 SIMPLE_SVG_CREATE(a, NS_NewSVGAFrame),
4867 SIMPLE_SVG_CREATE(altGlyph, NS_NewSVGTSpanFrame),
4868 SIMPLE_SVG_CREATE(text, NS_NewSVGTextFrame),
4869 SIMPLE_SVG_CREATE(tspan, NS_NewSVGTSpanFrame),
4870 SIMPLE_SVG_CREATE(linearGradient, NS_NewSVGLinearGradientFrame),
4871 SIMPLE_SVG_CREATE(radialGradient, NS_NewSVGRadialGradientFrame),
4872 SIMPLE_SVG_CREATE(stop, NS_NewSVGStopFrame),
4873 SIMPLE_SVG_CREATE(use, NS_NewSVGUseFrame),
4874 SIMPLE_SVG_CREATE(marker, NS_NewSVGMarkerFrame),
4875 SIMPLE_SVG_CREATE(image, NS_NewSVGImageFrame),
4876 SIMPLE_SVG_CREATE(clipPath, NS_NewSVGClipPathFrame),
4877 SIMPLE_SVG_CREATE(textPath, NS_NewSVGTextPathFrame),
4878 SIMPLE_SVG_CREATE(filter, NS_NewSVGFilterFrame),
4879 SIMPLE_SVG_CREATE(pattern, NS_NewSVGPatternFrame),
4880 SIMPLE_SVG_CREATE(mask, NS_NewSVGMaskFrame),
4881 SIMPLE_SVG_CREATE(feDistantLight, NS_NewSVGLeafFrame),
4882 SIMPLE_SVG_CREATE(fePointLight, NS_NewSVGLeafFrame),
4883 SIMPLE_SVG_CREATE(feSpotLight, NS_NewSVGLeafFrame),
4884 SIMPLE_SVG_CREATE(feBlend, NS_NewSVGLeafFrame),
4885 SIMPLE_SVG_CREATE(feColorMatrix, NS_NewSVGLeafFrame),
4886 SIMPLE_SVG_CREATE(feFuncR, NS_NewSVGLeafFrame),
4887 SIMPLE_SVG_CREATE(feFuncG, NS_NewSVGLeafFrame),
4888 SIMPLE_SVG_CREATE(feFuncB, NS_NewSVGLeafFrame),
4889 SIMPLE_SVG_CREATE(feFuncA, NS_NewSVGLeafFrame),
4890 SIMPLE_SVG_CREATE(feComposite, NS_NewSVGLeafFrame),
4891 SIMPLE_SVG_CREATE(feComponentTransfer, NS_NewSVGLeafFrame),
4892 SIMPLE_SVG_CREATE(feConvolveMatrix, NS_NewSVGLeafFrame),
4893 SIMPLE_SVG_CREATE(feDiffuseLighting, NS_NewSVGLeafFrame),
4894 SIMPLE_SVG_CREATE(feDisplacementMap, NS_NewSVGLeafFrame),
4895 SIMPLE_SVG_CREATE(feFlood, NS_NewSVGLeafFrame),
4896 SIMPLE_SVG_CREATE(feGaussianBlur, NS_NewSVGLeafFrame),
4897 SIMPLE_SVG_CREATE(feImage, NS_NewSVGLeafFrame),
4898 SIMPLE_SVG_CREATE(feMergeNode, NS_NewSVGLeafFrame),
4899 SIMPLE_SVG_CREATE(feMorphology, NS_NewSVGLeafFrame),
4900 SIMPLE_SVG_CREATE(feOffset, NS_NewSVGLeafFrame),
4901 SIMPLE_SVG_CREATE(feSpecularLighting, NS_NewSVGLeafFrame),
4902 SIMPLE_SVG_CREATE(feTile, NS_NewSVGLeafFrame),
4903 SIMPLE_SVG_CREATE(feTurbulence, NS_NewSVGLeafFrame)
4906 const FrameConstructionData* data =
4907 FindDataByTag(aTag, aContent, aStyleContext, sSVGData,
4908 NS_ARRAY_LENGTH(sSVGData));
4910 if (!data) {
4911 data = &sSuppressData;
4914 return data;
4917 nsresult
4918 nsCSSFrameConstructor::ConstructSVGForeignObjectFrame(nsFrameConstructorState& aState,
4919 FrameConstructionItem& aItem,
4920 nsIFrame* aParentFrame,
4921 const nsStyleDisplay* aStyleDisplay,
4922 nsFrameItems& aFrameItems,
4923 nsIFrame** aNewFrame)
4925 nsIContent* const content = aItem.mContent;
4926 nsStyleContext* const styleContext = aItem.mStyleContext;
4928 nsIFrame* newFrame = NS_NewSVGForeignObjectFrame(mPresShell, styleContext);
4929 if (NS_UNLIKELY(!newFrame)) {
4930 return NS_ERROR_OUT_OF_MEMORY;
4933 // We don't allow this frame to be out of flow
4934 InitAndRestoreFrame(aState, content, aParentFrame, nsnull, newFrame);
4935 nsHTMLContainerFrame::CreateViewForFrame(newFrame, PR_FALSE);
4937 nsresult rv = aState.AddChild(newFrame, aFrameItems, content, styleContext,
4938 aParentFrame, PR_FALSE, PR_FALSE);
4939 if (NS_FAILED(rv)) {
4940 return rv;
4943 nsRefPtr<nsStyleContext> innerPseudoStyle;
4944 innerPseudoStyle = mPresShell->StyleSet()->
4945 ResolveAnonymousBoxStyle(nsCSSAnonBoxes::mozSVGForeignContent, styleContext);
4947 nsIFrame* blockFrame = NS_NewBlockFrame(mPresShell, innerPseudoStyle,
4948 NS_BLOCK_FLOAT_MGR |
4949 NS_BLOCK_MARGIN_ROOT);
4950 if (NS_UNLIKELY(!blockFrame)) {
4951 newFrame->Destroy();
4952 return NS_ERROR_OUT_OF_MEMORY;
4955 nsFrameItems childItems;
4956 // Claim to be relatively positioned so that we end up being the
4957 // absolute containing block.
4958 rv = ConstructBlock(aState, innerPseudoStyle->GetStyleDisplay(), content,
4959 newFrame, newFrame, innerPseudoStyle,
4960 &blockFrame, childItems, PR_TRUE,
4961 aItem.mPendingBinding);
4963 // Give the blockFrame a view so that GetOffsetTo works for descendants
4964 // of blockFrame with views...
4965 nsHTMLContainerFrame::CreateViewForFrame(blockFrame, PR_TRUE);
4967 newFrame->SetInitialChildList(nsnull, childItems);
4969 *aNewFrame = newFrame;
4971 return rv;
4974 #endif // MOZ_SVG
4976 void
4977 nsCSSFrameConstructor::AddPageBreakItem(nsIContent* aContent,
4978 nsStyleContext* aMainStyleContext,
4979 FrameConstructionItemList& aItems)
4981 nsRefPtr<nsStyleContext> pseudoStyle;
4982 // Use the same parent style context that |aMainStyleContext| has, since
4983 // that's easier to re-resolve and it doesn't matter in practice.
4984 // (Getting different parents can result in framechange hints, e.g.,
4985 // for user-modify.)
4986 pseudoStyle =
4987 mPresShell->StyleSet()->
4988 ResolveAnonymousBoxStyle(nsCSSAnonBoxes::pageBreak,
4989 aMainStyleContext->GetParent());
4991 NS_ASSERTION(pseudoStyle->GetStyleDisplay()->mDisplay ==
4992 NS_STYLE_DISPLAY_BLOCK, "Unexpected display");
4994 static const FrameConstructionData sPageBreakData =
4995 FCDATA_DECL(FCDATA_SKIP_FRAMESET, NS_NewPageBreakFrame);
4997 // Lie about the tag and namespace so we don't trigger anything
4998 // interesting during frame construction.
4999 aItems.AppendItem(&sPageBreakData, aContent, nsCSSAnonBoxes::pageBreak,
5000 kNameSpaceID_None, nsnull, pseudoStyle.forget(), PR_TRUE);
5003 nsresult
5004 nsCSSFrameConstructor::ConstructFrame(nsFrameConstructorState& aState,
5005 nsIContent* aContent,
5006 nsIFrame* aParentFrame,
5007 nsFrameItems& aFrameItems)
5010 NS_PRECONDITION(nsnull != aParentFrame, "no parent frame");
5011 FrameConstructionItemList items;
5012 AddFrameConstructionItems(aState, aContent, PR_TRUE, aParentFrame, items);
5014 for (FCItemIterator iter(items); !iter.IsDone(); iter.Next()) {
5015 NS_ASSERTION(iter.item().DesiredParentType() == GetParentType(aParentFrame),
5016 "This is not going to work");
5017 nsresult rv =
5018 ConstructFramesFromItem(aState, iter, aParentFrame, aFrameItems);
5019 NS_ENSURE_SUCCESS(rv, rv);
5022 return NS_OK;
5025 void
5026 nsCSSFrameConstructor::AddFrameConstructionItems(nsFrameConstructorState& aState,
5027 nsIContent* aContent,
5028 PRBool aSuppressWhiteSpaceOptimizations,
5029 nsIFrame* aParentFrame,
5030 FrameConstructionItemList& aItems)
5032 aContent->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
5033 if (aContent->IsElement()) {
5034 // We can't just remove our pending restyle flags, since we may
5035 // have restyle-later-siblings set on us. But we _can_ remove the
5036 // "is possible restyle root" flags, and need to. Otherwise we can
5037 // end up with stale such flags (e.g. if we used to have a
5038 // display:none parent when our last restyle was posted and
5039 // processed and now no longer do).
5040 aContent->UnsetFlags(ELEMENT_ALL_RESTYLE_FLAGS &
5041 ~ELEMENT_PENDING_RESTYLE_FLAGS);
5044 // don't create a whitespace frame if aParent doesn't want it
5045 if (!NeedFrameFor(aState, aParentFrame, aContent)) {
5046 return;
5049 // never create frames for comments or PIs
5050 if (aContent->IsNodeOfType(nsINode::eCOMMENT) ||
5051 aContent->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION))
5052 return;
5054 nsRefPtr<nsStyleContext> styleContext;
5055 styleContext = ResolveStyleContext(aParentFrame, aContent);
5057 AddFrameConstructionItemsInternal(aState, aContent, aParentFrame,
5058 aContent->Tag(), aContent->GetNameSpaceID(),
5059 aSuppressWhiteSpaceOptimizations,
5060 styleContext,
5061 ITEM_ALLOW_XBL_BASE | ITEM_ALLOW_PAGE_BREAK,
5062 aItems);
5066 * Set aContent as undisplayed content with style context aStyleContext. This
5067 * method enforces the invariant that all style contexts in the undisplayed
5068 * content map must be non-pseudo contexts and also handles unbinding
5069 * undisplayed generated content as needed.
5071 static void
5072 SetAsUndisplayedContent(nsFrameManager* aFrameManager, nsIContent* aContent,
5073 nsStyleContext* aStyleContext,
5074 PRBool aIsGeneratedContent)
5076 if (aStyleContext->GetPseudo()) {
5077 if (aIsGeneratedContent) {
5078 aContent->UnbindFromTree();
5080 return;
5083 NS_ASSERTION(!aIsGeneratedContent, "Should have had pseudo type");
5084 aFrameManager->SetUndisplayedContent(aContent, aStyleContext);
5087 void
5088 nsCSSFrameConstructor::AddFrameConstructionItemsInternal(nsFrameConstructorState& aState,
5089 nsIContent* aContent,
5090 nsIFrame* aParentFrame,
5091 nsIAtom* aTag,
5092 PRInt32 aNameSpaceID,
5093 PRBool aSuppressWhiteSpaceOptimizations,
5094 nsStyleContext* aStyleContext,
5095 PRUint32 aFlags,
5096 FrameConstructionItemList& aItems)
5098 // The following code allows the user to specify the base tag
5099 // of an element using XBL. XUL and HTML objects (like boxes, menus, etc.)
5100 // can then be extended arbitrarily.
5101 const nsStyleDisplay* display = aStyleContext->GetStyleDisplay();
5102 nsRefPtr<nsStyleContext> styleContext(aStyleContext);
5103 PendingBinding* pendingBinding = nsnull;
5104 if ((aFlags & ITEM_ALLOW_XBL_BASE) && display->mBinding)
5106 // Ensure that our XBL bindings are installed.
5108 nsIXBLService * xblService = GetXBLService();
5109 if (!xblService)
5110 return;
5112 PRBool resolveStyle;
5114 nsAutoPtr<PendingBinding> newPendingBinding(new PendingBinding());
5115 if (!newPendingBinding) {
5116 return;
5118 nsresult rv = xblService->LoadBindings(aContent, display->mBinding->mURI,
5119 display->mBinding->mOriginPrincipal,
5120 PR_FALSE,
5121 getter_AddRefs(newPendingBinding->mBinding),
5122 &resolveStyle);
5123 if (NS_FAILED(rv) && rv != NS_ERROR_XBL_BLOCKED)
5124 return;
5126 if (newPendingBinding->mBinding) {
5127 pendingBinding = newPendingBinding;
5128 // aState takes over owning newPendingBinding
5129 aState.AddPendingBinding(newPendingBinding.forget());
5132 if (resolveStyle) {
5133 styleContext = ResolveStyleContext(styleContext->GetParent(), aContent);
5134 display = styleContext->GetStyleDisplay();
5135 aStyleContext = styleContext;
5138 aTag = mDocument->BindingManager()->ResolveTag(aContent, &aNameSpaceID);
5141 PRBool isGeneratedContent = ((aFlags & ITEM_IS_GENERATED_CONTENT) != 0);
5143 // Pre-check for display "none" - if we find that, don't create
5144 // any frame at all
5145 if (NS_STYLE_DISPLAY_NONE == display->mDisplay) {
5146 SetAsUndisplayedContent(aState.mFrameManager, aContent, styleContext,
5147 isGeneratedContent);
5148 return;
5151 PRBool isText = aContent->IsNodeOfType(nsINode::eTEXT);
5152 PRBool isPopup = PR_FALSE;
5153 // Try to find frame construction data for this content
5154 const FrameConstructionData* data;
5155 if (isText) {
5156 data = FindTextData(aParentFrame);
5157 #ifdef MOZ_SVG
5158 if (!data) {
5159 // Nothing to do here; suppressed text inside SVG
5160 return;
5162 #endif /* MOZ_SVG */
5163 } else {
5164 #ifdef MOZ_SVG
5165 // Don't create frames for non-SVG element children of SVG elements.
5166 if (aNameSpaceID != kNameSpaceID_SVG &&
5167 aParentFrame &&
5168 aParentFrame->IsFrameOfType(nsIFrame::eSVG) &&
5169 !aParentFrame->IsFrameOfType(nsIFrame::eSVGForeignObject)
5171 SetAsUndisplayedContent(aState.mFrameManager, aContent, styleContext,
5172 isGeneratedContent);
5173 return;
5175 #endif /* MOZ_SVG */
5177 data = FindHTMLData(aContent, aTag, aNameSpaceID, aParentFrame,
5178 styleContext);
5179 if (!data) {
5180 data = FindXULTagData(aContent, aTag, aNameSpaceID, styleContext);
5182 #ifdef MOZ_MATHML
5183 if (!data) {
5184 data = FindMathMLData(aContent, aTag, aNameSpaceID, styleContext);
5186 #endif
5187 #ifdef MOZ_SVG
5188 if (!data) {
5189 data = FindSVGData(aContent, aTag, aNameSpaceID, aParentFrame,
5190 styleContext);
5192 #endif /* MOZ_SVG */
5194 // Now check for XUL display types
5195 if (!data) {
5196 data = FindXULDisplayData(display, aContent, styleContext);
5199 // And general display types
5200 if (!data) {
5201 data = FindDisplayData(display, aContent, styleContext);
5204 NS_ASSERTION(data, "Should have frame construction data now");
5206 if (data->mBits & FCDATA_SUPPRESS_FRAME) {
5207 SetAsUndisplayedContent(aState.mFrameManager, aContent, styleContext,
5208 isGeneratedContent);
5209 return;
5212 #ifdef MOZ_XUL
5213 if ((data->mBits & FCDATA_IS_POPUP) &&
5214 (!aParentFrame || // Parent is inline
5215 aParentFrame->GetType() != nsGkAtoms::menuFrame)) {
5216 if (!aState.mPopupItems.containingBlock &&
5217 !aState.mHavePendingPopupgroup) {
5218 SetAsUndisplayedContent(aState.mFrameManager, aContent, styleContext,
5219 isGeneratedContent);
5220 return;
5223 isPopup = PR_TRUE;
5225 #endif /* MOZ_XUL */
5228 PRUint32 bits = data->mBits;
5230 // Inside colgroups, suppress everything except columns.
5231 if (aParentFrame &&
5232 aParentFrame->GetType() == nsGkAtoms::tableColGroupFrame &&
5233 (!(bits & FCDATA_IS_TABLE_PART) ||
5234 display->mDisplay != NS_STYLE_DISPLAY_TABLE_COLUMN)) {
5235 SetAsUndisplayedContent(aState.mFrameManager, aContent, styleContext,
5236 isGeneratedContent);
5237 return;
5240 PRBool canHavePageBreak =
5241 (aFlags & ITEM_ALLOW_PAGE_BREAK) &&
5242 aState.mPresContext->IsPaginated() &&
5243 !display->IsAbsolutelyPositioned() &&
5244 !(bits & FCDATA_IS_TABLE_PART);
5246 if (canHavePageBreak && display->mBreakBefore) {
5247 AddPageBreakItem(aContent, aStyleContext, aItems);
5250 FrameConstructionItem* item =
5251 aItems.AppendItem(data, aContent, aTag, aNameSpaceID,
5252 pendingBinding, styleContext.forget(),
5253 aSuppressWhiteSpaceOptimizations);
5254 if (!item) {
5255 if (isGeneratedContent) {
5256 aContent->UnbindFromTree();
5258 return;
5261 item->mIsText = isText;
5262 item->mIsGeneratedContent = isGeneratedContent;
5263 if (isGeneratedContent) {
5264 NS_ADDREF(item->mContent);
5266 item->mIsRootPopupgroup =
5267 aNameSpaceID == kNameSpaceID_XUL && aTag == nsGkAtoms::popupgroup &&
5268 aContent->IsRootOfNativeAnonymousSubtree();
5269 if (item->mIsRootPopupgroup) {
5270 aState.mHavePendingPopupgroup = PR_TRUE;
5272 item->mIsPopup = isPopup;
5274 if (canHavePageBreak && display->mBreakAfter) {
5275 AddPageBreakItem(aContent, aStyleContext, aItems);
5278 if (bits & FCDATA_IS_INLINE) {
5279 // To correctly set item->mIsAllInline we need to build up our child items
5280 // right now.
5281 BuildInlineChildItems(aState, *item);
5282 item->mHasInlineEnds = PR_TRUE;
5283 item->mIsBlock = PR_FALSE;
5284 } else {
5285 // Compute a boolean isInline which is guaranteed to be false for blocks
5286 // (but may also be false for some inlines).
5287 PRBool isInline =
5288 // Table-internal things are inline-outside if and only if they're kids of
5289 // inlines, since they'll trigger construction of inline-table
5290 // pseudos.
5291 ((bits & FCDATA_IS_TABLE_PART) &&
5292 (!aParentFrame || // No aParentFrame means inline
5293 aParentFrame->GetStyleDisplay()->mDisplay == NS_STYLE_DISPLAY_INLINE)) ||
5294 // Things that are inline-outside but aren't inline frames are inline
5295 display->IsInlineOutside() ||
5296 // Popups that are certainly out of flow.
5297 isPopup;
5299 // Set mIsAllInline conservatively. It just might be that even an inline
5300 // that has mIsAllInline false doesn't need an {ib} split. So this is just
5301 // an optimization to keep from doing too much work in cases when we can
5302 // show that mIsAllInline is true..
5303 item->mIsAllInline = item->mHasInlineEnds = isInline ||
5304 // Figure out whether we're guaranteed this item will be out of flow.
5305 // This is not a precise test, since one of our ancestor inlines might add
5306 // an absolute containing block (if it's relatively positioned) when there
5307 // wasn't such a containing block before. But it's conservative in the
5308 // sense that anything that will really end up as an in-flow non-inline
5309 // will test false here. In other words, if this test is true we're
5310 // guaranteed to be inline; if it's false we don't know what we'll end up
5311 // as.
5313 // If we make this test precise, we can remove some of the code dealing
5314 // with the imprecision in ConstructInline and adjust the comments on
5315 // mIsAllInline and mIsBlock in the header. And probably remove mIsBlock
5316 // altogether, since then it will always be equal to !mHasInlineEnds.
5317 (!(bits & FCDATA_DISALLOW_OUT_OF_FLOW) &&
5318 aState.GetGeometricParent(display, nsnull));
5320 // Set mIsBlock conservatively. It's OK to set it false for some real
5321 // blocks, but not OK to set it true for things that aren't blocks. Since
5322 // isOutOfFlow might be false even in cases when the frame will end up
5323 // out-of-flow, we can't use it here. But we _can_ say that the frame will
5324 // for sure end up in-flow if it's not floated or absolutely positioned.
5325 item->mIsBlock =
5326 !isInline && !display->IsAbsolutelyPositioned() && !display->IsFloating();
5329 if (item->mIsAllInline) {
5330 aItems.InlineItemAdded();
5331 } else if (item->mIsBlock) {
5332 aItems.BlockItemAdded();
5335 // Our item should be treated as a line participant if we have the relevant
5336 // bit and are going to be in-flow. Note that this really only matters if
5337 // our ancestor is a box or some such, so the fact that we might have an
5338 // inline ancestor that might become a containing block is not relevant here.
5339 if ((bits & FCDATA_IS_LINE_PARTICIPANT) &&
5340 ((bits & FCDATA_DISALLOW_OUT_OF_FLOW) ||
5341 !aState.GetGeometricParent(display, nsnull))) {
5342 item->mIsLineParticipant = PR_TRUE;
5343 aItems.LineParticipantItemAdded();
5347 static void
5348 DestroyContent(void* aPropertyValue)
5350 nsIContent* content = static_cast<nsIContent*>(aPropertyValue);
5351 content->UnbindFromTree();
5352 NS_RELEASE(content);
5355 NS_DECLARE_FRAME_PROPERTY(BeforeProperty, DestroyContent)
5356 NS_DECLARE_FRAME_PROPERTY(AfterProperty, DestroyContent)
5358 static const FramePropertyDescriptor*
5359 GenConPseudoToProperty(nsIAtom* aPseudo)
5361 NS_ASSERTION(aPseudo == nsCSSPseudoElements::before ||
5362 aPseudo == nsCSSPseudoElements::after,
5363 "Bad gen-con pseudo");
5364 return aPseudo == nsCSSPseudoElements::before ? BeforeProperty()
5365 : AfterProperty();
5369 * Return true if the frame construction item pointed to by aIter will
5370 * create a frame adjacent to a line boundary in the frame tree, and that
5371 * line boundary is induced by a content node adjacent to the frame's
5372 * content node in the content tree. The latter condition is necessary so
5373 * that ContentAppended/ContentInserted/ContentRemoved can easily find any
5374 * text nodes that were suppressed here.
5376 PRBool
5377 nsCSSFrameConstructor::AtLineBoundary(FCItemIterator& aIter)
5379 if (aIter.item().mSuppressWhiteSpaceOptimizations) {
5380 return PR_FALSE;
5383 if (aIter.AtStart()) {
5384 if (aIter.List()->HasLineBoundaryAtStart() &&
5385 !aIter.item().mContent->GetPreviousSibling())
5386 return PR_TRUE;
5387 } else {
5388 FCItemIterator prev = aIter;
5389 prev.Prev();
5390 if (prev.item().IsLineBoundary() &&
5391 !prev.item().mSuppressWhiteSpaceOptimizations &&
5392 aIter.item().mContent->GetPreviousSibling() == prev.item().mContent)
5393 return PR_TRUE;
5396 FCItemIterator next = aIter;
5397 next.Next();
5398 if (next.IsDone()) {
5399 if (aIter.List()->HasLineBoundaryAtEnd() &&
5400 !aIter.item().mContent->GetNextSibling())
5401 return PR_TRUE;
5402 } else {
5403 if (next.item().IsLineBoundary() &&
5404 !next.item().mSuppressWhiteSpaceOptimizations &&
5405 aIter.item().mContent->GetNextSibling() == next.item().mContent)
5406 return PR_TRUE;
5409 return PR_FALSE;
5412 nsresult
5413 nsCSSFrameConstructor::ConstructFramesFromItem(nsFrameConstructorState& aState,
5414 FCItemIterator& aIter,
5415 nsIFrame* aParentFrame,
5416 nsFrameItems& aFrameItems)
5418 nsIFrame* adjParentFrame = aParentFrame;
5419 FrameConstructionItem& item = aIter.item();
5420 nsStyleContext* styleContext = item.mStyleContext;
5421 AdjustParentFrame(adjParentFrame, item.mFCData, styleContext);
5423 if (item.mIsText) {
5424 // If this is collapsible whitespace next to a line boundary,
5425 // don't create a frame. item.IsWhitespace() also sets the
5426 // NS_CREATE_FRAME_IF_NON_WHITESPACE flag in the text node. (If we
5427 // end up creating a frame, nsTextFrame::Init will clear the flag.)
5428 // We don't do this for generated content, because some generated
5429 // text content is empty text nodes that are about to be initialized.
5430 // (We check mAdditionalStateBits because only the generated content
5431 // container's frame construction item is marked with
5432 // mIsGeneratedContent, and we might not have an aParentFrame.)
5433 // We don't do it for content that may have XBL anonymous siblings,
5434 // because they make it difficult to correctly create the frame
5435 // due to dynamic changes.
5436 // We don't do it for text that's not a line participant (i.e. SVG text).
5437 if (AtLineBoundary(aIter) &&
5438 !styleContext->GetStyleText()->NewlineIsSignificant() &&
5439 aIter.List()->ParentHasNoXBLChildren() &&
5440 !(aState.mAdditionalStateBits & NS_FRAME_GENERATED_CONTENT) &&
5441 (item.mFCData->mBits & FCDATA_IS_LINE_PARTICIPANT) &&
5442 item.IsWhitespace(aState))
5443 return NS_OK;
5445 return ConstructTextFrame(item.mFCData, aState, item.mContent,
5446 adjParentFrame, styleContext,
5447 aFrameItems);
5450 // Start background loads during frame construction. This is just
5451 // a hint; the paint code will do the right thing in any case.
5453 styleContext->GetStyleBackground();
5456 nsFrameState savedStateBits = aState.mAdditionalStateBits;
5457 if (item.mIsGeneratedContent) {
5458 // Ensure that frames created here are all tagged with
5459 // NS_FRAME_GENERATED_CONTENT.
5460 aState.mAdditionalStateBits |= NS_FRAME_GENERATED_CONTENT;
5462 // Note that we're not necessarily setting this property on the primary
5463 // frame for the content for which this is generated content. We might be
5464 // setting it on a table pseudo-frame inserted under that instead. That's
5465 // OK, though; we just need to do the property set so that the content will
5466 // get cleaned up when the frame is destroyed.
5467 aParentFrame->Properties().Set(GenConPseudoToProperty(styleContext->GetPseudo()),
5468 item.mContent);
5470 // Now that we've passed ownership of item.mContent to the frame, unset
5471 // our generated content flag so we don't release or unbind it ourselves.
5472 item.mIsGeneratedContent = PR_FALSE;
5475 // XXXbz maybe just inline ConstructFrameFromItemInternal here or something?
5476 nsresult rv = ConstructFrameFromItemInternal(item, aState, adjParentFrame,
5477 aFrameItems);
5479 aState.mAdditionalStateBits = savedStateBits;
5481 return rv;
5485 inline PRBool
5486 IsRootBoxFrame(nsIFrame *aFrame)
5488 return (aFrame->GetType() == nsGkAtoms::rootFrame);
5491 nsresult
5492 nsCSSFrameConstructor::ReconstructDocElementHierarchy()
5494 return RecreateFramesForContent(mPresShell->GetDocument()->GetRootElement(),
5495 PR_FALSE);
5498 nsIFrame*
5499 nsCSSFrameConstructor::GetFrameFor(nsIContent* aContent)
5501 // Get the primary frame associated with the content
5502 nsIFrame* frame = aContent->GetPrimaryFrame();
5504 if (!frame)
5505 return nsnull;
5507 // If the content of the frame is not the desired content then this is not
5508 // really a frame for the desired content.
5509 // XXX This check is needed due to bug 135040. Remove it once that's fixed.
5510 if (frame->GetContent() != aContent) {
5511 return nsnull;
5514 nsIFrame* insertionFrame = frame->GetContentInsertionFrame();
5516 NS_ASSERTION(insertionFrame == frame || !frame->IsLeaf(),
5517 "The insertion frame is the primary frame or the primary frame isn't a leaf");
5519 return insertionFrame;
5522 nsIFrame*
5523 nsCSSFrameConstructor::GetAbsoluteContainingBlock(nsIFrame* aFrame)
5525 NS_PRECONDITION(nsnull != mRootElementFrame, "no root element frame");
5527 // Starting with aFrame, look for a frame that is absolutely positioned or
5528 // relatively positioned
5529 nsIFrame* containingBlock = nsnull;
5530 for (nsIFrame* frame = aFrame; frame && !containingBlock;
5531 frame = frame->GetParent()) {
5532 if (frame->IsFrameOfType(nsIFrame::eMathML)) {
5533 // If it's mathml, bail out -- no absolute positioning out from inside
5534 // mathml frames. Note that we don't make this part of the loop
5535 // condition because of the stuff at the end of this method...
5536 return nsnull;
5539 // Is it positioned?
5540 // If it's table-related then ignore it, because for the time
5541 // being table-related frames are not containers for absolutely
5542 // positioned child frames.
5543 const nsStyleDisplay* disp = frame->GetStyleDisplay();
5545 if (disp->IsPositioned() && !IsTableRelated(frame->GetType())) {
5546 // Find the outermost wrapped block under this frame
5547 for (nsIFrame* wrappedFrame = aFrame; wrappedFrame != frame->GetParent();
5548 wrappedFrame = wrappedFrame->GetParent()) {
5549 nsIAtom* frameType = wrappedFrame->GetType();
5550 if (nsGkAtoms::blockFrame == frameType ||
5551 #ifdef MOZ_XUL
5552 nsGkAtoms::XULLabelFrame == frameType ||
5553 #endif
5554 nsGkAtoms::positionedInlineFrame == frameType) {
5555 containingBlock = wrappedFrame;
5556 } else if (nsGkAtoms::fieldSetFrame == frameType) {
5557 // If the positioned frame is a fieldset, use the area frame inside it.
5558 // We don't use GetContentInsertionFrame for fieldsets yet.
5559 containingBlock = GetFieldSetBlockFrame(wrappedFrame);
5563 // We sometimes have a null containing block here because we
5564 // haven't yet fixed bug 455338. Once we fix that we shouldn't
5565 // have to loop here.
5569 // If we found an absolutely positioned containing block, then use the
5570 // first-continuation.
5571 if (containingBlock)
5572 return AdjustAbsoluteContainingBlock(containingBlock);
5574 // If we didn't find it, then use the document element containing block
5575 return mHasRootAbsPosContainingBlock ? mDocElementContainingBlock : nsnull;
5578 nsIFrame*
5579 nsCSSFrameConstructor::GetFloatContainingBlock(nsIFrame* aFrame)
5581 // Starting with aFrame, look for a frame that is a float containing block.
5582 // IF we hit a mathml frame, bail out; we don't allow floating out of mathml
5583 // frames, because they don't seem to be able to deal.
5584 // The logic here needs to match the logic in ProcessChildren()
5585 for (nsIFrame* containingBlock = aFrame;
5586 containingBlock && !containingBlock->IsFrameOfType(nsIFrame::eMathML) &&
5587 !containingBlock->IsBoxFrame();
5588 containingBlock = containingBlock->GetParent()) {
5589 if (containingBlock->IsFloatContainingBlock()) {
5590 return containingBlock;
5594 // If we didn't find a containing block, then there just isn't
5595 // one.... return null
5596 return nsnull;
5600 * This function will check whether aContainer has :after generated content.
5601 * If so, appending to it should actually insert. The return value is the
5602 * parent to use for newly-appended content. *aAfterFrame points to the :after
5603 * frame before which appended content should go, if there is one.
5605 static nsIFrame*
5606 AdjustAppendParentForAfterContent(nsPresContext* aPresContext,
5607 nsIContent* aContainer,
5608 nsIFrame* aParentFrame,
5609 nsIFrame** aAfterFrame)
5611 // See if the parent has an :after pseudo-element. Check for the presence
5612 // of style first, since nsLayoutUtils::GetAfterFrame is sorta expensive.
5613 nsStyleContext* parentStyle = aParentFrame->GetStyleContext();
5614 if (nsLayoutUtils::HasPseudoStyle(aContainer, parentStyle,
5615 nsCSSPseudoElements::ePseudo_after,
5616 aPresContext)) {
5617 nsIFrame* afterFrame = nsLayoutUtils::GetAfterFrame(aParentFrame);
5618 if (afterFrame) {
5619 *aAfterFrame = afterFrame;
5620 return afterFrame->GetParent();
5624 *aAfterFrame = nsnull;
5626 if (IsFrameSpecial(aParentFrame)) {
5627 // We might be in a situation where the last part of the {ib} split was
5628 // empty. Since we have no ::after pseudo-element, we do in fact want to be
5629 // appending to that last part, so advance to it if needed. Note that here
5630 // aParentFrame is the result of a GetLastSpecialSibling call, so must be
5631 // either the last or next to last special sibling.
5632 nsIFrame* trailingInline = GetSpecialSibling(aParentFrame);
5633 if (trailingInline) {
5634 aParentFrame = trailingInline;
5637 // Always make sure to look at the last continuation of the frame
5638 // for the {ib} case, even if that continuation is empty. We
5639 // don't do this for the non-special-frame case, since in the
5640 // other cases appending to the last nonempty continuation is fine
5641 // and in fact not doing that can confuse code that doesn't know
5642 // to pull kids from continuations other than its next one.
5643 aParentFrame = aParentFrame->GetLastContinuation();
5646 return aParentFrame;
5650 * This function will get the previous sibling to use for an append operation.
5651 * it takes a parent frame (must not be null) and its :after frame (may be
5652 * null).
5654 static nsIFrame*
5655 FindAppendPrevSibling(nsIFrame* aParentFrame, nsIFrame* aAfterFrame)
5657 if (aAfterFrame) {
5658 NS_ASSERTION(aAfterFrame->GetParent() == aParentFrame, "Wrong parent");
5659 return aAfterFrame->GetPrevSibling();
5662 return aParentFrame->GetLastChild(nsnull);
5666 * This function will get the next sibling for a frame insert operation given
5667 * the parent and previous sibling. aPrevSibling may be null.
5669 static nsIFrame*
5670 GetInsertNextSibling(nsIFrame* aParentFrame, nsIFrame* aPrevSibling)
5672 if (aPrevSibling) {
5673 return aPrevSibling->GetNextSibling();
5676 return aParentFrame->GetFirstChild(nsnull);
5680 * This function is called by ContentAppended() and ContentInserted() when
5681 * appending flowed frames to a parent's principal child list. It handles the
5682 * case where the parent is the trailing inline of an {ib} split.
5684 nsresult
5685 nsCSSFrameConstructor::AppendFrames(nsFrameConstructorState& aState,
5686 nsIFrame* aParentFrame,
5687 nsFrameItems& aFrameList,
5688 nsIFrame* aPrevSibling,
5689 PRBool aIsRecursiveCall)
5691 NS_PRECONDITION(!IsFrameSpecial(aParentFrame) ||
5692 !GetSpecialSibling(aParentFrame) ||
5693 !GetSpecialSibling(aParentFrame)->GetFirstChild(nsnull),
5694 "aParentFrame has a special sibling with kids?");
5695 NS_PRECONDITION(!aPrevSibling || aPrevSibling->GetParent() == aParentFrame,
5696 "Parent and prevsibling don't match");
5698 nsIFrame* nextSibling = ::GetInsertNextSibling(aParentFrame, aPrevSibling);
5700 NS_ASSERTION(nextSibling ||
5701 !aParentFrame->GetNextContinuation() ||
5702 !aParentFrame->GetNextContinuation()->GetFirstChild(nsnull) ||
5703 aIsRecursiveCall,
5704 "aParentFrame has later continuations with kids?");
5705 NS_ASSERTION(nextSibling ||
5706 !IsFrameSpecial(aParentFrame) ||
5707 (IsInlineFrame(aParentFrame) &&
5708 !GetSpecialSibling(aParentFrame) &&
5709 !aParentFrame->GetNextContinuation()) ||
5710 aIsRecursiveCall,
5711 "aParentFrame is not last?");
5713 // If we're inserting a list of frames at the end of the trailing inline
5714 // of an {ib} split, we may need to create additional {ib} siblings to parent
5715 // them.
5716 if (!nextSibling && IsFrameSpecial(aParentFrame)) {
5717 // When we get here, our frame list might start with a block. If it does
5718 // so, and aParentFrame is an inline, and it and all its previous
5719 // continuations have no siblings, then put the initial blocks from the
5720 // frame list into the previous block of the {ib} split. Note that we
5721 // didn't want to stop at the block part of the split when figuring out
5722 // initial parent, because that could screw up float parenting; it's easier
5723 // to do this little fixup here instead.
5724 if (aFrameList.NotEmpty() && !IsInlineOutside(aFrameList.FirstChild())) {
5725 // See whether our trailing inline is empty
5726 nsIFrame* firstContinuation = aParentFrame->GetFirstContinuation();
5727 if (firstContinuation->GetChildList(nsnull).IsEmpty()) {
5728 // Our trailing inline is empty. Collect our starting blocks from
5729 // aFrameList, get the right parent frame for them, and put them in.
5730 nsFrameList::FrameLinkEnumerator firstNonBlockEnumerator =
5731 FindFirstNonBlock(aFrameList);
5732 nsFrameList blockKids = aFrameList.ExtractHead(firstNonBlockEnumerator);
5733 NS_ASSERTION(blockKids.NotEmpty(), "No blocks?");
5735 nsIFrame* prevBlock =
5736 GetSpecialPrevSibling(firstContinuation)->GetLastContinuation();
5737 NS_ASSERTION(prevBlock, "Should have previous block here");
5739 MoveChildrenTo(aState.mPresContext, aParentFrame, prevBlock, blockKids);
5743 // We want to put some of the frames into this inline frame.
5744 nsFrameList::FrameLinkEnumerator firstBlockEnumerator(aFrameList);
5745 FindFirstBlock(firstBlockEnumerator);
5747 nsFrameList inlineKids = aFrameList.ExtractHead(firstBlockEnumerator);
5748 if (!inlineKids.IsEmpty()) {
5749 aState.mFrameManager->AppendFrames(aParentFrame, nsnull, inlineKids);
5752 if (!aFrameList.IsEmpty()) {
5753 const nsStyleDisplay* parentDisplay = aParentFrame->GetStyleDisplay();
5754 PRBool positioned =
5755 parentDisplay->mPosition == NS_STYLE_POSITION_RELATIVE ||
5756 parentDisplay->HasTransform();
5757 nsFrameItems ibSiblings;
5758 CreateIBSiblings(aState, aParentFrame, positioned, aFrameList,
5759 ibSiblings);
5761 // Make sure to trigger reflow of the inline that used to be our
5762 // last one and now isn't anymore, since its GetSkipSides() has
5763 // changed.
5764 mPresShell->FrameNeedsReflow(aParentFrame,
5765 nsIPresShell::eTreeChange,
5766 NS_FRAME_HAS_DIRTY_CHILDREN);
5768 // Recurse so we create new ib siblings as needed for aParentFrame's parent
5769 return AppendFrames(aState, aParentFrame->GetParent(), ibSiblings,
5770 aParentFrame, PR_TRUE);
5773 return NS_OK;
5776 // Insert the frames after our aPrevSibling
5777 return aState.mFrameManager->InsertFrames(aParentFrame, nsnull, aPrevSibling,
5778 aFrameList);
5781 #define UNSET_DISPLAY 255
5783 // This gets called to see if the frames corresponding to aSibling and aContent
5784 // should be siblings in the frame tree. Although (1) rows and cols, (2) row
5785 // groups and col groups, (3) row groups and captions, (4) legends and content
5786 // inside fieldsets, (5) popups and other kids of the menu are siblings from a
5787 // content perspective, they are not considered siblings in the frame tree.
5788 PRBool
5789 nsCSSFrameConstructor::IsValidSibling(nsIFrame* aSibling,
5790 nsIContent* aContent,
5791 PRUint8& aDisplay)
5793 nsIFrame* parentFrame = aSibling->GetParent();
5794 nsIAtom* parentType = nsnull;
5795 nsIAtom* grandparentType = nsnull;
5796 if (parentFrame) {
5797 parentType = parentFrame->GetType();
5798 nsIFrame* grandparentFrame = parentFrame->GetParent();
5799 if (grandparentFrame) {
5800 grandparentType = grandparentFrame->GetType();
5804 PRUint8 siblingDisplay = aSibling->GetStyleDisplay()->mDisplay;
5805 if ((NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == siblingDisplay) ||
5806 (NS_STYLE_DISPLAY_TABLE_COLUMN == siblingDisplay) ||
5807 (NS_STYLE_DISPLAY_TABLE_CAPTION == siblingDisplay) ||
5808 (NS_STYLE_DISPLAY_TABLE_HEADER_GROUP == siblingDisplay) ||
5809 (NS_STYLE_DISPLAY_TABLE_ROW_GROUP == siblingDisplay) ||
5810 (NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP == siblingDisplay) ||
5811 nsGkAtoms::menuFrame == parentType) {
5812 // if we haven't already, construct a style context to find the display type of aContent
5813 if (UNSET_DISPLAY == aDisplay) {
5814 nsRefPtr<nsStyleContext> styleContext;
5815 nsIFrame* styleParent;
5816 PRBool providerIsChild;
5817 if (NS_FAILED(aSibling->
5818 GetParentStyleContextFrame(aSibling->PresContext(),
5819 &styleParent,
5820 &providerIsChild)) ||
5821 !styleParent) {
5822 NS_NOTREACHED("Shouldn't happen");
5823 return PR_FALSE;
5825 styleContext = ResolveStyleContext(styleParent, aContent);
5826 if (!styleContext) return PR_FALSE;
5827 const nsStyleDisplay* display = styleContext->GetStyleDisplay();
5828 aDisplay = display->mDisplay;
5830 if (nsGkAtoms::menuFrame == parentType) {
5831 return
5832 (NS_STYLE_DISPLAY_POPUP == aDisplay) ==
5833 (NS_STYLE_DISPLAY_POPUP == siblingDisplay);
5835 // To have decent performance we want to return false in cases in which
5836 // reordering the two siblings has no effect on display. To ensure
5837 // correctness, we MUST return false in cases where the two siblings have
5838 // the same desired parent type and live on different display lists.
5839 // Specificaly, columns and column groups should only consider columns and
5840 // column groups as valid siblings. Captions should only consider other
5841 // captions. All other things should consider each other as valid
5842 // siblings. The restriction in the |if| above on siblingDisplay is ok,
5843 // because for correctness the only part that really needs to happen is to
5844 // not consider captions, column groups, and row/header/footer groups
5845 // siblings of each other. Treating a column or colgroup as a valid
5846 // sibling of a non-table-related frame will just mean we end up reframing.
5847 if ((siblingDisplay == NS_STYLE_DISPLAY_TABLE_CAPTION) !=
5848 (aDisplay == NS_STYLE_DISPLAY_TABLE_CAPTION)) {
5849 // One's a caption and the other is not. Not valid siblings.
5850 return PR_FALSE;
5853 if ((siblingDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP ||
5854 siblingDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN) !=
5855 (aDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP ||
5856 aDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN)) {
5857 // One's a column or column group and the other is not. Not valid
5858 // siblings.
5859 return PR_FALSE;
5862 return PR_TRUE;
5864 else if (nsGkAtoms::fieldSetFrame == parentType ||
5865 (nsGkAtoms::fieldSetFrame == grandparentType &&
5866 nsGkAtoms::blockFrame == parentType)) {
5867 // Legends can be sibling of legends but not of other content in the fieldset
5868 nsIAtom* sibType = aSibling->GetType();
5869 nsCOMPtr<nsIDOMHTMLLegendElement> legendContent(do_QueryInterface(aContent));
5871 if ((legendContent && (nsGkAtoms::legendFrame != sibType)) ||
5872 (!legendContent && (nsGkAtoms::legendFrame == sibType)))
5873 return PR_FALSE;
5876 return PR_TRUE;
5879 nsIFrame*
5880 nsCSSFrameConstructor::FindFrameForContentSibling(nsIContent* aContent,
5881 nsIContent* aTargetContent,
5882 PRUint8& aTargetContentDisplay,
5883 PRBool aPrevSibling)
5885 nsIFrame* sibling = aContent->GetPrimaryFrame();
5886 if (!sibling || sibling->GetContent() != aContent) {
5887 // XXX the GetContent() != aContent check is needed due to bug 135040.
5888 // Remove it once that's fixed.
5889 return nsnull;
5892 // If the frame is out-of-flow, GetPrimaryFrame() will have returned the
5893 // out-of-flow frame; we want the placeholder.
5894 if (sibling->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
5895 nsIFrame* placeholderFrame = mPresShell->FrameManager()->GetPlaceholderFrameFor(sibling);
5896 NS_ASSERTION(placeholderFrame, "no placeholder for out-of-flow frame");
5897 sibling = placeholderFrame;
5900 // The frame we have now should never be a continuation
5901 NS_ASSERTION(!sibling->GetPrevContinuation(), "How did that happen?");
5903 if (aPrevSibling) {
5904 // The frame may be a special frame (a split inline frame that
5905 // contains a block). Get the last part of that split.
5906 if (IsFrameSpecial(sibling)) {
5907 sibling = GetLastSpecialSibling(sibling, PR_TRUE);
5910 // The frame may have a continuation. If so, we want the last
5911 // non-overflow-container continuation as our previous sibling.
5912 sibling = sibling->GetTailContinuation();
5915 if (aTargetContent &&
5916 !IsValidSibling(sibling, aTargetContent, aTargetContentDisplay)) {
5917 sibling = nsnull;
5920 return sibling;
5923 nsIFrame*
5924 nsCSSFrameConstructor::FindPreviousSibling(const ChildIterator& aFirst,
5925 ChildIterator aIter,
5926 PRUint8& aTargetContentDisplay)
5928 nsIContent* child = *aIter;
5930 // Note: not all content objects are associated with a frame (e.g., if it's
5931 // `display: none') so keep looking until we find a previous frame
5932 while (aIter != aFirst) {
5933 --aIter;
5934 nsIFrame* prevSibling =
5935 FindFrameForContentSibling(*aIter, child, aTargetContentDisplay, PR_TRUE);
5937 if (prevSibling) {
5938 // Found a previous sibling, we're done!
5939 return prevSibling;
5943 return nsnull;
5946 nsIFrame*
5947 nsCSSFrameConstructor::FindNextSibling(ChildIterator aIter,
5948 const ChildIterator& aLast,
5949 PRUint8& aTargetContentDisplay)
5951 if (aIter == aLast) {
5952 // XXXbz Can happen when XBL lies to us about insertion points. This check
5953 // might be able to go away once bug 474324 is fixed.
5954 return nsnull;
5957 nsIContent* child = *aIter;
5959 while (++aIter != aLast) {
5960 nsIFrame* nextSibling =
5961 FindFrameForContentSibling(*aIter, child, aTargetContentDisplay, PR_FALSE);
5963 if (nextSibling) {
5964 // We found a next sibling, we're done!
5965 return nextSibling;
5969 return nsnull;
5972 // For fieldsets, returns the area frame, if the child is not a legend.
5973 static nsIFrame*
5974 GetAdjustedParentFrame(nsIFrame* aParentFrame,
5975 nsIAtom* aParentFrameType,
5976 nsIContent* aChildContent)
5978 NS_PRECONDITION(nsGkAtoms::tableOuterFrame != aParentFrameType,
5979 "Shouldn't be happening!");
5981 nsIFrame* newParent = nsnull;
5983 if (nsGkAtoms::fieldSetFrame == aParentFrameType) {
5984 // If the parent is a fieldSet, use the fieldSet's area frame as the
5985 // parent unless the new content is a legend.
5986 nsCOMPtr<nsIDOMHTMLLegendElement> legendContent(do_QueryInterface(aChildContent));
5987 if (!legendContent) {
5988 newParent = GetFieldSetBlockFrame(aParentFrame);
5991 return (newParent) ? newParent : aParentFrame;
5994 nsIFrame*
5995 nsCSSFrameConstructor::GetInsertionPrevSibling(nsIFrame*& aParentFrame,
5996 nsIContent* aContainer,
5997 nsIContent* aChild,
5998 PRBool* aIsAppend,
5999 PRBool* aIsRangeInsertSafe,
6000 nsIContent* aStartSkipChild,
6001 nsIContent* aEndSkipChild)
6003 *aIsAppend = PR_FALSE;
6005 // Find the frame that precedes the insertion point. Walk backwards
6006 // from the parent frame to get the parent content, because if an
6007 // XBL insertion point is involved, we'll need to use _that_ to find
6008 // the preceding frame.
6010 NS_PRECONDITION(aParentFrame, "Must have parent frame to start with");
6011 nsIContent* container = aParentFrame->GetContent();
6013 ChildIterator first, last;
6014 ChildIterator::Init(container, &first, &last);
6015 ChildIterator iter(first);
6016 PRBool xblCase = iter.XBLInvolved() || container != aContainer;
6017 if (xblCase || !aChild->IsRootOfAnonymousSubtree()) {
6018 // The check for IsRootOfAnonymousSubtree() is because editor is
6019 // severely broken and calls us directly for native anonymous
6020 // nodes that it creates.
6021 if (aStartSkipChild) {
6022 iter.seek(aStartSkipChild);
6023 } else {
6024 iter.seek(aChild);
6027 #ifdef DEBUG
6028 else {
6029 NS_WARNING("Someone passed native anonymous content directly into frame "
6030 "construction. Stop doing that!");
6032 #endif
6034 PRUint8 childDisplay = UNSET_DISPLAY;
6035 nsIFrame* prevSibling = FindPreviousSibling(first, iter, childDisplay);
6037 // Now, find the geometric parent so that we can handle
6038 // continuations properly. Use the prev sibling if we have it;
6039 // otherwise use the next sibling.
6040 if (prevSibling) {
6041 aParentFrame = prevSibling->GetParent()->GetContentInsertionFrame();
6043 else {
6044 // If there is no previous sibling, then find the frame that follows
6045 if (aEndSkipChild) {
6046 iter.seek(aEndSkipChild);
6047 iter--;
6049 nsIFrame* nextSibling = FindNextSibling(iter, last, childDisplay);
6051 if (nextSibling) {
6052 aParentFrame = nextSibling->GetParent()->GetContentInsertionFrame();
6054 else {
6055 // No previous or next sibling, so treat this like an appended frame.
6056 *aIsAppend = PR_TRUE;
6057 if (IsFrameSpecial(aParentFrame)) {
6058 // Since we're appending, we'll walk to the last anonymous frame
6059 // that was created for the broken inline frame. But don't walk
6060 // to the trailing inline if it's empty; stop at the block.
6061 aParentFrame = GetLastSpecialSibling(aParentFrame, PR_FALSE);
6063 // Get continuation that parents the last child. This MUST be done
6064 // before the AdjustAppendParentForAfterContent call.
6065 aParentFrame = nsLayoutUtils::GetLastContinuationWithChild(aParentFrame);
6066 // Deal with fieldsets
6067 aParentFrame = ::GetAdjustedParentFrame(aParentFrame,
6068 aParentFrame->GetType(),
6069 aChild);
6070 nsIFrame* appendAfterFrame;
6071 aParentFrame =
6072 ::AdjustAppendParentForAfterContent(mPresShell->GetPresContext(),
6073 container, aParentFrame,
6074 &appendAfterFrame);
6075 prevSibling = ::FindAppendPrevSibling(aParentFrame, appendAfterFrame);
6079 *aIsRangeInsertSafe = (childDisplay == UNSET_DISPLAY);
6080 return prevSibling;
6083 static PRBool
6084 IsSpecialFramesetChild(nsIContent* aContent)
6086 // IMPORTANT: This must match the conditions in nsHTMLFramesetFrame::Init.
6087 return aContent->IsHTML() &&
6088 (aContent->Tag() == nsGkAtoms::frameset ||
6089 aContent->Tag() == nsGkAtoms::frame);
6092 static void
6093 InvalidateCanvasIfNeeded(nsIPresShell* presShell, nsIContent* node);
6095 #ifdef MOZ_XUL
6097 static
6098 PRBool
6099 IsXULListBox(nsIContent* aContainer)
6101 return (aContainer->IsXUL() && aContainer->Tag() == nsGkAtoms::listbox);
6104 static
6105 nsListBoxBodyFrame*
6106 MaybeGetListBoxBodyFrame(nsIContent* aContainer, nsIContent* aChild)
6108 if (!aContainer)
6109 return nsnull;
6111 if (IsXULListBox(aContainer) &&
6112 aChild->IsXUL() && aChild->Tag() == nsGkAtoms::listitem) {
6113 nsCOMPtr<nsIDOMXULElement> xulElement = do_QueryInterface(aContainer);
6114 nsCOMPtr<nsIBoxObject> boxObject;
6115 xulElement->GetBoxObject(getter_AddRefs(boxObject));
6116 nsCOMPtr<nsPIListBoxObject> listBoxObject = do_QueryInterface(boxObject);
6117 if (listBoxObject) {
6118 return listBoxObject->GetListBoxBody(PR_FALSE);
6122 return nsnull;
6124 #endif
6126 void
6127 nsCSSFrameConstructor::AddTextItemIfNeeded(nsFrameConstructorState& aState,
6128 nsIFrame* aParentFrame,
6129 nsIContent* aPossibleTextContent,
6130 FrameConstructionItemList& aItems)
6132 NS_PRECONDITION(aPossibleTextContent, "Must have node");
6133 if (!aPossibleTextContent->IsNodeOfType(nsINode::eTEXT) ||
6134 !aPossibleTextContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE)) {
6135 // Not text, or not suppressed due to being all-whitespace (if it
6136 // were being suppressed, it would have the
6137 // NS_CREATE_FRAME_IF_NON_WHITESPACE flag)
6138 return;
6140 NS_ASSERTION(!aPossibleTextContent->GetPrimaryFrame(),
6141 "Text node has a frame and NS_CREATE_FRAME_IF_NON_WHITESPACE");
6142 AddFrameConstructionItems(aState, aPossibleTextContent, PR_FALSE,
6143 aParentFrame, aItems);
6146 void
6147 nsCSSFrameConstructor::ReframeTextIfNeeded(nsIContent* aParentContent,
6148 nsIContent* aContent)
6150 if (!aContent->IsNodeOfType(nsINode::eTEXT) ||
6151 !aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE)) {
6152 // Not text, or not suppressed due to being all-whitespace (if it
6153 // were being suppressed, it would have the
6154 // NS_CREATE_FRAME_IF_NON_WHITESPACE flag)
6155 return;
6157 NS_ASSERTION(!aContent->GetPrimaryFrame(),
6158 "Text node has a frame and NS_CREATE_FRAME_IF_NON_WHITESPACE");
6159 ContentInserted(aParentContent, aContent, nsnull, PR_FALSE);
6162 // We want to disable lazy frame construction for nodes that are under an
6163 // editor. We use nsINode::IsEditable, but that includes inputs with type text
6164 // and password and textareas, which are common and aren't really editable (the
6165 // native anonymous content under them is what is actually editable) so we want
6166 // to construct frames for those lazily.
6167 // The logic for this check is based on
6168 // nsGenericHTMLFormElement::UpdateEditableFormControlState and so must be kept
6169 // in sync with that. The presence of the NODE_MAY_HAVE_CONTENT_EDITABLE_ATTR
6170 // flag only indicates a contenteditable attribute, it doesn't indicate if it
6171 // is true or false, so we force eager construction in some cases when the node
6172 // is not editable, but that should be rare.
6173 static inline PRBool
6174 IsActuallyEditable(nsIContent* aContainer, nsIContent* aChild)
6176 return (aChild->IsEditable() &&
6177 (aContainer->IsEditable() ||
6178 aChild->HasFlag(NODE_MAY_HAVE_CONTENT_EDITABLE_ATTR)));
6181 // For inserts aChild should be valid, for appends it should be null.
6182 // Returns true if this operation can be lazy, false if not.
6183 PRBool
6184 nsCSSFrameConstructor::MaybeConstructLazily(Operation aOperation,
6185 nsIContent* aContainer,
6186 nsIContent* aChild)
6188 if (mPresShell->GetPresContext()->IsChrome() || !aContainer ||
6189 aContainer->IsInNativeAnonymousSubtree() || aContainer->IsXUL()) {
6190 return PR_FALSE;
6193 if (aOperation == CONTENTINSERT) {
6194 if (aChild->IsRootOfAnonymousSubtree() ||
6195 aChild->IsXUL() || IsActuallyEditable(aContainer, aChild)) {
6196 return PR_FALSE;
6198 } else { // CONTENTAPPEND
6199 NS_ASSERTION(aOperation == CONTENTAPPEND,
6200 "operation should be either insert or append");
6201 for (nsIContent* child = aChild; child; child = child->GetNextSibling()) {
6202 NS_ASSERTION(!child->IsRootOfAnonymousSubtree(),
6203 "Should be coming through the CONTENTAPPEND case");
6204 if (child->IsXUL() || IsActuallyEditable(aContainer, child)) {
6205 return PR_FALSE;
6210 // We can construct lazily; just need to set suitable bits in the content
6211 // tree.
6213 // Walk up the tree setting the NODE_DESCENDANTS_NEED_FRAMES bit as we go.
6214 nsIContent* content = aContainer;
6215 #ifdef DEBUG
6216 // If we hit a node with no primary frame, or the NODE_NEEDS_FRAME bit set
6217 // we want to assert, but leaf frames that process their own children and may
6218 // ignore anonymous children (eg framesets) make this complicated. So we set
6219 // these two booleans if we encounter these situations and unset them if we
6220 // hit a node with a leaf frame.
6221 PRBool noPrimaryFrame = PR_FALSE;
6222 PRBool needsFrameBitSet = PR_FALSE;
6223 #endif
6224 while (content &&
6225 !content->HasFlag(NODE_DESCENDANTS_NEED_FRAMES)) {
6226 #ifdef DEBUG
6227 if (content->GetPrimaryFrame() && content->GetPrimaryFrame()->IsLeaf()) {
6228 noPrimaryFrame = needsFrameBitSet = PR_FALSE;
6230 if (!noPrimaryFrame && !content->GetPrimaryFrame()) {
6231 noPrimaryFrame = PR_TRUE;
6233 if (!needsFrameBitSet && content->HasFlag(NODE_NEEDS_FRAME)) {
6234 needsFrameBitSet = PR_TRUE;
6236 #endif
6237 content->SetFlags(NODE_DESCENDANTS_NEED_FRAMES);
6238 content = content->GetFlattenedTreeParent();
6240 #ifdef DEBUG
6241 if (content && content->GetPrimaryFrame() &&
6242 content->GetPrimaryFrame()->IsLeaf()) {
6243 noPrimaryFrame = needsFrameBitSet = PR_FALSE;
6245 NS_ASSERTION(!noPrimaryFrame, "Ancestors of nodes with frames to be "
6246 "constructed lazily should have frames");
6247 NS_ASSERTION(!needsFrameBitSet, "Ancestors of nodes with frames to be "
6248 "constructed lazily should not have NEEDS_FRAME bit set");
6249 #endif
6251 // Set NODE_NEEDS_FRAME on the new nodes.
6252 if (aOperation == CONTENTINSERT) {
6253 NS_ASSERTION(!aChild->GetPrimaryFrame() ||
6254 aChild->GetPrimaryFrame()->GetContent() != aChild,
6255 //XXX the aChild->GetPrimaryFrame()->GetContent() != aChild
6256 // check is needed due to bug 135040. Remove it once that's
6257 // fixed.
6258 "setting NEEDS_FRAME on a node that already has a frame?");
6259 aChild->SetFlags(NODE_NEEDS_FRAME);
6260 } else { // CONTENTAPPEND
6261 for (nsIContent* child = aChild; child; child = child->GetNextSibling()) {
6262 NS_ASSERTION(!child->GetPrimaryFrame() ||
6263 child->GetPrimaryFrame()->GetContent() != child,
6264 //XXX the child->GetPrimaryFrame()->GetContent() != child
6265 // check is needed due to bug 135040. Remove it once that's
6266 // fixed.
6267 "setting NEEDS_FRAME on a node that already has a frame?");
6268 child->SetFlags(NODE_NEEDS_FRAME);
6272 PostRestyleEventInternal(PR_TRUE);
6273 return PR_TRUE;
6276 void
6277 nsCSSFrameConstructor::CreateNeededFrames(nsIContent* aContent)
6279 NS_ASSERTION(!aContent->HasFlag(NODE_NEEDS_FRAME),
6280 "shouldn't get here with a content node that has needs frame bit set");
6281 NS_ASSERTION(aContent->HasFlag(NODE_DESCENDANTS_NEED_FRAMES),
6282 "should only get here with a content node that has descendants needing frames");
6284 aContent->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES);
6286 // We could either descend first (on nodes that don't have NODE_NEEDS_FRAME
6287 // set) or issue content notifications for our kids first. In absence of
6288 // anything definitive either way we'll go with the latter.
6290 // It might be better to use GetChildArray and scan it completely first and
6291 // then issue all notifications. (We have to scan it completely first because
6292 // constructing frames can set attributes, which can change the storage of
6293 // child lists).
6295 // Scan the children of aContent to see what operations (if any) we need to
6296 // perform.
6297 PRUint32 childCount = aContent->GetChildCount();
6298 PRBool inRun = PR_FALSE;
6299 nsIContent* firstChildInRun = nsnull;
6300 for (PRUint32 i = 0; i < childCount; i++) {
6301 nsIContent* child = aContent->GetChildAt(i);
6302 if (child->HasFlag(NODE_NEEDS_FRAME)) {
6303 NS_ASSERTION(!child->GetPrimaryFrame() ||
6304 child->GetPrimaryFrame()->GetContent() != child,
6305 //XXX the child->GetPrimaryFrame()->GetContent() != child
6306 // check is needed due to bug 135040. Remove it once that's
6307 // fixed.
6308 "NEEDS_FRAME set on a node that already has a frame?");
6309 if (!inRun) {
6310 inRun = PR_TRUE;
6311 firstChildInRun = child;
6313 } else {
6314 if (inRun) {
6315 inRun = PR_FALSE;
6316 // generate a ContentRangeInserted for [startOfRun,i)
6317 ContentRangeInserted(aContent, firstChildInRun, child, nsnull,
6318 PR_FALSE);
6322 if (inRun) {
6323 ContentAppended(aContent, firstChildInRun, PR_FALSE);
6326 // Now descend.
6327 ChildIterator iter, last;
6328 for (ChildIterator::Init(aContent, &iter, &last);
6329 iter != last;
6330 ++iter) {
6331 nsIContent* child = *iter;
6332 if (child->HasFlag(NODE_DESCENDANTS_NEED_FRAMES)) {
6333 CreateNeededFrames(child);
6338 void nsCSSFrameConstructor::CreateNeededFrames()
6340 NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
6341 "Someone forgot a script blocker");
6343 Element* rootElement = mDocument->GetRootElement();
6344 NS_ASSERTION(!rootElement || !rootElement->HasFlag(NODE_NEEDS_FRAME),
6345 "root element should not have frame created lazily");
6346 if (rootElement && rootElement->HasFlag(NODE_DESCENDANTS_NEED_FRAMES)) {
6347 BeginUpdate();
6348 CreateNeededFrames(rootElement);
6349 EndUpdate();
6353 void
6354 nsCSSFrameConstructor::IssueSingleInsertNofications(nsIContent* aContainer,
6355 nsIContent* aStartChild,
6356 nsIContent* aEndChild,
6357 PRBool aAllowLazyConstruction)
6359 for (nsIContent* child = aStartChild;
6360 child != aEndChild;
6361 child = child->GetNextSibling()) {
6362 if ((child->GetPrimaryFrame() ||
6363 mPresShell->FrameManager()->GetUndisplayedContent(child))
6364 #ifdef MOZ_XUL
6365 // Except listboxes suck, so do NOT skip anything here if
6366 // we plan to notify a listbox.
6367 && !MaybeGetListBoxBodyFrame(aContainer, child)
6368 #endif
6370 // Already have a frame or undisplayed entry for this content; a
6371 // previous ContentInserted in this loop must have reconstructed
6372 // its insertion parent. Skip it.
6373 continue;
6375 // Call ContentInserted with this node.
6376 ContentInserted(aContainer, child, mTempFrameTreeState,
6377 aAllowLazyConstruction);
6381 nsIFrame*
6382 nsCSSFrameConstructor::GetRangeInsertionPoint(nsIContent* aContainer,
6383 nsIFrame* aParentFrame,
6384 nsIContent* aStartChild,
6385 nsIContent* aEndChild,
6386 PRBool aAllowLazyConstruction)
6388 // See if we have an XBL insertion point. If so, then that's our
6389 // real parent frame; if not, then the frame hasn't been built yet
6390 // and we just bail.
6391 nsIFrame* insertionPoint;
6392 PRBool multiple = PR_FALSE;
6393 GetInsertionPoint(aParentFrame, nsnull, &insertionPoint, &multiple);
6394 if (! insertionPoint)
6395 return nsnull; // Don't build the frames.
6397 PRBool hasInsertion = PR_FALSE;
6398 if (!multiple) {
6399 // XXXbz XBL2/sXBL issue
6400 nsIDocument* document = aStartChild->GetDocument();
6401 // XXXbz how would |document| be null here?
6402 if (document &&
6403 document->BindingManager()->GetInsertionParent(aStartChild)) {
6404 hasInsertion = PR_TRUE;
6408 if (multiple || hasInsertion) {
6409 // We have an insertion point. There are some additional tests we need to do
6410 // in order to ensure that an append is a safe operation.
6411 PRUint32 childCount = 0;
6413 if (!multiple) {
6414 // We may need to make multiple ContentInserted calls instead. A
6415 // reasonable heuristic to employ (in order to maintain good performance)
6416 // is to find out if the insertion point's content node contains any
6417 // explicit children. If it does not, then it is highly likely that
6418 // an append is occurring. (Note it is not definite, and there are insane
6419 // cases we will not deal with by employing this heuristic, but it beats
6420 // always falling back to multiple ContentInserted calls).
6422 // In the multiple insertion point case, we know we're going to need to do
6423 // multiple ContentInserted calls anyway.
6424 childCount = insertionPoint->GetContent()->GetChildCount();
6427 // If we have multiple insertion points or if we have an insertion point
6428 // and the operation is not a true append or if the insertion point already
6429 // has explicit children, then we must fall back.
6430 if (multiple || aEndChild != nsnull || childCount > 0) {
6431 // Now comes the fun part. For each inserted child, make a
6432 // ContentInserted call as if it had just gotten inserted and
6433 // let ContentInserted handle the mess.
6434 IssueSingleInsertNofications(aContainer, aStartChild, aEndChild,
6435 aAllowLazyConstruction);
6436 return nsnull;
6440 return insertionPoint;
6443 PRBool
6444 nsCSSFrameConstructor::MaybeRecreateForFrameset(nsIFrame* aParentFrame,
6445 nsIContent* aStartChild,
6446 nsIContent* aEndChild)
6448 if (aParentFrame->GetType() == nsGkAtoms::frameSetFrame) {
6449 // Check whether we have any kids we care about.
6450 for (nsIContent* cur = aStartChild;
6451 cur != aEndChild;
6452 cur = cur->GetNextSibling()) {
6453 if (IsSpecialFramesetChild(cur)) {
6454 // Just reframe the parent, since framesets are weird like that.
6455 RecreateFramesForContent(aParentFrame->GetContent(), PR_FALSE);
6456 return PR_TRUE;
6460 return PR_FALSE;
6463 nsresult
6464 nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer,
6465 nsIContent* aFirstNewContent,
6466 PRBool aAllowLazyConstruction)
6468 AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
6469 NS_PRECONDITION(mUpdateCount != 0,
6470 "Should be in an update while creating frames");
6472 #ifdef DEBUG
6473 if (gNoisyContentUpdates) {
6474 printf("nsCSSFrameConstructor::ContentAppended container=%p "
6475 "first-child=%p lazy=%d\n",
6476 static_cast<void*>(aContainer), aFirstNewContent,
6477 aAllowLazyConstruction);
6478 if (gReallyNoisyContentUpdates && aContainer) {
6479 aContainer->List(stdout, 0);
6482 #endif
6484 #ifdef MOZ_XUL
6485 if (aContainer) {
6486 PRInt32 namespaceID;
6487 nsIAtom* tag =
6488 mDocument->BindingManager()->ResolveTag(aContainer, &namespaceID);
6490 // Just ignore tree tags, anyway we don't create any frames for them.
6491 if (tag == nsGkAtoms::treechildren ||
6492 tag == nsGkAtoms::treeitem ||
6493 tag == nsGkAtoms::treerow)
6494 return NS_OK;
6497 #endif // MOZ_XUL
6499 // Get the frame associated with the content
6500 nsIFrame* parentFrame = GetFrameFor(aContainer);
6501 if (! parentFrame)
6502 return NS_OK;
6504 if (aAllowLazyConstruction &&
6505 MaybeConstructLazily(CONTENTAPPEND, aContainer, aFirstNewContent)) {
6506 return NS_OK;
6509 LAYOUT_PHASE_TEMP_EXIT();
6510 parentFrame = GetRangeInsertionPoint(aContainer, parentFrame,
6511 aFirstNewContent, nsnull,
6512 aAllowLazyConstruction);
6513 LAYOUT_PHASE_TEMP_REENTER();
6514 if (!parentFrame) {
6515 return NS_OK;
6518 LAYOUT_PHASE_TEMP_EXIT();
6519 if (MaybeRecreateForFrameset(parentFrame, aFirstNewContent, nsnull)) {
6520 LAYOUT_PHASE_TEMP_REENTER();
6521 return NS_OK;
6523 LAYOUT_PHASE_TEMP_REENTER();
6525 if (parentFrame->IsLeaf()) {
6526 // Nothing to do here; we shouldn't be constructing kids of leaves
6527 // Clear lazy bits so we don't try to construct again.
6528 ClearLazyBits(aFirstNewContent, nsnull);
6529 return NS_OK;
6532 #ifdef MOZ_MATHML
6533 if (parentFrame->IsFrameOfType(nsIFrame::eMathML)) {
6534 LAYOUT_PHASE_TEMP_EXIT();
6535 nsresult rv = RecreateFramesForContent(parentFrame->GetContent(), PR_FALSE);
6536 LAYOUT_PHASE_TEMP_REENTER();
6537 return rv;
6539 #endif
6541 // If the frame we are manipulating is a ``special'' frame (that is, one
6542 // that's been created as a result of a block-in-inline situation) then we
6543 // need to append to the last special sibling, not to the frame itself.
6544 PRBool parentSpecial = IsFrameSpecial(parentFrame);
6545 if (parentSpecial) {
6546 #ifdef DEBUG
6547 if (gNoisyContentUpdates) {
6548 printf("nsCSSFrameConstructor::ContentAppended: parentFrame=");
6549 nsFrame::ListTag(stdout, parentFrame);
6550 printf(" is special\n");
6552 #endif
6554 // Since we're appending, we'll walk to the last anonymous frame
6555 // that was created for the broken inline frame. But don't walk
6556 // to the trailing inline if it's empty; stop at the block.
6557 parentFrame = GetLastSpecialSibling(parentFrame, PR_FALSE);
6560 // Get continuation that parents the last child. This MUST be done
6561 // before the AdjustAppendParentForAfterContent call.
6562 parentFrame = nsLayoutUtils::GetLastContinuationWithChild(parentFrame);
6564 // We should never get here with fieldsets, since they have multiple
6565 // insertion points.
6566 NS_ASSERTION(parentFrame->GetType() != nsGkAtoms::fieldSetFrame,
6567 "Unexpected parent");
6569 // Deal with possible :after generated content on the parent
6570 nsIFrame* parentAfterFrame;
6571 parentFrame =
6572 ::AdjustAppendParentForAfterContent(mPresShell->GetPresContext(),
6573 aContainer, parentFrame,
6574 &parentAfterFrame);
6576 // Create some new frames
6577 nsFrameConstructorState state(mPresShell, mFixedContainingBlock,
6578 GetAbsoluteContainingBlock(parentFrame),
6579 GetFloatContainingBlock(parentFrame));
6581 // See if the containing block has :first-letter style applied.
6582 PRBool haveFirstLetterStyle = PR_FALSE, haveFirstLineStyle = PR_FALSE;
6583 nsIFrame* containingBlock = state.mFloatedItems.containingBlock;
6584 if (containingBlock) {
6585 haveFirstLetterStyle = HasFirstLetterStyle(containingBlock);
6586 haveFirstLineStyle =
6587 ShouldHaveFirstLineStyle(containingBlock->GetContent(),
6588 containingBlock->GetStyleContext());
6591 if (haveFirstLetterStyle) {
6592 // Before we get going, remove the current letter frames
6593 RemoveLetterFrames(state.mPresContext, state.mPresShell,
6594 state.mFrameManager, containingBlock);
6597 nsIAtom* frameType = parentFrame->GetType();
6598 PRBool haveNoXBLChildren =
6599 mDocument->BindingManager()->GetXBLChildNodesFor(aContainer) == nsnull;
6600 FrameConstructionItemList items;
6601 if (aFirstNewContent->GetPreviousSibling() &&
6602 GetParentType(frameType) == eTypeBlock &&
6603 haveNoXBLChildren) {
6604 // If there's a text node in the normal content list just before the new
6605 // items, and it has no frame, make a frame construction item for it. If it
6606 // doesn't need a frame, ConstructFramesFromItemList below won't give it
6607 // one. No need to do all this if our parent type is not block, though,
6608 // since WipeContainingBlock already handles that situation.
6610 // Because we're appending, we don't need to worry about any text
6611 // after the appended content; there can only be XBL anonymous content
6612 // (text in an XBL binding is not suppressed) or generated content
6613 // (and bare text nodes are not generated). Native anonymous content
6614 // generated by frames never participates in inline layout.
6615 AddTextItemIfNeeded(state, parentFrame,
6616 aFirstNewContent->GetPreviousSibling(), items);
6618 for (nsIContent* child = aFirstNewContent;
6619 child;
6620 child = child->GetNextSibling()) {
6621 AddFrameConstructionItems(state, child, PR_FALSE, parentFrame, items);
6624 nsIFrame* prevSibling = ::FindAppendPrevSibling(parentFrame, parentAfterFrame);
6626 // Perform special check for diddling around with the frames in
6627 // a special inline frame.
6628 // If we're appending before :after content, then we're not really
6629 // appending, so let WipeContainingBlock know that.
6630 LAYOUT_PHASE_TEMP_EXIT();
6631 if (WipeContainingBlock(state, containingBlock, parentFrame, items,
6632 PR_TRUE, prevSibling)) {
6633 LAYOUT_PHASE_TEMP_REENTER();
6634 return NS_OK;
6636 LAYOUT_PHASE_TEMP_REENTER();
6638 // If the parent is a block frame, and we're not in a special case
6639 // where frames can be moved around, determine if the list is for the
6640 // start or end of the block.
6641 if (nsLayoutUtils::GetAsBlock(parentFrame) && !haveFirstLetterStyle &&
6642 !haveFirstLineStyle && !parentSpecial) {
6643 items.SetLineBoundaryAtStart(!prevSibling ||
6644 !prevSibling->GetStyleDisplay()->IsInlineOutside() ||
6645 prevSibling->GetType() == nsGkAtoms::brFrame);
6646 // :after content can't be <br> so no need to check it
6647 items.SetLineBoundaryAtEnd(!parentAfterFrame ||
6648 !parentAfterFrame->GetStyleDisplay()->IsInlineOutside());
6650 // To suppress whitespace-only text frames, we have to verify that
6651 // our container's DOM child list matches its flattened tree child list.
6652 // This is guaranteed to be true if GetXBLChildNodesFor() returns null.
6653 items.SetParentHasNoXBLChildren(haveNoXBLChildren);
6655 nsFrameItems frameItems;
6656 ConstructFramesFromItemList(state, items, parentFrame, frameItems);
6658 for (nsIContent* child = aFirstNewContent;
6659 child;
6660 child = child->GetNextSibling()) {
6661 // Invalidate now instead of before the WipeContainingBlock call, just in
6662 // case we do wipe; in that case we don't need to do this walk at all.
6663 // XXXbz does that matter? Would it make more sense to save some virtual
6664 // GetChildAt calls instead and do this during construction of our
6665 // FrameConstructionItemList?
6666 InvalidateCanvasIfNeeded(mPresShell, child);
6669 // if the container is a table and a caption was appended, it needs to be put
6670 // in the outer table frame's additional child list.
6671 nsFrameItems captionItems;
6672 if (nsGkAtoms::tableFrame == frameType) {
6673 // Pull out the captions. Note that we don't want to do that as we go,
6674 // because processing a single caption can add a whole bunch of things to
6675 // the frame items due to pseudoframe processing. So we'd have to pull
6676 // captions from a list anyway; might as well do that here.
6677 // XXXbz this is no longer true; we could pull captions directly out of the
6678 // FrameConstructionItemList now.
6679 PullOutCaptionFrames(frameItems, captionItems);
6682 if (haveFirstLineStyle && parentFrame == containingBlock) {
6683 // It's possible that some of the new frames go into a
6684 // first-line frame. Look at them and see...
6685 AppendFirstLineFrames(state, containingBlock->GetContent(),
6686 containingBlock, frameItems);
6689 // Notify the parent frame passing it the list of new frames
6690 // Append the flowed frames to the principal child list; captions
6691 // need special treatment
6692 if (captionItems.NotEmpty()) { // append the caption to the outer table
6693 NS_ASSERTION(nsGkAtoms::tableFrame == frameType, "how did that happen?");
6694 nsIFrame* outerTable = parentFrame->GetParent();
6695 if (outerTable) {
6696 state.mFrameManager->AppendFrames(outerTable, nsGkAtoms::captionList,
6697 captionItems);
6701 if (frameItems.NotEmpty()) { // append the in-flow kids
6702 AppendFrames(state, parentFrame, frameItems, prevSibling);
6705 // Recover first-letter frames
6706 if (haveFirstLetterStyle) {
6707 RecoverLetterFrames(containingBlock);
6710 #ifdef DEBUG
6711 if (gReallyNoisyContentUpdates) {
6712 printf("nsCSSFrameConstructor::ContentAppended: resulting frame model:\n");
6713 parentFrame->List(stdout, 0);
6715 #endif
6717 #ifdef ACCESSIBILITY
6718 if (mPresShell->IsAccessibilityActive()) {
6719 nsCOMPtr<nsIAccessibilityService> accService =
6720 do_GetService("@mozilla.org/accessibilityService;1");
6721 if (accService) {
6722 accService->ContentRangeInserted(mPresShell, aContainer,
6723 aFirstNewContent, nsnull);
6726 #endif
6728 return NS_OK;
6731 #ifdef MOZ_XUL
6733 enum content_operation
6735 CONTENT_INSERTED,
6736 CONTENT_REMOVED
6739 // Helper function to lookup the listbox body frame and send a notification
6740 // for insertion or removal of content
6741 static
6742 PRBool NotifyListBoxBody(nsPresContext* aPresContext,
6743 nsIContent* aContainer,
6744 nsIContent* aChild,
6745 // Only used for the removed notification
6746 nsIContent* aOldNextSibling,
6747 nsIDocument* aDocument,
6748 nsIFrame* aChildFrame,
6749 content_operation aOperation)
6751 nsListBoxBodyFrame* listBoxBodyFrame =
6752 MaybeGetListBoxBodyFrame(aContainer, aChild);
6753 if (listBoxBodyFrame) {
6754 if (aOperation == CONTENT_REMOVED) {
6755 // Except if we have an aChildFrame and its parent is not the right
6756 // thing, then we don't do this. Pseudo frames are so much fun....
6757 if (!aChildFrame || aChildFrame->GetParent() == listBoxBodyFrame) {
6758 listBoxBodyFrame->OnContentRemoved(aPresContext, aContainer,
6759 aChildFrame, aOldNextSibling);
6760 return PR_TRUE;
6762 } else {
6763 listBoxBodyFrame->OnContentInserted(aPresContext, aChild);
6764 return PR_TRUE;
6768 return PR_FALSE;
6770 #endif // MOZ_XUL
6772 nsresult
6773 nsCSSFrameConstructor::ContentInserted(nsIContent* aContainer,
6774 nsIContent* aChild,
6775 nsILayoutHistoryState* aFrameState,
6776 PRBool aAllowLazyConstruction)
6778 return ContentRangeInserted(aContainer,
6779 aChild,
6780 aChild->GetNextSibling(),
6781 aFrameState,
6782 aAllowLazyConstruction);
6785 // ContentRangeInserted handles creating frames for a range of nodes that
6786 // aren't at the end of their childlist. ContentRangeInserted isn't a real
6787 // content notification, but rather it handles regular ContentInserted calls
6788 // for a single node as well as the lazy construction of frames for a range of
6789 // nodes when called from CreateNeededFrames. For a range of nodes to be
6790 // suitable to have its frames constructed all at once they must meet the same
6791 // conditions that ContentAppended imposes (GetRangeInsertionPoint checks
6792 // these), plus more. Namely when finding the insertion prevsibling we must not
6793 // need to consult something specific to any one node in the range, so that the
6794 // insertion prevsibling would be the same for each node in the range. So we
6795 // pass the first node in the range to GetInsertionPrevSibling, and if
6796 // IsValidSibling (the only place GetInsertionPrevSibling might look at the
6797 // passed in node itself) needs to resolve style on the node we record this and
6798 // return that this range needs to be split up and inserted separately. Table
6799 // captions need extra attention as we need to determine where to insert them
6800 // in the caption list, while skipping any nodes in the range being inserted
6801 // (because when we treat the caption frames the other nodes have had their
6802 // frames constructed but not yet inserted into the frame tree).
6803 nsresult
6804 nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer,
6805 nsIContent* aStartChild,
6806 nsIContent* aEndChild,
6807 nsILayoutHistoryState* aFrameState,
6808 PRBool aAllowLazyConstruction)
6810 AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
6811 NS_PRECONDITION(mUpdateCount != 0,
6812 "Should be in an update while creating frames");
6814 NS_PRECONDITION(aStartChild, "must always pass a child");
6816 // XXXldb Do we need to re-resolve style to handle the CSS2 + combinator and
6817 // the :empty pseudo-class?
6818 #ifdef DEBUG
6819 if (gNoisyContentUpdates) {
6820 printf("nsCSSFrameConstructor::ContentRangeInserted container=%p "
6821 "start-child=%p end-child=%p lazy=%d\n",
6822 static_cast<void*>(aContainer),
6823 static_cast<void*>(aStartChild), static_cast<void*>(aEndChild),
6824 aAllowLazyConstruction);
6825 if (gReallyNoisyContentUpdates) {
6826 if (aContainer) {
6827 aContainer->List(stdout,0);
6828 } else {
6829 aStartChild->List(stdout, 0);
6833 #endif
6835 nsresult rv = NS_OK;
6837 PRBool isSingleInsert = (aStartChild->GetNextSibling() == aEndChild);
6838 NS_ASSERTION(isSingleInsert || !aAllowLazyConstruction,
6839 "range insert shouldn't be lazy");
6840 NS_ASSERTION(isSingleInsert || aEndChild,
6841 "range should not include all nodes after aStartChild");
6843 #ifdef MOZ_XUL
6844 if (aContainer && IsXULListBox(aContainer)) {
6845 if (isSingleInsert) {
6846 if (NotifyListBoxBody(mPresShell->GetPresContext(), aContainer,
6847 // The insert case in NotifyListBoxBody
6848 // doesn't use "old next sibling".
6849 aStartChild, nsnull,
6850 mDocument, nsnull, CONTENT_INSERTED)) {
6851 return NS_OK;
6853 } else {
6854 // We don't handle a range insert to a listbox parent, issue single
6855 // ContertInserted calls for each node inserted.
6856 LAYOUT_PHASE_TEMP_EXIT();
6857 IssueSingleInsertNofications(aContainer, aStartChild, aEndChild,
6858 aAllowLazyConstruction);
6859 LAYOUT_PHASE_TEMP_REENTER();
6860 return NS_OK;
6863 #endif // MOZ_XUL
6865 // If we have a null parent, then this must be the document element being
6866 // inserted, or some other child of the document in the DOM (might be a PI,
6867 // say).
6868 if (! aContainer) {
6869 NS_ASSERTION(isSingleInsert,
6870 "root node insertion should be a single insertion");
6871 Element *docElement = mDocument->GetRootElement();
6873 if (aStartChild != docElement) {
6874 // Not the root element; just bail out
6875 return NS_OK;
6878 NS_PRECONDITION(nsnull == mRootElementFrame,
6879 "root element frame already created");
6881 // Create frames for the document element and its child elements
6882 nsIFrame* docElementFrame;
6883 rv = ConstructDocElementFrame(docElement, aFrameState, &docElementFrame);
6885 if (NS_SUCCEEDED(rv) && docElementFrame) {
6886 InvalidateCanvasIfNeeded(mPresShell, aStartChild);
6887 #ifdef DEBUG
6888 if (gReallyNoisyContentUpdates) {
6889 printf("nsCSSFrameConstructor::ContentRangeInserted: resulting frame "
6890 "model:\n");
6891 mFixedContainingBlock->List(stdout, 0);
6893 #endif
6896 #ifdef ACCESSIBILITY
6897 if (mPresShell->IsAccessibilityActive()) {
6898 nsCOMPtr<nsIAccessibilityService> accService =
6899 do_GetService("@mozilla.org/accessibilityService;1");
6900 if (accService) {
6901 accService->ContentRangeInserted(mPresShell, aContainer,
6902 aStartChild, aEndChild);
6905 #endif
6907 return NS_OK;
6910 // Otherwise, we've got parent content. Find its frame.
6911 nsIFrame* parentFrame = GetFrameFor(aContainer);
6912 if (! parentFrame)
6913 return NS_OK;
6915 if (aAllowLazyConstruction &&
6916 MaybeConstructLazily(CONTENTINSERT, aContainer, aStartChild)) {
6917 return NS_OK;
6920 if (isSingleInsert) {
6921 // See if we have an XBL insertion point. If so, then that's our
6922 // real parent frame; if not, then the frame hasn't been built yet
6923 // and we just bail.
6924 nsIFrame* insertionPoint;
6925 GetInsertionPoint(parentFrame, aStartChild, &insertionPoint);
6926 if (! insertionPoint)
6927 return NS_OK; // Don't build the frames.
6929 parentFrame = insertionPoint;
6930 } else {
6931 // Get our insertion point. If we need to issue single ContentInserted's
6932 // GetRangeInsertionPoint will take care of that for us.
6933 LAYOUT_PHASE_TEMP_EXIT();
6934 parentFrame = GetRangeInsertionPoint(aContainer, parentFrame,
6935 aStartChild, aEndChild,
6936 aAllowLazyConstruction);
6937 LAYOUT_PHASE_TEMP_REENTER();
6938 if (!parentFrame) {
6939 return NS_OK;
6943 PRBool isAppend, isRangeInsertSafe;
6944 nsIFrame* prevSibling =
6945 GetInsertionPrevSibling(parentFrame, aContainer, aStartChild,
6946 &isAppend, &isRangeInsertSafe);
6948 // check if range insert is safe
6949 if (!isSingleInsert && !isRangeInsertSafe) {
6950 // must fall back to a single ContertInserted for each child in the range
6951 LAYOUT_PHASE_TEMP_EXIT();
6952 IssueSingleInsertNofications(aContainer, aStartChild, aEndChild,
6953 aAllowLazyConstruction);
6954 LAYOUT_PHASE_TEMP_REENTER();
6955 return NS_OK;
6958 nsIContent* container = parentFrame->GetContent();
6960 nsIAtom* frameType = parentFrame->GetType();
6961 LAYOUT_PHASE_TEMP_EXIT();
6962 if (MaybeRecreateForFrameset(parentFrame, aStartChild, aEndChild)) {
6963 LAYOUT_PHASE_TEMP_REENTER();
6964 return NS_OK;
6966 LAYOUT_PHASE_TEMP_REENTER();
6968 // We should only get here with fieldsets when doing a single insert, because
6969 // fieldsets have multiple insertion points.
6970 NS_ASSERTION(isSingleInsert || frameType != nsGkAtoms::fieldSetFrame,
6971 "Unexpected parent");
6972 if (frameType == nsGkAtoms::fieldSetFrame &&
6973 aStartChild->Tag() == nsGkAtoms::legend) {
6974 // Just reframe the parent, since figuring out whether this
6975 // should be the new legend and then handling it is too complex.
6976 // We could do a little better here --- check if the fieldset already
6977 // has a legend which occurs earlier in its child list than this node,
6978 // and if so, proceed. But we'd have to extend nsFieldSetFrame
6979 // to locate this legend in the inserted frames and extract it.
6980 LAYOUT_PHASE_TEMP_EXIT();
6981 nsresult rv = RecreateFramesForContent(parentFrame->GetContent(), PR_FALSE);
6982 LAYOUT_PHASE_TEMP_REENTER();
6983 return rv;
6986 // Don't construct kids of leaves
6987 if (parentFrame->IsLeaf()) {
6988 // Clear lazy bits so we don't try to construct again.
6989 ClearLazyBits(aStartChild, aEndChild);
6990 return NS_OK;
6993 #ifdef MOZ_MATHML
6994 if (parentFrame->IsFrameOfType(nsIFrame::eMathML)) {
6995 LAYOUT_PHASE_TEMP_EXIT();
6996 nsresult rv = RecreateFramesForContent(parentFrame->GetContent(), PR_FALSE);
6997 LAYOUT_PHASE_TEMP_REENTER();
6998 return rv;
7000 #endif
7002 nsFrameConstructorState state(mPresShell, mFixedContainingBlock,
7003 GetAbsoluteContainingBlock(parentFrame),
7004 GetFloatContainingBlock(parentFrame),
7005 aFrameState);
7008 // Recover state for the containing block - we need to know if
7009 // it has :first-letter or :first-line style applied to it. The
7010 // reason we care is that the internal structure in these cases
7011 // is not the normal structure and requires custom updating
7012 // logic.
7013 nsIFrame* containingBlock = state.mFloatedItems.containingBlock;
7014 PRBool haveFirstLetterStyle = PR_FALSE;
7015 PRBool haveFirstLineStyle = PR_FALSE;
7017 // In order to shave off some cycles, we only dig up the
7018 // containing block haveFirst* flags if the parent frame where
7019 // the insertion/append is occurring is an inline or block
7020 // container. For other types of containers this isn't relevant.
7021 const nsStyleDisplay* parentDisplay = parentFrame->GetStyleDisplay();
7023 // Examine the parentFrame where the insertion is taking
7024 // place. If it's a certain kind of container then some special
7025 // processing is done.
7026 if ((NS_STYLE_DISPLAY_BLOCK == parentDisplay->mDisplay) ||
7027 (NS_STYLE_DISPLAY_LIST_ITEM == parentDisplay->mDisplay) ||
7028 (NS_STYLE_DISPLAY_INLINE == parentDisplay->mDisplay) ||
7029 (NS_STYLE_DISPLAY_INLINE_BLOCK == parentDisplay->mDisplay)) {
7030 // Recover the special style flags for the containing block
7031 if (containingBlock) {
7032 haveFirstLetterStyle = HasFirstLetterStyle(containingBlock);
7033 haveFirstLineStyle =
7034 ShouldHaveFirstLineStyle(containingBlock->GetContent(),
7035 containingBlock->GetStyleContext());
7038 if (haveFirstLetterStyle) {
7039 // If our current parentFrame is a Letter frame, use its parent as our
7040 // new parent hint
7041 if (parentFrame->GetType() == nsGkAtoms::letterFrame) {
7042 // If parentFrame is out of flow, then we actually want the parent of
7043 // the placeholder frame.
7044 if (parentFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
7045 nsPlaceholderFrame* placeholderFrame =
7046 state.mFrameManager->GetPlaceholderFrameFor(parentFrame);
7047 NS_ASSERTION(placeholderFrame, "No placeholder for out-of-flow?");
7048 parentFrame = placeholderFrame->GetParent();
7049 } else {
7050 parentFrame = parentFrame->GetParent();
7054 // Remove the old letter frames before doing the insertion
7055 RemoveLetterFrames(state.mPresContext, mPresShell,
7056 state.mFrameManager,
7057 state.mFloatedItems.containingBlock);
7059 // Removing the letterframes messes around with the frame tree, removing
7060 // and creating frames. We need to reget our prevsibling, parent frame,
7061 // etc.
7062 prevSibling = GetInsertionPrevSibling(parentFrame, aContainer,
7063 aStartChild, &isAppend,
7064 &isRangeInsertSafe);
7066 // Need check whether a range insert is still safe.
7067 if (!isSingleInsert && !isRangeInsertSafe) {
7068 // Need to recover the letter frames first.
7069 RecoverLetterFrames(state.mFloatedItems.containingBlock);
7071 // must fall back to a single ContertInserted for each child in the range
7072 LAYOUT_PHASE_TEMP_EXIT();
7073 IssueSingleInsertNofications(aContainer, aStartChild, aEndChild,
7074 aAllowLazyConstruction);
7075 LAYOUT_PHASE_TEMP_REENTER();
7076 return NS_OK;
7079 container = parentFrame->GetContent();
7080 frameType = parentFrame->GetType();
7084 if (!prevSibling) {
7085 // We're inserting the new frames as the first child. See if the
7086 // parent has a :before pseudo-element
7087 nsIFrame* firstChild = parentFrame->GetFirstChild(nsnull);
7089 if (firstChild &&
7090 nsLayoutUtils::IsGeneratedContentFor(container, firstChild,
7091 nsCSSPseudoElements::before)) {
7092 // Insert the new frames after the last continuation of the :before
7093 prevSibling = firstChild->GetTailContinuation();
7094 parentFrame = prevSibling->GetParent()->GetContentInsertionFrame();
7095 // Don't change isAppend here; we'll can call AppendFrames as needed, and
7096 // the change to our prevSibling doesn't affect that.
7100 FrameConstructionItemList items;
7101 ParentType parentType = GetParentType(frameType);
7102 PRBool haveNoXBLChildren =
7103 mDocument->BindingManager()->GetXBLChildNodesFor(aContainer) == nsnull;
7104 if (aStartChild->GetPreviousSibling() &&
7105 parentType == eTypeBlock && haveNoXBLChildren) {
7106 // If there's a text node in the normal content list just before the
7107 // new nodes, and it has no frame, make a frame construction item for
7108 // it, because it might need a frame now. No need to do this if our
7109 // parent type is not block, though, since WipeContainingBlock
7110 // already handles that sitation.
7111 AddTextItemIfNeeded(state, parentFrame, aStartChild->GetPreviousSibling(),
7112 items);
7115 if (isSingleInsert) {
7116 AddFrameConstructionItems(state, aStartChild,
7117 aStartChild->IsRootOfAnonymousSubtree(),
7118 parentFrame, items);
7119 } else {
7120 for (nsIContent* child = aStartChild;
7121 child != aEndChild;
7122 child = child->GetNextSibling()){
7123 AddFrameConstructionItems(state, child, PR_FALSE, parentFrame, items);
7127 if (aEndChild && parentType == eTypeBlock && haveNoXBLChildren) {
7128 // If there's a text node in the normal content list just after the
7129 // new nodes, and it has no frame, make a frame construction item for
7130 // it, because it might need a frame now. No need to do this if our
7131 // parent type is not block, though, since WipeContainingBlock
7132 // already handles that sitation.
7133 AddTextItemIfNeeded(state, parentFrame, aEndChild, items);
7136 // Perform special check for diddling around with the frames in
7137 // a special inline frame.
7138 // If we're appending before :after content, then we're not really
7139 // appending, so let WipeContainingBlock know that.
7140 LAYOUT_PHASE_TEMP_EXIT();
7141 if (WipeContainingBlock(state, containingBlock, parentFrame, items,
7142 isAppend, prevSibling)) {
7143 LAYOUT_PHASE_TEMP_REENTER();
7144 return NS_OK;
7146 LAYOUT_PHASE_TEMP_REENTER();
7148 // If the container is a table and a caption will be appended, it needs to be
7149 // put in the outer table frame's additional child list.
7150 // We make no attempt here to set flags to indicate whether the list
7151 // will be at the start or end of a block. It doesn't seem worthwhile.
7152 nsFrameItems frameItems, captionItems;
7153 ConstructFramesFromItemList(state, items, parentFrame, frameItems);
7155 if (frameItems.NotEmpty()) {
7156 for (nsIContent* child = aStartChild;
7157 child != aEndChild;
7158 child = child->GetNextSibling()){
7159 InvalidateCanvasIfNeeded(mPresShell, child);
7162 if (nsGkAtoms::tableFrame == frameType ||
7163 nsGkAtoms::tableOuterFrame == frameType) {
7164 PullOutCaptionFrames(frameItems, captionItems);
7168 // If the parent of our current prevSibling is different from the frame we'll
7169 // actually use as the parent, then the calculated insertion point is now
7170 // invalid and as it is unknown where to insert correctly we append instead
7171 // (bug 341858).
7172 // This can affect our prevSibling and isAppend, but should not have any
7173 // effect on the WipeContainingBlock above, since this should only happen
7174 // when neither parent is a special frame and should not affect whitespace
7175 // handling inside table-related frames (and in fact, can only happen when
7176 // one of the parents is an outer table and one is an inner table or when the
7177 // parent is a fieldset or fieldset content frame). So it won't affect the
7178 // {ib} or XUL box cases in WipeContainingBlock(), and the table pseudo
7179 // handling will only be affected by us maybe thinking we're not inserting
7180 // at the beginning, whereas we really are. That would have made us reframe
7181 // unnecessarily, but that's ok.
7182 // XXXbz we should push our frame construction item code up higher, so we
7183 // know what our items are by the time we start figuring out previous
7184 // siblings
7185 if (prevSibling && frameItems.NotEmpty() &&
7186 frameItems.FirstChild()->GetParent() != prevSibling->GetParent()) {
7187 #ifdef DEBUG
7188 nsIFrame* frame1 = frameItems.FirstChild()->GetParent();
7189 nsIFrame* frame2 = prevSibling->GetParent();
7190 NS_ASSERTION(!IsFrameSpecial(frame1) && !IsFrameSpecial(frame2),
7191 "Neither should be special");
7192 NS_ASSERTION((frame1->GetType() == nsGkAtoms::tableFrame &&
7193 frame2->GetType() == nsGkAtoms::tableOuterFrame) ||
7194 (frame1->GetType() == nsGkAtoms::tableOuterFrame &&
7195 frame2->GetType() == nsGkAtoms::tableFrame) ||
7196 frame1->GetType() == nsGkAtoms::fieldSetFrame ||
7197 (frame1->GetParent() &&
7198 frame1->GetParent()->GetType() == nsGkAtoms::fieldSetFrame),
7199 "Unexpected frame types");
7200 #endif
7201 isAppend = PR_TRUE;
7202 nsIFrame* appendAfterFrame;
7203 parentFrame =
7204 ::AdjustAppendParentForAfterContent(mPresShell->GetPresContext(),
7205 container,
7206 frameItems.FirstChild()->GetParent(),
7207 &appendAfterFrame);
7208 prevSibling = ::FindAppendPrevSibling(parentFrame, appendAfterFrame);
7211 if (haveFirstLineStyle && parentFrame == containingBlock) {
7212 // It's possible that the new frame goes into a first-line
7213 // frame. Look at it and see...
7214 if (isAppend) {
7215 // Use append logic when appending
7216 AppendFirstLineFrames(state, containingBlock->GetContent(),
7217 containingBlock, frameItems);
7219 else {
7220 // Use more complicated insert logic when inserting
7221 // XXXbz this method is a no-op, so it's easy for the args being passed
7222 // here to make no sense without anyone noticing... If it ever stops
7223 // being a no-op, vet them carefully!
7224 InsertFirstLineFrames(state, container, containingBlock, &parentFrame,
7225 prevSibling, frameItems);
7229 // We might have captions; put them into the caption list of the
7230 // outer table frame.
7231 if (captionItems.NotEmpty()) {
7232 NS_ASSERTION(nsGkAtoms::tableFrame == frameType ||
7233 nsGkAtoms::tableOuterFrame == frameType,
7234 "parent for caption is not table?");
7235 // We need to determine where to put the caption items; start with the
7236 // the parent frame that has already been determined and get the insertion
7237 // prevsibling of the first caption item.
7238 nsIFrame* captionParent = parentFrame;
7239 PRBool captionIsAppend;
7240 nsIFrame* captionPrevSibling = nsnull;
7242 // aIsRangeInsertSafe is ignored on purpose because it is irrelevant here.
7243 PRBool ignored;
7244 if (isSingleInsert) {
7245 captionPrevSibling =
7246 GetInsertionPrevSibling(captionParent, aContainer, aStartChild,
7247 &captionIsAppend, &ignored);
7248 } else {
7249 nsIContent* firstCaption = captionItems.FirstChild()->GetContent();
7250 // It is very important here that we skip the children in
7251 // [aStartChild,aEndChild) when looking for a
7252 // prevsibling.
7253 captionPrevSibling =
7254 GetInsertionPrevSibling(captionParent, aContainer, firstCaption,
7255 &captionIsAppend, &ignored,
7256 aStartChild, aEndChild);
7259 nsIFrame* outerTable = nsnull;
7260 if (GetCaptionAdjustedParent(captionParent, captionItems.FirstChild(),
7261 &outerTable)) {
7262 // If the parent is not an outer table frame we will try to add frames
7263 // to a named child list that the parent does not honour and the frames
7264 // will get lost
7265 NS_ASSERTION(nsGkAtoms::tableOuterFrame == outerTable->GetType(),
7266 "Pseudo frame construction failure; "
7267 "a caption can be only a child of an outer table frame");
7269 // If the parent of our current prevSibling is different from the frame
7270 // we'll actually use as the parent, then the calculated insertion
7271 // point is now invalid (bug 341382).
7272 if (captionPrevSibling &&
7273 captionPrevSibling->GetParent() != outerTable) {
7274 captionPrevSibling = nsnull;
7276 if (captionIsAppend) {
7277 state.mFrameManager->AppendFrames(outerTable, nsGkAtoms::captionList,
7278 captionItems);
7279 } else {
7280 state.mFrameManager->InsertFrames(outerTable, nsGkAtoms::captionList,
7281 captionPrevSibling, captionItems);
7286 if (frameItems.NotEmpty()) {
7287 // Notify the parent frame
7288 if (isAppend) {
7289 AppendFrames(state, parentFrame, frameItems, prevSibling);
7290 } else {
7291 state.mFrameManager->InsertFrames(parentFrame, nsnull, prevSibling,
7292 frameItems);
7296 if (haveFirstLetterStyle) {
7297 // Recover the letter frames for the containing block when
7298 // it has first-letter style.
7299 RecoverLetterFrames(state.mFloatedItems.containingBlock);
7302 #ifdef DEBUG
7303 if (gReallyNoisyContentUpdates && parentFrame) {
7304 printf("nsCSSFrameConstructor::ContentRangeInserted: resulting frame model:\n");
7305 parentFrame->List(stdout, 0);
7307 #endif
7309 #ifdef ACCESSIBILITY
7310 if (mPresShell->IsAccessibilityActive()) {
7311 nsCOMPtr<nsIAccessibilityService> accService =
7312 do_GetService("@mozilla.org/accessibilityService;1");
7313 if (accService) {
7314 accService->ContentRangeInserted(mPresShell, aContainer,
7315 aStartChild, aEndChild);
7318 #endif
7320 return NS_OK;
7323 nsresult
7324 nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer,
7325 nsIContent* aChild,
7326 nsIContent* aOldNextSibling,
7327 RemoveFlags aFlags,
7328 PRBool* aDidReconstruct)
7330 AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
7331 NS_PRECONDITION(mUpdateCount != 0,
7332 "Should be in an update while destroying frames");
7334 *aDidReconstruct = PR_FALSE;
7336 // XXXldb Do we need to re-resolve style to handle the CSS2 + combinator and
7337 // the :empty pseudo-class?
7339 #ifdef DEBUG
7340 if (gNoisyContentUpdates) {
7341 printf("nsCSSFrameConstructor::ContentRemoved container=%p child=%p "
7342 "old-next-sibling=%p\n",
7343 static_cast<void*>(aContainer),
7344 static_cast<void*>(aChild),
7345 static_cast<void*>(aOldNextSibling));
7346 if (gReallyNoisyContentUpdates) {
7347 aContainer->List(stdout, 0);
7350 #endif
7352 nsFrameManager *frameManager = mPresShell->FrameManager();
7353 nsPresContext *presContext = mPresShell->GetPresContext();
7354 nsresult rv = NS_OK;
7356 // Find the child frame that maps the content
7357 nsIFrame* childFrame = aChild->GetPrimaryFrame();
7359 if (!childFrame || childFrame->GetContent() != aChild) {
7360 // XXXbz the GetContent() != aChild check is needed due to bug 135040.
7361 // Remove it once that's fixed.
7362 frameManager->ClearUndisplayedContentIn(aChild, aContainer);
7365 #ifdef MOZ_XUL
7366 if (NotifyListBoxBody(presContext, aContainer, aChild, aOldNextSibling,
7367 mDocument, childFrame, CONTENT_REMOVED))
7368 return NS_OK;
7370 #endif // MOZ_XUL
7372 // If we're removing the root, then make sure to remove things starting at
7373 // the viewport's child instead of the primary frame (which might even be
7374 // null if the root had an XBL binding or display:none, even though the
7375 // frames above it got created). We do the adjustment after the childFrame
7376 // check above, because we do want to clear any undisplayed content we might
7377 // have for the root. Detecting removal of a root is a little exciting; in
7378 // particular, having a null aContainer is necessary but NOT sufficient. Due
7379 // to how we process reframes, the content node might not even be in our
7380 // document by now. So explicitly check whether the viewport's first kid's
7381 // content node is aChild.
7382 PRBool isRoot = PR_FALSE;
7383 if (!aContainer) {
7384 nsIFrame* viewport = frameManager->GetRootFrame();
7385 if (viewport) {
7386 nsIFrame* firstChild = viewport->GetFirstChild(nsnull);
7387 if (firstChild && firstChild->GetContent() == aChild) {
7388 isRoot = PR_TRUE;
7389 childFrame = firstChild;
7390 NS_ASSERTION(!childFrame->GetNextSibling(), "How did that happen?");
7395 if (childFrame) {
7396 InvalidateCanvasIfNeeded(mPresShell, aChild);
7398 // See whether we need to remove more than just childFrame
7399 LAYOUT_PHASE_TEMP_EXIT();
7400 if (MaybeRecreateContainerForFrameRemoval(childFrame, &rv)) {
7401 LAYOUT_PHASE_TEMP_REENTER();
7402 *aDidReconstruct = PR_TRUE;
7403 return rv;
7405 LAYOUT_PHASE_TEMP_REENTER();
7407 // Get the childFrame's parent frame
7408 nsIFrame* parentFrame = childFrame->GetParent();
7409 nsIAtom* parentType = parentFrame->GetType();
7411 if (parentType == nsGkAtoms::frameSetFrame &&
7412 IsSpecialFramesetChild(aChild)) {
7413 // Just reframe the parent, since framesets are weird like that.
7414 *aDidReconstruct = PR_TRUE;
7415 LAYOUT_PHASE_TEMP_EXIT();
7416 nsresult rv = RecreateFramesForContent(parentFrame->GetContent(), PR_FALSE);
7417 LAYOUT_PHASE_TEMP_REENTER();
7418 return rv;
7421 #ifdef MOZ_MATHML
7422 // If we're a child of MathML, then we should reframe the MathML content.
7423 // If we're non-MathML, then we would be wrapped in a block so we need to
7424 // check our grandparent in that case.
7425 nsIFrame* possibleMathMLAncestor = parentType == nsGkAtoms::blockFrame ?
7426 parentFrame->GetParent() : parentFrame;
7427 if (possibleMathMLAncestor->IsFrameOfType(nsIFrame::eMathML)) {
7428 *aDidReconstruct = PR_TRUE;
7429 LAYOUT_PHASE_TEMP_EXIT();
7430 nsresult rv = RecreateFramesForContent(possibleMathMLAncestor->GetContent(), PR_FALSE);
7431 LAYOUT_PHASE_TEMP_REENTER();
7432 return rv;
7434 #endif
7436 // Undo XUL wrapping if it's no longer needed.
7437 // (If we're in the XUL block-wrapping situation, parentFrame is the
7438 // wrapper frame.)
7439 nsIFrame* grandparentFrame = parentFrame->GetParent();
7440 if (grandparentFrame && grandparentFrame->IsBoxFrame() &&
7441 (grandparentFrame->GetStateBits() & NS_STATE_BOX_WRAPS_KIDS_IN_BLOCK) &&
7442 // check if this frame is the only one needing wrapping
7443 aChild == AnyKidsNeedBlockParent(parentFrame->GetFirstChild(nsnull)) &&
7444 !AnyKidsNeedBlockParent(childFrame->GetNextSibling())) {
7445 *aDidReconstruct = PR_TRUE;
7446 LAYOUT_PHASE_TEMP_EXIT();
7447 nsresult rv = RecreateFramesForContent(grandparentFrame->GetContent(), PR_TRUE);
7448 LAYOUT_PHASE_TEMP_REENTER();
7449 return rv;
7452 #ifdef ACCESSIBILITY
7453 if (mPresShell->IsAccessibilityActive()) {
7454 nsCOMPtr<nsIAccessibilityService> accService =
7455 do_GetService("@mozilla.org/accessibilityService;1");
7456 if (accService) {
7457 accService->ContentRemoved(mPresShell, aContainer, aChild);
7460 #endif
7462 // Examine the containing-block for the removed content and see if
7463 // :first-letter style applies.
7464 nsIFrame* inflowChild = childFrame;
7465 if (childFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
7466 inflowChild = frameManager->GetPlaceholderFrameFor(childFrame);
7467 NS_ASSERTION(inflowChild, "No placeholder for out-of-flow?");
7469 nsIFrame* containingBlock =
7470 GetFloatContainingBlock(inflowChild->GetParent());
7471 PRBool haveFLS = containingBlock && HasFirstLetterStyle(containingBlock);
7472 if (haveFLS) {
7473 // Trap out to special routine that handles adjusting a blocks
7474 // frame tree when first-letter style is present.
7475 #ifdef NOISY_FIRST_LETTER
7476 printf("ContentRemoved: containingBlock=");
7477 nsFrame::ListTag(stdout, containingBlock);
7478 printf(" parentFrame=");
7479 nsFrame::ListTag(stdout, parentFrame);
7480 printf(" childFrame=");
7481 nsFrame::ListTag(stdout, childFrame);
7482 printf("\n");
7483 #endif
7485 // First update the containing blocks structure by removing the
7486 // existing letter frames. This makes the subsequent logic
7487 // simpler.
7488 RemoveLetterFrames(presContext, mPresShell, frameManager,
7489 containingBlock);
7491 // Recover childFrame and parentFrame
7492 childFrame = aChild->GetPrimaryFrame();
7493 if (!childFrame || childFrame->GetContent() != aChild) {
7494 // XXXbz the GetContent() != aChild check is needed due to bug 135040.
7495 // Remove it once that's fixed.
7496 frameManager->ClearUndisplayedContentIn(aChild, aContainer);
7497 return NS_OK;
7499 parentFrame = childFrame->GetParent();
7500 parentType = parentFrame->GetType();
7502 #ifdef NOISY_FIRST_LETTER
7503 printf(" ==> revised parentFrame=");
7504 nsFrame::ListTag(stdout, parentFrame);
7505 printf(" childFrame=");
7506 nsFrame::ListTag(stdout, childFrame);
7507 printf("\n");
7508 #endif
7511 #ifdef DEBUG
7512 if (gReallyNoisyContentUpdates) {
7513 printf("nsCSSFrameConstructor::ContentRemoved: childFrame=");
7514 nsFrame::ListTag(stdout, childFrame);
7515 putchar('\n');
7516 parentFrame->List(stdout, 0);
7518 #endif
7521 // Notify the parent frame that it should delete the frame
7522 if (childFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
7523 childFrame = frameManager->GetPlaceholderFrameFor(childFrame);
7524 NS_ASSERTION(childFrame, "Missing placeholder frame for out of flow.");
7525 parentFrame = childFrame->GetParent();
7527 rv = frameManager->RemoveFrame(nsLayoutUtils::GetChildListNameFor(childFrame),
7528 childFrame);
7529 //XXXfr NS_ENSURE_SUCCESS(rv, rv) ?
7531 if (isRoot) {
7532 mRootElementFrame = nsnull;
7533 mRootElementStyleFrame = nsnull;
7534 mDocElementContainingBlock = nsnull;
7535 mPageSequenceFrame = nsnull;
7536 mGfxScrollFrame = nsnull;
7537 mHasRootAbsPosContainingBlock = PR_FALSE;
7538 mFixedContainingBlock = frameManager->GetRootFrame();
7541 if (haveFLS && mRootElementFrame) {
7542 RecoverLetterFrames(containingBlock);
7545 // If we're just reconstructing frames for the element, then the
7546 // following ContentInserted notification on the element will
7547 // take care of fixing up any adjacent text nodes. We don't need
7548 // to do this if the table parent type of our parent type is not
7549 // eTypeBlock, though, because in that case the whitespace isn't
7550 // being suppressed due to us anyway.
7551 if (aContainer && !aChild->IsRootOfAnonymousSubtree() &&
7552 aFlags != REMOVE_FOR_RECONSTRUCTION &&
7553 GetParentType(parentType) == eTypeBlock) {
7554 // Adjacent whitespace-only text nodes might have been suppressed if
7555 // this node does not have inline ends. Create frames for them now
7556 // if necessary.
7557 // Reframe any text node just before the node being removed, if there is
7558 // one, and if it's not the last child or the first child. If a whitespace
7559 // textframe was being suppressed and it's now the last child or first
7560 // child then it can stay suppressed since the parent must be a block
7561 // and hence it's adjacent to a block end.
7562 // If aOldNextSibling is null, then the text node before the node being
7563 // removed is the last node, and we don't need to worry about it.
7564 if (aOldNextSibling) {
7565 nsIContent* prevSibling = aOldNextSibling->GetPreviousSibling();
7566 if (prevSibling && prevSibling->GetPreviousSibling()) {
7567 LAYOUT_PHASE_TEMP_EXIT();
7568 ReframeTextIfNeeded(aContainer, prevSibling);
7569 LAYOUT_PHASE_TEMP_REENTER();
7572 // Reframe any text node just after the node being removed, if there is
7573 // one, and if it's not the last child or the first child.
7574 if (aOldNextSibling && aOldNextSibling->GetNextSibling() &&
7575 aOldNextSibling->GetPreviousSibling()) {
7576 LAYOUT_PHASE_TEMP_EXIT();
7577 ReframeTextIfNeeded(aContainer, aOldNextSibling);
7578 LAYOUT_PHASE_TEMP_REENTER();
7582 #ifdef DEBUG
7583 if (gReallyNoisyContentUpdates && parentFrame) {
7584 printf("nsCSSFrameConstructor::ContentRemoved: resulting frame model:\n");
7585 parentFrame->List(stdout, 0);
7587 #endif
7590 return rv;
7593 #ifdef DEBUG
7594 // To ensure that the functions below are only called within
7595 // |ApplyRenderingChangeToTree|.
7596 static PRBool gInApplyRenderingChangeToTree = PR_FALSE;
7597 #endif
7599 static void
7600 DoApplyRenderingChangeToTree(nsIFrame* aFrame,
7601 nsIViewManager* aViewManager,
7602 nsFrameManager* aFrameManager,
7603 nsChangeHint aChange);
7606 * @param aBoundsRect returns the bounds enclosing the areas covered by aFrame and its childre
7607 * This rect is relative to aFrame's parent
7609 static void
7610 UpdateViewsForTree(nsIFrame* aFrame, nsIViewManager* aViewManager,
7611 nsFrameManager* aFrameManager,
7612 nsChangeHint aChange)
7614 NS_PRECONDITION(gInApplyRenderingChangeToTree,
7615 "should only be called within ApplyRenderingChangeToTree");
7617 nsIView* view = aFrame->GetView();
7618 if (view) {
7619 if (aChange & nsChangeHint_SyncFrameView) {
7620 nsContainerFrame::SyncFrameViewProperties(aFrame->PresContext(),
7621 aFrame, nsnull, view);
7625 // now do children of frame
7626 PRInt32 listIndex = 0;
7627 nsIAtom* childList = nsnull;
7629 do {
7630 nsIFrame* child = aFrame->GetFirstChild(childList);
7631 while (child) {
7632 if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)
7633 || (child->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) {
7634 // only do frames that don't have placeholders
7635 if (nsGkAtoms::placeholderFrame == child->GetType()) {
7636 // do the out-of-flow frame and its continuations
7637 nsIFrame* outOfFlowFrame =
7638 nsPlaceholderFrame::GetRealFrameForPlaceholder(child);
7639 do {
7640 DoApplyRenderingChangeToTree(outOfFlowFrame, aViewManager,
7641 aFrameManager, aChange);
7642 } while (outOfFlowFrame = outOfFlowFrame->GetNextContinuation());
7643 } else if (childList == nsGkAtoms::popupList) {
7644 DoApplyRenderingChangeToTree(child, aViewManager,
7645 aFrameManager, aChange);
7646 } else { // regular frame
7647 if ((child->GetStateBits() & NS_FRAME_HAS_CONTAINER_LAYER) &&
7648 (aChange & nsChangeHint_RepaintFrame)) {
7649 FrameLayerBuilder::InvalidateThebesLayerContents(child,
7650 child->GetVisualOverflowRectRelativeToSelf());
7652 UpdateViewsForTree(child, aViewManager, aFrameManager, aChange);
7655 child = child->GetNextSibling();
7657 childList = aFrame->GetAdditionalChildListName(listIndex++);
7658 } while (childList);
7661 static void
7662 DoApplyRenderingChangeToTree(nsIFrame* aFrame,
7663 nsIViewManager* aViewManager,
7664 nsFrameManager* aFrameManager,
7665 nsChangeHint aChange)
7667 NS_PRECONDITION(gInApplyRenderingChangeToTree,
7668 "should only be called within ApplyRenderingChangeToTree");
7670 for ( ; aFrame; aFrame = nsLayoutUtils::GetNextContinuationOrSpecialSibling(aFrame)) {
7671 // Get view if this frame has one and trigger an update. If the
7672 // frame doesn't have a view, find the nearest containing view
7673 // (adjusting r's coordinate system to reflect the nesting) and
7674 // update there.
7675 UpdateViewsForTree(aFrame, aViewManager, aFrameManager, aChange);
7677 // if frame has view, will already be invalidated
7678 if (aChange & nsChangeHint_RepaintFrame) {
7679 if (aFrame->IsFrameOfType(nsIFrame::eSVG)) {
7680 #ifdef MOZ_SVG
7681 if (!(aFrame->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD)) {
7682 nsSVGOuterSVGFrame *outerSVGFrame = nsSVGUtils::GetOuterSVGFrame(aFrame);
7683 if (outerSVGFrame) {
7684 // We need this to invalidate frames when their 'filter' or 'marker'
7685 // property changes. XXX in theory changes to 'marker' should be
7686 // handled in nsSVGPathGeometryFrame::DidSetStyleContext, but for
7687 // some reason that's broken.
7689 // This call is also currently the only mechanism for invalidating
7690 // the area covered by a <foreignObject> when 'opacity' changes on
7691 // it or one of its ancestors. (For 'opacity' changes on <image> or
7692 // a graphical element such as <path>, or on one of their
7693 // ancestors, this is redundant since
7694 // nsSVGPathGeometryFrame::DidSetStyleContext also invalidates.)
7695 outerSVGFrame->UpdateAndInvalidateCoveredRegion(aFrame);
7698 #endif
7699 } else {
7700 aFrame->InvalidateOverflowRect();
7703 if (aChange & nsChangeHint_UpdateOpacityLayer) {
7704 aFrame->MarkLayersActive();
7705 aFrame->InvalidateLayer(aFrame->GetVisualOverflowRectRelativeToSelf(),
7706 nsDisplayItem::TYPE_OPACITY);
7709 if (aChange & nsChangeHint_UpdateTransformLayer) {
7710 aFrame->MarkLayersActive();
7711 aFrame->InvalidateLayer(aFrame->GetVisualOverflowRectRelativeToSelf(),
7712 nsDisplayItem::TYPE_TRANSFORM);
7717 static void
7718 ApplyRenderingChangeToTree(nsPresContext* aPresContext,
7719 nsIFrame* aFrame,
7720 nsChangeHint aChange)
7722 nsIPresShell *shell = aPresContext->PresShell();
7723 if (shell->IsPaintingSuppressed()) {
7724 // Don't allow synchronous rendering changes when painting is turned off.
7725 aChange = NS_SubtractHint(aChange, nsChangeHint_RepaintFrame);
7726 if (!aChange) {
7727 return;
7731 // If the frame's background is propagated to an ancestor, walk up to
7732 // that ancestor.
7733 nsStyleContext *bgSC;
7734 while (!nsCSSRendering::FindBackground(aPresContext, aFrame, &bgSC)) {
7735 aFrame = aFrame->GetParent();
7736 NS_ASSERTION(aFrame, "root frame must paint");
7739 nsIViewManager* viewManager = shell->GetViewManager();
7741 // Trigger rendering updates by damaging this frame and any
7742 // continuations of this frame.
7744 // XXX this needs to detect the need for a view due to an opacity change and deal with it...
7746 nsIViewManager::UpdateViewBatch batch(viewManager);
7748 #ifdef DEBUG
7749 gInApplyRenderingChangeToTree = PR_TRUE;
7750 #endif
7751 DoApplyRenderingChangeToTree(aFrame, viewManager, shell->FrameManager(),
7752 aChange);
7753 #ifdef DEBUG
7754 gInApplyRenderingChangeToTree = PR_FALSE;
7755 #endif
7757 batch.EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC);
7761 * This method invalidates the canvas when frames are removed or added for a
7762 * node that might have its background propagated to the canvas, i.e., a
7763 * document root node or an HTML BODY which is a child of the root node.
7765 * @param aFrame a frame for a content node about to be removed or a frame that
7766 * was just created for a content node that was inserted.
7768 static void
7769 InvalidateCanvasIfNeeded(nsIPresShell* presShell, nsIContent* node)
7771 NS_PRECONDITION(presShell->GetRootFrame(), "What happened here?");
7772 NS_PRECONDITION(presShell->GetPresContext(), "Say what?");
7774 // Note that both in ContentRemoved and ContentInserted the content node
7775 // will still have the right parent pointer, so looking at that is ok.
7777 nsIContent* parent = node->GetParent();
7778 if (parent) {
7779 // Has a parent; might not be what we want
7780 nsIContent* grandParent = parent->GetParent();
7781 if (grandParent) {
7782 // Has a grandparent, so not what we want
7783 return;
7786 // Check whether it's an HTML body
7787 if (node->Tag() != nsGkAtoms::body ||
7788 !node->IsHTML()) {
7789 return;
7793 // At this point the node has no parent or it's an HTML <body> child of the
7794 // root. We might not need to invalidate in this case (eg we might be in
7795 // XHTML or something), but chances are we want to. Play it safe.
7796 // Invalidate the viewport.
7798 // Wrap this in a DEFERRED view update batch so we don't try to
7799 // flush out layout here
7801 nsIViewManager::UpdateViewBatch batch(presShell->GetViewManager());
7802 nsIFrame* rootFrame = presShell->GetRootFrame();
7803 rootFrame->InvalidateFrameSubtree();
7804 batch.EndUpdateViewBatch(NS_VMREFRESH_DEFERRED);
7807 nsresult
7808 nsCSSFrameConstructor::StyleChangeReflow(nsIFrame* aFrame,
7809 nsChangeHint aHint)
7811 // If the frame hasn't even received an initial reflow, then don't
7812 // send it a style-change reflow!
7813 if (aFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW)
7814 return NS_OK;
7816 #ifdef DEBUG
7817 if (gNoisyContentUpdates) {
7818 printf("nsCSSFrameConstructor::StyleChangeReflow: aFrame=");
7819 nsFrame::ListTag(stdout, aFrame);
7820 printf("\n");
7822 #endif
7824 nsIPresShell::IntrinsicDirty dirtyType;
7825 if (aHint & nsChangeHint_ClearDescendantIntrinsics) {
7826 NS_ASSERTION(aHint & nsChangeHint_ClearAncestorIntrinsics,
7827 "Please read the comments in nsChangeHint.h");
7828 dirtyType = nsIPresShell::eStyleChange;
7829 } else if (aHint & nsChangeHint_ClearAncestorIntrinsics) {
7830 dirtyType = nsIPresShell::eTreeChange;
7831 } else {
7832 dirtyType = nsIPresShell::eResize;
7835 nsFrameState dirtyBits;
7836 if (aHint & nsChangeHint_NeedDirtyReflow) {
7837 dirtyBits = NS_FRAME_IS_DIRTY;
7838 } else {
7839 dirtyBits = NS_FRAME_HAS_DIRTY_CHILDREN;
7842 do {
7843 mPresShell->FrameNeedsReflow(aFrame, dirtyType, dirtyBits);
7844 aFrame = nsLayoutUtils::GetNextContinuationOrSpecialSibling(aFrame);
7845 } while (aFrame);
7847 return NS_OK;
7850 nsresult
7851 nsCSSFrameConstructor::CharacterDataChanged(nsIContent* aContent,
7852 CharacterDataChangeInfo* aInfo)
7854 AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
7855 nsresult rv = NS_OK;
7857 if ((aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE) &&
7858 !aContent->TextIsOnlyWhitespace()) ||
7859 (aContent->HasFlag(NS_REFRAME_IF_WHITESPACE) &&
7860 aContent->TextIsOnlyWhitespace())) {
7861 #ifdef DEBUG
7862 nsIFrame* frame = aContent->GetPrimaryFrame();
7863 NS_ASSERTION(!frame || !frame->IsGeneratedContentFrame(),
7864 "Bit should never be set on generated content");
7865 #endif
7866 LAYOUT_PHASE_TEMP_EXIT();
7867 nsresult rv = RecreateFramesForContent(aContent, PR_FALSE);
7868 LAYOUT_PHASE_TEMP_REENTER();
7869 return rv;
7872 // Find the child frame
7873 nsIFrame* frame = aContent->GetPrimaryFrame();
7875 // Notify the first frame that maps the content. It will generate a reflow
7876 // command
7878 // It's possible the frame whose content changed isn't inserted into the
7879 // frame hierarchy yet, or that there is no frame that maps the content
7880 if (nsnull != frame) {
7881 #if 0
7882 NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
7883 ("nsCSSFrameConstructor::CharacterDataChanged: content=%p[%s] subcontent=%p frame=%p",
7884 aContent, ContentTag(aContent, 0),
7885 aSubContent, frame));
7886 #endif
7888 // Special check for text content that is a child of a letter frame. If
7889 // this happens, we should remove the letter frame, do whatever we're
7890 // planning to do with this notification, then put the letter frame back.
7891 // Note that this is basically what RecreateFramesForContent ends up doing;
7892 // the reason we dont' want to call that here is that our text content
7893 // could be native anonymous, in which case RecreateFramesForContent would
7894 // completely barf on it. And recreating the non-anonymous ancestor would
7895 // just lead us to come back into this notification (e.g. if quotes or
7896 // counters are involved), leading to a loop.
7897 nsIFrame* block = GetFloatContainingBlock(frame);
7898 PRBool haveFirstLetterStyle = PR_FALSE;
7899 if (block) {
7900 // See if the block has first-letter style applied to it.
7901 haveFirstLetterStyle = HasFirstLetterStyle(block);
7902 if (haveFirstLetterStyle) {
7903 RemoveLetterFrames(mPresShell->GetPresContext(), mPresShell,
7904 mPresShell->FrameManager(), block);
7905 // Reget |frame|, since we might have killed it.
7906 // Do we really need to call CharacterDataChanged in this case, though?
7907 frame = aContent->GetPrimaryFrame();
7908 NS_ASSERTION(frame, "Should have frame here!");
7912 frame->CharacterDataChanged(aInfo);
7914 if (haveFirstLetterStyle) {
7915 RecoverLetterFrames(block);
7919 return rv;
7922 NS_DECLARE_FRAME_PROPERTY(ChangeListProperty, nsnull)
7924 nsresult
7925 nsCSSFrameConstructor::ProcessRestyledFrames(nsStyleChangeList& aChangeList)
7927 NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
7928 "Someone forgot a script blocker");
7929 PRInt32 count = aChangeList.Count();
7930 if (!count)
7931 return NS_OK;
7933 // Make sure to not rebuild quote or counter lists while we're
7934 // processing restyles
7935 BeginUpdate();
7937 nsPresContext* presContext = mPresShell->GetPresContext();
7938 FramePropertyTable* propTable = presContext->PropertyTable();
7940 // Mark frames so that we skip frames that die along the way, bug 123049.
7941 // A frame can be in the list multiple times with different hints. Further
7942 // optmization is possible if nsStyleChangeList::AppendChange could coalesce
7943 PRInt32 index = count;
7945 while (0 <= --index) {
7946 const nsStyleChangeData* changeData;
7947 aChangeList.ChangeAt(index, &changeData);
7948 if (changeData->mFrame) {
7949 propTable->Set(changeData->mFrame, ChangeListProperty(),
7950 NS_INT32_TO_PTR(1));
7954 index = count;
7955 PRBool didInvalidate = PR_FALSE;
7956 PRBool didReflow = PR_FALSE;
7958 while (0 <= --index) {
7959 nsIFrame* frame;
7960 nsIContent* content;
7961 nsChangeHint hint;
7962 aChangeList.ChangeAt(index, frame, content, hint);
7964 NS_ASSERTION(!(hint & nsChangeHint_ReflowFrame) ||
7965 (hint & nsChangeHint_NeedReflow),
7966 "Reflow hint bits set without actually asking for a reflow");
7968 if (frame && frame->GetContent() != content) {
7969 // XXXbz this is due to image maps messing with the primary frame of
7970 // <area>s. See bug 135040. Remove this block once that's fixed.
7971 frame = nsnull;
7972 if (!(hint & nsChangeHint_ReconstructFrame)) {
7973 continue;
7977 // skip any frame that has been destroyed due to a ripple effect
7978 if (frame) {
7979 if (!propTable->Get(frame, ChangeListProperty()))
7980 continue;
7983 if (hint & nsChangeHint_ReconstructFrame) {
7984 RecreateFramesForContent(content, PR_FALSE);
7985 } else {
7986 NS_ASSERTION(frame, "This shouldn't happen");
7987 #ifdef MOZ_SVG
7988 if (hint & nsChangeHint_UpdateEffects) {
7989 nsSVGEffects::UpdateEffects(frame);
7991 #endif
7992 if (hint & nsChangeHint_NeedReflow) {
7993 StyleChangeReflow(frame, hint);
7994 didReflow = PR_TRUE;
7996 if (hint & (nsChangeHint_RepaintFrame | nsChangeHint_SyncFrameView |
7997 nsChangeHint_UpdateOpacityLayer | nsChangeHint_UpdateTransformLayer)) {
7998 ApplyRenderingChangeToTree(presContext, frame, hint);
7999 didInvalidate = PR_TRUE;
8001 if (hint & nsChangeHint_UpdateCursor) {
8002 mPresShell->SynthesizeMouseMove(PR_FALSE);
8007 EndUpdate();
8009 if (didInvalidate && !didReflow) {
8010 // RepaintFrame changes can indicate changes in opacity etc which
8011 // can require plugin clipping to change. If we requested a reflow,
8012 // we don't need to do this since the reflow will do it for us.
8013 nsIFrame* rootFrame = mPresShell->FrameManager()->GetRootFrame();
8014 nsRootPresContext* rootPC = presContext->GetRootPresContext();
8015 if (rootPC) {
8016 rootPC->RequestUpdatePluginGeometry(rootFrame);
8020 // cleanup references and verify the style tree. Note that the latter needs
8021 // to happen once we've processed the whole list, since until then the tree
8022 // is not in fact in a consistent state.
8023 index = count;
8024 while (0 <= --index) {
8025 const nsStyleChangeData* changeData;
8026 aChangeList.ChangeAt(index, &changeData);
8027 if (changeData->mFrame) {
8028 propTable->Delete(changeData->mFrame, ChangeListProperty());
8031 #ifdef DEBUG
8032 // reget frame from content since it may have been regenerated...
8033 if (changeData->mContent) {
8034 nsIFrame* frame = changeData->mContent->GetPrimaryFrame();
8035 if (frame) {
8036 mPresShell->FrameManager()->DebugVerifyStyleTree(frame);
8038 } else {
8039 NS_WARNING("Unable to test style tree integrity -- no content node");
8041 #endif
8044 aChangeList.Clear();
8045 return NS_OK;
8048 void
8049 nsCSSFrameConstructor::RestyleElement(Element *aElement,
8050 nsIFrame *aPrimaryFrame,
8051 nsChangeHint aMinHint,
8052 RestyleTracker& aRestyleTracker,
8053 PRBool aRestyleDescendants)
8055 NS_ASSERTION(aPrimaryFrame == aElement->GetPrimaryFrame(),
8056 "frame/content mismatch");
8057 if (aPrimaryFrame && aPrimaryFrame->GetContent() != aElement) {
8058 // XXXbz this is due to image maps messing with the primary frame pointer
8059 // of <area>s. See bug 135040. We can remove this block once that's fixed.
8060 aPrimaryFrame = nsnull;
8062 NS_ASSERTION(!aPrimaryFrame || aPrimaryFrame->GetContent() == aElement,
8063 "frame/content mismatch");
8065 if (aMinHint & nsChangeHint_ReconstructFrame) {
8066 RecreateFramesForContent(aElement, PR_FALSE);
8067 } else if (aPrimaryFrame) {
8068 nsStyleChangeList changeList;
8069 mPresShell->FrameManager()->
8070 ComputeStyleChangeFor(aPrimaryFrame, &changeList, aMinHint,
8071 aRestyleTracker, aRestyleDescendants);
8072 ProcessRestyledFrames(changeList);
8073 } else {
8074 // no frames, reconstruct for content
8075 MaybeRecreateFramesForElement(aElement);
8079 nsresult
8080 nsCSSFrameConstructor::ContentStatesChanged(nsIContent* aContent1,
8081 nsIContent* aContent2,
8082 nsEventStates aStateMask)
8084 // XXXbz it would be good if this function only took Elements, but
8085 // we'd have to make ESM guarantee that usefully.
8086 if (NS_LIKELY(aContent1 && aContent1->IsElement())) {
8087 DoContentStateChanged(aContent1->AsElement(), aStateMask);
8089 if (aContent2 && aContent2->IsElement()) {
8090 DoContentStateChanged(aContent2->AsElement(), aStateMask);
8092 return NS_OK;
8095 void
8096 nsCSSFrameConstructor::DoContentStateChanged(Element* aElement,
8097 nsEventStates aStateMask)
8099 nsStyleSet *styleSet = mPresShell->StyleSet();
8100 nsPresContext *presContext = mPresShell->GetPresContext();
8101 NS_ASSERTION(styleSet, "couldn't get style set");
8103 nsChangeHint hint = NS_STYLE_HINT_NONE;
8104 // Any change to a content state that affects which frames we construct
8105 // must lead to a frame reconstruct here if we already have a frame.
8106 // Note that we never decide through non-CSS means to not create frames
8107 // based on content states, so if we already don't have a frame we don't
8108 // need to force a reframe -- if it's needed, the HasStateDependentStyle
8109 // call will handle things.
8110 nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
8111 if (primaryFrame) {
8112 // If it's generated content, ignore LOADING/etc state changes on it.
8113 if (!primaryFrame->IsGeneratedContentFrame() &&
8114 aStateMask.HasAtLeastOneOfStates(NS_EVENT_STATE_BROKEN |
8115 NS_EVENT_STATE_USERDISABLED |
8116 NS_EVENT_STATE_SUPPRESSED |
8117 NS_EVENT_STATE_LOADING)) {
8118 hint = nsChangeHint_ReconstructFrame;
8119 } else {
8120 PRUint8 app = primaryFrame->GetStyleDisplay()->mAppearance;
8121 if (app) {
8122 nsITheme *theme = presContext->GetTheme();
8123 if (theme && theme->ThemeSupportsWidget(presContext,
8124 primaryFrame, app)) {
8125 PRBool repaint = PR_FALSE;
8126 theme->WidgetStateChanged(primaryFrame, app, nsnull, &repaint);
8127 if (repaint) {
8128 NS_UpdateHint(hint, nsChangeHint_RepaintFrame);
8135 nsRestyleHint rshint =
8136 styleSet->HasStateDependentStyle(presContext, aElement, aStateMask);
8138 if (aStateMask.HasState(NS_EVENT_STATE_HOVER) && rshint != 0) {
8139 ++mHoverGeneration;
8142 if (aStateMask.HasState(NS_EVENT_STATE_VISITED)) {
8143 // Exposing information to the page about whether the link is
8144 // visited or not isn't really something we can worry about here.
8145 // FIXME: We could probably do this a bit better.
8146 NS_UpdateHint(hint, nsChangeHint_RepaintFrame);
8149 PostRestyleEvent(aElement, rshint, hint);
8152 void
8153 nsCSSFrameConstructor::AttributeWillChange(Element* aElement,
8154 PRInt32 aNameSpaceID,
8155 nsIAtom* aAttribute,
8156 PRInt32 aModType)
8158 nsRestyleHint rshint =
8159 mPresShell->StyleSet()->HasAttributeDependentStyle(mPresShell->GetPresContext(),
8160 aElement,
8161 aAttribute,
8162 aModType,
8163 PR_FALSE);
8164 PostRestyleEvent(aElement, rshint, NS_STYLE_HINT_NONE);
8167 void
8168 nsCSSFrameConstructor::AttributeChanged(Element* aElement,
8169 PRInt32 aNameSpaceID,
8170 nsIAtom* aAttribute,
8171 PRInt32 aModType)
8173 // Hold onto the PresShell to prevent ourselves from being destroyed.
8174 // XXXbz how, exactly, would this attribute change cause us to be
8175 // destroyed from inside this function?
8176 nsCOMPtr<nsIPresShell> shell = mPresShell;
8178 // Get the frame associated with the content which is the highest in the frame tree
8179 nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
8181 #if 0
8182 NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
8183 ("HTMLStyleSheet::AttributeChanged: content=%p[%s] frame=%p",
8184 aContent, ContentTag(aElement, 0), frame));
8185 #endif
8187 // the style tag has its own interpretation based on aHint
8188 nsChangeHint hint = aElement->GetAttributeChangeHint(aAttribute, aModType);
8190 PRBool reframe = (hint & nsChangeHint_ReconstructFrame) != 0;
8192 #ifdef MOZ_XUL
8193 // The following listbox widget trap prevents offscreen listbox widget
8194 // content from being removed and re-inserted (which is what would
8195 // happen otherwise).
8196 if (!primaryFrame && !reframe) {
8197 PRInt32 namespaceID;
8198 nsIAtom* tag =
8199 mDocument->BindingManager()->ResolveTag(aElement, &namespaceID);
8201 if (namespaceID == kNameSpaceID_XUL &&
8202 (tag == nsGkAtoms::listitem ||
8203 tag == nsGkAtoms::listcell))
8204 return;
8207 if (aAttribute == nsGkAtoms::tooltiptext ||
8208 aAttribute == nsGkAtoms::tooltip)
8210 nsIRootBox* rootBox = nsIRootBox::GetRootBox(mPresShell);
8211 if (rootBox) {
8212 if (aModType == nsIDOMMutationEvent::REMOVAL)
8213 rootBox->RemoveTooltipSupport(aElement);
8214 if (aModType == nsIDOMMutationEvent::ADDITION)
8215 rootBox->AddTooltipSupport(aElement);
8219 #endif // MOZ_XUL
8221 if (primaryFrame) {
8222 // See if we have appearance information for a theme.
8223 const nsStyleDisplay* disp = primaryFrame->GetStyleDisplay();
8224 if (disp->mAppearance) {
8225 nsPresContext* presContext = mPresShell->GetPresContext();
8226 nsITheme *theme = presContext->GetTheme();
8227 if (theme && theme->ThemeSupportsWidget(presContext, primaryFrame, disp->mAppearance)) {
8228 PRBool repaint = PR_FALSE;
8229 theme->WidgetStateChanged(primaryFrame, disp->mAppearance, aAttribute, &repaint);
8230 if (repaint)
8231 NS_UpdateHint(hint, nsChangeHint_RepaintFrame);
8235 // let the frame deal with it now, so we don't have to deal later
8236 primaryFrame->AttributeChanged(aNameSpaceID, aAttribute, aModType);
8237 // XXXwaterson should probably check for special IB siblings
8238 // here, and propagate the AttributeChanged notification to
8239 // them, as well. Currently, inline frames don't do anything on
8240 // this notification, so it's not that big a deal.
8243 // See if we can optimize away the style re-resolution -- must be called after
8244 // the frame's AttributeChanged() in case it does something that affects the style
8245 nsRestyleHint rshint =
8246 mPresShell->StyleSet()->HasAttributeDependentStyle(mPresShell->GetPresContext(),
8247 aElement,
8248 aAttribute,
8249 aModType,
8250 PR_TRUE);
8252 PostRestyleEvent(aElement, rshint, hint);
8255 void
8256 nsCSSFrameConstructor::BeginUpdate() {
8257 NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
8258 "Someone forgot a script blocker");
8260 nsRootPresContext* rootPresContext =
8261 mPresShell->GetPresContext()->GetRootPresContext();
8262 if (rootPresContext) {
8263 rootPresContext->IncrementDOMGeneration();
8266 ++mUpdateCount;
8269 void
8270 nsCSSFrameConstructor::EndUpdate()
8272 if (mUpdateCount == 1) {
8273 // This is the end of our last update. Before we decrement
8274 // mUpdateCount, recalc quotes and counters as needed.
8276 RecalcQuotesAndCounters();
8277 NS_ASSERTION(mUpdateCount == 1, "Odd update count");
8279 --mUpdateCount;
8282 void
8283 nsCSSFrameConstructor::RecalcQuotesAndCounters()
8285 if (mQuotesDirty) {
8286 mQuotesDirty = PR_FALSE;
8287 mQuoteList.RecalcAll();
8290 if (mCountersDirty) {
8291 mCountersDirty = PR_FALSE;
8292 mCounterManager.RecalcAll();
8295 NS_ASSERTION(!mQuotesDirty, "Quotes updates will be lost");
8296 NS_ASSERTION(!mCountersDirty, "Counter updates will be lost");
8299 void
8300 nsCSSFrameConstructor::WillDestroyFrameTree()
8302 #if defined(DEBUG_dbaron_off)
8303 mCounterManager.Dump();
8304 #endif
8306 mIsDestroyingFrameTree = PR_TRUE;
8308 // Prevent frame tree destruction from being O(N^2)
8309 mQuoteList.Clear();
8310 mCounterManager.Clear();
8312 // Remove our presshell as a style flush observer. But leave
8313 // mObservingRefreshDriver true so we don't readd to it even if someone tries
8314 // to post restyle events on us from this point on for some reason.
8315 mPresShell->GetPresContext()->RefreshDriver()->
8316 RemoveStyleFlushObserver(mPresShell);
8319 //STATIC
8321 // XXXbz I'd really like this method to go away. Once we have inline-block and
8322 // I can just use that for sized broken images, that can happen, maybe.
8323 void nsCSSFrameConstructor::GetAlternateTextFor(nsIContent* aContent,
8324 nsIAtom* aTag, // content object's tag
8325 nsXPIDLString& aAltText)
8327 // The "alt" attribute specifies alternate text that is rendered
8328 // when the image can not be displayed
8330 // If there's no "alt" attribute, and aContent is an input
8331 // element, then use the value of the "value" attribute
8332 if (!aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::alt, aAltText) &&
8333 nsGkAtoms::input == aTag) {
8334 // If there's no "value" attribute either, then use the localized string
8335 // for "Submit" as the alternate text.
8336 if (!aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::value, aAltText)) {
8337 nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
8338 "Submit", aAltText);
8343 nsresult
8344 nsCSSFrameConstructor::CreateContinuingOuterTableFrame(nsIPresShell* aPresShell,
8345 nsPresContext* aPresContext,
8346 nsIFrame* aFrame,
8347 nsIFrame* aParentFrame,
8348 nsIContent* aContent,
8349 nsStyleContext* aStyleContext,
8350 nsIFrame** aContinuingFrame)
8352 nsIFrame* newFrame = NS_NewTableOuterFrame(aPresShell, aStyleContext);
8354 if (newFrame) {
8355 newFrame->Init(aContent, aParentFrame, aFrame);
8356 nsHTMLContainerFrame::CreateViewForFrame(newFrame, PR_FALSE);
8358 // Create a continuing inner table frame, and if there's a caption then
8359 // replicate the caption
8360 nsFrameItems newChildFrames;
8362 nsIFrame* childFrame = aFrame->GetFirstChild(nsnull);
8363 if (childFrame) {
8364 nsIFrame* continuingTableFrame;
8365 nsresult rv = CreateContinuingFrame(aPresContext, childFrame, newFrame,
8366 &continuingTableFrame);
8367 if (NS_FAILED(rv)) {
8368 newFrame->Destroy();
8369 *aContinuingFrame = nsnull;
8370 return rv;
8372 newChildFrames.AddChild(continuingTableFrame);
8374 NS_ASSERTION(!childFrame->GetNextSibling(),"there can be only one inner table frame");
8377 // Set the outer table's initial child list
8378 newFrame->SetInitialChildList(nsnull, newChildFrames);
8380 *aContinuingFrame = newFrame;
8381 return NS_OK;
8383 else {
8384 *aContinuingFrame = nsnull;
8385 return NS_ERROR_OUT_OF_MEMORY;
8389 nsresult
8390 nsCSSFrameConstructor::CreateContinuingTableFrame(nsIPresShell* aPresShell,
8391 nsPresContext* aPresContext,
8392 nsIFrame* aFrame,
8393 nsIFrame* aParentFrame,
8394 nsIContent* aContent,
8395 nsStyleContext* aStyleContext,
8396 nsIFrame** aContinuingFrame)
8398 nsIFrame* newFrame = NS_NewTableFrame(aPresShell, aStyleContext);
8400 if (newFrame) {
8401 newFrame->Init(aContent, aParentFrame, aFrame);
8402 nsHTMLContainerFrame::CreateViewForFrame(newFrame, PR_FALSE);
8404 // Replicate any header/footer frames
8405 nsFrameItems childFrames;
8406 nsIFrame* childFrame = aFrame->GetFirstChild(nsnull);
8407 for ( ; childFrame; childFrame = childFrame->GetNextSibling()) {
8408 // See if it's a header/footer, possibly wrapped in a scroll frame.
8409 nsTableRowGroupFrame* rowGroupFrame =
8410 static_cast<nsTableRowGroupFrame*>(childFrame);
8411 // If the row group was continued, then don't replicate it.
8412 nsIFrame* rgNextInFlow = rowGroupFrame->GetNextInFlow();
8413 if (rgNextInFlow) {
8414 rowGroupFrame->SetRepeatable(PR_FALSE);
8416 else if (rowGroupFrame->IsRepeatable()) {
8417 // Replicate the header/footer frame.
8418 nsTableRowGroupFrame* headerFooterFrame;
8419 nsFrameItems childItems;
8420 nsFrameConstructorState state(mPresShell, mFixedContainingBlock,
8421 GetAbsoluteContainingBlock(newFrame),
8422 nsnull);
8423 state.mCreatingExtraFrames = PR_TRUE;
8425 headerFooterFrame = static_cast<nsTableRowGroupFrame*>
8426 (NS_NewTableRowGroupFrame(aPresShell, rowGroupFrame->GetStyleContext()));
8427 nsIContent* headerFooter = rowGroupFrame->GetContent();
8428 headerFooterFrame->Init(headerFooter, newFrame, nsnull);
8429 ProcessChildren(state, headerFooter, rowGroupFrame->GetStyleContext(),
8430 headerFooterFrame, PR_TRUE, childItems, PR_FALSE,
8431 nsnull);
8432 NS_ASSERTION(state.mFloatedItems.IsEmpty(), "unexpected floated element");
8433 headerFooterFrame->SetInitialChildList(nsnull, childItems);
8434 headerFooterFrame->SetRepeatable(PR_TRUE);
8436 // Table specific initialization
8437 headerFooterFrame->InitRepeatedFrame(aPresContext, rowGroupFrame);
8439 // XXX Deal with absolute and fixed frames...
8440 childFrames.AddChild(headerFooterFrame);
8444 // Set the table frame's initial child list
8445 newFrame->SetInitialChildList(nsnull, childFrames);
8447 *aContinuingFrame = newFrame;
8448 return NS_OK;
8450 else {
8451 *aContinuingFrame = nsnull;
8452 return NS_ERROR_OUT_OF_MEMORY;
8456 nsresult
8457 nsCSSFrameConstructor::CreateContinuingFrame(nsPresContext* aPresContext,
8458 nsIFrame* aFrame,
8459 nsIFrame* aParentFrame,
8460 nsIFrame** aContinuingFrame,
8461 PRBool aIsFluid)
8463 nsIPresShell* shell = aPresContext->PresShell();
8464 nsStyleContext* styleContext = aFrame->GetStyleContext();
8465 nsIFrame* newFrame = nsnull;
8466 nsresult rv = NS_OK;
8467 nsIFrame* nextContinuation = aFrame->GetNextContinuation();
8468 nsIFrame* nextInFlow = aFrame->GetNextInFlow();
8470 // Use the frame type to determine what type of frame to create
8471 nsIAtom* frameType = aFrame->GetType();
8472 nsIContent* content = aFrame->GetContent();
8474 NS_ASSERTION(aFrame->GetSplittableType() != NS_FRAME_NOT_SPLITTABLE,
8475 "why CreateContinuingFrame for a non-splittable frame?");
8477 if (nsGkAtoms::textFrame == frameType) {
8478 newFrame = NS_NewContinuingTextFrame(shell, styleContext);
8480 if (newFrame) {
8481 newFrame->Init(content, aParentFrame, aFrame);
8482 nsHTMLContainerFrame::CreateViewForFrame(newFrame, PR_FALSE);
8485 } else if (nsGkAtoms::inlineFrame == frameType) {
8486 newFrame = NS_NewInlineFrame(shell, styleContext);
8488 if (newFrame) {
8489 newFrame->Init(content, aParentFrame, aFrame);
8490 nsHTMLContainerFrame::CreateViewForFrame(newFrame, PR_FALSE);
8493 } else if (nsGkAtoms::blockFrame == frameType) {
8494 newFrame = NS_NewBlockFrame(shell, styleContext);
8496 if (newFrame) {
8497 newFrame->Init(content, aParentFrame, aFrame);
8498 nsHTMLContainerFrame::CreateViewForFrame(newFrame, PR_FALSE);
8501 #ifdef MOZ_XUL
8502 } else if (nsGkAtoms::XULLabelFrame == frameType) {
8503 newFrame = NS_NewXULLabelFrame(shell, styleContext);
8505 if (newFrame) {
8506 newFrame->Init(content, aParentFrame, aFrame);
8507 nsHTMLContainerFrame::CreateViewForFrame(newFrame, PR_FALSE);
8509 #endif
8510 } else if (nsGkAtoms::columnSetFrame == frameType) {
8511 newFrame = NS_NewColumnSetFrame(shell, styleContext, 0);
8513 if (newFrame) {
8514 newFrame->Init(content, aParentFrame, aFrame);
8515 nsHTMLContainerFrame::CreateViewForFrame(newFrame, PR_FALSE);
8518 } else if (nsGkAtoms::positionedInlineFrame == frameType) {
8519 newFrame = NS_NewPositionedInlineFrame(shell, styleContext);
8521 if (newFrame) {
8522 newFrame->Init(content, aParentFrame, aFrame);
8523 nsHTMLContainerFrame::CreateViewForFrame(newFrame, PR_FALSE);
8526 } else if (nsGkAtoms::pageFrame == frameType) {
8527 nsIFrame* canvasFrame;
8528 rv = ConstructPageFrame(shell, aPresContext, aParentFrame, aFrame,
8529 newFrame, canvasFrame);
8530 } else if (nsGkAtoms::tableOuterFrame == frameType) {
8531 rv = CreateContinuingOuterTableFrame(shell, aPresContext, aFrame, aParentFrame,
8532 content, styleContext, &newFrame);
8534 } else if (nsGkAtoms::tableFrame == frameType) {
8535 rv = CreateContinuingTableFrame(shell, aPresContext, aFrame, aParentFrame,
8536 content, styleContext, &newFrame);
8538 } else if (nsGkAtoms::tableRowGroupFrame == frameType) {
8539 newFrame = NS_NewTableRowGroupFrame(shell, styleContext);
8541 if (newFrame) {
8542 newFrame->Init(content, aParentFrame, aFrame);
8543 nsHTMLContainerFrame::CreateViewForFrame(newFrame, PR_FALSE);
8546 } else if (nsGkAtoms::tableRowFrame == frameType) {
8547 newFrame = NS_NewTableRowFrame(shell, styleContext);
8549 if (newFrame) {
8550 newFrame->Init(content, aParentFrame, aFrame);
8551 nsHTMLContainerFrame::CreateViewForFrame(newFrame, PR_FALSE);
8553 // Create a continuing frame for each table cell frame
8554 nsFrameItems newChildList;
8555 nsIFrame* cellFrame = aFrame->GetFirstChild(nsnull);
8556 while (cellFrame) {
8557 // See if it's a table cell frame
8558 if (IS_TABLE_CELL(cellFrame->GetType())) {
8559 nsIFrame* continuingCellFrame;
8560 rv = CreateContinuingFrame(aPresContext, cellFrame, newFrame,
8561 &continuingCellFrame);
8562 if (NS_FAILED(rv)) {
8563 newChildList.DestroyFrames();
8564 newFrame->Destroy();
8565 *aContinuingFrame = nsnull;
8566 return NS_ERROR_OUT_OF_MEMORY;
8568 newChildList.AddChild(continuingCellFrame);
8570 cellFrame = cellFrame->GetNextSibling();
8573 // Set the table cell's initial child list
8574 newFrame->SetInitialChildList(nsnull, newChildList);
8577 } else if (IS_TABLE_CELL(frameType)) {
8578 // Warning: If you change this and add a wrapper frame around table cell
8579 // frames, make sure Bug 368554 doesn't regress!
8580 // See IsInAutoWidthTableCellForQuirk() in nsImageFrame.cpp.
8581 newFrame = NS_NewTableCellFrame(shell, styleContext, IsBorderCollapse(aParentFrame));
8583 if (newFrame) {
8584 newFrame->Init(content, aParentFrame, aFrame);
8585 nsHTMLContainerFrame::CreateViewForFrame(newFrame, PR_FALSE);
8587 // Create a continuing area frame
8588 nsIFrame* continuingBlockFrame;
8589 nsIFrame* blockFrame = aFrame->GetFirstChild(nsnull);
8590 rv = CreateContinuingFrame(aPresContext, blockFrame, newFrame,
8591 &continuingBlockFrame);
8592 if (NS_FAILED(rv)) {
8593 newFrame->Destroy();
8594 *aContinuingFrame = nsnull;
8595 return rv;
8598 // Set the table cell's initial child list
8599 SetInitialSingleChild(newFrame, continuingBlockFrame);
8602 } else if (nsGkAtoms::lineFrame == frameType) {
8603 newFrame = NS_NewFirstLineFrame(shell, styleContext);
8605 if (newFrame) {
8606 newFrame->Init(content, aParentFrame, aFrame);
8607 nsHTMLContainerFrame::CreateViewForFrame(newFrame, PR_FALSE);
8610 } else if (nsGkAtoms::letterFrame == frameType) {
8611 newFrame = NS_NewFirstLetterFrame(shell, styleContext);
8613 if (newFrame) {
8614 newFrame->Init(content, aParentFrame, aFrame);
8615 nsHTMLContainerFrame::CreateViewForFrame(newFrame, PR_FALSE);
8618 } else if (nsGkAtoms::imageFrame == frameType) {
8619 newFrame = NS_NewImageFrame(shell, styleContext);
8621 if (newFrame) {
8622 newFrame->Init(content, aParentFrame, aFrame);
8624 } else if (nsGkAtoms::imageControlFrame == frameType) {
8625 newFrame = NS_NewImageControlFrame(shell, styleContext);
8627 if (newFrame) {
8628 newFrame->Init(content, aParentFrame, aFrame);
8630 } else if (nsGkAtoms::placeholderFrame == frameType) {
8631 // create a continuing out of flow frame
8632 nsIFrame* oofFrame = nsPlaceholderFrame::GetRealFrameForPlaceholder(aFrame);
8633 nsIFrame* oofContFrame;
8634 rv = CreateContinuingFrame(aPresContext, oofFrame, aParentFrame, &oofContFrame);
8635 if (NS_FAILED(rv)) {
8636 *aContinuingFrame = nsnull;
8637 return rv;
8639 // create a continuing placeholder frame
8640 rv = CreatePlaceholderFrameFor(shell, content, oofContFrame, styleContext,
8641 aParentFrame, aFrame,
8642 aFrame->GetStateBits() & PLACEHOLDER_TYPE_MASK,
8643 &newFrame);
8644 if (NS_FAILED(rv)) {
8645 oofContFrame->Destroy();
8646 *aContinuingFrame = nsnull;
8647 return rv;
8649 } else if (nsGkAtoms::fieldSetFrame == frameType) {
8650 newFrame = NS_NewFieldSetFrame(shell, styleContext);
8652 if (newFrame) {
8653 newFrame->Init(content, aParentFrame, aFrame);
8655 nsHTMLContainerFrame::CreateViewForFrame(newFrame, PR_FALSE);
8657 // Create a continuing area frame
8658 // XXXbz we really shouldn't have to do this by hand!
8659 nsIFrame* continuingBlockFrame;
8660 nsIFrame* blockFrame = GetFieldSetBlockFrame(aFrame);
8661 rv = CreateContinuingFrame(aPresContext, blockFrame, newFrame,
8662 &continuingBlockFrame);
8663 if (NS_FAILED(rv)) {
8664 newFrame->Destroy();
8665 *aContinuingFrame = nsnull;
8666 return rv;
8668 // Set the fieldset's initial child list
8669 SetInitialSingleChild(newFrame, continuingBlockFrame);
8671 } else if (nsGkAtoms::legendFrame == frameType) {
8672 newFrame = NS_NewLegendFrame(shell, styleContext);
8674 if (newFrame) {
8675 newFrame->Init(content, aParentFrame, aFrame);
8676 nsHTMLContainerFrame::CreateViewForFrame(newFrame, PR_FALSE);
8678 } else {
8679 NS_NOTREACHED("unexpected frame type");
8680 *aContinuingFrame = nsnull;
8681 return NS_ERROR_UNEXPECTED;
8684 *aContinuingFrame = newFrame;
8686 if (!newFrame) {
8687 return NS_ERROR_OUT_OF_MEMORY;
8690 // Init() set newFrame to be a fluid continuation of aFrame.
8691 // If we want a non-fluid continuation, we need to call SetPrevContinuation()
8692 // to reset NS_FRAME_IS_FLUID_CONTINUATION.
8693 if (!aIsFluid) {
8694 newFrame->SetPrevContinuation(aFrame);
8697 // A continuation of generated content is also generated content
8698 if (aFrame->GetStateBits() & NS_FRAME_GENERATED_CONTENT) {
8699 newFrame->AddStateBits(NS_FRAME_GENERATED_CONTENT);
8702 // A continuation of an out-of-flow is also an out-of-flow
8703 if (aFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
8704 newFrame->AddStateBits(NS_FRAME_OUT_OF_FLOW);
8707 if (nextInFlow) {
8708 nextInFlow->SetPrevInFlow(newFrame);
8709 newFrame->SetNextInFlow(nextInFlow);
8710 } else if (nextContinuation) {
8711 nextContinuation->SetPrevContinuation(newFrame);
8712 newFrame->SetNextContinuation(nextContinuation);
8715 NS_POSTCONDITION(!newFrame->GetNextSibling(), "unexpected sibling");
8716 return NS_OK;
8719 nsresult
8720 nsCSSFrameConstructor::ReplicateFixedFrames(nsPageContentFrame* aParentFrame)
8722 // Now deal with fixed-pos things.... They should appear on all pages,
8723 // so we want to move over the placeholders when processing the child
8724 // of the pageContentFrame.
8726 nsIFrame* prevPageContentFrame = aParentFrame->GetPrevInFlow();
8727 if (!prevPageContentFrame) {
8728 return NS_OK;
8730 nsIFrame* canvasFrame = aParentFrame->GetFirstChild(nsnull);
8731 nsIFrame* prevCanvasFrame = prevPageContentFrame->GetFirstChild(nsnull);
8732 if (!canvasFrame || !prevCanvasFrame) {
8733 // document's root element frame missing
8734 return NS_ERROR_UNEXPECTED;
8737 nsFrameItems fixedPlaceholders;
8738 nsIFrame* firstFixed = prevPageContentFrame->GetFirstChild(nsGkAtoms::fixedList);
8739 if (!firstFixed) {
8740 return NS_OK;
8743 // Don't allow abs-pos descendants of the fixed content to escape the content.
8744 // This should not normally be possible (because fixed-pos elements should
8745 // be absolute containers) but fixed-pos tables currently aren't abs-pos
8746 // containers.
8747 nsFrameConstructorState state(mPresShell, aParentFrame,
8748 nsnull,
8749 mRootElementFrame);
8750 state.mCreatingExtraFrames = PR_TRUE;
8752 // Iterate across fixed frames and replicate each whose placeholder is a
8753 // descendant of aFrame. (We don't want to explicitly copy placeholders that
8754 // are within fixed frames, because that would cause duplicates on the new
8755 // page - bug 389619)
8756 for (nsIFrame* fixed = firstFixed; fixed; fixed = fixed->GetNextSibling()) {
8757 nsIFrame* prevPlaceholder = mPresShell->FrameManager()->GetPlaceholderFrameFor(fixed);
8758 if (prevPlaceholder &&
8759 nsLayoutUtils::IsProperAncestorFrame(prevCanvasFrame, prevPlaceholder)) {
8760 nsresult rv = ConstructFrame(state, fixed->GetContent(),
8761 canvasFrame, fixedPlaceholders);
8762 NS_ENSURE_SUCCESS(rv, rv);
8766 // Add the placeholders to our primary child list.
8767 // XXXbz this is a little screwed up, since the fixed frames will have
8768 // broken auto-positioning. Oh, well.
8769 NS_ASSERTION(!canvasFrame->GetFirstChild(nsnull),
8770 "leaking frames; doc root continuation must be empty");
8771 canvasFrame->SetInitialChildList(nsnull, fixedPlaceholders);
8772 return NS_OK;
8775 nsresult
8776 nsCSSFrameConstructor::GetInsertionPoint(nsIFrame* aParentFrame,
8777 nsIContent* aChildContent,
8778 nsIFrame** aInsertionPoint,
8779 PRBool* aMultiple)
8781 // Make the insertion point be the parent frame by default, in case
8782 // we have to bail early.
8783 *aInsertionPoint = aParentFrame;
8785 nsIContent* container = aParentFrame->GetContent();
8786 if (!container)
8787 return NS_OK;
8789 nsBindingManager *bindingManager = mDocument->BindingManager();
8791 nsIContent* insertionElement;
8792 if (aChildContent) {
8793 // We've got an explicit insertion child. Check to see if it's
8794 // anonymous.
8795 if (aChildContent->GetBindingParent() == container) {
8796 // This child content is anonymous. Don't use the insertion
8797 // point, since that's only for the explicit kids.
8798 return NS_OK;
8801 PRUint32 index;
8802 insertionElement = bindingManager->GetInsertionPoint(container,
8803 aChildContent,
8804 &index);
8806 else {
8807 PRBool multiple;
8808 PRUint32 index;
8809 insertionElement = bindingManager->GetSingleInsertionPoint(container,
8810 &index,
8811 &multiple);
8812 if (multiple && aMultiple)
8813 *aMultiple = multiple; // Record the fact that filters are in use.
8816 if (insertionElement) {
8817 nsIFrame* insertionPoint = insertionElement->GetPrimaryFrame();
8818 if (insertionPoint) {
8819 // Use the content insertion frame of the insertion point.
8820 insertionPoint = insertionPoint->GetContentInsertionFrame();
8821 if (insertionPoint && insertionPoint != aParentFrame)
8822 GetInsertionPoint(insertionPoint, aChildContent, aInsertionPoint, aMultiple);
8824 else {
8825 // There was no frame created yet for the insertion point.
8826 *aInsertionPoint = nsnull;
8830 // fieldsets have multiple insertion points. Note that we might
8831 // have to look at insertionElement here...
8832 if (aMultiple && !*aMultiple) {
8833 nsIContent* content = insertionElement ? insertionElement : container;
8834 if (content->IsHTML() &&
8835 content->Tag() == nsGkAtoms::fieldset) {
8836 *aMultiple = PR_TRUE;
8840 return NS_OK;
8843 // Capture state for the frame tree rooted at the frame associated with the
8844 // content object, aContent
8845 nsresult
8846 nsCSSFrameConstructor::CaptureStateForFramesOf(nsIContent* aContent,
8847 nsILayoutHistoryState* aHistoryState)
8849 nsIFrame* frame = aContent->GetPrimaryFrame();
8850 if (frame == mRootElementFrame) {
8851 frame = mFixedContainingBlock;
8853 if (frame) {
8854 CaptureStateFor(frame, aHistoryState);
8856 return NS_OK;
8859 // Capture state for the frame tree rooted at aFrame.
8860 nsresult
8861 nsCSSFrameConstructor::CaptureStateFor(nsIFrame* aFrame,
8862 nsILayoutHistoryState* aHistoryState)
8864 if (aFrame && aHistoryState) {
8865 mPresShell->FrameManager()->CaptureFrameState(aFrame, aHistoryState);
8867 return NS_OK;
8870 nsresult
8871 nsCSSFrameConstructor::MaybeRecreateFramesForElement(Element* aElement)
8873 nsresult result = NS_OK;
8874 nsFrameManager *frameManager = mPresShell->FrameManager();
8876 nsStyleContext *oldContext = frameManager->GetUndisplayedContent(aElement);
8877 if (oldContext) {
8878 // The parent has a frame, so try resolving a new context.
8879 nsRefPtr<nsStyleContext> newContext = mPresShell->StyleSet()->
8880 ResolveStyleFor(aElement, oldContext->GetParent());
8882 frameManager->ChangeUndisplayedContent(aElement, newContext);
8883 if (newContext->GetStyleDisplay()->mDisplay != NS_STYLE_DISPLAY_NONE) {
8884 result = RecreateFramesForContent(aElement, PR_FALSE);
8887 return result;
8890 static nsIFrame*
8891 FindFirstNonWhitespaceChild(nsIFrame* aParentFrame)
8893 nsIFrame* f = aParentFrame->GetFirstChild(nsnull);
8894 while (f && f->GetType() == nsGkAtoms::textFrame &&
8895 f->GetContent()->TextIsOnlyWhitespace()) {
8896 f = f->GetNextSibling();
8898 return f;
8901 static nsIFrame*
8902 FindNextNonWhitespaceSibling(nsIFrame* aFrame)
8904 nsIFrame* f = aFrame;
8905 do {
8906 f = f->GetNextSibling();
8907 } while (f && f->GetType() == nsGkAtoms::textFrame &&
8908 f->GetContent()->TextIsOnlyWhitespace());
8909 return f;
8912 PRBool
8913 nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame,
8914 nsresult* aResult)
8916 NS_PRECONDITION(aFrame, "Must have a frame");
8917 NS_PRECONDITION(aFrame->GetParent(), "Frame shouldn't be root");
8918 NS_PRECONDITION(aResult, "Null out param?");
8919 NS_PRECONDITION(aFrame == aFrame->GetFirstContinuation(),
8920 "aFrame not the result of GetPrimaryFrame()?");
8922 if (IsFrameSpecial(aFrame)) {
8923 // The removal functions can't handle removal of an {ib} split directly; we
8924 // need to rebuild the containing block.
8925 #ifdef DEBUG
8926 if (gNoisyContentUpdates) {
8927 printf("nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval: "
8928 "frame=");
8929 nsFrame::ListTag(stdout, aFrame);
8930 printf(" is special\n");
8932 #endif
8934 *aResult = ReframeContainingBlock(aFrame);
8935 return PR_TRUE;
8938 if (aFrame->GetType() == nsGkAtoms::legendFrame &&
8939 aFrame->GetParent()->GetType() == nsGkAtoms::fieldSetFrame) {
8940 // When we remove the legend for a fieldset, we should reframe
8941 // the fieldset to ensure another legend is used, if there is one
8942 *aResult = RecreateFramesForContent(aFrame->GetParent()->GetContent(), PR_FALSE);
8943 return PR_TRUE;
8946 // Now check for possibly needing to reconstruct due to a pseudo parent
8947 nsIFrame* inFlowFrame =
8948 (aFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) ?
8949 mPresShell->FrameManager()->GetPlaceholderFrameFor(aFrame) : aFrame;
8950 NS_ASSERTION(inFlowFrame, "How did that happen?");
8951 nsIFrame* parent = inFlowFrame->GetParent();
8952 if (IsTablePseudo(parent)) {
8953 if (FindFirstNonWhitespaceChild(parent) == inFlowFrame ||
8954 !FindNextNonWhitespaceSibling(inFlowFrame->GetLastContinuation()) ||
8955 // If we're a table-column-group, then the GetFirstChild check above is
8956 // not going to catch cases when we're the first child.
8957 (inFlowFrame->GetType() == nsGkAtoms::tableColGroupFrame &&
8958 parent->GetFirstChild(nsGkAtoms::colGroupList) == inFlowFrame) ||
8959 // Similar if we're a table-caption.
8960 (inFlowFrame->GetType() == nsGkAtoms::tableCaptionFrame &&
8961 parent->GetFirstChild(nsGkAtoms::captionList) == inFlowFrame)) {
8962 // We're the first or last frame in the pseudo. Need to reframe.
8963 // Good enough to recreate frames for |parent|'s content
8964 *aResult = RecreateFramesForContent(parent->GetContent(), PR_TRUE);
8965 return PR_TRUE;
8969 // Might need to reconstruct things if this frame's nextSibling is a table
8970 // pseudo, since removal of this frame might mean that this pseudo needs to
8971 // get merged with the frame's prevSibling.
8972 // XXXbz it would be really nice if we had the prevSibling here too, to check
8973 // whether this is in fact the case...
8974 nsIFrame* nextSibling =
8975 FindNextNonWhitespaceSibling(inFlowFrame->GetLastContinuation());
8976 NS_ASSERTION(!IsTablePseudo(inFlowFrame), "Shouldn't happen here");
8977 if (nextSibling && IsTablePseudo(nextSibling)) {
8978 #ifdef DEBUG
8979 if (gNoisyContentUpdates) {
8980 printf("nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval: "
8981 "frame=");
8982 nsFrame::ListTag(stdout, aFrame);
8983 printf(" has a table pseudo next sibling of different type\n");
8985 #endif
8986 // Good enough to recreate frames for aFrame's parent's content; even if
8987 // aFrame's parent is a table pseudo, that'll be the right content node.
8988 *aResult = RecreateFramesForContent(parent->GetContent(), PR_TRUE);
8989 return PR_TRUE;
8992 #ifdef MOZ_XUL
8993 if (aFrame->GetType() == nsGkAtoms::popupSetFrame) {
8994 nsIRootBox* rootBox = nsIRootBox::GetRootBox(mPresShell);
8995 if (rootBox && rootBox->GetPopupSetFrame() == aFrame) {
8996 *aResult = ReconstructDocElementHierarchy();
8997 return PR_TRUE;
9000 #endif
9002 // We might still need to reconstruct things if the parent of inFlowFrame is
9003 // special, since in that case the removal of aFrame might affect the
9004 // splitting of its parent.
9005 if (!IsFrameSpecial(parent)) {
9006 return PR_FALSE;
9009 // If inFlowFrame is not the only in-flow child of |parent|, then removing
9010 // it will change nothing about the {ib} split.
9011 if (inFlowFrame != parent->GetFirstChild(nsnull) ||
9012 inFlowFrame->GetLastContinuation()->GetNextSibling()) {
9013 return PR_FALSE;
9016 // If the parent is the first or last part of the {ib} split, then
9017 // removing one of its kids will have no effect on the splitting.
9018 // Get the first continuation up front so we don't have to do it twice.
9019 nsIFrame* parentFirstContinuation = parent->GetFirstContinuation();
9020 if (!GetSpecialSibling(parentFirstContinuation) ||
9021 !GetSpecialPrevSibling(parentFirstContinuation)) {
9022 return PR_FALSE;
9025 #ifdef DEBUG
9026 if (gNoisyContentUpdates) {
9027 printf("nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval: "
9028 "frame=");
9029 nsFrame::ListTag(stdout, parent);
9030 printf(" is special\n");
9032 #endif
9034 *aResult = ReframeContainingBlock(parent);
9035 return PR_TRUE;
9038 nsresult
9039 nsCSSFrameConstructor::RecreateFramesForContent(nsIContent* aContent,
9040 PRBool aAsyncInsert)
9042 NS_PRECONDITION(!aAsyncInsert || aContent->IsElement(),
9043 "Can only insert elements async");
9044 // If there is no document, we don't want to recreate frames for it. (You
9045 // shouldn't generally be giving this method content without a document
9046 // anyway).
9047 // Rebuilding the frame tree can have bad effects, especially if it's the
9048 // frame tree for chrome (see bug 157322).
9049 NS_ENSURE_TRUE(aContent->GetDocument(), NS_ERROR_FAILURE);
9051 // Is the frame `special'? If so, we need to reframe the containing
9052 // block *here*, rather than trying to remove and re-insert the
9053 // content (which would otherwise result in *two* nested reframe
9054 // containing block from ContentRemoved() and ContentInserted(),
9055 // below!). We'd really like to optimize away one of those
9056 // containing block reframes, hence the code here.
9058 nsIFrame* frame = aContent->GetPrimaryFrame();
9059 if (frame && frame->IsFrameOfType(nsIFrame::eMathML)) {
9060 // Reframe the topmost MathML element to prevent exponential blowup
9061 // (see bug 397518)
9062 while (PR_TRUE) {
9063 nsIContent* parentContent = aContent->GetParent();
9064 nsIFrame* parentContentFrame = parentContent->GetPrimaryFrame();
9065 if (!parentContentFrame || !parentContentFrame->IsFrameOfType(nsIFrame::eMathML))
9066 break;
9067 aContent = parentContent;
9068 frame = parentContentFrame;
9072 if (frame) {
9073 nsIFrame* nonGeneratedAncestor = nsLayoutUtils::GetNonGeneratedAncestor(frame);
9074 if (nonGeneratedAncestor->GetContent() != aContent) {
9075 return RecreateFramesForContent(nonGeneratedAncestor->GetContent(), aAsyncInsert);
9078 nsIFrame* parent = frame->GetParent();
9079 nsIContent* parentContent = parent ? parent->GetContent() : nsnull;
9080 // If the parent frame is a leaf then the subsequent insert will fail to
9081 // create a frame, so we need to recreate the parent content. This happens
9082 // with native anonymous content from the editor.
9083 if (parent && parent->IsLeaf() && parentContent &&
9084 parentContent != aContent) {
9085 return RecreateFramesForContent(parentContent, aAsyncInsert);
9089 nsresult rv = NS_OK;
9091 if (frame && MaybeRecreateContainerForFrameRemoval(frame, &rv)) {
9092 return rv;
9095 nsINode* containerNode = aContent->GetNodeParent();
9096 // XXXbz how can containerNode be null here?
9097 if (containerNode) {
9098 // Before removing the frames associated with the content object,
9099 // ask them to save their state onto a temporary state object.
9100 CaptureStateForFramesOf(aContent, mTempFrameTreeState);
9102 // Need the nsIContent parent, which might be null here, since we need to
9103 // pass it to ContentInserted and ContentRemoved.
9104 nsCOMPtr<nsIContent> container = aContent->GetParent();
9106 // Remove the frames associated with the content object.
9107 PRBool didReconstruct;
9108 rv = ContentRemoved(container, aContent,
9109 aContent->IsRootOfAnonymousSubtree() ?
9110 nsnull :
9111 aContent->GetNextSibling(),
9112 REMOVE_FOR_RECONSTRUCTION, &didReconstruct);
9114 if (NS_SUCCEEDED(rv) && !didReconstruct) {
9115 // Now, recreate the frames associated with this content object. If
9116 // ContentRemoved triggered reconstruction, then we don't need to do this
9117 // because the frames will already have been built.
9118 if (aAsyncInsert) {
9119 PostRestyleEvent(aContent->AsElement(), nsRestyleHint(0),
9120 nsChangeHint_ReconstructFrame);
9121 } else {
9122 rv = ContentInserted(container, aContent, mTempFrameTreeState, PR_FALSE);
9127 return rv;
9130 //////////////////////////////////////////////////////////////////////
9132 // Block frame construction code
9134 already_AddRefed<nsStyleContext>
9135 nsCSSFrameConstructor::GetFirstLetterStyle(nsIContent* aContent,
9136 nsStyleContext* aStyleContext)
9138 if (aContent) {
9139 return mPresShell->StyleSet()->
9140 ResolvePseudoElementStyle(aContent->AsElement(),
9141 nsCSSPseudoElements::ePseudo_firstLetter,
9142 aStyleContext);
9144 return nsnull;
9147 already_AddRefed<nsStyleContext>
9148 nsCSSFrameConstructor::GetFirstLineStyle(nsIContent* aContent,
9149 nsStyleContext* aStyleContext)
9151 if (aContent) {
9152 return mPresShell->StyleSet()->
9153 ResolvePseudoElementStyle(aContent->AsElement(),
9154 nsCSSPseudoElements::ePseudo_firstLine,
9155 aStyleContext);
9157 return nsnull;
9160 // Predicate to see if a given content (block element) has
9161 // first-letter style applied to it.
9162 PRBool
9163 nsCSSFrameConstructor::ShouldHaveFirstLetterStyle(nsIContent* aContent,
9164 nsStyleContext* aStyleContext)
9166 return nsLayoutUtils::HasPseudoStyle(aContent, aStyleContext,
9167 nsCSSPseudoElements::ePseudo_firstLetter,
9168 mPresShell->GetPresContext());
9171 PRBool
9172 nsCSSFrameConstructor::HasFirstLetterStyle(nsIFrame* aBlockFrame)
9174 NS_PRECONDITION(aBlockFrame, "Need a frame");
9175 NS_ASSERTION(nsLayoutUtils::GetAsBlock(aBlockFrame),
9176 "Not a block frame?");
9178 return (aBlockFrame->GetStateBits() & NS_BLOCK_HAS_FIRST_LETTER_STYLE) != 0;
9181 PRBool
9182 nsCSSFrameConstructor::ShouldHaveFirstLineStyle(nsIContent* aContent,
9183 nsStyleContext* aStyleContext)
9185 PRBool hasFirstLine =
9186 nsLayoutUtils::HasPseudoStyle(aContent, aStyleContext,
9187 nsCSSPseudoElements::ePseudo_firstLine,
9188 mPresShell->GetPresContext());
9189 if (hasFirstLine) {
9190 // But disable for fieldsets
9191 PRInt32 namespaceID;
9192 nsIAtom* tag = mDocument->BindingManager()->ResolveTag(aContent,
9193 &namespaceID);
9194 // This check must match the one in FindHTMLData.
9195 hasFirstLine = tag != nsGkAtoms::fieldset ||
9196 namespaceID != kNameSpaceID_XHTML;
9199 return hasFirstLine;
9202 void
9203 nsCSSFrameConstructor::ShouldHaveSpecialBlockStyle(nsIContent* aContent,
9204 nsStyleContext* aStyleContext,
9205 PRBool* aHaveFirstLetterStyle,
9206 PRBool* aHaveFirstLineStyle)
9208 *aHaveFirstLetterStyle =
9209 ShouldHaveFirstLetterStyle(aContent, aStyleContext);
9210 *aHaveFirstLineStyle =
9211 ShouldHaveFirstLineStyle(aContent, aStyleContext);
9214 /* static */
9215 const nsCSSFrameConstructor::PseudoParentData
9216 nsCSSFrameConstructor::sPseudoParentData[eParentTypeCount] = {
9217 { // Cell
9218 FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET |
9219 FCDATA_USE_CHILD_ITEMS |
9220 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRow),
9221 &nsCSSFrameConstructor::ConstructTableCell),
9222 &nsCSSAnonBoxes::tableCell
9224 { // Row
9225 FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET |
9226 FCDATA_USE_CHILD_ITEMS |
9227 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRowGroup),
9228 &nsCSSFrameConstructor::ConstructTableRow),
9229 &nsCSSAnonBoxes::tableRow
9231 { // Row group
9232 FCDATA_DECL(FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET |
9233 FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_USE_CHILD_ITEMS |
9234 FCDATA_SKIP_ABSPOS_PUSH |
9235 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
9236 NS_NewTableRowGroupFrame),
9237 &nsCSSAnonBoxes::tableRowGroup
9239 { // Column group
9240 FCDATA_DECL(FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET |
9241 FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_USE_CHILD_ITEMS |
9242 FCDATA_SKIP_ABSPOS_PUSH |
9243 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
9244 NS_NewTableColGroupFrame),
9245 &nsCSSAnonBoxes::tableColGroup
9247 { // Table
9248 FULL_CTOR_FCDATA(FCDATA_SKIP_FRAMESET | FCDATA_USE_CHILD_ITEMS,
9249 &nsCSSFrameConstructor::ConstructTable),
9250 &nsCSSAnonBoxes::table
9255 * This function works as follows: we walk through the child list (aItems) and
9256 * find items that cannot have aParentFrame as their parent. We wrap
9257 * continuous runs of such items into a FrameConstructionItem for a frame that
9258 * gets them closer to their desired parents. For example, a run of non-row
9259 * children of a row-group will get wrapped in a row. When we later construct
9260 * the frame for this wrapper (in this case for the row), it'll be the correct
9261 * parent for the cells in the set of items we wrapped or we'll wrap cells
9262 * around everything else. At the end of this method, aItems is guaranteed to
9263 * contain only items for frames that can be direct kids of aParentFrame.
9265 nsresult
9266 nsCSSFrameConstructor::CreateNeededTablePseudos(nsFrameConstructorState& aState,
9267 FrameConstructionItemList& aItems,
9268 nsIFrame* aParentFrame)
9270 ParentType ourParentType = GetParentType(aParentFrame);
9271 if (aItems.AllWantParentType(ourParentType)) {
9272 // Nothing to do here
9273 return NS_OK;
9276 FCItemIterator iter(aItems);
9277 do {
9278 if (iter.SkipItemsWantingParentType(ourParentType)) {
9279 // Nothing else to do here; we're finished
9280 return NS_OK;
9283 // Now we're pointing to the first child that wants a different parent
9284 // type.
9286 // Now try to figure out what kids we can group together. We can generally
9287 // group everything that has a different desired parent type from us. Two
9288 // exceptions to this:
9289 // 1) If our parent type is table, we can't group columns with anything
9290 // else other than whitespace.
9291 // 2) Whitespace that lies between two things we can group which both want
9292 // a non-block parent should be dropped, even if we can't group them
9293 // with each other and even if the whitespace wants a parent of
9294 // ourParentType. Ends of the list count as things that don't want a
9295 // block parent (so that for example we'll drop a whitespace-only list).
9297 FCItemIterator endIter(iter); /* iterator to find the end of the group */
9298 ParentType groupingParentType = endIter.item().DesiredParentType();
9299 if (aItems.AllWantParentType(groupingParentType) &&
9300 groupingParentType != eTypeBlock) {
9301 // Just group them all and be done with it. We need the check for
9302 // eTypeBlock here to catch the "all the items are whitespace" case
9303 // described above.
9304 endIter.SetToEnd();
9305 } else {
9306 // Locate the end of the group.
9308 // Keep track of the type the previous item wanted, in case we have to
9309 // deal with whitespace. Start it off with ourParentType, since that's
9310 // the last thing |iter| would have skipped over.
9311 ParentType prevParentType = ourParentType;
9312 do {
9313 /* Walk an iterator past any whitespace that we might be able to drop from the list */
9314 FCItemIterator spaceEndIter(endIter);
9315 if (prevParentType != eTypeBlock &&
9316 !aParentFrame->IsGeneratedContentFrame() &&
9317 spaceEndIter.item().IsWhitespace(aState)) {
9318 PRBool trailingSpaces = spaceEndIter.SkipWhitespace(aState);
9320 // See whether we can drop the whitespace
9321 if (trailingSpaces ||
9322 spaceEndIter.item().DesiredParentType() != eTypeBlock) {
9323 PRBool updateStart = (iter == endIter);
9324 endIter.DeleteItemsTo(spaceEndIter);
9325 NS_ASSERTION(trailingSpaces == endIter.IsDone(), "These should match");
9327 if (updateStart) {
9328 iter = endIter;
9331 if (trailingSpaces) {
9332 break; /* Found group end */
9335 if (updateStart) {
9336 // Update groupingParentType, since it might have been eTypeBlock
9337 // just because of the whitespace.
9338 groupingParentType = iter.item().DesiredParentType();
9343 // Now endIter points to a non-whitespace item or a non-droppable
9344 // whitespace item. In the latter case, if this is the end of the group
9345 // we'll traverse this whitespace again. But it'll all just be quick
9346 // DesiredParentType() checks which will match ourParentType (that's
9347 // what it means that this is the group end), so it's OK.
9348 prevParentType = endIter.item().DesiredParentType();
9349 if (prevParentType == ourParentType) {
9350 // End the group at endIter.
9351 break;
9354 if (ourParentType == eTypeTable &&
9355 (prevParentType == eTypeColGroup) !=
9356 (groupingParentType == eTypeColGroup)) {
9357 // Either we started with columns and now found something else, or vice
9358 // versa. In any case, end the grouping.
9359 break;
9362 // Include the whitespace we didn't drop (if any) in the group, since
9363 // this is not the end of the group. Note that this doesn't change
9364 // prevParentType, since if we didn't drop the whitespace then we ended
9365 // at something that wants a block parent.
9366 endIter = spaceEndIter;
9368 endIter.Next();
9369 } while (!endIter.IsDone());
9372 if (iter == endIter) {
9373 // Nothing to wrap here; just skipped some whitespace
9374 continue;
9377 // Now group together all the items between iter and endIter. The right
9378 // parent type to use depends on ourParentType.
9379 ParentType wrapperType;
9380 switch (ourParentType) {
9381 case eTypeBlock:
9382 wrapperType = eTypeTable;
9383 break;
9384 case eTypeRow:
9385 // The parent type for a cell is eTypeBlock, since that's what a cell
9386 // looks like to its kids.
9387 wrapperType = eTypeBlock;
9388 break;
9389 case eTypeRowGroup:
9390 wrapperType = eTypeRow;
9391 break;
9392 case eTypeTable:
9393 // Either colgroup or rowgroup, depending on what we're grouping.
9394 wrapperType = groupingParentType == eTypeColGroup ?
9395 eTypeColGroup : eTypeRowGroup;
9396 break;
9397 default:
9398 NS_NOTREACHED("Colgroups should be suppresing non-col child items");
9399 break;
9402 const PseudoParentData& pseudoData = sPseudoParentData[wrapperType];
9403 nsIAtom* pseudoType = *pseudoData.mPseudoType;
9404 nsStyleContext* parentStyle = aParentFrame->GetStyleContext();
9405 nsIContent* parentContent = aParentFrame->GetContent();
9407 if (pseudoType == nsCSSAnonBoxes::table &&
9408 parentStyle->GetStyleDisplay()->mDisplay == NS_STYLE_DISPLAY_INLINE) {
9409 pseudoType = nsCSSAnonBoxes::inlineTable;
9412 nsRefPtr<nsStyleContext> wrapperStyle =
9413 mPresShell->StyleSet()->ResolveAnonymousBoxStyle(pseudoType, parentStyle);
9414 FrameConstructionItem* newItem =
9415 new FrameConstructionItem(&pseudoData.mFCData,
9416 // Use the content of our parent frame
9417 parentContent,
9418 // Lie about the tag; it doesn't matter anyway
9419 pseudoType,
9420 // The namespace does matter, however; it needs
9421 // to match that of our first child item to
9422 // match the old behavior
9423 iter.item().mNameSpaceID,
9424 // no pending binding
9425 nsnull,
9426 wrapperStyle.forget(),
9427 PR_TRUE);
9429 if (!newItem) {
9430 return NS_ERROR_OUT_OF_MEMORY;
9433 // Here we're cheating a tad... technically, table-internal items should be
9434 // inline if aParentFrame is inline, but they'll get wrapped in an
9435 // inline-table in the end, so it'll all work out. In any case, arguably
9436 // we don't need to maintain this state at this point... but it's better
9437 // to, I guess.
9438 newItem->mIsAllInline = newItem->mHasInlineEnds =
9439 newItem->mStyleContext->GetStyleDisplay()->IsInlineOutside();
9441 // Table pseudo frames always induce line boundaries around their
9442 // contents.
9443 newItem->mChildItems.SetLineBoundaryAtStart(PR_TRUE);
9444 newItem->mChildItems.SetLineBoundaryAtEnd(PR_TRUE);
9445 // The parent of the items in aItems is also the parent of the items
9446 // in mChildItems
9447 newItem->mChildItems.SetParentHasNoXBLChildren(
9448 aItems.ParentHasNoXBLChildren());
9450 // Eat up all items between |iter| and |endIter| and put them in our wrapper
9451 // Advances |iter| to point to |endIter|.
9452 iter.AppendItemsToList(endIter, newItem->mChildItems);
9454 iter.InsertItem(newItem);
9456 // Now |iter| points to the item that was the first one we didn't wrap;
9457 // loop and see whether we need to skip it or wrap it in something
9458 // different.
9459 } while (!iter.IsDone());
9461 return NS_OK;
9464 inline nsresult
9465 nsCSSFrameConstructor::ConstructFramesFromItemList(nsFrameConstructorState& aState,
9466 FrameConstructionItemList& aItems,
9467 nsIFrame* aParentFrame,
9468 nsFrameItems& aFrameItems)
9470 nsresult rv = CreateNeededTablePseudos(aState, aItems, aParentFrame);
9471 NS_ENSURE_SUCCESS(rv, rv);
9473 #ifdef DEBUG
9474 for (FCItemIterator iter(aItems); !iter.IsDone(); iter.Next()) {
9475 NS_ASSERTION(iter.item().DesiredParentType() == GetParentType(aParentFrame),
9476 "Needed pseudos didn't get created; expect bad things");
9478 #endif
9480 for (FCItemIterator iter(aItems); !iter.IsDone(); iter.Next()) {
9481 rv = ConstructFramesFromItem(aState, iter, aParentFrame, aFrameItems);
9482 NS_ENSURE_SUCCESS(rv, rv);
9485 NS_ASSERTION(!aState.mHavePendingPopupgroup,
9486 "Should have proccessed it by now");
9488 return NS_OK;
9491 nsresult
9492 nsCSSFrameConstructor::ProcessChildren(nsFrameConstructorState& aState,
9493 nsIContent* aContent,
9494 nsStyleContext* aStyleContext,
9495 nsIFrame* aFrame,
9496 const PRBool aCanHaveGeneratedContent,
9497 nsFrameItems& aFrameItems,
9498 const PRBool aAllowBlockStyles,
9499 PendingBinding* aPendingBinding)
9501 NS_PRECONDITION(aFrame, "Must have parent frame here");
9502 NS_PRECONDITION(aFrame->GetContentInsertionFrame() == aFrame,
9503 "Parent frame in ProcessChildren should be its own "
9504 "content insertion frame");
9506 // XXXbz ideally, this would do all the pushing of various
9507 // containing blocks as needed, so callers don't have to do it...
9509 PRBool haveFirstLetterStyle = PR_FALSE, haveFirstLineStyle = PR_FALSE;
9510 if (aAllowBlockStyles) {
9511 ShouldHaveSpecialBlockStyle(aContent, aStyleContext, &haveFirstLetterStyle,
9512 &haveFirstLineStyle);
9515 // The logic here needs to match the logic in GetFloatContainingBlock()
9516 nsFrameConstructorSaveState floatSaveState;
9517 if (aFrame->IsFrameOfType(nsIFrame::eMathML) ||
9518 aFrame->IsBoxFrame()) {
9519 aState.PushFloatContainingBlock(nsnull, floatSaveState);
9520 } else if (aFrame->IsFloatContainingBlock()) {
9521 aState.PushFloatContainingBlock(aFrame, floatSaveState);
9524 nsFrameConstructorState::PendingBindingAutoPusher pusher(aState,
9525 aPendingBinding);
9527 FrameConstructionItemList itemsToConstruct;
9528 nsresult rv = NS_OK;
9530 // If we have first-letter or first-line style then frames can get
9531 // moved around so don't set these flags.
9532 if (aAllowBlockStyles && !haveFirstLetterStyle && !haveFirstLineStyle) {
9533 itemsToConstruct.SetLineBoundaryAtStart(PR_TRUE);
9534 itemsToConstruct.SetLineBoundaryAtEnd(PR_TRUE);
9537 // Create any anonymous frames we need here. This must happen before the
9538 // non-anonymous children are processed to ensure that popups are never
9539 // constructed before the popupset.
9540 nsAutoTArray<nsIContent*, 4> anonymousItems;
9541 GetAnonymousContent(aContent, aFrame, anonymousItems);
9542 for (PRUint32 i = 0; i < anonymousItems.Length(); ++i) {
9543 #ifdef DEBUG
9544 nsIAnonymousContentCreator* creator = do_QueryFrame(aFrame);
9545 NS_ASSERTION(!creator || !creator->CreateFrameFor(anonymousItems[i]),
9546 "If you need to use CreateFrameFor, you need to call "
9547 "CreateAnonymousFrames manually and not follow the standard "
9548 "ProcessChildren() codepath for this frame");
9549 #endif
9550 AddFrameConstructionItems(aState, anonymousItems[i], PR_TRUE, aFrame,
9551 itemsToConstruct);
9554 if (!aFrame->IsLeaf()) {
9555 // :before/:after content should have the same style context parent
9556 // as normal kids.
9557 // Note that we don't use this style context for looking up things like
9558 // special block styles because in some cases involving table pseudo-frames
9559 // it has nothing to do with the parent frame's desired behavior.
9560 nsStyleContext* styleContext;
9562 if (aCanHaveGeneratedContent) {
9563 styleContext =
9564 nsFrame::CorrectStyleParentFrame(aFrame, nsnull)->GetStyleContext();
9565 // Probe for generated content before
9566 CreateGeneratedContentItem(aState, aFrame, aContent, styleContext,
9567 nsCSSPseudoElements::ePseudo_before,
9568 itemsToConstruct);
9571 ChildIterator iter, last;
9572 for (ChildIterator::Init(aContent, &iter, &last);
9573 iter != last;
9574 ++iter) {
9575 nsIContent* child = *iter;
9576 // Frame construction item construction should not post
9577 // restyles, so removing restyle flags here is safe.
9578 if (child->IsElement()) {
9579 child->UnsetFlags(ELEMENT_ALL_RESTYLE_FLAGS);
9581 AddFrameConstructionItems(aState, child, iter.XBLInvolved(), aFrame,
9582 itemsToConstruct);
9584 itemsToConstruct.SetParentHasNoXBLChildren(!iter.XBLInvolved());
9586 if (aCanHaveGeneratedContent) {
9587 // Probe for generated content after
9588 CreateGeneratedContentItem(aState, aFrame, aContent, styleContext,
9589 nsCSSPseudoElements::ePseudo_after,
9590 itemsToConstruct);
9592 } else {
9593 ClearLazyBits(aContent->GetFirstChild(), nsnull);
9596 rv = ConstructFramesFromItemList(aState, itemsToConstruct, aFrame,
9597 aFrameItems);
9598 NS_ENSURE_SUCCESS(rv, rv);
9600 NS_ASSERTION(!aAllowBlockStyles || !aFrame->IsBoxFrame(),
9601 "can't be both block and box");
9603 if (haveFirstLetterStyle) {
9604 rv = WrapFramesInFirstLetterFrame(aContent, aFrame, aFrameItems);
9606 if (haveFirstLineStyle) {
9607 rv = WrapFramesInFirstLineFrame(aState, aContent, aFrame, nsnull,
9608 aFrameItems);
9611 // We might end up with first-line frames that change
9612 // AnyKidsNeedBlockParent() without changing itemsToConstruct, but that
9613 // should never happen for cases whan aFrame->IsBoxFrame().
9614 NS_ASSERTION(!haveFirstLineStyle || !aFrame->IsBoxFrame(),
9615 "Shouldn't have first-line style if we're a box");
9616 NS_ASSERTION(!aFrame->IsBoxFrame() ||
9617 itemsToConstruct.AnyItemsNeedBlockParent() ==
9618 (AnyKidsNeedBlockParent(aFrameItems.FirstChild()) != nsnull),
9619 "Something went awry in our block parent calculations");
9621 if (aFrame->IsBoxFrame() && itemsToConstruct.AnyItemsNeedBlockParent()) {
9622 // XXXbz we could do this on the FrameConstructionItemList level,
9623 // no? And if we cared we could look through the item list
9624 // instead of groveling through the framelist here..
9625 nsIContent *badKid = AnyKidsNeedBlockParent(aFrameItems.FirstChild());
9626 nsDependentAtomString parentTag(aContent->Tag()), kidTag(badKid->Tag());
9627 const PRUnichar* params[] = { parentTag.get(), kidTag.get() };
9628 nsStyleContext *frameStyleContext = aFrame->GetStyleContext();
9629 const nsStyleDisplay *display = frameStyleContext->GetStyleDisplay();
9630 const char *message =
9631 (display->mDisplay == NS_STYLE_DISPLAY_INLINE_BOX)
9632 ? "NeededToWrapXULInlineBox" : "NeededToWrapXUL";
9633 nsContentUtils::ReportToConsole(nsContentUtils::eXUL_PROPERTIES,
9634 message,
9635 params, NS_ARRAY_LENGTH(params),
9636 mDocument->GetDocumentURI(),
9637 EmptyString(), 0, 0, // not useful
9638 nsIScriptError::warningFlag,
9639 "FrameConstructor");
9641 nsRefPtr<nsStyleContext> blockSC = mPresShell->StyleSet()->
9642 ResolveAnonymousBoxStyle(nsCSSAnonBoxes::mozXULAnonymousBlock,
9643 frameStyleContext);
9644 nsIFrame *blockFrame = NS_NewBlockFrame(mPresShell, blockSC);
9645 // We might, in theory, want to set NS_BLOCK_FLOAT_MGR and
9646 // NS_BLOCK_MARGIN_ROOT, but I think it's a bad idea given that
9647 // a real block placed here wouldn't get those set on it.
9649 InitAndRestoreFrame(aState, aContent, aFrame, nsnull,
9650 blockFrame, PR_FALSE);
9652 NS_ASSERTION(!blockFrame->HasView(), "need to do view reparenting");
9653 ReparentFrames(aState.mFrameManager, blockFrame, aFrameItems);
9655 blockFrame->SetInitialChildList(nsnull, aFrameItems);
9656 NS_ASSERTION(aFrameItems.IsEmpty(), "How did that happen?");
9657 aFrameItems.Clear();
9658 aFrameItems.AddChild(blockFrame);
9660 aFrame->AddStateBits(NS_STATE_BOX_WRAPS_KIDS_IN_BLOCK);
9663 return rv;
9666 //----------------------------------------------------------------------
9668 // Support for :first-line style
9670 // Special routine to handle placing a list of frames into a block
9671 // frame that has first-line style. The routine ensures that the first
9672 // collection of inline frames end up in a first-line frame.
9673 // NOTE: aState may have containing block information related to a
9674 // different part of the frame tree than where the first line occurs.
9675 // In particular aState may be set up for where ContentInserted or
9676 // ContentAppended is inserting content, which may be some
9677 // non-first-in-flow continuation of the block to which the first-line
9678 // belongs. So this function needs to be careful about how it uses
9679 // aState.
9680 nsresult
9681 nsCSSFrameConstructor::WrapFramesInFirstLineFrame(
9682 nsFrameConstructorState& aState,
9683 nsIContent* aBlockContent,
9684 nsIFrame* aBlockFrame,
9685 nsIFrame* aLineFrame,
9686 nsFrameItems& aFrameItems)
9688 nsresult rv = NS_OK;
9690 // Find the part of aFrameItems that we want to put in the first-line
9691 nsFrameList::FrameLinkEnumerator link(aFrameItems);
9692 while (!link.AtEnd() && IsInlineOutside(link.NextFrame())) {
9693 link.Next();
9696 nsFrameList firstLineChildren = aFrameItems.ExtractHead(link);
9698 if (firstLineChildren.IsEmpty()) {
9699 // Nothing is supposed to go into the first-line; nothing to do
9700 return NS_OK;
9703 if (!aLineFrame) {
9704 // Create line frame
9705 nsStyleContext* parentStyle =
9706 nsFrame::CorrectStyleParentFrame(aBlockFrame,
9707 nsCSSPseudoElements::firstLine)->
9708 GetStyleContext();
9709 nsRefPtr<nsStyleContext> firstLineStyle = GetFirstLineStyle(aBlockContent,
9710 parentStyle);
9712 aLineFrame = NS_NewFirstLineFrame(mPresShell, firstLineStyle);
9714 if (aLineFrame) {
9715 // Initialize the line frame
9716 rv = InitAndRestoreFrame(aState, aBlockContent, aBlockFrame, nsnull,
9717 aLineFrame);
9719 // The lineFrame will be the block's first child; the rest of the
9720 // frame list (after lastInlineFrame) will be the second and
9721 // subsequent children; insert lineFrame into aFrameItems.
9722 aFrameItems.InsertFrame(nsnull, nsnull, aLineFrame);
9724 NS_ASSERTION(aLineFrame->GetStyleContext() == firstLineStyle,
9725 "Bogus style context on line frame");
9729 if (aLineFrame) {
9730 // Give the inline frames to the lineFrame <b>after</b> reparenting them
9731 ReparentFrames(aState.mFrameManager, aLineFrame, firstLineChildren);
9732 if (aLineFrame->GetChildList(nsnull).IsEmpty() &&
9733 (aLineFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
9734 aLineFrame->SetInitialChildList(nsnull, firstLineChildren);
9735 } else {
9736 aState.mFrameManager->AppendFrames(aLineFrame, nsnull, firstLineChildren);
9739 else {
9740 rv = NS_ERROR_OUT_OF_MEMORY;
9743 return rv;
9746 // Special routine to handle appending a new frame to a block frame's
9747 // child list. Takes care of placing the new frame into the right
9748 // place when first-line style is present.
9749 nsresult
9750 nsCSSFrameConstructor::AppendFirstLineFrames(
9751 nsFrameConstructorState& aState,
9752 nsIContent* aBlockContent,
9753 nsIFrame* aBlockFrame,
9754 nsFrameItems& aFrameItems)
9756 // It's possible that aBlockFrame needs to have a first-line frame
9757 // created because it doesn't currently have any children.
9758 const nsFrameList& blockKids = aBlockFrame->GetChildList(nsnull);
9759 if (blockKids.IsEmpty()) {
9760 return WrapFramesInFirstLineFrame(aState, aBlockContent,
9761 aBlockFrame, nsnull, aFrameItems);
9764 // Examine the last block child - if it's a first-line frame then
9765 // appended frames need special treatment.
9766 nsIFrame* lastBlockKid = blockKids.LastChild();
9767 if (lastBlockKid->GetType() != nsGkAtoms::lineFrame) {
9768 // No first-line frame at the end of the list, therefore there is
9769 // an intervening block between any first-line frame the frames
9770 // we are appending. Therefore, we don't need any special
9771 // treatment of the appended frames.
9772 return NS_OK;
9775 return WrapFramesInFirstLineFrame(aState, aBlockContent, aBlockFrame,
9776 lastBlockKid, aFrameItems);
9779 // Special routine to handle inserting a new frame into a block
9780 // frame's child list. Takes care of placing the new frame into the
9781 // right place when first-line style is present.
9782 nsresult
9783 nsCSSFrameConstructor::InsertFirstLineFrames(
9784 nsFrameConstructorState& aState,
9785 nsIContent* aContent,
9786 nsIFrame* aBlockFrame,
9787 nsIFrame** aParentFrame,
9788 nsIFrame* aPrevSibling,
9789 nsFrameItems& aFrameItems)
9791 nsresult rv = NS_OK;
9792 // XXXbz If you make this method actually do something, check to
9793 // make sure that the caller is passing what you expect. In
9794 // particular, which content is aContent? And audit the rest of
9795 // this code too; it makes bogus assumptions and may not build.
9796 #if 0
9797 nsIFrame* parentFrame = *aParentFrame;
9798 nsIFrame* newFrame = aFrameItems.childList;
9799 PRBool isInline = IsInlineOutside(newFrame);
9801 if (!aPrevSibling) {
9802 // Insertion will become the first frame. Two cases: we either
9803 // already have a first-line frame or we don't.
9804 nsIFrame* firstBlockKid = aBlockFrame->GetFirstChild(nsnull);
9805 if (firstBlockKid->GetType() == nsGkAtoms::lineFrame) {
9806 // We already have a first-line frame
9807 nsIFrame* lineFrame = firstBlockKid;
9809 if (isInline) {
9810 // Easy case: the new inline frame will go into the lineFrame.
9811 ReparentFrame(aState.mFrameManager, lineFrame, newFrame);
9812 aState.mFrameManager->InsertFrames(lineFrame, nsnull, nsnull,
9813 newFrame);
9815 // Since the frame is going into the lineFrame, don't let it
9816 // go into the block too.
9817 aFrameItems.childList = nsnull;
9818 aFrameItems.lastChild = nsnull;
9820 else {
9821 // Harder case: We are about to insert a block level element
9822 // before the first-line frame.
9823 // XXX need a method to steal away frames from the line-frame
9826 else {
9827 // We do not have a first-line frame
9828 if (isInline) {
9829 // We now need a first-line frame to contain the inline frame.
9830 nsIFrame* lineFrame = NS_NewFirstLineFrame(firstLineStyle);
9831 if (!lineFrame) {
9832 rv = NS_ERROR_OUT_OF_MEMORY;
9835 if (NS_SUCCEEDED(rv)) {
9836 // Lookup first-line style context
9837 nsStyleContext* parentStyle =
9838 nsFrame::CorrectStyleParentFrame(aBlockFrame,
9839 nsCSSPseudoElements::firstLine)->
9840 GetStyleContext();
9841 nsRefPtr<nsStyleContext> firstLineStyle =
9842 GetFirstLineStyle(aContent, parentStyle);
9844 // Initialize the line frame
9845 rv = InitAndRestoreFrame(aState, aContent, aBlockFrame,
9846 nsnull, lineFrame);
9848 // Make sure the caller inserts the lineFrame into the
9849 // blocks list of children.
9850 aFrameItems.childList = lineFrame;
9851 aFrameItems.lastChild = lineFrame;
9853 // Give the inline frames to the lineFrame <b>after</b>
9854 // reparenting them
9855 NS_ASSERTION(lineFrame->GetStyleContext() == firstLineStyle,
9856 "Bogus style context on line frame");
9857 ReparentFrame(aPresContext, lineFrame, newFrame);
9858 lineFrame->SetInitialChildList(nsnull, newFrame);
9861 else {
9862 // Easy case: the regular insertion logic can insert the new
9863 // frame because it's a block frame.
9867 else {
9868 // Insertion will not be the first frame.
9869 nsIFrame* prevSiblingParent = aPrevSibling->GetParent();
9870 if (prevSiblingParent == aBlockFrame) {
9871 // Easy case: The prev-siblings parent is the block
9872 // frame. Therefore the prev-sibling is not currently in a
9873 // line-frame. Therefore the new frame which is going after it,
9874 // regardless of type, is not going into a line-frame.
9876 else {
9877 // If the prevSiblingParent is not the block-frame then it must
9878 // be a line-frame (if it were a letter-frame, that logic would
9879 // already have adjusted the prev-sibling to be the
9880 // letter-frame).
9881 if (isInline) {
9882 // Easy case: the insertion can go where the caller thinks it
9883 // should go (which is into prevSiblingParent).
9885 else {
9886 // Block elements don't end up in line-frames, therefore
9887 // change the insertion point to aBlockFrame. However, there
9888 // might be more inline elements following aPrevSibling that
9889 // need to be pulled out of the line-frame and become children
9890 // of the block.
9891 nsIFrame* nextSibling = aPrevSibling->GetNextSibling();
9892 nsIFrame* nextLineFrame = prevSiblingParent->GetNextInFlow();
9893 if (nextSibling || nextLineFrame) {
9894 // Oy. We have work to do. Create a list of the new frames
9895 // that are going into the block by stripping them away from
9896 // the line-frame(s).
9897 if (nextSibling) {
9898 nsLineFrame* lineFrame = (nsLineFrame*) prevSiblingParent;
9899 nsFrameList tail = lineFrame->StealFramesAfter(aPrevSibling);
9900 // XXX do something with 'tail'
9903 nsLineFrame* nextLineFrame = (nsLineFrame*) lineFrame;
9904 for (;;) {
9905 nextLineFrame = nextLineFrame->GetNextInFlow();
9906 if (!nextLineFrame) {
9907 break;
9909 nsIFrame* kids = nextLineFrame->GetFirstChild(nsnull);
9912 else {
9913 // We got lucky: aPrevSibling was the last inline frame in
9914 // the line-frame.
9915 ReparentFrame(aState.mFrameManager, aBlockFrame, newFrame);
9916 aState.mFrameManager->InsertFrames(aBlockFrame, nsnull,
9917 prevSiblingParent, newFrame);
9918 aFrameItems.childList = nsnull;
9919 aFrameItems.lastChild = nsnull;
9925 #endif
9926 return rv;
9929 //----------------------------------------------------------------------
9931 // First-letter support
9933 // Determine how many characters in the text fragment apply to the
9934 // first letter
9935 static PRInt32
9936 FirstLetterCount(const nsTextFragment* aFragment)
9938 PRInt32 count = 0;
9939 PRInt32 firstLetterLength = 0;
9940 PRBool done = PR_FALSE;
9942 PRInt32 i, n = aFragment->GetLength();
9943 for (i = 0; i < n; i++) {
9944 PRUnichar ch = aFragment->CharAt(i);
9945 if (XP_IS_SPACE(ch)) {
9946 if (firstLetterLength) {
9947 done = PR_TRUE;
9948 break;
9950 count++;
9951 continue;
9953 // XXX I18n
9954 if ((ch == '\'') || (ch == '\"')) {
9955 if (firstLetterLength) {
9956 done = PR_TRUE;
9957 break;
9959 // keep looping
9960 firstLetterLength = 1;
9962 else {
9963 count++;
9964 done = PR_TRUE;
9965 break;
9969 return count;
9972 static PRBool
9973 NeedFirstLetterContinuation(nsIContent* aContent)
9975 NS_PRECONDITION(aContent, "null ptr");
9977 PRBool result = PR_FALSE;
9978 if (aContent) {
9979 const nsTextFragment* frag = aContent->GetText();
9980 if (frag) {
9981 PRInt32 flc = FirstLetterCount(frag);
9982 PRInt32 tl = frag->GetLength();
9983 if (flc < tl) {
9984 result = PR_TRUE;
9988 return result;
9991 static PRBool IsFirstLetterContent(nsIContent* aContent)
9993 return aContent->TextLength() &&
9994 !aContent->TextIsOnlyWhitespace();
9998 * Create a letter frame, only make it a floating frame.
10000 void
10001 nsCSSFrameConstructor::CreateFloatingLetterFrame(
10002 nsFrameConstructorState& aState,
10003 nsIFrame* aBlockFrame,
10004 nsIContent* aTextContent,
10005 nsIFrame* aTextFrame,
10006 nsIContent* aBlockContent,
10007 nsIFrame* aParentFrame,
10008 nsStyleContext* aStyleContext,
10009 nsFrameItems& aResult)
10011 // Create the first-letter-frame
10012 nsresult rv;
10013 nsIFrame* letterFrame;
10014 nsStyleSet *styleSet = mPresShell->StyleSet();
10016 letterFrame = NS_NewFirstLetterFrame(mPresShell, aStyleContext);
10017 // We don't want to use a text content for a non-text frame (because we want
10018 // its primary frame to be a text frame). So use its parent for the
10019 // first-letter.
10020 nsIContent* letterContent = aTextContent->GetParent();
10021 nsIFrame* containingBlock = aState.GetGeometricParent(
10022 aStyleContext->GetStyleDisplay(), aParentFrame);
10023 InitAndRestoreFrame(aState, letterContent, containingBlock, nsnull,
10024 letterFrame);
10026 // Init the text frame to refer to the letter frame. Make sure we
10027 // get a proper style context for it (the one passed in is for the
10028 // letter frame and will have the float property set on it; the text
10029 // frame shouldn't have that set).
10030 nsRefPtr<nsStyleContext> textSC;
10031 textSC = styleSet->ResolveStyleForNonElement(aStyleContext);
10032 aTextFrame->SetStyleContextWithoutNotification(textSC);
10033 InitAndRestoreFrame(aState, aTextContent, letterFrame, nsnull, aTextFrame);
10035 // And then give the text frame to the letter frame
10036 SetInitialSingleChild(letterFrame, aTextFrame);
10038 // See if we will need to continue the text frame (does it contain
10039 // more than just the first-letter text or not?) If it does, then we
10040 // create (in advance) a continuation frame for it.
10041 nsIFrame* nextTextFrame = nsnull;
10042 if (NeedFirstLetterContinuation(aTextContent)) {
10043 // Create continuation
10044 rv = CreateContinuingFrame(aState.mPresContext, aTextFrame, aParentFrame,
10045 &nextTextFrame);
10046 if (NS_FAILED(rv)) {
10047 letterFrame->Destroy();
10048 return;
10050 // Repair the continuations style context
10051 nsStyleContext* parentStyleContext = aStyleContext->GetParent();
10052 if (parentStyleContext) {
10053 nsRefPtr<nsStyleContext> newSC;
10054 newSC = styleSet->ResolveStyleForNonElement(parentStyleContext);
10055 if (newSC) {
10056 nextTextFrame->SetStyleContext(newSC);
10061 NS_ASSERTION(aResult.IsEmpty(), "aResult should be an empty nsFrameItems!");
10062 // Put the new float before any of the floats in the block we're doing
10063 // first-letter for, that is, before any floats whose parent is
10064 // containingBlock.
10065 nsFrameList::FrameLinkEnumerator link(aState.mFloatedItems);
10066 while (!link.AtEnd() && link.NextFrame()->GetParent() != containingBlock) {
10067 link.Next();
10070 rv = aState.AddChild(letterFrame, aResult, letterContent, aStyleContext,
10071 aParentFrame, PR_FALSE, PR_TRUE, PR_FALSE, PR_TRUE,
10072 link.PrevFrame());
10074 if (nextTextFrame) {
10075 if (NS_FAILED(rv)) {
10076 nextTextFrame->Destroy();
10077 } else {
10078 aResult.AddChild(nextTextFrame);
10084 * Create a new letter frame for aTextFrame. The letter frame will be
10085 * a child of aParentFrame.
10087 nsresult
10088 nsCSSFrameConstructor::CreateLetterFrame(nsIFrame* aBlockFrame,
10089 nsIFrame* aBlockContinuation,
10090 nsIContent* aTextContent,
10091 nsIFrame* aParentFrame,
10092 nsFrameItems& aResult)
10094 NS_PRECONDITION(aTextContent->IsNodeOfType(nsINode::eTEXT),
10095 "aTextContent isn't text");
10096 NS_ASSERTION(nsLayoutUtils::GetAsBlock(aBlockFrame),
10097 "Not a block frame?");
10099 // Get style context for the first-letter-frame
10100 nsStyleContext* parentStyleContext =
10101 nsFrame::CorrectStyleParentFrame(aParentFrame,
10102 nsCSSPseudoElements::firstLetter)->
10103 GetStyleContext();
10105 // Use content from containing block so that we can actually
10106 // find a matching style rule.
10107 nsIContent* blockContent = aBlockFrame->GetContent();
10109 // Create first-letter style rule
10110 nsRefPtr<nsStyleContext> sc = GetFirstLetterStyle(blockContent,
10111 parentStyleContext);
10112 if (sc) {
10113 nsRefPtr<nsStyleContext> textSC;
10114 textSC = mPresShell->StyleSet()->ResolveStyleForNonElement(sc);
10116 // Create a new text frame (the original one will be discarded)
10117 // pass a temporary stylecontext, the correct one will be set
10118 // later. Start off by unsetting the primary frame for
10119 // aTextContent, so it's no longer pointing to the to-be-destroyed
10120 // frame.
10121 // XXXbz it would be really nice to destroy the old frame _first_,
10122 // then create the new one, so we could avoid this hack.
10123 aTextContent->SetPrimaryFrame(nsnull);
10124 nsIFrame* textFrame = NS_NewTextFrame(mPresShell, textSC);
10126 NS_ASSERTION(aBlockContinuation == GetFloatContainingBlock(aParentFrame),
10127 "Containing block is confused");
10128 nsFrameConstructorState state(mPresShell, mFixedContainingBlock,
10129 GetAbsoluteContainingBlock(aParentFrame),
10130 aBlockContinuation);
10132 // Create the right type of first-letter frame
10133 const nsStyleDisplay* display = sc->GetStyleDisplay();
10134 if (display->IsFloating()) {
10135 // Make a floating first-letter frame
10136 CreateFloatingLetterFrame(state, aBlockFrame, aTextContent, textFrame,
10137 blockContent, aParentFrame, sc, aResult);
10139 else {
10140 // Make an inflow first-letter frame
10141 nsIFrame* letterFrame = NS_NewFirstLetterFrame(mPresShell, sc);
10143 if (letterFrame) {
10144 // Initialize the first-letter-frame. We don't want to use a text
10145 // content for a non-text frame (because we want its primary frame to
10146 // be a text frame). So use its parent for the first-letter.
10147 nsIContent* letterContent = aTextContent->GetParent();
10148 letterFrame->Init(letterContent, aParentFrame, nsnull);
10150 InitAndRestoreFrame(state, aTextContent, letterFrame, nsnull,
10151 textFrame);
10153 SetInitialSingleChild(letterFrame, textFrame);
10154 aResult.Clear();
10155 aResult.AddChild(letterFrame);
10156 NS_ASSERTION(!aBlockFrame->GetPrevContinuation(),
10157 "should have the first continuation here");
10158 aBlockFrame->AddStateBits(NS_BLOCK_HAS_FIRST_LETTER_CHILD);
10161 aTextContent->SetPrimaryFrame(textFrame);
10164 return NS_OK;
10167 nsresult
10168 nsCSSFrameConstructor::WrapFramesInFirstLetterFrame(
10169 nsIContent* aBlockContent,
10170 nsIFrame* aBlockFrame,
10171 nsFrameItems& aBlockFrames)
10173 nsresult rv = NS_OK;
10175 aBlockFrame->AddStateBits(NS_BLOCK_HAS_FIRST_LETTER_STYLE);
10177 nsIFrame* parentFrame = nsnull;
10178 nsIFrame* textFrame = nsnull;
10179 nsIFrame* prevFrame = nsnull;
10180 nsFrameItems letterFrames;
10181 PRBool stopLooking = PR_FALSE;
10182 rv = WrapFramesInFirstLetterFrame(aBlockFrame, aBlockFrame, aBlockFrame,
10183 aBlockFrames.FirstChild(),
10184 &parentFrame, &textFrame, &prevFrame,
10185 letterFrames, &stopLooking);
10186 if (NS_FAILED(rv)) {
10187 return rv;
10189 if (parentFrame) {
10190 if (parentFrame == aBlockFrame) {
10191 // Take textFrame out of the block's frame list and substitute the
10192 // letter frame(s) instead.
10193 aBlockFrames.DestroyFrame(textFrame);
10194 aBlockFrames.InsertFrames(nsnull, prevFrame, letterFrames);
10196 else {
10197 // Take the old textFrame out of the inline parent's child list
10198 mPresShell->FrameManager()->RemoveFrame(nsnull, textFrame);
10200 // Insert in the letter frame(s)
10201 parentFrame->InsertFrames(nsnull, prevFrame, letterFrames);
10205 return rv;
10208 nsresult
10209 nsCSSFrameConstructor::WrapFramesInFirstLetterFrame(
10210 nsIFrame* aBlockFrame,
10211 nsIFrame* aBlockContinuation,
10212 nsIFrame* aParentFrame,
10213 nsIFrame* aParentFrameList,
10214 nsIFrame** aModifiedParent,
10215 nsIFrame** aTextFrame,
10216 nsIFrame** aPrevFrame,
10217 nsFrameItems& aLetterFrames,
10218 PRBool* aStopLooking)
10220 nsresult rv = NS_OK;
10222 nsIFrame* prevFrame = nsnull;
10223 nsIFrame* frame = aParentFrameList;
10225 while (frame) {
10226 nsIFrame* nextFrame = frame->GetNextSibling();
10228 nsIAtom* frameType = frame->GetType();
10229 if (nsGkAtoms::textFrame == frameType) {
10230 // Wrap up first-letter content in a letter frame
10231 nsIContent* textContent = frame->GetContent();
10232 if (IsFirstLetterContent(textContent)) {
10233 // Create letter frame to wrap up the text
10234 rv = CreateLetterFrame(aBlockFrame, aBlockContinuation, textContent,
10235 aParentFrame, aLetterFrames);
10236 if (NS_FAILED(rv)) {
10237 return rv;
10240 // Provide adjustment information for parent
10241 *aModifiedParent = aParentFrame;
10242 *aTextFrame = frame;
10243 *aPrevFrame = prevFrame;
10244 *aStopLooking = PR_TRUE;
10245 return NS_OK;
10248 else if (IsInlineFrame(frame) && frameType != nsGkAtoms::brFrame) {
10249 nsIFrame* kids = frame->GetFirstChild(nsnull);
10250 WrapFramesInFirstLetterFrame(aBlockFrame, aBlockContinuation, frame,
10251 kids, aModifiedParent, aTextFrame,
10252 aPrevFrame, aLetterFrames, aStopLooking);
10253 if (*aStopLooking) {
10254 return NS_OK;
10257 else {
10258 // This will stop us looking to create more letter frames. For
10259 // example, maybe the frame-type is "letterFrame" or
10260 // "placeholderFrame". This keeps us from creating extra letter
10261 // frames, and also prevents us from creating letter frames when
10262 // the first real content child of a block is not text (e.g. an
10263 // image, hr, etc.)
10264 *aStopLooking = PR_TRUE;
10265 break;
10268 prevFrame = frame;
10269 frame = nextFrame;
10272 return rv;
10275 nsresult
10276 nsCSSFrameConstructor::RemoveFloatingFirstLetterFrames(
10277 nsPresContext* aPresContext,
10278 nsIPresShell* aPresShell,
10279 nsFrameManager* aFrameManager,
10280 nsIFrame* aBlockFrame,
10281 PRBool* aStopLooking)
10283 // First look for the float frame that is a letter frame
10284 nsIFrame* floatFrame = aBlockFrame->GetFirstChild(nsGkAtoms::floatList);
10285 while (floatFrame) {
10286 // See if we found a floating letter frame
10287 if (nsGkAtoms::letterFrame == floatFrame->GetType()) {
10288 break;
10290 floatFrame = floatFrame->GetNextSibling();
10292 if (!floatFrame) {
10293 // No such frame
10294 return NS_OK;
10297 // Take the text frame away from the letter frame (so it isn't
10298 // destroyed when we destroy the letter frame).
10299 nsIFrame* textFrame = floatFrame->GetFirstChild(nsnull);
10300 if (!textFrame) {
10301 return NS_OK;
10304 // Discover the placeholder frame for the letter frame
10305 nsIFrame* parentFrame;
10306 nsPlaceholderFrame* placeholderFrame =
10307 aFrameManager->GetPlaceholderFrameFor(floatFrame);
10309 if (!placeholderFrame) {
10310 // Somethings really wrong
10311 return NS_OK;
10313 parentFrame = placeholderFrame->GetParent();
10314 if (!parentFrame) {
10315 // Somethings really wrong
10316 return NS_OK;
10319 // Create a new text frame with the right style context that maps
10320 // all of the content that was previously part of the letter frame
10321 // (and probably continued elsewhere).
10322 nsStyleContext* parentSC = parentFrame->GetStyleContext();
10323 if (!parentSC) {
10324 return NS_OK;
10326 nsIContent* textContent = textFrame->GetContent();
10327 if (!textContent) {
10328 return NS_OK;
10330 nsRefPtr<nsStyleContext> newSC;
10331 newSC = aPresShell->StyleSet()->ResolveStyleForNonElement(parentSC);
10332 if (!newSC) {
10333 return NS_OK;
10335 nsIFrame* newTextFrame = NS_NewTextFrame(aPresShell, newSC);
10336 if (NS_UNLIKELY(!newTextFrame)) {
10337 return NS_ERROR_OUT_OF_MEMORY;;
10339 newTextFrame->Init(textContent, parentFrame, nsnull);
10341 // Destroy the old text frame's continuations (the old text frame
10342 // will be destroyed when its letter frame is destroyed).
10343 nsIFrame* frameToDelete = textFrame->GetLastContinuation();
10344 while (frameToDelete != textFrame) {
10345 nsIFrame* nextFrameToDelete = frameToDelete->GetPrevContinuation();
10346 aFrameManager->RemoveFrame(nsnull, frameToDelete);
10347 frameToDelete = nextFrameToDelete;
10350 nsIFrame* prevSibling = placeholderFrame->GetPrevSibling();
10352 // Now that everything is set...
10353 #ifdef NOISY_FIRST_LETTER
10354 printf("RemoveFloatingFirstLetterFrames: textContent=%p oldTextFrame=%p newTextFrame=%p\n",
10355 textContent.get(), textFrame, newTextFrame);
10356 #endif
10358 // Remove placeholder frame and the float
10359 aFrameManager->RemoveFrame(nsnull, placeholderFrame);
10361 // Now that the old frames are gone, we can start pointing to our
10362 // new primary frame.
10363 textContent->SetPrimaryFrame(newTextFrame);
10365 // Insert text frame in its place
10366 nsFrameList textList(newTextFrame, newTextFrame);
10367 aFrameManager->InsertFrames(parentFrame, nsnull, prevSibling, textList);
10369 return NS_OK;
10372 nsresult
10373 nsCSSFrameConstructor::RemoveFirstLetterFrames(nsPresContext* aPresContext,
10374 nsIPresShell* aPresShell,
10375 nsFrameManager* aFrameManager,
10376 nsIFrame* aFrame,
10377 nsIFrame* aBlockFrame,
10378 PRBool* aStopLooking)
10380 nsIFrame* prevSibling = nsnull;
10381 nsIFrame* kid = aFrame->GetFirstChild(nsnull);
10383 while (kid) {
10384 if (nsGkAtoms::letterFrame == kid->GetType()) {
10385 // Bingo. Found it. First steal away the text frame.
10386 nsIFrame* textFrame = kid->GetFirstChild(nsnull);
10387 if (!textFrame) {
10388 break;
10391 // Create a new textframe
10392 nsStyleContext* parentSC = aFrame->GetStyleContext();
10393 if (!parentSC) {
10394 break;
10396 nsIContent* textContent = textFrame->GetContent();
10397 if (!textContent) {
10398 break;
10400 nsRefPtr<nsStyleContext> newSC;
10401 newSC = aPresShell->StyleSet()->ResolveStyleForNonElement(parentSC);
10402 if (!newSC) {
10403 break;
10405 textFrame = NS_NewTextFrame(aPresShell, newSC);
10406 textFrame->Init(textContent, aFrame, nsnull);
10408 // Next rip out the kid and replace it with the text frame
10409 aFrameManager->RemoveFrame(nsnull, kid);
10411 // Now that the old frames are gone, we can start pointing to our
10412 // new primary frame.
10413 textContent->SetPrimaryFrame(textFrame);
10415 // Insert text frame in its place
10416 nsFrameList textList(textFrame, textFrame);
10417 aFrameManager->InsertFrames(aFrame, nsnull, prevSibling, textList);
10419 *aStopLooking = PR_TRUE;
10420 NS_ASSERTION(!aBlockFrame->GetPrevContinuation(),
10421 "should have the first continuation here");
10422 aBlockFrame->RemoveStateBits(NS_BLOCK_HAS_FIRST_LETTER_CHILD);
10423 break;
10425 else if (IsInlineFrame(kid)) {
10426 // Look inside child inline frame for the letter frame
10427 RemoveFirstLetterFrames(aPresContext, aPresShell, aFrameManager,
10428 kid, aBlockFrame, aStopLooking);
10429 if (*aStopLooking) {
10430 break;
10433 prevSibling = kid;
10434 kid = kid->GetNextSibling();
10437 return NS_OK;
10440 nsresult
10441 nsCSSFrameConstructor::RemoveLetterFrames(nsPresContext* aPresContext,
10442 nsIPresShell* aPresShell,
10443 nsFrameManager* aFrameManager,
10444 nsIFrame* aBlockFrame)
10446 aBlockFrame = aBlockFrame->GetFirstContinuation();
10447 nsIFrame* continuation = aBlockFrame;
10449 PRBool stopLooking = PR_FALSE;
10450 nsresult rv;
10451 do {
10452 rv = RemoveFloatingFirstLetterFrames(aPresContext, aPresShell,
10453 aFrameManager,
10454 continuation, &stopLooking);
10455 if (NS_SUCCEEDED(rv) && !stopLooking) {
10456 rv = RemoveFirstLetterFrames(aPresContext, aPresShell, aFrameManager,
10457 continuation, aBlockFrame, &stopLooking);
10459 if (stopLooking) {
10460 break;
10462 continuation = continuation->GetNextContinuation();
10463 } while (continuation);
10464 return rv;
10467 // Fixup the letter frame situation for the given block
10468 nsresult
10469 nsCSSFrameConstructor::RecoverLetterFrames(nsIFrame* aBlockFrame)
10471 aBlockFrame = aBlockFrame->GetFirstContinuation();
10472 nsIFrame* continuation = aBlockFrame;
10474 nsIFrame* parentFrame = nsnull;
10475 nsIFrame* textFrame = nsnull;
10476 nsIFrame* prevFrame = nsnull;
10477 nsFrameItems letterFrames;
10478 PRBool stopLooking = PR_FALSE;
10479 nsresult rv;
10480 do {
10481 // XXX shouldn't this bit be set already (bug 408493), assert instead?
10482 continuation->AddStateBits(NS_BLOCK_HAS_FIRST_LETTER_STYLE);
10483 rv = WrapFramesInFirstLetterFrame(aBlockFrame, continuation, continuation,
10484 continuation->GetFirstChild(nsnull),
10485 &parentFrame, &textFrame, &prevFrame,
10486 letterFrames, &stopLooking);
10487 if (NS_FAILED(rv)) {
10488 return rv;
10490 if (stopLooking) {
10491 break;
10493 continuation = continuation->GetNextContinuation();
10494 } while (continuation);
10496 if (parentFrame) {
10497 // Take the old textFrame out of the parents child list
10498 mPresShell->FrameManager()->RemoveFrame(nsnull, textFrame);
10500 // Insert in the letter frame(s)
10501 parentFrame->InsertFrames(nsnull, prevFrame, letterFrames);
10503 return rv;
10506 //----------------------------------------------------------------------
10508 // listbox Widget Routines
10510 nsresult
10511 nsCSSFrameConstructor::CreateListBoxContent(nsPresContext* aPresContext,
10512 nsIFrame* aParentFrame,
10513 nsIFrame* aPrevFrame,
10514 nsIContent* aChild,
10515 nsIFrame** aNewFrame,
10516 PRBool aIsAppend,
10517 PRBool aIsScrollbar,
10518 nsILayoutHistoryState* aFrameState)
10520 #ifdef MOZ_XUL
10521 nsresult rv = NS_OK;
10523 // Construct a new frame
10524 if (nsnull != aParentFrame) {
10525 nsFrameItems frameItems;
10526 nsFrameConstructorState state(mPresShell, mFixedContainingBlock,
10527 GetAbsoluteContainingBlock(aParentFrame),
10528 GetFloatContainingBlock(aParentFrame),
10529 mTempFrameTreeState);
10531 nsRefPtr<nsStyleContext> styleContext;
10532 styleContext = ResolveStyleContext(aParentFrame, aChild);
10534 // Pre-check for display "none" - only if we find that, do we create
10535 // any frame at all
10536 const nsStyleDisplay* display = styleContext->GetStyleDisplay();
10538 if (NS_STYLE_DISPLAY_NONE == display->mDisplay) {
10539 *aNewFrame = nsnull;
10540 return NS_OK;
10543 BeginUpdate();
10545 FrameConstructionItemList items;
10546 AddFrameConstructionItemsInternal(state, aChild, aParentFrame,
10547 aChild->Tag(), aChild->GetNameSpaceID(),
10548 PR_TRUE, styleContext,
10549 ITEM_ALLOW_XBL_BASE, items);
10550 ConstructFramesFromItemList(state, items, aParentFrame, frameItems);
10552 nsIFrame* newFrame = frameItems.FirstChild();
10553 *aNewFrame = newFrame;
10555 if (NS_SUCCEEDED(rv) && (nsnull != newFrame)) {
10556 // Notify the parent frame
10557 if (aIsAppend)
10558 rv = ((nsListBoxBodyFrame*)aParentFrame)->ListBoxAppendFrames(frameItems);
10559 else
10560 rv = ((nsListBoxBodyFrame*)aParentFrame)->ListBoxInsertFrames(aPrevFrame, frameItems);
10563 EndUpdate();
10566 return rv;
10567 #else
10568 return NS_ERROR_FAILURE;
10569 #endif
10572 //----------------------------------------
10574 nsresult
10575 nsCSSFrameConstructor::ConstructBlock(nsFrameConstructorState& aState,
10576 const nsStyleDisplay* aDisplay,
10577 nsIContent* aContent,
10578 nsIFrame* aParentFrame,
10579 nsIFrame* aContentParentFrame,
10580 nsStyleContext* aStyleContext,
10581 nsIFrame** aNewFrame,
10582 nsFrameItems& aFrameItems,
10583 PRBool aAbsPosContainer,
10584 PendingBinding* aPendingBinding)
10586 // Create column wrapper if necessary
10587 nsIFrame* blockFrame = *aNewFrame;
10588 nsIFrame* parent = aParentFrame;
10589 nsRefPtr<nsStyleContext> blockStyle = aStyleContext;
10590 const nsStyleColumn* columns = aStyleContext->GetStyleColumn();
10592 if (columns->mColumnCount != NS_STYLE_COLUMN_COUNT_AUTO
10593 || columns->mColumnWidth.GetUnit() != eStyleUnit_Auto) {
10594 nsIFrame* columnSetFrame = nsnull;
10595 columnSetFrame = NS_NewColumnSetFrame(mPresShell, aStyleContext, 0);
10596 if (!columnSetFrame) {
10597 return NS_ERROR_OUT_OF_MEMORY;
10600 InitAndRestoreFrame(aState, aContent, aParentFrame, nsnull, columnSetFrame);
10601 // See if we need to create a view
10602 nsHTMLContainerFrame::CreateViewForFrame(columnSetFrame, PR_FALSE);
10603 blockStyle = mPresShell->StyleSet()->
10604 ResolveAnonymousBoxStyle(nsCSSAnonBoxes::columnContent, aStyleContext);
10605 parent = columnSetFrame;
10606 *aNewFrame = columnSetFrame;
10608 SetInitialSingleChild(columnSetFrame, blockFrame);
10611 blockFrame->SetStyleContextWithoutNotification(blockStyle);
10612 InitAndRestoreFrame(aState, aContent, parent, nsnull, blockFrame);
10614 nsresult rv = aState.AddChild(*aNewFrame, aFrameItems, aContent,
10615 aStyleContext,
10616 aContentParentFrame ? aContentParentFrame :
10617 aParentFrame);
10618 if (NS_FAILED(rv)) {
10619 return rv;
10622 // See if we need to create a view, e.g. the frame is absolutely positioned
10623 nsHTMLContainerFrame::CreateViewForFrame(blockFrame, PR_FALSE);
10625 if (!mRootElementFrame) {
10626 // The frame we're constructing will be the root element frame.
10627 // Set mRootElementFrame before processing children.
10628 mRootElementFrame = *aNewFrame;
10631 // We should make the outer frame be the absolute containing block,
10632 // if one is required. We have to do this because absolute
10633 // positioning must be computed with respect to the CSS dimensions
10634 // of the element, which are the dimensions of the outer block. But
10635 // we can't really do that because only blocks can have absolute
10636 // children. So use the block and try to compensate with hacks
10637 // in nsBlockFrame::CalculateContainingBlockSizeForAbsolutes.
10638 nsFrameConstructorSaveState absoluteSaveState;
10639 if (aAbsPosContainer) {
10640 // NS_ASSERTION(aRelPos, "should have made area frame for this");
10641 aState.PushAbsoluteContainingBlock(blockFrame, absoluteSaveState);
10644 // Process the child content
10645 nsFrameItems childItems;
10646 rv = ProcessChildren(aState, aContent, aStyleContext, blockFrame, PR_TRUE,
10647 childItems, PR_TRUE, aPendingBinding);
10649 // Set the frame's initial child list
10650 blockFrame->SetInitialChildList(nsnull, childItems);
10652 return rv;
10655 nsresult
10656 nsCSSFrameConstructor::ConstructInline(nsFrameConstructorState& aState,
10657 FrameConstructionItem& aItem,
10658 nsIFrame* aParentFrame,
10659 const nsStyleDisplay* aDisplay,
10660 nsFrameItems& aFrameItems,
10661 nsIFrame** aNewFrame)
10663 // If an inline frame has non-inline kids, then we chop up the child list
10664 // into runs of blocks and runs of inlines, create anonymous block frames to
10665 // contain the runs of blocks, inline frames with our style context for the
10666 // runs of inlines, and put all these frames, in order, into aFrameItems. We
10667 // put the first one into *aNewFrame. The whole setup is called an {ib}
10668 // split; in what follows "frames in the split" refers to the anonymous blocks
10669 // and inlines that contain our children.
10671 // {ib} splits maintain the following invariants:
10672 // 1) All frames in the split have the NS_FRAME_IS_SPECIAL bit set.
10673 // 2) Each frame in the split has the nsIFrame::IBSplitSpecialSibling
10674 // property pointing to the next frame in the split, except for the last
10675 // one, which does not have it set.
10676 // 3) Each frame in the split has the nsIFrame::IBSplitSpecialPrevSibling
10677 // property pointing to the previous frame in the split, except for the
10678 // first one, which does not have it set.
10679 // 4) The first and last frame in the split are always inlines.
10681 // An invariant that is NOT maintained is that the wrappers are actually
10682 // linked via GetNextSibling linkage. A simple example is an inline
10683 // containing an inline that contains a block. The three parts of the inner
10684 // inline end up with three different parents.
10686 // For example, this HTML:
10687 // <span>
10688 // <div>a</div>
10689 // <span>
10690 // b
10691 // <div>c</div>
10692 // </span>
10693 // d
10694 // <div>e</div>
10695 // f
10696 // </span>
10697 // Gives the following frame tree:
10699 // Inline (outer span)
10700 // Block (anonymous, outer span)
10701 // Block (div)
10702 // Text("a")
10703 // Inline (outer span)
10704 // Inline (inner span)
10705 // Text("b")
10706 // Block (anonymous, outer span)
10707 // Block (anonymous, inner span)
10708 // Block (div)
10709 // Text("c")
10710 // Inline (outer span)
10711 // Inline (inner span)
10712 // Text("d")
10713 // Block (anonymous, outer span)
10714 // Block (div)
10715 // Text("e")
10716 // Inline (outer span)
10717 // Text("f")
10719 nsIContent* const content = aItem.mContent;
10720 nsStyleContext* const styleContext = aItem.mStyleContext;
10722 nsIFrame *newFrame;
10724 PRBool positioned =
10725 NS_STYLE_DISPLAY_INLINE == aDisplay->mDisplay &&
10726 (NS_STYLE_POSITION_RELATIVE == aDisplay->mPosition ||
10727 aDisplay->HasTransform());
10728 if (positioned) {
10729 newFrame = NS_NewPositionedInlineFrame(mPresShell, styleContext);
10730 } else {
10731 newFrame = NS_NewInlineFrame(mPresShell, styleContext);
10734 // Initialize the frame
10735 InitAndRestoreFrame(aState, content, aParentFrame, nsnull, newFrame);
10737 nsFrameConstructorSaveState absoluteSaveState; // definition cannot be inside next block
10738 // because the object's destructor is significant
10739 // this is part of the fix for bug 42372
10741 // Any inline frame might need a view (because of opacity, or fixed background)
10742 nsHTMLContainerFrame::CreateViewForFrame(newFrame, PR_FALSE);
10744 if (positioned) {
10745 // Relatively positioned frames becomes a container for child
10746 // frames that are positioned
10747 aState.PushAbsoluteContainingBlock(newFrame, absoluteSaveState);
10750 // Process the child content
10751 nsFrameItems childItems;
10752 nsresult rv = ConstructFramesFromItemList(aState, aItem.mChildItems, newFrame,
10753 childItems);
10754 if (NS_FAILED(rv)) {
10755 // Clean up?
10756 return rv;
10759 nsFrameList::FrameLinkEnumerator firstBlockEnumerator(childItems);
10760 if (!aItem.mIsAllInline) {
10761 FindFirstBlock(firstBlockEnumerator);
10764 if (aItem.mIsAllInline || firstBlockEnumerator.AtEnd()) {
10765 // This part is easy. We either already know we have no non-inline kids,
10766 // or haven't found any when constructing actual frames (the latter can
10767 // happen only if out-of-flows that we thought had no containing block
10768 // acquired one when ancestor inline frames and {ib} splits got
10769 // constructed). Just put all the kids into the single inline frame and
10770 // bail.
10771 newFrame->SetInitialChildList(nsnull, childItems);
10772 if (NS_SUCCEEDED(rv)) {
10773 aState.AddChild(newFrame, aFrameItems, content, styleContext, aParentFrame);
10774 *aNewFrame = newFrame;
10776 return rv;
10779 // This inline frame contains several types of children. Therefore this frame
10780 // has to be chopped into several pieces, as described above.
10782 // Grab the first inline's kids
10783 nsFrameList firstInlineKids = childItems.ExtractHead(firstBlockEnumerator);
10784 newFrame->SetInitialChildList(nsnull, firstInlineKids);
10786 aFrameItems.AddChild(newFrame);
10788 CreateIBSiblings(aState, newFrame, positioned, childItems, aFrameItems);
10790 *aNewFrame = newFrame;
10791 return NS_OK;
10794 void
10795 nsCSSFrameConstructor::CreateIBSiblings(nsFrameConstructorState& aState,
10796 nsIFrame* aInitialInline,
10797 PRBool aIsPositioned,
10798 nsFrameItems& aChildItems,
10799 nsFrameItems& aSiblings)
10801 nsIContent* content = aInitialInline->GetContent();
10802 nsStyleContext* styleContext = aInitialInline->GetStyleContext();
10803 nsIFrame* parentFrame = aInitialInline->GetParent();
10805 // Resolve the right style context for our anonymous blocks.
10806 nsRefPtr<nsStyleContext> blockSC =
10807 mPresShell->StyleSet()->
10808 ResolveAnonymousBoxStyle(aIsPositioned ?
10809 nsCSSAnonBoxes::mozAnonymousPositionedBlock :
10810 nsCSSAnonBoxes::mozAnonymousBlock,
10811 styleContext);
10813 nsIFrame* lastNewInline = aInitialInline->GetFirstContinuation();
10814 do {
10815 // On entry to this loop aChildItems is not empty and the first frame in it
10816 // is block-level.
10817 NS_PRECONDITION(aChildItems.NotEmpty(), "Should have child items");
10818 NS_PRECONDITION(!IsInlineOutside(aChildItems.FirstChild()),
10819 "Must have list starting with block");
10821 // The initial run of blocks belongs to an anonymous block that we create
10822 // right now. The anonymous block will be the parent of these block
10823 // children of the inline.
10824 nsIFrame* blockFrame;
10825 blockFrame = NS_NewBlockFrame(mPresShell, blockSC);
10827 InitAndRestoreFrame(aState, content, parentFrame, nsnull, blockFrame,
10828 PR_FALSE);
10830 // Any frame could have a view
10831 nsHTMLContainerFrame::CreateViewForFrame(blockFrame, PR_FALSE);
10833 // Find the first non-block child which defines the end of our block kids
10834 // and the start of our next inline's kids
10835 nsFrameList::FrameLinkEnumerator firstNonBlock =
10836 FindFirstNonBlock(aChildItems);
10837 nsFrameList blockKids = aChildItems.ExtractHead(firstNonBlock);
10839 MoveChildrenTo(aState.mPresContext, aInitialInline, blockFrame, blockKids);
10841 SetFrameIsSpecial(lastNewInline, blockFrame);
10842 aSiblings.AddChild(blockFrame);
10844 // Now grab the initial inlines in aChildItems and put them into an inline
10845 // frame
10846 nsIFrame* inlineFrame;
10847 if (aIsPositioned) {
10848 inlineFrame = NS_NewPositionedInlineFrame(mPresShell, styleContext);
10850 else {
10851 inlineFrame = NS_NewInlineFrame(mPresShell, styleContext);
10854 InitAndRestoreFrame(aState, content, parentFrame, nsnull, inlineFrame,
10855 PR_FALSE);
10857 // Any frame might need a view
10858 nsHTMLContainerFrame::CreateViewForFrame(inlineFrame, PR_FALSE);
10860 if (aChildItems.NotEmpty()) {
10861 nsFrameList::FrameLinkEnumerator firstBlock(aChildItems);
10862 FindFirstBlock(firstBlock);
10863 nsFrameList inlineKids = aChildItems.ExtractHead(firstBlock);
10865 MoveChildrenTo(aState.mPresContext, aInitialInline, inlineFrame,
10866 inlineKids);
10869 SetFrameIsSpecial(blockFrame, inlineFrame);
10870 aSiblings.AddChild(inlineFrame);
10871 lastNewInline = inlineFrame;
10872 } while (aChildItems.NotEmpty());
10874 SetFrameIsSpecial(lastNewInline, nsnull);
10877 void
10878 nsCSSFrameConstructor::BuildInlineChildItems(nsFrameConstructorState& aState,
10879 FrameConstructionItem& aParentItem)
10881 // XXXbz should we preallocate aParentItem.mChildItems to some sane
10882 // length? Maybe even to parentContent->GetChildCount()?
10883 nsFrameConstructorState::PendingBindingAutoPusher
10884 pusher(aState, aParentItem.mPendingBinding);
10886 // Probe for generated content before
10887 nsStyleContext* const parentStyleContext = aParentItem.mStyleContext;
10888 nsIContent* const parentContent = aParentItem.mContent;
10889 CreateGeneratedContentItem(aState, nsnull, parentContent, parentStyleContext,
10890 nsCSSPseudoElements::ePseudo_before,
10891 aParentItem.mChildItems);
10893 ChildIterator iter, last;
10894 for (ChildIterator::Init(parentContent, &iter, &last);
10895 iter != last;
10896 ++iter) {
10897 // Manually check for comments/PIs, since we do't have a frame to pass to
10898 // AddFrameConstructionItems. We know our parent is a non-replaced inline,
10899 // so there is no need to do the NeedFrameFor check.
10900 nsIContent* content = *iter;
10901 content->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
10902 if (content->IsNodeOfType(nsINode::eCOMMENT) ||
10903 content->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION)) {
10904 continue;
10906 if (content->IsElement()) {
10907 // See comment explaining why we need to remove the "is possible
10908 // restyle root" flags in AddFrameConstructionItems. But note
10909 // that we can remove all restyle flags, just like in
10910 // ProcessChildren and for the same reason.
10911 content->UnsetFlags(ELEMENT_ALL_RESTYLE_FLAGS);
10914 nsRefPtr<nsStyleContext> childContext =
10915 ResolveStyleContext(parentStyleContext, content);
10917 AddFrameConstructionItemsInternal(aState, content, nsnull, content->Tag(),
10918 content->GetNameSpaceID(),
10919 iter.XBLInvolved(), childContext,
10920 ITEM_ALLOW_XBL_BASE | ITEM_ALLOW_PAGE_BREAK,
10921 aParentItem.mChildItems);
10924 // Probe for generated content after
10925 CreateGeneratedContentItem(aState, nsnull, parentContent, parentStyleContext,
10926 nsCSSPseudoElements::ePseudo_after,
10927 aParentItem.mChildItems);
10929 aParentItem.mIsAllInline = aParentItem.mChildItems.AreAllItemsInline();
10932 // return whether it's ok to append (in the AppendFrames sense) to
10933 // aParentFrame if our nextSibling is aNextSibling. aParentFrame must
10934 // be an {ib} special inline.
10935 static PRBool
10936 IsSafeToAppendToSpecialInline(nsIFrame* aParentFrame, nsIFrame* aNextSibling)
10938 NS_PRECONDITION(IsInlineFrame(aParentFrame),
10939 "Must have an inline parent here");
10940 do {
10941 NS_ASSERTION(IsFrameSpecial(aParentFrame), "How is this not special?");
10942 if (aNextSibling || aParentFrame->GetNextContinuation() ||
10943 GetSpecialSibling(aParentFrame)) {
10944 return PR_FALSE;
10947 aNextSibling = aParentFrame->GetNextSibling();
10948 aParentFrame = aParentFrame->GetParent();
10949 } while (IsInlineFrame(aParentFrame));
10951 return PR_TRUE;
10954 PRBool
10955 nsCSSFrameConstructor::WipeContainingBlock(nsFrameConstructorState& aState,
10956 nsIFrame* aContainingBlock,
10957 nsIFrame* aFrame,
10958 FrameConstructionItemList& aItems,
10959 PRBool aIsAppend,
10960 nsIFrame* aPrevSibling)
10962 if (aItems.IsEmpty()) {
10963 return PR_FALSE;
10966 // Before we go and append the frames, we must check for several
10967 // special situations.
10969 // Situation #1 is a XUL frame that contains frames that are required
10970 // to be wrapped in blocks.
10971 if (aFrame->IsBoxFrame() &&
10972 !(aFrame->GetStateBits() & NS_STATE_BOX_WRAPS_KIDS_IN_BLOCK) &&
10973 aItems.AnyItemsNeedBlockParent()) {
10974 RecreateFramesForContent(aFrame->GetContent(), PR_TRUE);
10975 return PR_TRUE;
10978 nsIFrame* nextSibling = ::GetInsertNextSibling(aFrame, aPrevSibling);
10980 // Situation #2 is a case when table pseudo-frames don't work out right
10981 ParentType parentType = GetParentType(aFrame);
10982 // If all the kids want a parent of the type that aFrame is, then we're all
10983 // set to go. Indeed, there won't be any table pseudo-frames created between
10984 // aFrame and the kids, so those won't need to be merged with any table
10985 // pseudo-frames that might already be kids of aFrame. If aFrame itself is a
10986 // table pseudo-frame, then all the kids in this list would have wanted a
10987 // frame of that type wrapping them anyway, so putting them inside it is ok.
10988 if (!aItems.AllWantParentType(parentType)) {
10989 // Don't give up yet. If parentType is not eTypeBlock and the parent is
10990 // not a generated content frame, then try filtering whitespace out of the
10991 // list.
10992 if (parentType != eTypeBlock && !aFrame->IsGeneratedContentFrame()) {
10993 // For leading whitespace followed by a kid that wants our parent type,
10994 // there are four cases:
10995 // 1) We have a previous sibling which is not a table pseudo. That means
10996 // that previous sibling wanted a (non-block) parent of the type we're
10997 // looking at. Then the whitespace comes between two table-internal
10998 // elements, so should be collapsed out.
10999 // 2) We have a previous sibling which is a table pseudo. It might have
11000 // kids who want this whitespace, so we need to reframe.
11001 // 3) We have no previous sibling and our parent frame is not a table
11002 // pseudo. That means that we'll be at the beginning of our actual
11003 // non-block-type parent, and the whitespace is OK to collapse out.
11004 // If something is ever inserted before us, it'll find our own parent
11005 // as its parent and if it's something that would care about the
11006 // whitespace it'll want a block parent, so it'll trigger a reframe at
11007 // that point.
11008 // 4) We have no previous sibling and our parent frame is a table pseudo.
11009 // Need to reframe.
11010 // All that is predicated on finding the correct previous sibling. We
11011 // might have to walk backwards along continuations from aFrame to do so.
11013 // It's always OK to drop whitespace between any two items that want a
11014 // parent of type parentType.
11016 // For trailing whitespace preceded by a kid that wants our parent type,
11017 // there are four cases:
11018 // 1) We have a next sibling which is not a table pseudo. That means
11019 // that next sibling wanted a (non-block) parent of the type we're
11020 // looking at. Then the whitespace comes between two table-internal
11021 // elements, so should be collapsed out.
11022 // 2) We have a next sibling which is a table pseudo. It might have
11023 // kids who want this whitespace, so we need to reframe.
11024 // 3) We have no next sibling and our parent frame is not a table
11025 // pseudo. That means that we'll be at the end of our actual
11026 // non-block-type parent, and the whitespace is OK to collapse out.
11027 // If something is ever inserted after us, it'll find our own parent
11028 // as its parent and if it's something that would care about the
11029 // whitespace it'll want a block parent, so it'll trigger a reframe at
11030 // that point.
11031 // 4) We have no next sibling and our parent frame is a table pseudo.
11032 // Need to reframe.
11033 // All that is predicated on finding the correct next sibling. We might
11034 // have to walk forward along continuations from aFrame to do so. That
11035 // said, in the case when nextSibling is null at this point and aIsAppend
11036 // is true, we know we're in case 3. Furthermore, in that case we don't
11037 // even have to worry about the table pseudo situation; we know our
11038 // parent is not a table pseudo there.
11039 FCItemIterator iter(aItems);
11040 FCItemIterator start(iter);
11041 do {
11042 if (iter.SkipItemsWantingParentType(parentType)) {
11043 break;
11046 // iter points to an item that wants a different parent. If it's not
11047 // whitespace, we're done; no more point scanning the list.
11048 if (!iter.item().IsWhitespace(aState)) {
11049 break;
11052 if (iter == start) {
11053 // Leading whitespace. How to handle this depends on our
11054 // previous sibling and aFrame. See the long comment above.
11055 nsIFrame* prevSibling = aPrevSibling;
11056 if (!prevSibling) {
11057 // Try to find one after all
11058 nsIFrame* parentPrevCont = aFrame->GetPrevContinuation();
11059 while (parentPrevCont) {
11060 prevSibling = parentPrevCont->GetLastChild(nsnull);
11061 if (prevSibling) {
11062 break;
11064 parentPrevCont = parentPrevCont->GetPrevContinuation();
11067 if (prevSibling) {
11068 if (IsTablePseudo(prevSibling)) {
11069 // need to reframe
11070 break;
11072 } else if (IsTablePseudo(aFrame)) {
11073 // need to reframe
11074 break;
11078 FCItemIterator spaceEndIter(iter);
11079 // Advance spaceEndIter past any whitespace
11080 PRBool trailingSpaces = spaceEndIter.SkipWhitespace(aState);
11082 PRBool okToDrop;
11083 if (trailingSpaces) {
11084 // Trailing whitespace. How to handle this depeds on aIsAppend, our
11085 // next sibling and aFrame. See the long comment above.
11086 okToDrop = aIsAppend && !nextSibling;
11087 if (!okToDrop) {
11088 if (!nextSibling) {
11089 // Try to find one after all
11090 nsIFrame* parentNextCont = aFrame->GetNextContinuation();
11091 while (parentNextCont) {
11092 nextSibling = parentNextCont->GetFirstChild(nsnull);
11093 if (nextSibling) {
11094 break;
11096 parentNextCont = parentNextCont->GetNextContinuation();
11100 okToDrop = (nextSibling && !IsTablePseudo(nextSibling)) ||
11101 (!nextSibling && !IsTablePseudo(aFrame));
11103 #ifdef DEBUG
11104 else {
11105 NS_ASSERTION(!IsTablePseudo(aFrame), "How did that happen?");
11107 #endif
11108 } else {
11109 okToDrop = (spaceEndIter.item().DesiredParentType() == parentType);
11112 if (okToDrop) {
11113 iter.DeleteItemsTo(spaceEndIter);
11114 } else {
11115 // We're done: we don't want to drop the whitespace, and it has the
11116 // wrong parent type.
11117 break;
11120 // Now loop, since |iter| points to item right after the whitespace we
11121 // removed.
11122 } while (!iter.IsDone());
11125 // We might be able to figure out some sort of optimizations here, but they
11126 // would have to depend on having a correct aPrevSibling and a correct next
11127 // sibling. For example, we can probably avoid reframing if none of
11128 // aFrame, aPrevSibling, and next sibling are table pseudo-frames. But it
11129 // doesn't seem worth it to worry about that for now, especially since we
11130 // in fact do not have a reliable aPrevSibling, nor any next sibling, in
11131 // this method.
11133 // aItems might have changed, so recheck the parent type thing. In fact,
11134 // it might be empty, so recheck that too.
11135 if (aItems.IsEmpty()) {
11136 return PR_FALSE;
11139 if (!aItems.AllWantParentType(parentType)) {
11140 // Reframing aFrame->GetContent() is good enough, since the content of
11141 // table pseudo-frames is the ancestor content.
11142 RecreateFramesForContent(aFrame->GetContent(), PR_TRUE);
11143 return PR_TRUE;
11147 // Now we have several cases involving {ib} splits. Put them all in a
11148 // do/while with breaks to take us to the "go and reconstruct" code.
11149 do {
11150 if (IsInlineFrame(aFrame)) {
11151 if (aItems.AreAllItemsInline()) {
11152 // We can just put the kids in.
11153 return PR_FALSE;
11156 if (!IsFrameSpecial(aFrame)) {
11157 // Need to go ahead and reconstruct.
11158 break;
11161 // Now we're adding kids including some blocks to an inline part of an
11162 // {ib} split. If we plan to call AppendFrames, and don't have a next
11163 // sibling for the new frames, and our parent is the last continuation of
11164 // the last part of the {ib} split, and the same is true of all our
11165 // ancestor inlines (they have no following continuations and they're the
11166 // last part of their {ib} splits and we'd be adding to the end for all
11167 // of them), then AppendFrames will handle things for us. Bail out in
11168 // that case.
11169 if (aIsAppend && IsSafeToAppendToSpecialInline(aFrame, nextSibling)) {
11170 return PR_FALSE;
11173 // Need to reconstruct.
11174 break;
11177 // Now we know we have a block parent. If it's not special, we're all set.
11178 if (!IsFrameSpecial(aFrame)) {
11179 return PR_FALSE;
11182 // We're adding some kids to a block part of an {ib} split. If all the
11183 // kids are blocks, we don't need to reconstruct.
11184 if (aItems.AreAllItemsBlock()) {
11185 return PR_FALSE;
11188 // We might have some inline kids for this block. Just reconstruct.
11189 break;
11190 } while (0);
11192 // If we don't have a containing block, start with aFrame and look for one.
11193 if (!aContainingBlock) {
11194 aContainingBlock = aFrame;
11197 // To find the right block to reframe, just walk up the tree until we find a
11198 // frame that is:
11199 // 1) Not part of an IB split (not special)
11200 // 2) Not a pseudo-frame
11201 // 3) Not an inline frame
11202 // We're guaranteed to find one, since nsStyleContext::ApplyStyleFixups
11203 // enforces that the root is display:none, display:table, or display:block.
11204 // Note that walking up "too far" is OK in terms of correctness, even if it
11205 // might be a little inefficient. This is why we walk out of all
11206 // pseudo-frames -- telling which ones are or are not OK to walk out of is
11207 // too hard (and I suspect that we do in fact need to walk out of all of
11208 // them).
11209 while (IsFrameSpecial(aContainingBlock) || IsInlineOutside(aContainingBlock) ||
11210 aContainingBlock->GetStyleContext()->GetPseudo()) {
11211 aContainingBlock = aContainingBlock->GetParent();
11212 NS_ASSERTION(aContainingBlock,
11213 "Must have non-inline, non-special, non-pseudo frame as root "
11214 "(or child of root, for a table root)!");
11217 // Tell parent of the containing block to reformulate the
11218 // entire block. This is painful and definitely not optimal
11219 // but it will *always* get the right answer.
11221 nsIContent *blockContent = aContainingBlock->GetContent();
11222 #ifdef DEBUG
11223 if (gNoisyContentUpdates) {
11224 printf("nsCSSFrameConstructor::WipeContainingBlock: blockContent=%p\n",
11225 static_cast<void*>(blockContent));
11227 #endif
11228 RecreateFramesForContent(blockContent, PR_TRUE);
11229 return PR_TRUE;
11232 nsresult
11233 nsCSSFrameConstructor::ReframeContainingBlock(nsIFrame* aFrame)
11236 #ifdef DEBUG
11237 // ReframeContainingBlock is a NASTY routine, it causes terrible performance problems
11238 // so I want to see when it is happening! Unfortunately, it is happening way to often because
11239 // so much content on the web causes 'special' block-in-inline frame situations and we handle them
11240 // very poorly
11241 if (gNoisyContentUpdates) {
11242 printf("nsCSSFrameConstructor::ReframeContainingBlock frame=%p\n",
11243 static_cast<void*>(aFrame));
11245 #endif
11247 // XXXbz how exactly would we get here while isReflowing anyway? Should this
11248 // whole test be ifdef DEBUG?
11249 if (mPresShell->IsReflowLocked()) {
11250 // don't ReframeContainingBlock, this will result in a crash
11251 // if we remove a tree that's in reflow - see bug 121368 for testcase
11252 NS_ERROR("Atemptted to nsCSSFrameConstructor::ReframeContainingBlock during a Reflow!!!");
11253 return NS_OK;
11256 // Get the first "normal" ancestor of the target frame.
11257 nsIFrame* containingBlock = GetIBContainingBlockFor(aFrame);
11258 if (containingBlock) {
11259 // From here we look for the containing block in case the target
11260 // frame is already a block (which can happen when an inline frame
11261 // wraps some of its content in an anonymous block; see
11262 // ConstructInline)
11264 // NOTE: We used to get the FloatContainingBlock here, but it was often wrong.
11265 // GetIBContainingBlock works much better and provides the correct container in all cases
11266 // so GetFloatContainingBlock(aFrame) has been removed
11268 // And get the containingBlock's content
11269 nsCOMPtr<nsIContent> blockContent = containingBlock->GetContent();
11270 if (blockContent) {
11271 #ifdef DEBUG
11272 if (gNoisyContentUpdates) {
11273 printf(" ==> blockContent=%p\n", static_cast<void*>(blockContent));
11275 #endif
11276 return RecreateFramesForContent(blockContent, PR_TRUE);
11280 // If we get here, we're screwed!
11281 return RecreateFramesForContent(mPresShell->GetDocument()->GetRootElement(),
11282 PR_TRUE);
11285 void
11286 nsCSSFrameConstructor::RestyleForEmptyChange(Element* aContainer)
11288 // In some cases (:empty + E, :empty ~ E), a change if the content of
11289 // an element requires restyling its parent's siblings.
11290 nsRestyleHint hint = eRestyle_Subtree;
11291 nsIContent* grandparent = aContainer->GetParent();
11292 if (grandparent &&
11293 (grandparent->GetFlags() & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS)) {
11294 hint = nsRestyleHint(hint | eRestyle_LaterSiblings);
11296 PostRestyleEvent(aContainer, hint, NS_STYLE_HINT_NONE);
11299 void
11300 nsCSSFrameConstructor::RestyleForAppend(Element* aContainer,
11301 nsIContent* aFirstNewContent)
11303 NS_ASSERTION(aContainer, "must have container for append");
11304 #ifdef DEBUG
11306 for (nsIContent* cur = aFirstNewContent; cur; cur = cur->GetNextSibling()) {
11307 NS_ASSERTION(!cur->IsRootOfAnonymousSubtree(),
11308 "anonymous nodes should not be in child lists");
11311 #endif
11312 PRUint32 selectorFlags =
11313 aContainer->GetFlags() & (NODE_ALL_SELECTOR_FLAGS &
11314 ~NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS);
11315 if (selectorFlags == 0)
11316 return;
11318 if (selectorFlags & NODE_HAS_EMPTY_SELECTOR) {
11319 // see whether we need to restyle the container
11320 PRBool wasEmpty = PR_TRUE; // :empty or :-moz-only-whitespace
11321 for (nsIContent* cur = aContainer->GetFirstChild();
11322 cur != aFirstNewContent;
11323 cur = cur->GetNextSibling()) {
11324 // We don't know whether we're testing :empty or :-moz-only-whitespace,
11325 // so be conservative and assume :-moz-only-whitespace (i.e., make
11326 // IsSignificantChild less likely to be true, and thus make us more
11327 // likely to restyle).
11328 if (nsStyleUtil::IsSignificantChild(cur, PR_TRUE, PR_FALSE)) {
11329 wasEmpty = PR_FALSE;
11330 break;
11333 if (wasEmpty) {
11334 RestyleForEmptyChange(aContainer);
11335 return;
11339 if (selectorFlags & NODE_HAS_SLOW_SELECTOR) {
11340 PostRestyleEvent(aContainer, eRestyle_Subtree, NS_STYLE_HINT_NONE);
11341 // Restyling the container is the most we can do here, so we're done.
11342 return;
11345 if (selectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) {
11346 // restyle the last element child before this node
11347 for (nsIContent* cur = aFirstNewContent->GetPreviousSibling();
11348 cur;
11349 cur = cur->GetPreviousSibling()) {
11350 if (cur->IsElement()) {
11351 PostRestyleEvent(cur->AsElement(), eRestyle_Subtree, NS_STYLE_HINT_NONE);
11352 break;
11358 // Needed since we can't use PostRestyleEvent on non-elements (with
11359 // eRestyle_LaterSiblings or nsRestyleHint(eRestyle_Subtree |
11360 // eRestyle_LaterSiblings) as appropriate).
11361 static void
11362 RestyleSiblingsStartingWith(nsCSSFrameConstructor *aFrameConstructor,
11363 nsIContent *aStartingSibling /* may be null */)
11365 for (nsIContent *sibling = aStartingSibling; sibling;
11366 sibling = sibling->GetNextSibling()) {
11367 if (sibling->IsElement()) {
11368 aFrameConstructor->
11369 PostRestyleEvent(sibling->AsElement(),
11370 nsRestyleHint(eRestyle_Subtree | eRestyle_LaterSiblings),
11371 NS_STYLE_HINT_NONE);
11372 break;
11377 // Restyling for a ContentInserted or CharacterDataChanged notification.
11378 // This could be used for ContentRemoved as well if we got the
11379 // notification before the removal happened (and sometimes
11380 // CharacterDataChanged is more like a removal than an addition).
11381 // The comments are written and variables are named in terms of it being
11382 // a ContentInserted notification.
11383 void
11384 nsCSSFrameConstructor::RestyleForInsertOrChange(Element* aContainer,
11385 nsIContent* aChild)
11387 NS_ASSERTION(!aChild->IsRootOfAnonymousSubtree(),
11388 "anonymous nodes should not be in child lists");
11389 PRUint32 selectorFlags =
11390 aContainer ? (aContainer->GetFlags() & NODE_ALL_SELECTOR_FLAGS) : 0;
11391 if (selectorFlags == 0)
11392 return;
11394 if (selectorFlags & NODE_HAS_EMPTY_SELECTOR) {
11395 // see whether we need to restyle the container
11396 PRBool wasEmpty = PR_TRUE; // :empty or :-moz-only-whitespace
11397 for (nsIContent* child = aContainer->GetFirstChild();
11398 child;
11399 child = child->GetNextSibling()) {
11400 if (child == aChild)
11401 continue;
11402 // We don't know whether we're testing :empty or :-moz-only-whitespace,
11403 // so be conservative and assume :-moz-only-whitespace (i.e., make
11404 // IsSignificantChild less likely to be true, and thus make us more
11405 // likely to restyle).
11406 if (nsStyleUtil::IsSignificantChild(child, PR_TRUE, PR_FALSE)) {
11407 wasEmpty = PR_FALSE;
11408 break;
11411 if (wasEmpty) {
11412 RestyleForEmptyChange(aContainer);
11413 return;
11417 if (selectorFlags & NODE_HAS_SLOW_SELECTOR) {
11418 PostRestyleEvent(aContainer, eRestyle_Subtree, NS_STYLE_HINT_NONE);
11419 // Restyling the container is the most we can do here, so we're done.
11420 return;
11423 if (selectorFlags & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS) {
11424 // Restyle all later siblings.
11425 RestyleSiblingsStartingWith(this, aChild->GetNextSibling());
11428 if (selectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) {
11429 // restyle the previously-first element child if it is after this node
11430 PRBool passedChild = PR_FALSE;
11431 for (nsIContent* content = aContainer->GetFirstChild();
11432 content;
11433 content = content->GetNextSibling()) {
11434 if (content == aChild) {
11435 passedChild = PR_TRUE;
11436 continue;
11438 if (content->IsElement()) {
11439 if (passedChild) {
11440 PostRestyleEvent(content->AsElement(), eRestyle_Subtree,
11441 NS_STYLE_HINT_NONE);
11443 break;
11446 // restyle the previously-last element child if it is before this node
11447 passedChild = PR_FALSE;
11448 for (nsIContent* content = aContainer->GetLastChild();
11449 content;
11450 content = content->GetPreviousSibling()) {
11451 if (content == aChild) {
11452 passedChild = PR_TRUE;
11453 continue;
11455 if (content->IsElement()) {
11456 if (passedChild) {
11457 PostRestyleEvent(content->AsElement(), eRestyle_Subtree,
11458 NS_STYLE_HINT_NONE);
11460 break;
11466 void
11467 nsCSSFrameConstructor::RestyleForRemove(Element* aContainer,
11468 nsIContent* aOldChild,
11469 nsIContent* aFollowingSibling)
11471 NS_ASSERTION(!aOldChild->IsRootOfAnonymousSubtree(),
11472 "anonymous nodes should not be in child lists");
11473 PRUint32 selectorFlags =
11474 aContainer ? (aContainer->GetFlags() & NODE_ALL_SELECTOR_FLAGS) : 0;
11475 if (selectorFlags == 0)
11476 return;
11478 if (selectorFlags & NODE_HAS_EMPTY_SELECTOR) {
11479 // see whether we need to restyle the container
11480 PRBool isEmpty = PR_TRUE; // :empty or :-moz-only-whitespace
11481 for (nsIContent* child = aContainer->GetFirstChild();
11482 child;
11483 child = child->GetNextSibling()) {
11484 // We don't know whether we're testing :empty or :-moz-only-whitespace,
11485 // so be conservative and assume :-moz-only-whitespace (i.e., make
11486 // IsSignificantChild less likely to be true, and thus make us more
11487 // likely to restyle).
11488 if (nsStyleUtil::IsSignificantChild(child, PR_TRUE, PR_FALSE)) {
11489 isEmpty = PR_FALSE;
11490 break;
11493 if (isEmpty) {
11494 RestyleForEmptyChange(aContainer);
11495 return;
11499 if (selectorFlags & NODE_HAS_SLOW_SELECTOR) {
11500 PostRestyleEvent(aContainer, eRestyle_Subtree, NS_STYLE_HINT_NONE);
11501 // Restyling the container is the most we can do here, so we're done.
11502 return;
11505 if (selectorFlags & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS) {
11506 // Restyle all later siblings.
11507 RestyleSiblingsStartingWith(this, aFollowingSibling);
11510 if (selectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) {
11511 // restyle the now-first element child if it was after aOldChild
11512 PRBool reachedFollowingSibling = PR_FALSE;
11513 for (nsIContent* content = aContainer->GetFirstChild();
11514 content;
11515 content = content->GetNextSibling()) {
11516 if (content == aFollowingSibling) {
11517 reachedFollowingSibling = PR_TRUE;
11518 // do NOT continue here; we might want to restyle this node
11520 if (content->IsElement()) {
11521 if (reachedFollowingSibling) {
11522 PostRestyleEvent(content->AsElement(), eRestyle_Subtree,
11523 NS_STYLE_HINT_NONE);
11525 break;
11528 // restyle the now-last element child if it was before aOldChild
11529 reachedFollowingSibling = (aFollowingSibling == nsnull);
11530 for (nsIContent* content = aContainer->GetLastChild();
11531 content;
11532 content = content->GetPreviousSibling()) {
11533 if (content->IsElement()) {
11534 if (reachedFollowingSibling) {
11535 PostRestyleEvent(content->AsElement(), eRestyle_Subtree, NS_STYLE_HINT_NONE);
11537 break;
11539 if (content == aFollowingSibling) {
11540 reachedFollowingSibling = PR_TRUE;
11547 void
11548 nsCSSFrameConstructor::RebuildAllStyleData(nsChangeHint aExtraHint)
11550 NS_ASSERTION(!(aExtraHint & nsChangeHint_ReconstructFrame),
11551 "Should not reconstruct the root of the frame tree. "
11552 "Use ReconstructDocElementHierarchy instead.");
11554 mRebuildAllStyleData = PR_FALSE;
11555 NS_UpdateHint(aExtraHint, mRebuildAllExtraHint);
11556 mRebuildAllExtraHint = nsChangeHint(0);
11558 if (!mPresShell || !mPresShell->GetRootFrame())
11559 return;
11561 // Make sure that the viewmanager will outlive the presshell
11562 nsIViewManager::UpdateViewBatch batch(mPresShell->GetViewManager());
11564 // Processing the style changes could cause a flush that propagates to
11565 // the parent frame and thus destroys the pres shell.
11566 nsCOMPtr<nsIPresShell> kungFuDeathGrip(mPresShell);
11568 // We may reconstruct frames below and hence process anything that is in the
11569 // tree. We don't want to get notified to process those items again after.
11570 mPresShell->GetDocument()->FlushPendingNotifications(Flush_ContentAndNotify);
11572 nsAutoScriptBlocker scriptBlocker;
11574 // Tell the style set to get the old rule tree out of the way
11575 // so we can recalculate while maintaining rule tree immutability
11576 nsresult rv = mPresShell->StyleSet()->BeginReconstruct();
11577 if (NS_FAILED(rv)) {
11578 batch.EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC);
11579 return;
11582 // Recalculate all of the style contexts for the document
11583 // Note that we can ignore the return value of ComputeStyleChangeFor
11584 // because we never need to reframe the root frame
11585 // XXX This could be made faster by not rerunning rule matching
11586 // (but note that nsPresShell::SetPreferenceStyleRules currently depends
11587 // on us re-running rule matching here
11588 nsStyleChangeList changeList;
11589 // XXX Does it matter that we're passing aExtraHint to the real root
11590 // frame and not the root node's primary frame?
11591 // Note: The restyle tracker we pass in here doesn't matter.
11592 mPresShell->FrameManager()->ComputeStyleChangeFor(mPresShell->GetRootFrame(),
11593 &changeList, aExtraHint,
11594 mPendingRestyles, PR_TRUE);
11595 // Process the required changes
11596 ProcessRestyledFrames(changeList);
11597 // Tell the style set it's safe to destroy the old rule tree. We
11598 // must do this after the ProcessRestyledFrames call in case the
11599 // change list has frame reconstructs in it (since frames to be
11600 // reconstructed will still have their old style context pointers
11601 // until they are destroyed).
11602 mPresShell->StyleSet()->EndReconstruct();
11603 batch.EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC);
11606 void
11607 nsCSSFrameConstructor::ProcessPendingRestyles()
11609 NS_PRECONDITION(mDocument, "No document? Pshaw!");
11610 NS_PRECONDITION(!nsContentUtils::IsSafeToRunScript(),
11611 "Missing a script blocker!");
11613 // Process non-animation restyles...
11614 nsPresContext *presContext = mPresShell->GetPresContext();
11615 NS_ABORT_IF_FALSE(!presContext->IsProcessingRestyles(),
11616 "Nesting calls to ProcessPendingRestyles?");
11617 presContext->SetProcessingRestyles(PR_TRUE);
11619 mPendingRestyles.ProcessRestyles();
11621 #ifdef DEBUG
11622 PRUint32 oldPendingRestyleCount = mPendingRestyles.Count();
11623 #endif
11625 // ...and then process animation restyles. This needs to happen
11626 // second because we need to start animations that resulted from the
11627 // first set of restyles (e.g., CSS transitions with negative
11628 // transition-delay), and because we need to immediately
11629 // restyle-with-animation any just-restyled elements that are
11630 // mid-transition (since processing the non-animation restyle ignores
11631 // the running transition so it can check for a new change on the same
11632 // property, and then posts an immediate animation style change).
11633 presContext->SetProcessingAnimationStyleChange(PR_TRUE);
11634 mPendingAnimationRestyles.ProcessRestyles();
11635 presContext->SetProcessingAnimationStyleChange(PR_FALSE);
11637 presContext->SetProcessingRestyles(PR_FALSE);
11638 NS_POSTCONDITION(mPendingRestyles.Count() == oldPendingRestyleCount,
11639 "We should not have posted new non-animation restyles while "
11640 "processing animation restyles");
11642 if (mRebuildAllStyleData) {
11643 // We probably wasted a lot of work up above, but this seems safest
11644 // and it should be rarely used.
11645 // This might add us as a refresh observer again; that's ok.
11646 RebuildAllStyleData(nsChangeHint(0));
11650 void
11651 nsCSSFrameConstructor::PostRestyleEventCommon(Element* aElement,
11652 nsRestyleHint aRestyleHint,
11653 nsChangeHint aMinChangeHint,
11654 PRBool aForAnimation)
11656 if (NS_UNLIKELY(mPresShell->IsDestroying())) {
11657 return;
11660 if (aRestyleHint == 0 && !aMinChangeHint) {
11661 // Nothing to do here
11662 return;
11665 RestyleTracker& tracker =
11666 aForAnimation ? mPendingAnimationRestyles : mPendingRestyles;
11667 tracker.AddPendingRestyle(aElement, aRestyleHint, aMinChangeHint);
11669 PostRestyleEventInternal(PR_FALSE);
11672 void
11673 nsCSSFrameConstructor::PostRestyleEventInternal(PRBool aForLazyConstruction)
11675 // Make sure we're not in a style refresh; if we are, we still have
11676 // a call to ProcessPendingRestyles coming and there's no need to
11677 // add ourselves as a refresh observer until then.
11678 PRBool inRefresh = !aForLazyConstruction && mInStyleRefresh;
11679 if (!mObservingRefreshDriver && !inRefresh) {
11680 mObservingRefreshDriver = mPresShell->GetPresContext()->RefreshDriver()->
11681 AddStyleFlushObserver(mPresShell);
11685 void
11686 nsCSSFrameConstructor::PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint)
11688 NS_ASSERTION(!(aExtraHint & nsChangeHint_ReconstructFrame),
11689 "Should not reconstruct the root of the frame tree. "
11690 "Use ReconstructDocElementHierarchy instead.");
11692 mRebuildAllStyleData = PR_TRUE;
11693 NS_UpdateHint(mRebuildAllExtraHint, aExtraHint);
11694 // Get a restyle event posted if necessary
11695 PostRestyleEventInternal(PR_FALSE);
11698 nsresult
11699 nsCSSFrameConstructor::GenerateChildFrames(nsIFrame* aFrame)
11702 nsAutoScriptBlocker scriptBlocker;
11703 BeginUpdate();
11705 nsFrameItems childItems;
11706 nsFrameConstructorState state(mPresShell, nsnull, nsnull, nsnull);
11707 // We don't have a parent frame with a pending binding constructor here,
11708 // so no need to worry about ordering of the kids' constructors with it.
11709 // Pass null for the PendingBinding.
11710 nsresult rv = ProcessChildren(state, aFrame->GetContent(), aFrame->GetStyleContext(),
11711 aFrame, PR_FALSE, childItems, PR_FALSE,
11712 nsnull);
11713 if (NS_FAILED(rv)) {
11714 EndUpdate();
11715 return rv;
11718 aFrame->SetInitialChildList(nsnull, childItems);
11720 EndUpdate();
11723 // call XBL constructors after the frames are created
11724 mPresShell->GetDocument()->BindingManager()->ProcessAttachedQueue();
11726 return NS_OK;
11729 //////////////////////////////////////////////////////////
11730 // nsCSSFrameConstructor::FrameConstructionItem methods //
11731 //////////////////////////////////////////////////////////
11732 PRBool
11733 nsCSSFrameConstructor::
11734 FrameConstructionItem::IsWhitespace(nsFrameConstructorState& aState) const
11736 NS_PRECONDITION(aState.mCreatingExtraFrames ||
11737 !mContent->GetPrimaryFrame(), "How did that happen?");
11738 if (!mIsText) {
11739 return PR_FALSE;
11741 mContent->SetFlags(NS_CREATE_FRAME_IF_NON_WHITESPACE |
11742 NS_REFRAME_IF_WHITESPACE);
11743 return mContent->TextIsOnlyWhitespace();
11746 //////////////////////////////////////////////////////////////
11747 // nsCSSFrameConstructor::FrameConstructionItemList methods //
11748 //////////////////////////////////////////////////////////////
11749 void
11750 nsCSSFrameConstructor::FrameConstructionItemList::
11751 AdjustCountsForItem(FrameConstructionItem* aItem, PRInt32 aDelta)
11753 NS_PRECONDITION(aDelta == 1 || aDelta == -1, "Unexpected delta");
11754 mItemCount += aDelta;
11755 if (aItem->mIsAllInline) {
11756 mInlineCount += aDelta;
11758 if (aItem->mIsBlock) {
11759 mBlockCount += aDelta;
11761 if (aItem->mIsLineParticipant) {
11762 mLineParticipantCount += aDelta;
11764 mDesiredParentCounts[aItem->DesiredParentType()] += aDelta;
11767 ////////////////////////////////////////////////////////////////////////
11768 // nsCSSFrameConstructor::FrameConstructionItemList::Iterator methods //
11769 ////////////////////////////////////////////////////////////////////////
11770 inline PRBool
11771 nsCSSFrameConstructor::FrameConstructionItemList::
11772 Iterator::SkipItemsWantingParentType(ParentType aParentType)
11774 NS_PRECONDITION(!IsDone(), "Shouldn't be done yet");
11775 while (item().DesiredParentType() == aParentType) {
11776 Next();
11777 if (IsDone()) {
11778 return PR_TRUE;
11781 return PR_FALSE;
11784 inline PRBool
11785 nsCSSFrameConstructor::FrameConstructionItemList::
11786 Iterator::SkipWhitespace(nsFrameConstructorState& aState)
11788 NS_PRECONDITION(!IsDone(), "Shouldn't be done yet");
11789 NS_PRECONDITION(item().IsWhitespace(aState), "Not pointing to whitespace?");
11790 do {
11791 Next();
11792 if (IsDone()) {
11793 return PR_TRUE;
11795 } while (item().IsWhitespace(aState));
11797 return PR_FALSE;
11800 void
11801 nsCSSFrameConstructor::FrameConstructionItemList::
11802 Iterator::AppendItemToList(FrameConstructionItemList& aTargetList)
11804 NS_ASSERTION(&aTargetList != &mList, "Unexpected call");
11805 NS_PRECONDITION(!IsDone(), "should not be done");
11807 FrameConstructionItem* item = ToItem(mCurrent);
11808 Next();
11809 PR_REMOVE_LINK(item);
11810 PR_APPEND_LINK(item, &aTargetList.mItems);
11812 mList.AdjustCountsForItem(item, -1);
11813 aTargetList.AdjustCountsForItem(item, 1);
11816 void
11817 nsCSSFrameConstructor::FrameConstructionItemList::
11818 Iterator::AppendItemsToList(const Iterator& aEnd,
11819 FrameConstructionItemList& aTargetList)
11821 NS_ASSERTION(&aTargetList != &mList, "Unexpected call");
11822 NS_PRECONDITION(mEnd == aEnd.mEnd, "end iterator for some other list?");
11824 if (!AtStart() || !aEnd.IsDone() || !aTargetList.IsEmpty()) {
11825 do {
11826 AppendItemToList(aTargetList);
11827 } while (*this != aEnd);
11828 return;
11831 // move over the list of items
11832 PR_INSERT_AFTER(&aTargetList.mItems, &mList.mItems);
11833 PR_REMOVE_LINK(&mList.mItems);
11835 // Copy over the various counters
11836 aTargetList.mInlineCount = mList.mInlineCount;
11837 aTargetList.mBlockCount = mList.mBlockCount;
11838 aTargetList.mLineParticipantCount = mList.mLineParticipantCount;
11839 aTargetList.mItemCount = mList.mItemCount;
11840 memcpy(aTargetList.mDesiredParentCounts, mList.mDesiredParentCounts,
11841 sizeof(aTargetList.mDesiredParentCounts));
11843 // reset mList
11844 new (&mList) FrameConstructionItemList();
11846 // Point ourselves to aEnd, as advertised
11847 mCurrent = mEnd = &mList.mItems;
11848 NS_POSTCONDITION(*this == aEnd, "How did that happen?");
11851 void
11852 nsCSSFrameConstructor::FrameConstructionItemList::
11853 Iterator::InsertItem(FrameConstructionItem* aItem)
11855 // Just insert the item before us. There's no magic here.
11856 PR_INSERT_BEFORE(aItem, mCurrent);
11857 mList.AdjustCountsForItem(aItem, 1);
11859 NS_POSTCONDITION(PR_NEXT_LINK(aItem) == mCurrent, "How did that happen?");
11862 void
11863 nsCSSFrameConstructor::FrameConstructionItemList::
11864 Iterator::DeleteItemsTo(const Iterator& aEnd)
11866 NS_PRECONDITION(mEnd == aEnd.mEnd, "end iterator for some other list?");
11867 NS_PRECONDITION(*this != aEnd, "Shouldn't be at aEnd yet");
11869 do {
11870 NS_ASSERTION(!IsDone(), "Ran off end of list?");
11871 FrameConstructionItem* item = ToItem(mCurrent);
11872 Next();
11873 PR_REMOVE_LINK(item);
11874 mList.AdjustCountsForItem(item, -1);
11875 delete item;
11876 } while (*this != aEnd);