Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 // vim:cindent:ts=2:et:sw=2:
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /*
8 * construction of a frame tree that is nearly isomorphic to the content
9 * tree and updating of that tree in response to dynamic changes
10 */
12 #include "nsCSSFrameConstructor.h"
14 #include "mozilla/AutoRestore.h"
15 #include "mozilla/DebugOnly.h"
16 #include "mozilla/dom/HTMLSelectElement.h"
17 #include "mozilla/EventStates.h"
18 #include "mozilla/Likely.h"
19 #include "mozilla/LinkedList.h"
20 #include "nsAbsoluteContainingBlock.h"
21 #include "nsIAtom.h"
22 #include "nsIFrameInlines.h"
23 #include "nsGkAtoms.h"
24 #include "nsPresContext.h"
25 #include "nsIDocument.h"
26 #include "nsTableFrame.h"
27 #include "nsTableColFrame.h"
28 #include "nsIDOMHTMLDocument.h"
29 #include "nsHTMLParts.h"
30 #include "nsIPresShell.h"
31 #include "nsUnicharUtils.h"
32 #include "nsStyleSet.h"
33 #include "nsViewManager.h"
34 #include "nsStyleConsts.h"
35 #include "nsIDOMXULElement.h"
36 #include "nsContainerFrame.h"
37 #include "nsNameSpaceManager.h"
38 #include "nsIComboboxControlFrame.h"
39 #include "nsIListControlFrame.h"
40 #include "nsIDOMCharacterData.h"
41 #include "nsPlaceholderFrame.h"
42 #include "nsTableRowGroupFrame.h"
43 #include "nsIFormControl.h"
44 #include "nsCSSAnonBoxes.h"
45 #include "nsTextFragment.h"
46 #include "nsIAnonymousContentCreator.h"
47 #include "nsBindingManager.h"
48 #include "nsXBLBinding.h"
49 #include "nsContentUtils.h"
50 #include "nsIScriptError.h"
51 #ifdef XP_MACOSX
52 #include "nsIDocShell.h"
53 #endif
54 #include "ChildIterator.h"
55 #include "nsError.h"
56 #include "nsLayoutUtils.h"
57 #include "nsAutoPtr.h"
58 #include "nsBoxFrame.h"
59 #include "nsBoxLayout.h"
60 #include "nsFlexContainerFrame.h"
61 #include "nsGridContainerFrame.h"
62 #include "nsImageFrame.h"
63 #include "nsIObjectLoadingContent.h"
64 #include "nsTArray.h"
65 #include "nsGenericDOMDataNode.h"
66 #include "mozilla/dom/Element.h"
67 #include "nsAutoLayoutPhase.h"
68 #include "nsStyleStructInlines.h"
69 #include "nsPageContentFrame.h"
70 #include "RestyleManager.h"
71 #include "StickyScrollContainer.h"
72 #include "nsFieldSetFrame.h"
74 #ifdef MOZ_XUL
75 #include "nsIRootBox.h"
76 #endif
77 #ifdef ACCESSIBILITY
78 #include "nsAccessibilityService.h"
79 #endif
81 #include "nsBlockFrame.h"
83 #include "nsIScrollableFrame.h"
85 #include "nsXBLService.h"
87 #undef NOISY_FIRST_LETTER
89 #include "nsMathMLParts.h"
90 #include "mozilla/dom/SVGTests.h"
91 #include "nsSVGUtils.h"
93 #include "nsRefreshDriver.h"
94 #include "nsRuleProcessorData.h"
95 #include "nsTextNode.h"
97 using namespace mozilla;
98 using namespace mozilla::dom;
100 // An alias for convenience.
101 static const nsIFrame::ChildListID kPrincipalList = nsIFrame::kPrincipalList;
103 nsIFrame*
104 NS_NewHTMLCanvasFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
106 nsIFrame*
107 NS_NewHTMLVideoFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
109 nsIFrame*
110 NS_NewSVGOuterSVGFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
111 nsIFrame*
112 NS_NewSVGOuterSVGAnonChildFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
113 nsIFrame*
114 NS_NewSVGInnerSVGFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
115 nsIFrame*
116 NS_NewSVGPathGeometryFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
117 nsIFrame*
118 NS_NewSVGGFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
119 nsIFrame*
120 NS_NewSVGGenericContainerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
121 nsIFrame*
122 NS_NewSVGForeignObjectFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
123 nsIFrame*
124 NS_NewSVGAFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
125 nsIFrame*
126 NS_NewSVGSwitchFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
127 nsIFrame*
128 NS_NewSVGTextFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
129 nsIFrame*
130 NS_NewSVGContainerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
131 nsIFrame*
132 NS_NewSVGUseFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
133 nsIFrame*
134 NS_NewSVGViewFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
135 extern nsIFrame*
136 NS_NewSVGLinearGradientFrame(nsIPresShell *aPresShell, nsStyleContext* aContext);
137 extern nsIFrame*
138 NS_NewSVGRadialGradientFrame(nsIPresShell *aPresShell, nsStyleContext* aContext);
139 extern nsIFrame*
140 NS_NewSVGStopFrame(nsIPresShell *aPresShell, nsStyleContext* aContext);
141 nsIFrame*
142 NS_NewSVGMarkerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
143 nsIFrame*
144 NS_NewSVGMarkerAnonChildFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
145 extern nsIFrame*
146 NS_NewSVGImageFrame(nsIPresShell *aPresShell, nsStyleContext* aContext);
147 nsIFrame*
148 NS_NewSVGClipPathFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
149 nsIFrame*
150 NS_NewSVGFilterFrame(nsIPresShell *aPresShell, nsStyleContext* aContext);
151 nsIFrame*
152 NS_NewSVGPatternFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
153 nsIFrame*
154 NS_NewSVGMaskFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
155 nsIFrame*
156 NS_NewSVGFEContainerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
157 nsIFrame*
158 NS_NewSVGFELeafFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
159 nsIFrame*
160 NS_NewSVGFEImageFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
161 nsIFrame*
162 NS_NewSVGFEUnstyledLeafFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
164 #include "nsINodeInfo.h"
165 #include "prenv.h"
166 #include "nsNodeInfoManager.h"
167 #include "nsContentCreatorFunctions.h"
169 #ifdef DEBUG
170 // Set the environment variable GECKO_FRAMECTOR_DEBUG_FLAGS to one or
171 // more of the following flags (comma separated) for handy debug
172 // output.
173 static bool gNoisyContentUpdates = false;
174 static bool gReallyNoisyContentUpdates = false;
175 static bool gNoisyInlineConstruction = false;
177 struct FrameCtorDebugFlags {
178 const char* name;
179 bool* on;
180 };
182 static FrameCtorDebugFlags gFlags[] = {
183 { "content-updates", &gNoisyContentUpdates },
184 { "really-noisy-content-updates", &gReallyNoisyContentUpdates },
185 { "noisy-inline", &gNoisyInlineConstruction }
186 };
188 #define NUM_DEBUG_FLAGS (sizeof(gFlags) / sizeof(gFlags[0]))
189 #endif
192 #ifdef MOZ_XUL
193 #include "nsMenuFrame.h"
194 #include "nsPopupSetFrame.h"
195 #include "nsTreeColFrame.h"
196 #include "nsIBoxObject.h"
197 #include "nsPIListBoxObject.h"
198 #include "nsListBoxBodyFrame.h"
199 #include "nsListItemFrame.h"
200 #include "nsXULLabelFrame.h"
202 //------------------------------------------------------------------
204 nsIFrame*
205 NS_NewAutoRepeatBoxFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
207 nsIFrame*
208 NS_NewRootBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
210 nsIFrame*
211 NS_NewDocElementBoxFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
213 nsIFrame*
214 NS_NewThumbFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
216 nsIFrame*
217 NS_NewDeckFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
219 nsIFrame*
220 NS_NewLeafBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
222 nsIFrame*
223 NS_NewStackFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
225 nsIFrame*
226 NS_NewProgressMeterFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
228 nsIFrame*
229 NS_NewRangeFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
231 nsIFrame*
232 NS_NewImageBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
234 nsIFrame*
235 NS_NewTextBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
237 nsIFrame*
238 NS_NewGroupBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
240 nsIFrame*
241 NS_NewButtonBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
243 nsIFrame*
244 NS_NewSplitterFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
246 nsIFrame*
247 NS_NewMenuPopupFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
249 nsIFrame*
250 NS_NewPopupSetFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
252 nsIFrame*
253 NS_NewMenuFrame (nsIPresShell* aPresShell, nsStyleContext* aContext, uint32_t aFlags);
255 nsIFrame*
256 NS_NewMenuBarFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
258 nsIFrame*
259 NS_NewTreeBodyFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
261 // grid
262 nsresult
263 NS_NewGridLayout2 ( nsIPresShell* aPresShell, nsBoxLayout** aNewLayout );
264 nsIFrame*
265 NS_NewGridRowLeafFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
266 nsIFrame*
267 NS_NewGridRowGroupFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
269 // end grid
271 nsIFrame*
272 NS_NewTitleBarFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
274 nsIFrame*
275 NS_NewResizerFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
278 #endif
280 nsIFrame*
281 NS_NewHTMLScrollFrame (nsIPresShell* aPresShell, nsStyleContext* aContext, bool aIsRoot);
283 nsIFrame*
284 NS_NewXULScrollFrame (nsIPresShell* aPresShell, nsStyleContext* aContext,
285 bool aIsRoot, bool aClipAllDescendants);
287 nsIFrame*
288 NS_NewSliderFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
290 nsIFrame*
291 NS_NewScrollbarFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
293 nsIFrame*
294 NS_NewScrollbarButtonFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
297 #ifdef NOISY_FINDFRAME
298 static int32_t FFWC_totalCount=0;
299 static int32_t FFWC_doLoop=0;
300 static int32_t FFWC_doSibling=0;
301 static int32_t FFWC_recursions=0;
302 static int32_t FFWC_nextInFlows=0;
303 #endif
305 // Returns true if aFrame is an anonymous flex item
306 static inline bool
307 IsAnonymousFlexItem(const nsIFrame* aFrame)
308 {
309 const nsIAtom* pseudoType = aFrame->StyleContext()->GetPseudo();
310 return pseudoType == nsCSSAnonBoxes::anonymousFlexItem;
311 }
313 static inline nsIFrame*
314 GetFieldSetBlockFrame(nsIFrame* aFieldsetFrame)
315 {
316 // Depends on the fieldset child frame order - see ConstructFieldSetFrame() below.
317 nsIFrame* firstChild = aFieldsetFrame->GetFirstPrincipalChild();
318 nsIFrame* inner = firstChild && firstChild->GetNextSibling() ? firstChild->GetNextSibling() : firstChild;
319 return inner ? inner->GetContentInsertionFrame() : nullptr;
320 }
322 #define FCDATA_DECL(_flags, _func) \
323 { _flags, { (FrameCreationFunc)_func }, nullptr, nullptr }
324 #define FCDATA_WITH_WRAPPING_BLOCK(_flags, _func, _anon_box) \
325 { _flags | FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS, \
326 { (FrameCreationFunc)_func }, nullptr, &_anon_box }
328 //----------------------------------------------------------------------
330 /**
331 * True if aFrame is an actual inline frame in the sense of non-replaced
332 * display:inline CSS boxes. In other words, it can be affected by {ib}
333 * splitting and can contain first-letter frames. Basically, this is either an
334 * inline frame (positioned or otherwise) or an line frame (this last because
335 * it can contain first-letter and because inserting blocks in the middle of it
336 * needs to terminate it).
337 */
338 static bool
339 IsInlineFrame(const nsIFrame* aFrame)
340 {
341 return aFrame->IsFrameOfType(nsIFrame::eLineParticipant);
342 }
344 /**
345 * True if aFrame is an instance of an SVG frame class or is an inline/block
346 * frame being used for SVG text.
347 */
348 static bool
349 IsFrameForSVG(const nsIFrame* aFrame)
350 {
351 return aFrame->IsFrameOfType(nsIFrame::eSVG) ||
352 aFrame->IsSVGText();
353 }
355 /**
356 * Returns true iff aFrame explicitly prevents its descendants from floating
357 * (at least, down to the level of descendants which themselves are
358 * float-containing blocks -- those will manage the floating status of any
359 * lower-level descendents inside them, of course).
360 */
361 static bool
362 ShouldSuppressFloatingOfDescendants(nsIFrame* aFrame)
363 {
364 return aFrame->IsFrameOfType(nsIFrame::eMathML) ||
365 aFrame->IsBoxFrame() ||
366 aFrame->GetType() == nsGkAtoms::flexContainerFrame ||
367 aFrame->GetType() == nsGkAtoms::gridContainerFrame;
368 }
370 /**
371 * If any children require a block parent, return the first such child.
372 * Otherwise return null.
373 */
374 static nsIContent*
375 AnyKidsNeedBlockParent(nsIFrame *aFrameList)
376 {
377 for (nsIFrame *k = aFrameList; k; k = k->GetNextSibling()) {
378 // Line participants, such as text and inline frames, can't be
379 // directly inside a XUL box; they must be wrapped in an
380 // intermediate block.
381 if (k->IsFrameOfType(nsIFrame::eLineParticipant)) {
382 return k->GetContent();
383 }
384 }
385 return nullptr;
386 }
388 // Reparent a frame into a wrapper frame that is a child of its old parent.
389 static void
390 ReparentFrame(RestyleManager* aRestyleManager,
391 nsIFrame* aNewParentFrame,
392 nsIFrame* aFrame)
393 {
394 aFrame->SetParent(aNewParentFrame);
395 aRestyleManager->ReparentStyleContext(aFrame);
396 }
398 static void
399 ReparentFrames(nsCSSFrameConstructor* aFrameConstructor,
400 nsIFrame* aNewParentFrame,
401 const nsFrameList& aFrameList)
402 {
403 RestyleManager* restyleManager = aFrameConstructor->RestyleManager();
404 for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
405 ReparentFrame(restyleManager, aNewParentFrame, e.get());
406 }
407 }
409 //----------------------------------------------------------------------
410 //
411 // When inline frames get weird and have block frames in them, we
412 // annotate them to help us respond to incremental content changes
413 // more easily.
415 static inline bool
416 IsFramePartOfIBSplit(nsIFrame* aFrame)
417 {
418 return (aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) != 0;
419 }
421 static nsIFrame* GetIBSplitSibling(nsIFrame* aFrame)
422 {
423 NS_PRECONDITION(IsFramePartOfIBSplit(aFrame), "Shouldn't call this");
425 // We only store the "ib-split sibling" annotation with the first
426 // frame in the continuation chain. Walk back to find that frame now.
427 return static_cast<nsIFrame*>
428 (aFrame->FirstContinuation()->
429 Properties().Get(nsIFrame::IBSplitSibling()));
430 }
432 static nsIFrame* GetIBSplitPrevSibling(nsIFrame* aFrame)
433 {
434 NS_PRECONDITION(IsFramePartOfIBSplit(aFrame), "Shouldn't call this");
436 // We only store the ib-split sibling annotation with the first
437 // frame in the continuation chain. Walk back to find that frame now.
438 return static_cast<nsIFrame*>
439 (aFrame->FirstContinuation()->
440 Properties().Get(nsIFrame::IBSplitPrevSibling()));
441 }
443 static nsIFrame*
444 GetLastIBSplitSibling(nsIFrame* aFrame, bool aReturnEmptyTrailingInline)
445 {
446 for (nsIFrame *frame = aFrame, *next; ; frame = next) {
447 next = GetIBSplitSibling(frame);
448 if (!next ||
449 (!aReturnEmptyTrailingInline && !next->GetFirstPrincipalChild() &&
450 !GetIBSplitSibling(next))) {
451 NS_ASSERTION(!next || !frame->IsInlineOutside(),
452 "Should have a block here!");
453 return frame;
454 }
455 }
456 NS_NOTREACHED("unreachable code");
457 return nullptr;
458 }
460 static void
461 SetFrameIsIBSplit(nsIFrame* aFrame, nsIFrame* aIBSplitSibling)
462 {
463 NS_PRECONDITION(aFrame, "bad args!");
465 // We should be the only continuation
466 NS_ASSERTION(!aFrame->GetPrevContinuation(),
467 "assigning ib-split sibling to other than first continuation!");
468 NS_ASSERTION(!aFrame->GetNextContinuation() ||
469 IsFramePartOfIBSplit(aFrame->GetNextContinuation()),
470 "should have no non-ib-split continuations here");
472 // Mark the frame as ib-split.
473 aFrame->AddStateBits(NS_FRAME_PART_OF_IBSPLIT);
475 if (aIBSplitSibling) {
476 NS_ASSERTION(!aIBSplitSibling->GetPrevContinuation(),
477 "assigning something other than the first continuation as the "
478 "ib-split sibling");
480 // Store the ib-split sibling (if we were given one) with the
481 // first frame in the flow.
482 FramePropertyTable* props = aFrame->PresContext()->PropertyTable();
483 props->Set(aFrame, nsIFrame::IBSplitSibling(), aIBSplitSibling);
484 props->Set(aIBSplitSibling, nsIFrame::IBSplitPrevSibling(), aFrame);
485 }
486 }
488 static nsIFrame*
489 GetIBContainingBlockFor(nsIFrame* aFrame)
490 {
491 NS_PRECONDITION(IsFramePartOfIBSplit(aFrame),
492 "GetIBContainingBlockFor() should only be called on known IB frames");
494 // Get the first "normal" ancestor of the target frame.
495 nsIFrame* parentFrame;
496 do {
497 parentFrame = aFrame->GetParent();
499 if (! parentFrame) {
500 NS_ERROR("no unsplit block frame in IB hierarchy");
501 return aFrame;
502 }
504 // Note that we ignore non-ib-split frames which have a pseudo on their
505 // style context -- they're not the frames we're looking for! In
506 // particular, they may be hiding a real parent that _is_ in an ib-split.
507 if (!IsFramePartOfIBSplit(parentFrame) &&
508 !parentFrame->StyleContext()->GetPseudo())
509 break;
511 aFrame = parentFrame;
512 } while (1);
514 // post-conditions
515 NS_ASSERTION(parentFrame, "no normal ancestor found for ib-split frame "
516 "in GetIBContainingBlockFor");
517 NS_ASSERTION(parentFrame != aFrame, "parentFrame is actually the child frame - bogus reslt");
519 return parentFrame;
520 }
522 //----------------------------------------------------------------------
524 // Block/inline frame construction logic. We maintain a few invariants here:
525 //
526 // 1. Block frames contain block and inline frames.
527 //
528 // 2. Inline frames only contain inline frames. If an inline parent has a block
529 // child then the block child is migrated upward until it lands in a block
530 // parent (the inline frames containing block is where it will end up).
532 // After this function returns, aLink is pointing to the first link at or
533 // after its starting position for which the next frame is a block. If there
534 // is no such link, it points to the end of the list.
535 static void
536 FindFirstBlock(nsFrameList::FrameLinkEnumerator& aLink)
537 {
538 for ( ; !aLink.AtEnd(); aLink.Next()) {
539 if (!aLink.NextFrame()->IsInlineOutside()) {
540 return;
541 }
542 }
543 }
545 // This function returns a frame link enumerator pointing to the first link in
546 // the list for which the next frame is not block. If there is no such link,
547 // it points to the end of the list.
548 static nsFrameList::FrameLinkEnumerator
549 FindFirstNonBlock(const nsFrameList& aList)
550 {
551 nsFrameList::FrameLinkEnumerator link(aList);
552 for (; !link.AtEnd(); link.Next()) {
553 if (link.NextFrame()->IsInlineOutside()) {
554 break;
555 }
556 }
557 return link;
558 }
560 inline void
561 SetInitialSingleChild(nsIFrame* aParent, nsIFrame* aFrame)
562 {
563 NS_PRECONDITION(!aFrame->GetNextSibling(), "Should be using a frame list");
564 nsFrameList temp(aFrame, aFrame);
565 aParent->SetInitialChildList(kPrincipalList, temp);
566 }
568 // -----------------------------------------------------------
570 // Structure used when constructing formatting object trees.
571 struct nsFrameItems : public nsFrameList
572 {
573 // Appends the frame to the end of the list
574 void AddChild(nsIFrame* aChild);
575 };
577 void
578 nsFrameItems::AddChild(nsIFrame* aChild)
579 {
580 NS_PRECONDITION(aChild, "nsFrameItems::AddChild");
582 // It'd be really nice if we could just AppendFrames(kPrincipalList, aChild) here,
583 // but some of our callers put frames that have different
584 // parents (caption, I'm looking at you) on the same framelist, and
585 // nsFrameList asserts if you try to do that.
586 if (IsEmpty()) {
587 SetFrames(aChild);
588 }
589 else {
590 NS_ASSERTION(aChild != mLastChild,
591 "Same frame being added to frame list twice?");
592 mLastChild->SetNextSibling(aChild);
593 mLastChild = nsLayoutUtils::GetLastSibling(aChild);
594 }
595 }
597 // -----------------------------------------------------------
599 // Structure used when constructing formatting object trees. Contains
600 // state information needed for absolutely positioned elements
601 struct nsAbsoluteItems : nsFrameItems {
602 // containing block for absolutely positioned elements
603 nsIFrame* containingBlock;
605 nsAbsoluteItems(nsIFrame* aContainingBlock);
606 #ifdef DEBUG
607 // XXXbz Does this need a debug-only assignment operator that nulls out the
608 // childList in the nsAbsoluteItems we're copying? Introducing a difference
609 // between debug and non-debug behavior seems bad, so I guess not...
610 ~nsAbsoluteItems() {
611 NS_ASSERTION(!FirstChild(),
612 "Dangling child list. Someone forgot to insert it?");
613 }
614 #endif
616 // Appends the frame to the end of the list
617 void AddChild(nsIFrame* aChild);
618 };
620 nsAbsoluteItems::nsAbsoluteItems(nsIFrame* aContainingBlock)
621 : containingBlock(aContainingBlock)
622 {
623 }
625 // Additional behavior is that it sets the frame's NS_FRAME_OUT_OF_FLOW flag
626 void
627 nsAbsoluteItems::AddChild(nsIFrame* aChild)
628 {
629 NS_ASSERTION(aChild->PresContext()->FrameManager()->
630 GetPlaceholderFrameFor(aChild),
631 "Child without placeholder being added to nsAbsoluteItems?");
632 aChild->AddStateBits(NS_FRAME_OUT_OF_FLOW);
633 nsFrameItems::AddChild(aChild);
634 }
636 // -----------------------------------------------------------
638 // Structure for saving the existing state when pushing/poping containing
639 // blocks. The destructor restores the state to its previous state
640 class MOZ_STACK_CLASS nsFrameConstructorSaveState {
641 public:
642 typedef nsIFrame::ChildListID ChildListID;
643 nsFrameConstructorSaveState();
644 ~nsFrameConstructorSaveState();
646 private:
647 nsAbsoluteItems* mItems; // pointer to struct whose data we save/restore
648 nsAbsoluteItems mSavedItems; // copy of original data
650 // The name of the child list in which our frames would belong
651 ChildListID mChildListID;
652 nsFrameConstructorState* mState;
654 // State used only when we're saving the abs-pos state for a transformed
655 // element.
656 nsAbsoluteItems mSavedFixedItems;
658 bool mSavedFixedPosIsAbsPos;
660 friend class nsFrameConstructorState;
661 };
663 // Structure used to keep track of a list of bindings we need to call
664 // AddToAttachedQueue on. These should be in post-order depth-first
665 // flattened tree traversal order.
666 struct PendingBinding : public LinkedListElement<PendingBinding>
667 {
668 #ifdef NS_BUILD_REFCNT_LOGGING
669 PendingBinding() {
670 MOZ_COUNT_CTOR(PendingBinding);
671 }
672 ~PendingBinding() {
673 MOZ_COUNT_DTOR(PendingBinding);
674 }
675 #endif
677 nsRefPtr<nsXBLBinding> mBinding;
678 };
680 // Structure used for maintaining state information during the
681 // frame construction process
682 class MOZ_STACK_CLASS nsFrameConstructorState {
683 public:
684 typedef nsIFrame::ChildListID ChildListID;
686 nsPresContext *mPresContext;
687 nsIPresShell *mPresShell;
688 nsFrameManager *mFrameManager;
690 #ifdef MOZ_XUL
691 // Frames destined for the kPopupList.
692 nsAbsoluteItems mPopupItems;
693 #endif
695 // Containing block information for out-of-flow frames.
696 nsAbsoluteItems mFixedItems;
697 nsAbsoluteItems mAbsoluteItems;
698 nsAbsoluteItems mFloatedItems;
700 nsCOMPtr<nsILayoutHistoryState> mFrameState;
701 // These bits will be added to the state bits of any frame we construct
702 // using this state.
703 nsFrameState mAdditionalStateBits;
705 // When working with the -moz-transform property, we want to hook
706 // the abs-pos and fixed-pos lists together, since transformed
707 // elements are fixed-pos containing blocks. This flag determines
708 // whether or not we want to wire the fixed-pos and abs-pos lists
709 // together.
710 bool mFixedPosIsAbsPos;
712 // A boolean to indicate whether we have a "pending" popupgroup. That is, we
713 // have already created the FrameConstructionItem for the root popupgroup but
714 // we have not yet created the relevant frame.
715 bool mHavePendingPopupgroup;
717 // If false (which is the default) then call SetPrimaryFrame() as needed
718 // during frame construction. If true, don't make any SetPrimaryFrame()
719 // calls, except for generated content which doesn't have a primary frame
720 // yet. The mCreatingExtraFrames == true mode is meant to be used for
721 // construction of random "extra" frames for elements via normal frame
722 // construction APIs (e.g. replication of things across pages in paginated
723 // mode).
724 bool mCreatingExtraFrames;
726 nsCOMArray<nsIContent> mGeneratedTextNodesWithInitializer;
728 TreeMatchContext mTreeMatchContext;
730 // Constructor
731 // Use the passed-in history state.
732 nsFrameConstructorState(nsIPresShell* aPresShell,
733 nsIFrame* aFixedContainingBlock,
734 nsIFrame* aAbsoluteContainingBlock,
735 nsIFrame* aFloatContainingBlock,
736 nsILayoutHistoryState* aHistoryState);
737 // Get the history state from the pres context's pres shell.
738 nsFrameConstructorState(nsIPresShell* aPresShell,
739 nsIFrame* aFixedContainingBlock,
740 nsIFrame* aAbsoluteContainingBlock,
741 nsIFrame* aFloatContainingBlock);
743 ~nsFrameConstructorState();
745 // Function to push the existing absolute containing block state and
746 // create a new scope. Code that uses this function should get matching
747 // logic in GetAbsoluteContainingBlock.
748 // Also makes aNewAbsoluteContainingBlock the containing block for
749 // fixed-pos elements if necessary.
750 // aPositionedFrame is the frame whose style actually makes
751 // aNewAbsoluteContainingBlock a containing block. E.g. for a scrollable element
752 // aPositionedFrame is the element's primary frame and
753 // aNewAbsoluteContainingBlock is the scrolled frame.
754 void PushAbsoluteContainingBlock(nsIFrame* aNewAbsoluteContainingBlock,
755 nsIFrame* aPositionedFrame,
756 nsFrameConstructorSaveState& aSaveState);
758 // Function to push the existing float containing block state and
759 // create a new scope. Code that uses this function should get matching
760 // logic in GetFloatContainingBlock.
761 // Pushing a null float containing block forbids any frames from being
762 // floated until a new float containing block is pushed.
763 // XXX we should get rid of null float containing blocks and teach the
764 // various frame classes to deal with floats instead.
765 void PushFloatContainingBlock(nsIFrame* aNewFloatContainingBlock,
766 nsFrameConstructorSaveState& aSaveState);
768 // Function to return the proper geometric parent for a frame with display
769 // struct given by aStyleDisplay and parent's frame given by
770 // aContentParentFrame.
771 nsIFrame* GetGeometricParent(const nsStyleDisplay* aStyleDisplay,
772 nsIFrame* aContentParentFrame) const;
774 /**
775 * Function to add a new frame to the right frame list. This MUST be called
776 * on frames before their children have been processed if the frames might
777 * conceivably be out-of-flow; otherwise cleanup in error cases won't work
778 * right. Also, this MUST be called on frames after they have been
779 * initialized.
780 * @param aNewFrame the frame to add
781 * @param aFrameItems the list to add in-flow frames to
782 * @param aContent the content pointer for aNewFrame
783 * @param aStyleContext the style context resolved for aContent
784 * @param aParentFrame the parent frame for the content if it were in-flow
785 * @param aCanBePositioned pass false if the frame isn't allowed to be
786 * positioned
787 * @param aCanBeFloated pass false if the frame isn't allowed to be
788 * floated
789 * @param aIsOutOfFlowPopup pass true if the frame is an out-of-flow popup
790 * (XUL-only)
791 */
792 void AddChild(nsIFrame* aNewFrame,
793 nsFrameItems& aFrameItems,
794 nsIContent* aContent,
795 nsStyleContext* aStyleContext,
796 nsIFrame* aParentFrame,
797 bool aCanBePositioned = true,
798 bool aCanBeFloated = true,
799 bool aIsOutOfFlowPopup = false,
800 bool aInsertAfter = false,
801 nsIFrame* aInsertAfterFrame = nullptr);
803 /**
804 * Function to return the fixed-pos element list. Normally this will just hand back the
805 * fixed-pos element list, but in case we're dealing with a transformed element that's
806 * acting as an abs-pos and fixed-pos container, we'll hand back the abs-pos list. Callers should
807 * use this function if they want to get the list acting as the fixed-pos item parent.
808 */
809 nsAbsoluteItems& GetFixedItems()
810 {
811 return mFixedPosIsAbsPos ? mAbsoluteItems : mFixedItems;
812 }
813 const nsAbsoluteItems& GetFixedItems() const
814 {
815 return mFixedPosIsAbsPos ? mAbsoluteItems : mFixedItems;
816 }
819 /**
820 * class to automatically push and pop a pending binding in the frame
821 * constructor state. See nsCSSFrameConstructor::FrameConstructionItem
822 * mPendingBinding documentation.
823 */
824 class PendingBindingAutoPusher;
825 friend class PendingBindingAutoPusher;
826 class MOZ_STACK_CLASS PendingBindingAutoPusher {
827 public:
828 PendingBindingAutoPusher(nsFrameConstructorState& aState,
829 PendingBinding* aPendingBinding) :
830 mState(aState),
831 mPendingBinding(aState.mCurrentPendingBindingInsertionPoint)
832 {
833 if (aPendingBinding) {
834 aState.mCurrentPendingBindingInsertionPoint = aPendingBinding;
835 }
836 }
838 ~PendingBindingAutoPusher()
839 {
840 mState.mCurrentPendingBindingInsertionPoint = mPendingBinding;
841 }
843 private:
844 nsFrameConstructorState& mState;
845 PendingBinding* mPendingBinding;
846 };
848 /**
849 * Add a new pending binding to the list
850 */
851 void AddPendingBinding(PendingBinding* aPendingBinding) {
852 if (mCurrentPendingBindingInsertionPoint) {
853 mCurrentPendingBindingInsertionPoint->setPrevious(aPendingBinding);
854 } else {
855 mPendingBindings.insertBack(aPendingBinding);
856 }
857 }
859 protected:
860 friend class nsFrameConstructorSaveState;
862 /**
863 * ProcessFrameInsertions takes the frames in aFrameItems and adds them as
864 * kids to the aChildListID child list of |aFrameItems.containingBlock|.
865 */
866 void ProcessFrameInsertions(nsAbsoluteItems& aFrameItems,
867 ChildListID aChildListID);
869 // Our list of all pending bindings. When we're done, we need to call
870 // AddToAttachedQueue on all of them, in order.
871 LinkedList<PendingBinding> mPendingBindings;
873 PendingBinding* mCurrentPendingBindingInsertionPoint;
874 };
876 nsFrameConstructorState::nsFrameConstructorState(nsIPresShell* aPresShell,
877 nsIFrame* aFixedContainingBlock,
878 nsIFrame* aAbsoluteContainingBlock,
879 nsIFrame* aFloatContainingBlock,
880 nsILayoutHistoryState* aHistoryState)
881 : mPresContext(aPresShell->GetPresContext()),
882 mPresShell(aPresShell),
883 mFrameManager(aPresShell->FrameManager()),
884 #ifdef MOZ_XUL
885 mPopupItems(nullptr),
886 #endif
887 mFixedItems(aFixedContainingBlock),
888 mAbsoluteItems(aAbsoluteContainingBlock),
889 mFloatedItems(aFloatContainingBlock),
890 // See PushAbsoluteContaningBlock below
891 mFrameState(aHistoryState),
892 mAdditionalStateBits(nsFrameState(0)),
893 // If the fixed-pos containing block is equal to the abs-pos containing
894 // block, use the abs-pos containing block's abs-pos list for fixed-pos
895 // frames.
896 mFixedPosIsAbsPos(aFixedContainingBlock == aAbsoluteContainingBlock),
897 mHavePendingPopupgroup(false),
898 mCreatingExtraFrames(false),
899 mTreeMatchContext(true, nsRuleWalker::eRelevantLinkUnvisited,
900 aPresShell->GetDocument()),
901 mCurrentPendingBindingInsertionPoint(nullptr)
902 {
903 #ifdef MOZ_XUL
904 nsIRootBox* rootBox = nsIRootBox::GetRootBox(aPresShell);
905 if (rootBox) {
906 mPopupItems.containingBlock = rootBox->GetPopupSetFrame();
907 }
908 #endif
909 MOZ_COUNT_CTOR(nsFrameConstructorState);
910 }
912 nsFrameConstructorState::nsFrameConstructorState(nsIPresShell* aPresShell,
913 nsIFrame* aFixedContainingBlock,
914 nsIFrame* aAbsoluteContainingBlock,
915 nsIFrame* aFloatContainingBlock)
916 : mPresContext(aPresShell->GetPresContext()),
917 mPresShell(aPresShell),
918 mFrameManager(aPresShell->FrameManager()),
919 #ifdef MOZ_XUL
920 mPopupItems(nullptr),
921 #endif
922 mFixedItems(aFixedContainingBlock),
923 mAbsoluteItems(aAbsoluteContainingBlock),
924 mFloatedItems(aFloatContainingBlock),
925 // See PushAbsoluteContaningBlock below
926 mAdditionalStateBits(nsFrameState(0)),
927 // If the fixed-pos containing block is equal to the abs-pos containing
928 // block, use the abs-pos containing block's abs-pos list for fixed-pos
929 // frames.
930 mFixedPosIsAbsPos(aFixedContainingBlock == aAbsoluteContainingBlock),
931 mHavePendingPopupgroup(false),
932 mCreatingExtraFrames(false),
933 mTreeMatchContext(true, nsRuleWalker::eRelevantLinkUnvisited,
934 aPresShell->GetDocument()),
935 mCurrentPendingBindingInsertionPoint(nullptr)
936 {
937 #ifdef MOZ_XUL
938 nsIRootBox* rootBox = nsIRootBox::GetRootBox(aPresShell);
939 if (rootBox) {
940 mPopupItems.containingBlock = rootBox->GetPopupSetFrame();
941 }
942 #endif
943 MOZ_COUNT_CTOR(nsFrameConstructorState);
944 mFrameState = aPresShell->GetDocument()->GetLayoutHistoryState();
945 }
947 nsFrameConstructorState::~nsFrameConstructorState()
948 {
949 // Frame order comparison functions only work properly when the placeholders
950 // have been inserted into the frame tree. So for example if we have a new float
951 // containing the placeholder for a new abs-pos frame, and we process the abs-pos
952 // insertion first, then we won't be able to find the right place to insert in
953 // in the abs-pos list. So put floats in first, because they can contain placeholders
954 // for abs-pos and fixed-pos items whose containing blocks are outside the floats.
955 // Then put abs-pos frames in, because they can contain placeholders for fixed-pos
956 // items whose containing block is outside the abs-pos frames.
957 MOZ_COUNT_DTOR(nsFrameConstructorState);
958 ProcessFrameInsertions(mFloatedItems, nsIFrame::kFloatList);
959 ProcessFrameInsertions(mAbsoluteItems, nsIFrame::kAbsoluteList);
960 ProcessFrameInsertions(mFixedItems, nsIFrame::kFixedList);
961 #ifdef MOZ_XUL
962 ProcessFrameInsertions(mPopupItems, nsIFrame::kPopupList);
963 #endif
964 for (int32_t i = mGeneratedTextNodesWithInitializer.Count() - 1; i >= 0; --i) {
965 mGeneratedTextNodesWithInitializer[i]->
966 DeleteProperty(nsGkAtoms::genConInitializerProperty);
967 }
968 if (!mPendingBindings.isEmpty()) {
969 nsBindingManager* bindingManager = mPresShell->GetDocument()->BindingManager();
970 do {
971 nsAutoPtr<PendingBinding> pendingBinding;
972 pendingBinding = mPendingBindings.popFirst();
973 bindingManager->AddToAttachedQueue(pendingBinding->mBinding);
974 } while (!mPendingBindings.isEmpty());
975 mCurrentPendingBindingInsertionPoint = nullptr;
976 }
977 }
979 static nsIFrame*
980 AdjustAbsoluteContainingBlock(nsIFrame* aContainingBlockIn)
981 {
982 if (!aContainingBlockIn) {
983 return nullptr;
984 }
986 // Always use the container's first continuation. (Inline frames can have
987 // non-fluid bidi continuations...)
988 return aContainingBlockIn->FirstContinuation();
989 }
991 void
992 nsFrameConstructorState::PushAbsoluteContainingBlock(nsIFrame* aNewAbsoluteContainingBlock,
993 nsIFrame* aPositionedFrame,
994 nsFrameConstructorSaveState& aSaveState)
995 {
996 aSaveState.mItems = &mAbsoluteItems;
997 aSaveState.mSavedItems = mAbsoluteItems;
998 aSaveState.mChildListID = nsIFrame::kAbsoluteList;
999 aSaveState.mState = this;
1000 aSaveState.mSavedFixedPosIsAbsPos = mFixedPosIsAbsPos;
1002 if (mFixedPosIsAbsPos) {
1003 // Since we're going to replace mAbsoluteItems, we need to save it into
1004 // mFixedItems now (and save the current value of mFixedItems).
1005 aSaveState.mSavedFixedItems = mFixedItems;
1006 mFixedItems = mAbsoluteItems;
1007 }
1009 mAbsoluteItems =
1010 nsAbsoluteItems(AdjustAbsoluteContainingBlock(aNewAbsoluteContainingBlock));
1012 /* See if we're wiring the fixed-pos and abs-pos lists together. This happens iff
1013 * we're a transformed element.
1014 */
1015 mFixedPosIsAbsPos = aPositionedFrame &&
1016 (aPositionedFrame->StyleDisplay()->HasTransform(aPositionedFrame) ||
1017 aPositionedFrame->StyleDisplay()->HasPerspectiveStyle());
1019 if (aNewAbsoluteContainingBlock) {
1020 aNewAbsoluteContainingBlock->MarkAsAbsoluteContainingBlock();
1021 }
1022 }
1024 void
1025 nsFrameConstructorState::PushFloatContainingBlock(nsIFrame* aNewFloatContainingBlock,
1026 nsFrameConstructorSaveState& aSaveState)
1027 {
1028 NS_PRECONDITION(!aNewFloatContainingBlock ||
1029 aNewFloatContainingBlock->IsFloatContainingBlock(),
1030 "Please push a real float containing block!");
1031 NS_ASSERTION(!aNewFloatContainingBlock ||
1032 !ShouldSuppressFloatingOfDescendants(aNewFloatContainingBlock),
1033 "We should not push a frame that is supposed to _suppress_ "
1034 "floats as a float containing block!");
1035 aSaveState.mItems = &mFloatedItems;
1036 aSaveState.mSavedItems = mFloatedItems;
1037 aSaveState.mChildListID = nsIFrame::kFloatList;
1038 aSaveState.mState = this;
1039 mFloatedItems = nsAbsoluteItems(aNewFloatContainingBlock);
1040 }
1042 nsIFrame*
1043 nsFrameConstructorState::GetGeometricParent(const nsStyleDisplay* aStyleDisplay,
1044 nsIFrame* aContentParentFrame) const
1045 {
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.
1052 //
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.
1060 //
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 (aContentParentFrame && aContentParentFrame->IsSVGText()) {
1065 return aContentParentFrame;
1066 }
1068 if (aStyleDisplay->IsFloatingStyle() && mFloatedItems.containingBlock) {
1069 NS_ASSERTION(!aStyleDisplay->IsAbsolutelyPositionedStyle(),
1070 "Absolutely positioned _and_ floating?");
1071 return mFloatedItems.containingBlock;
1072 }
1074 if (aStyleDisplay->mPosition == NS_STYLE_POSITION_ABSOLUTE &&
1075 mAbsoluteItems.containingBlock) {
1076 return mAbsoluteItems.containingBlock;
1077 }
1079 if (aStyleDisplay->mPosition == NS_STYLE_POSITION_FIXED &&
1080 GetFixedItems().containingBlock) {
1081 return GetFixedItems().containingBlock;
1082 }
1084 return aContentParentFrame;
1085 }
1087 void
1088 nsFrameConstructorState::AddChild(nsIFrame* aNewFrame,
1089 nsFrameItems& aFrameItems,
1090 nsIContent* aContent,
1091 nsStyleContext* aStyleContext,
1092 nsIFrame* aParentFrame,
1093 bool aCanBePositioned,
1094 bool aCanBeFloated,
1095 bool aIsOutOfFlowPopup,
1096 bool aInsertAfter,
1097 nsIFrame* aInsertAfterFrame)
1098 {
1099 NS_PRECONDITION(!aNewFrame->GetNextSibling(), "Shouldn't happen");
1101 const nsStyleDisplay* disp = aNewFrame->StyleDisplay();
1103 // The comments in GetGeometricParent regarding root table frames
1104 // all apply here, unfortunately.
1106 bool needPlaceholder = false;
1107 nsFrameState placeholderType;
1108 nsFrameItems* frameItems = &aFrameItems;
1109 #ifdef MOZ_XUL
1110 if (MOZ_UNLIKELY(aIsOutOfFlowPopup)) {
1111 NS_ASSERTION(aNewFrame->GetParent() == mPopupItems.containingBlock,
1112 "Popup whose parent is not the popup containing block?");
1113 NS_ASSERTION(mPopupItems.containingBlock, "Must have a popup set frame!");
1114 needPlaceholder = true;
1115 frameItems = &mPopupItems;
1116 placeholderType = PLACEHOLDER_FOR_POPUP;
1117 }
1118 else
1119 #endif // MOZ_XUL
1120 if (aCanBeFloated && aNewFrame->IsFloating() &&
1121 mFloatedItems.containingBlock) {
1122 NS_ASSERTION(aNewFrame->GetParent() == mFloatedItems.containingBlock,
1123 "Float whose parent is not the float containing block?");
1124 needPlaceholder = true;
1125 frameItems = &mFloatedItems;
1126 placeholderType = PLACEHOLDER_FOR_FLOAT;
1127 }
1128 else if (aCanBePositioned) {
1129 if (disp->mPosition == NS_STYLE_POSITION_ABSOLUTE &&
1130 mAbsoluteItems.containingBlock) {
1131 NS_ASSERTION(aNewFrame->GetParent() == mAbsoluteItems.containingBlock,
1132 "Abs pos whose parent is not the abs pos containing block?");
1133 needPlaceholder = true;
1134 frameItems = &mAbsoluteItems;
1135 placeholderType = PLACEHOLDER_FOR_ABSPOS;
1136 }
1137 if (disp->mPosition == NS_STYLE_POSITION_FIXED &&
1138 GetFixedItems().containingBlock) {
1139 NS_ASSERTION(aNewFrame->GetParent() == GetFixedItems().containingBlock,
1140 "Fixed pos whose parent is not the fixed pos containing block?");
1141 needPlaceholder = true;
1142 frameItems = &GetFixedItems();
1143 placeholderType = PLACEHOLDER_FOR_FIXEDPOS;
1144 }
1145 }
1147 if (needPlaceholder) {
1148 NS_ASSERTION(frameItems != &aFrameItems,
1149 "Putting frame in-flow _and_ want a placeholder?");
1150 nsIFrame* placeholderFrame =
1151 nsCSSFrameConstructor::CreatePlaceholderFrameFor(mPresShell,
1152 aContent,
1153 aNewFrame,
1154 aStyleContext,
1155 aParentFrame,
1156 nullptr,
1157 placeholderType);
1159 placeholderFrame->AddStateBits(mAdditionalStateBits);
1160 // Add the placeholder frame to the flow
1161 aFrameItems.AddChild(placeholderFrame);
1162 }
1163 #ifdef DEBUG
1164 else {
1165 NS_ASSERTION(aNewFrame->GetParent() == aParentFrame,
1166 "In-flow frame has wrong parent");
1167 }
1168 #endif
1170 if (aInsertAfter) {
1171 frameItems->InsertFrame(nullptr, aInsertAfterFrame, aNewFrame);
1172 } else {
1173 frameItems->AddChild(aNewFrame);
1174 }
1175 }
1177 void
1178 nsFrameConstructorState::ProcessFrameInsertions(nsAbsoluteItems& aFrameItems,
1179 ChildListID aChildListID)
1180 {
1181 #define NS_NONXUL_LIST_TEST (&aFrameItems == &mFloatedItems && \
1182 aChildListID == nsIFrame::kFloatList) || \
1183 (&aFrameItems == &mAbsoluteItems && \
1184 aChildListID == nsIFrame::kAbsoluteList) || \
1185 (&aFrameItems == &mFixedItems && \
1186 aChildListID == nsIFrame::kFixedList)
1187 #ifdef MOZ_XUL
1188 NS_PRECONDITION(NS_NONXUL_LIST_TEST ||
1189 (&aFrameItems == &mPopupItems &&
1190 aChildListID == nsIFrame::kPopupList),
1191 "Unexpected aFrameItems/aChildListID combination");
1192 #else
1193 NS_PRECONDITION(NS_NONXUL_LIST_TEST,
1194 "Unexpected aFrameItems/aChildListID combination");
1195 #endif
1197 if (aFrameItems.IsEmpty()) {
1198 return;
1199 }
1201 nsIFrame* containingBlock = aFrameItems.containingBlock;
1203 NS_ASSERTION(containingBlock,
1204 "Child list without containing block?");
1206 if (aChildListID == nsIFrame::kFixedList) {
1207 // Put this frame on the transformed-frame's abs-pos list instead, if
1208 // it has abs-pos children instead of fixed-pos children.
1209 aChildListID = containingBlock->GetAbsoluteListID();
1210 }
1212 // Insert the frames hanging out in aItems. We can use SetInitialChildList()
1213 // if the containing block hasn't been reflowed yet (so NS_FRAME_FIRST_REFLOW
1214 // is set) and doesn't have any frames in the aChildListID child list yet.
1215 const nsFrameList& childList = containingBlock->GetChildList(aChildListID);
1216 DebugOnly<nsresult> rv = NS_OK;
1217 if (childList.IsEmpty() &&
1218 (containingBlock->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
1219 // If we're injecting absolutely positioned frames, inject them on the
1220 // absolute containing block
1221 if (aChildListID == containingBlock->GetAbsoluteListID()) {
1222 rv = containingBlock->GetAbsoluteContainingBlock()->
1223 SetInitialChildList(containingBlock, aChildListID, aFrameItems);
1224 } else {
1225 rv = containingBlock->SetInitialChildList(aChildListID, aFrameItems);
1226 }
1227 } else {
1228 // Note that whether the frame construction context is doing an append or
1229 // not is not helpful here, since it could be appending to some frame in
1230 // the middle of the document, which means we're not necessarily
1231 // appending to the children of the containing block.
1232 //
1233 // We need to make sure the 'append to the end of document' case is fast.
1234 // So first test the last child of the containing block
1235 nsIFrame* lastChild = childList.LastChild();
1237 // CompareTreePosition uses placeholder hierarchy for out of flow frames,
1238 // so this will make out-of-flows respect the ordering of placeholders,
1239 // which is great because it takes care of anonymous content.
1240 nsIFrame* firstNewFrame = aFrameItems.FirstChild();
1242 // Cache the ancestor chain so that we can reuse it if needed.
1243 nsAutoTArray<nsIFrame*, 20> firstNewFrameAncestors;
1244 nsIFrame* notCommonAncestor = nullptr;
1245 if (lastChild) {
1246 notCommonAncestor = nsLayoutUtils::FillAncestors(firstNewFrame,
1247 containingBlock,
1248 &firstNewFrameAncestors);
1249 }
1251 if (!lastChild ||
1252 nsLayoutUtils::CompareTreePosition(lastChild, firstNewFrame,
1253 firstNewFrameAncestors,
1254 notCommonAncestor ?
1255 containingBlock : nullptr) < 0) {
1256 // no lastChild, or lastChild comes before the new children, so just append
1257 rv = mFrameManager->AppendFrames(containingBlock, aChildListID, aFrameItems);
1258 } else {
1259 // Try the other children. First collect them to an array so that a
1260 // reasonable fast binary search can be used to find the insertion point.
1261 nsAutoTArray<nsIFrame*, 128> children;
1262 for (nsIFrame* f = childList.FirstChild(); f != lastChild;
1263 f = f->GetNextSibling()) {
1264 children.AppendElement(f);
1265 }
1267 nsIFrame* insertionPoint = nullptr;
1268 int32_t imin = 0;
1269 int32_t max = children.Length();
1270 while (max > imin) {
1271 int32_t imid = imin + ((max - imin) / 2);
1272 nsIFrame* f = children[imid];
1273 int32_t compare =
1274 nsLayoutUtils::CompareTreePosition(f, firstNewFrame, firstNewFrameAncestors,
1275 notCommonAncestor ? containingBlock : nullptr);
1276 if (compare > 0) {
1277 // f is after the new frame.
1278 max = imid;
1279 insertionPoint = imid > 0 ? children[imid - 1] : nullptr;
1280 } else if (compare < 0) {
1281 // f is before the new frame.
1282 imin = imid + 1;
1283 insertionPoint = f;
1284 } else {
1285 // This is for the old behavior. Should be removed once it is
1286 // guaranteed that CompareTreePosition can't return 0!
1287 // See bug 928645.
1288 NS_WARNING("Something odd happening???");
1289 insertionPoint = nullptr;
1290 for (uint32_t i = 0; i < children.Length(); ++i) {
1291 nsIFrame* f = children[i];
1292 if (nsLayoutUtils::CompareTreePosition(f, firstNewFrame,
1293 firstNewFrameAncestors,
1294 notCommonAncestor ?
1295 containingBlock : nullptr) > 0) {
1296 break;
1297 }
1298 insertionPoint = f;
1299 }
1300 break;
1301 }
1302 }
1303 rv = mFrameManager->InsertFrames(containingBlock, aChildListID,
1304 insertionPoint, aFrameItems);
1305 }
1306 }
1308 NS_POSTCONDITION(aFrameItems.IsEmpty(), "How did that happen?");
1310 // XXXbz And if NS_FAILED(rv), what? I guess we need to clean up the list
1311 // and deal with all the placeholders... but what if the placeholders aren't
1312 // in the document yet? Could that happen?
1313 NS_ASSERTION(NS_SUCCEEDED(rv), "Frames getting lost!");
1314 }
1317 nsFrameConstructorSaveState::nsFrameConstructorSaveState()
1318 : mItems(nullptr),
1319 mSavedItems(nullptr),
1320 mChildListID(kPrincipalList),
1321 mState(nullptr),
1322 mSavedFixedItems(nullptr),
1323 mSavedFixedPosIsAbsPos(false)
1324 {
1325 }
1327 nsFrameConstructorSaveState::~nsFrameConstructorSaveState()
1328 {
1329 // Restore the state
1330 if (mItems) {
1331 NS_ASSERTION(mState, "Can't have mItems set without having a state!");
1332 mState->ProcessFrameInsertions(*mItems, mChildListID);
1333 *mItems = mSavedItems;
1334 #ifdef DEBUG
1335 // We've transferred the child list, so drop the pointer we held to it.
1336 // Note that this only matters for the assert in ~nsAbsoluteItems.
1337 mSavedItems.Clear();
1338 #endif
1339 if (mItems == &mState->mAbsoluteItems) {
1340 mState->mFixedPosIsAbsPos = mSavedFixedPosIsAbsPos;
1341 if (mSavedFixedPosIsAbsPos) {
1342 // mAbsoluteItems was moved to mFixedItems, so move mFixedItems back
1343 // and repair the old mFixedItems now.
1344 mState->mAbsoluteItems = mState->mFixedItems;
1345 mState->mFixedItems = mSavedFixedItems;
1346 #ifdef DEBUG
1347 mSavedFixedItems.Clear();
1348 #endif
1349 }
1350 }
1351 NS_ASSERTION(!mItems->LastChild() || !mItems->LastChild()->GetNextSibling(),
1352 "Something corrupted our list");
1353 }
1354 }
1356 static
1357 bool IsBorderCollapse(nsIFrame* aFrame)
1358 {
1359 for (nsIFrame* frame = aFrame; frame; frame = frame->GetParent()) {
1360 if (nsGkAtoms::tableFrame == frame->GetType()) {
1361 return ((nsTableFrame*)frame)->IsBorderCollapse();
1362 }
1363 }
1364 NS_ASSERTION(false, "program error");
1365 return false;
1366 }
1368 /**
1369 * Moves aFrameList from aOldParent to aNewParent. This updates the parent
1370 * pointer of the frames in the list, and reparents their views as needed.
1371 * nsFrame::SetParent sets the NS_FRAME_HAS_VIEW bit on aNewParent and its
1372 * ancestors as needed. Then it sets the list as the initial child list
1373 * on aNewParent, unless aNewParent either already has kids or has been
1374 * reflowed; in that case it appends the new frames. Note that this
1375 * method differs from ReparentFrames in that it doesn't change the kids'
1376 * style contexts.
1377 */
1378 // XXXbz Since this is only used for {ib} splits, could we just copy the view
1379 // bits from aOldParent to aNewParent and then use the
1380 // nsFrameList::ApplySetParent? That would still leave us doing two passes
1381 // over the list, of course; if we really wanted to we could factor out the
1382 // relevant part of ReparentFrameViewList, I suppose... Or just get rid of
1383 // views, which would make most of this function go away.
1384 static void
1385 MoveChildrenTo(nsPresContext* aPresContext,
1386 nsIFrame* aOldParent,
1387 nsIFrame* aNewParent,
1388 nsFrameList& aFrameList)
1389 {
1390 bool sameGrandParent = aOldParent->GetParent() == aNewParent->GetParent();
1392 if (aNewParent->HasView() || aOldParent->HasView() || !sameGrandParent) {
1393 // Move the frames into the new view
1394 nsContainerFrame::ReparentFrameViewList(aFrameList, aOldParent, aNewParent);
1395 }
1397 for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
1398 e.get()->SetParent(aNewParent);
1399 }
1401 if (aNewParent->PrincipalChildList().IsEmpty() &&
1402 (aNewParent->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
1403 aNewParent->SetInitialChildList(kPrincipalList, aFrameList);
1404 } else {
1405 aNewParent->AppendFrames(kPrincipalList, aFrameList);
1406 }
1407 }
1409 //----------------------------------------------------------------------
1411 nsCSSFrameConstructor::nsCSSFrameConstructor(nsIDocument *aDocument,
1412 nsIPresShell *aPresShell,
1413 nsStyleSet* aStyleSet)
1414 : nsFrameManager(aPresShell, aStyleSet)
1415 , mDocument(aDocument)
1416 , mRootElementFrame(nullptr)
1417 , mRootElementStyleFrame(nullptr)
1418 , mFixedContainingBlock(nullptr)
1419 , mDocElementContainingBlock(nullptr)
1420 , mGfxScrollFrame(nullptr)
1421 , mPageSequenceFrame(nullptr)
1422 , mCurrentDepth(0)
1423 , mUpdateCount(0)
1424 , mQuotesDirty(false)
1425 , mCountersDirty(false)
1426 , mIsDestroyingFrameTree(false)
1427 , mHasRootAbsPosContainingBlock(false)
1428 , mAlwaysCreateFramesForIgnorableWhitespace(false)
1429 {
1430 #ifdef DEBUG
1431 static bool gFirstTime = true;
1432 if (gFirstTime) {
1433 gFirstTime = false;
1434 char* flags = PR_GetEnv("GECKO_FRAMECTOR_DEBUG_FLAGS");
1435 if (flags) {
1436 bool error = false;
1437 for (;;) {
1438 char* comma = PL_strchr(flags, ',');
1439 if (comma)
1440 *comma = '\0';
1442 bool found = false;
1443 FrameCtorDebugFlags* flag = gFlags;
1444 FrameCtorDebugFlags* limit = gFlags + NUM_DEBUG_FLAGS;
1445 while (flag < limit) {
1446 if (PL_strcasecmp(flag->name, flags) == 0) {
1447 *(flag->on) = true;
1448 printf("nsCSSFrameConstructor: setting %s debug flag on\n", flag->name);
1449 found = true;
1450 break;
1451 }
1452 ++flag;
1453 }
1455 if (! found)
1456 error = true;
1458 if (! comma)
1459 break;
1461 *comma = ',';
1462 flags = comma + 1;
1463 }
1465 if (error) {
1466 printf("Here are the available GECKO_FRAMECTOR_DEBUG_FLAGS:\n");
1467 FrameCtorDebugFlags* flag = gFlags;
1468 FrameCtorDebugFlags* limit = gFlags + NUM_DEBUG_FLAGS;
1469 while (flag < limit) {
1470 printf(" %s\n", flag->name);
1471 ++flag;
1472 }
1473 printf("Note: GECKO_FRAMECTOR_DEBUG_FLAGS is a comma separated list of flag\n");
1474 printf("names (no whitespace)\n");
1475 }
1476 }
1477 }
1478 #endif
1479 }
1481 void
1482 nsCSSFrameConstructor::NotifyDestroyingFrame(nsIFrame* aFrame)
1483 {
1484 NS_PRECONDITION(mUpdateCount != 0,
1485 "Should be in an update while destroying frames");
1487 if (aFrame->GetStateBits() & NS_FRAME_GENERATED_CONTENT) {
1488 if (mQuoteList.DestroyNodesFor(aFrame))
1489 QuotesDirty();
1490 }
1492 if (mCounterManager.DestroyNodesFor(aFrame)) {
1493 // Technically we don't need to update anything if we destroyed only
1494 // USE nodes. However, this is unlikely to happen in the real world
1495 // since USE nodes generally go along with INCREMENT nodes.
1496 CountersDirty();
1497 }
1499 RestyleManager()->NotifyDestroyingFrame(aFrame);
1501 nsFrameManager::NotifyDestroyingFrame(aFrame);
1502 }
1504 struct nsGenConInitializer {
1505 nsAutoPtr<nsGenConNode> mNode;
1506 nsGenConList* mList;
1507 void (nsCSSFrameConstructor::*mDirtyAll)();
1509 nsGenConInitializer(nsGenConNode* aNode, nsGenConList* aList,
1510 void (nsCSSFrameConstructor::*aDirtyAll)())
1511 : mNode(aNode), mList(aList), mDirtyAll(aDirtyAll) {}
1512 };
1514 already_AddRefed<nsIContent>
1515 nsCSSFrameConstructor::CreateGenConTextNode(nsFrameConstructorState& aState,
1516 const nsString& aString,
1517 nsCOMPtr<nsIDOMCharacterData>* aText,
1518 nsGenConInitializer* aInitializer)
1519 {
1520 nsRefPtr<nsTextNode> content = new nsTextNode(mDocument->NodeInfoManager());
1521 content->SetText(aString, false);
1522 if (aText) {
1523 *aText = content;
1524 }
1525 if (aInitializer) {
1526 content->SetProperty(nsGkAtoms::genConInitializerProperty, aInitializer,
1527 nsINode::DeleteProperty<nsGenConInitializer>);
1528 aState.mGeneratedTextNodesWithInitializer.AppendObject(content);
1529 }
1530 return content.forget();
1531 }
1533 already_AddRefed<nsIContent>
1534 nsCSSFrameConstructor::CreateGeneratedContent(nsFrameConstructorState& aState,
1535 nsIContent* aParentContent,
1536 nsStyleContext* aStyleContext,
1537 uint32_t aContentIndex)
1538 {
1539 // Get the content value
1540 const nsStyleContentData &data =
1541 aStyleContext->StyleContent()->ContentAt(aContentIndex);
1542 nsStyleContentType type = data.mType;
1544 if (eStyleContentType_Image == type) {
1545 if (!data.mContent.mImage) {
1546 // CSS had something specified that couldn't be converted to an
1547 // image object
1548 return nullptr;
1549 }
1551 // Create an image content object and pass it the image request.
1552 // XXX Check if it's an image type we can handle...
1554 nsCOMPtr<nsINodeInfo> nodeInfo;
1555 nodeInfo = mDocument->NodeInfoManager()->
1556 GetNodeInfo(nsGkAtoms::mozgeneratedcontentimage, nullptr,
1557 kNameSpaceID_XHTML, nsIDOMNode::ELEMENT_NODE);
1559 nsCOMPtr<nsIContent> content;
1560 NS_NewGenConImageContent(getter_AddRefs(content), nodeInfo.forget(),
1561 data.mContent.mImage);
1562 return content.forget();
1563 }
1565 switch (type) {
1566 case eStyleContentType_String:
1567 return CreateGenConTextNode(aState,
1568 nsDependentString(data.mContent.mString),
1569 nullptr, nullptr);
1571 case eStyleContentType_Attr:
1572 {
1573 nsCOMPtr<nsIAtom> attrName;
1574 int32_t attrNameSpace = kNameSpaceID_None;
1575 nsAutoString contentString(data.mContent.mString);
1577 int32_t barIndex = contentString.FindChar('|'); // CSS namespace delimiter
1578 if (-1 != barIndex) {
1579 nsAutoString nameSpaceVal;
1580 contentString.Left(nameSpaceVal, barIndex);
1581 nsresult error;
1582 attrNameSpace = nameSpaceVal.ToInteger(&error);
1583 contentString.Cut(0, barIndex + 1);
1584 if (contentString.Length()) {
1585 if (mDocument->IsHTML() && aParentContent->IsHTML()) {
1586 ToLowerCase(contentString);
1587 }
1588 attrName = do_GetAtom(contentString);
1589 }
1590 }
1591 else {
1592 if (mDocument->IsHTML() && aParentContent->IsHTML()) {
1593 ToLowerCase(contentString);
1594 }
1595 attrName = do_GetAtom(contentString);
1596 }
1598 if (!attrName) {
1599 return nullptr;
1600 }
1602 nsCOMPtr<nsIContent> content;
1603 NS_NewAttributeContent(mDocument->NodeInfoManager(),
1604 attrNameSpace, attrName, getter_AddRefs(content));
1605 return content.forget();
1606 }
1608 case eStyleContentType_Counter:
1609 case eStyleContentType_Counters:
1610 {
1611 nsCSSValue::Array* counters = data.mContent.mCounters;
1612 nsCounterList* counterList = mCounterManager.CounterListFor(
1613 nsDependentString(counters->Item(0).GetStringBufferValue()));
1614 if (!counterList)
1615 return nullptr;
1617 nsCounterUseNode* node =
1618 new nsCounterUseNode(counters, aContentIndex,
1619 type == eStyleContentType_Counters);
1621 nsGenConInitializer* initializer =
1622 new nsGenConInitializer(node, counterList,
1623 &nsCSSFrameConstructor::CountersDirty);
1624 return CreateGenConTextNode(aState, EmptyString(), &node->mText,
1625 initializer);
1626 }
1628 case eStyleContentType_Image:
1629 NS_NOTREACHED("handled by if above");
1630 return nullptr;
1632 case eStyleContentType_OpenQuote:
1633 case eStyleContentType_CloseQuote:
1634 case eStyleContentType_NoOpenQuote:
1635 case eStyleContentType_NoCloseQuote:
1636 {
1637 nsQuoteNode* node =
1638 new nsQuoteNode(type, aContentIndex);
1640 nsGenConInitializer* initializer =
1641 new nsGenConInitializer(node, &mQuoteList,
1642 &nsCSSFrameConstructor::QuotesDirty);
1643 return CreateGenConTextNode(aState, EmptyString(), &node->mText,
1644 initializer);
1645 }
1647 case eStyleContentType_AltContent:
1648 {
1649 // Use the "alt" attribute; if that fails and the node is an HTML
1650 // <input>, try the value attribute and then fall back to some default
1651 // localized text we have.
1652 // XXX what if the 'alt' attribute is added later, how will we
1653 // detect that and do the right thing here?
1654 if (aParentContent->HasAttr(kNameSpaceID_None, nsGkAtoms::alt)) {
1655 nsCOMPtr<nsIContent> content;
1656 NS_NewAttributeContent(mDocument->NodeInfoManager(),
1657 kNameSpaceID_None, nsGkAtoms::alt, getter_AddRefs(content));
1658 return content.forget();
1659 }
1661 if (aParentContent->IsHTML() &&
1662 aParentContent->NodeInfo()->Equals(nsGkAtoms::input)) {
1663 if (aParentContent->HasAttr(kNameSpaceID_None, nsGkAtoms::value)) {
1664 nsCOMPtr<nsIContent> content;
1665 NS_NewAttributeContent(mDocument->NodeInfoManager(),
1666 kNameSpaceID_None, nsGkAtoms::value, getter_AddRefs(content));
1667 return content.forget();
1668 }
1670 nsXPIDLString temp;
1671 nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
1672 "Submit", temp);
1673 return CreateGenConTextNode(aState, temp, nullptr, nullptr);
1674 }
1676 break;
1677 }
1679 case eStyleContentType_Uninitialized:
1680 NS_NOTREACHED("uninitialized content type");
1681 return nullptr;
1682 } // switch
1684 return nullptr;
1685 }
1687 /*
1688 * aParentFrame - the frame that should be the parent of the generated
1689 * content. This is the frame for the corresponding content node,
1690 * which must not be a leaf frame.
1691 *
1692 * Any items created are added to aItems.
1693 *
1694 * We create an XML element (tag _moz_generated_content_before or
1695 * _moz_generated_content_after) representing the pseudoelement. We
1696 * create a DOM node for each 'content' item and make those nodes the
1697 * children of the XML element. Then we create a frame subtree for
1698 * the XML element as if it were a regular child of
1699 * aParentFrame/aParentContent, giving the XML element the ::before or
1700 * ::after style.
1701 */
1702 void
1703 nsCSSFrameConstructor::CreateGeneratedContentItem(nsFrameConstructorState& aState,
1704 nsIFrame* aParentFrame,
1705 nsIContent* aParentContent,
1706 nsStyleContext* aStyleContext,
1707 nsCSSPseudoElements::Type aPseudoElement,
1708 FrameConstructionItemList& aItems)
1709 {
1710 // XXXbz is this ever true?
1711 if (!aParentContent->IsElement()) {
1712 NS_ERROR("Bogus generated content parent");
1713 return;
1714 }
1716 nsStyleSet *styleSet = mPresShell->StyleSet();
1718 // Probe for the existence of the pseudo-element
1719 nsRefPtr<nsStyleContext> pseudoStyleContext;
1720 pseudoStyleContext =
1721 styleSet->ProbePseudoElementStyle(aParentContent->AsElement(),
1722 aPseudoElement,
1723 aStyleContext,
1724 aState.mTreeMatchContext);
1725 if (!pseudoStyleContext)
1726 return;
1727 // |ProbePseudoStyleFor| checked the 'display' property and the
1728 // |ContentCount()| of the 'content' property for us.
1729 nsCOMPtr<nsINodeInfo> nodeInfo;
1730 nsIAtom* elemName = aPseudoElement == nsCSSPseudoElements::ePseudo_before ?
1731 nsGkAtoms::mozgeneratedcontentbefore : nsGkAtoms::mozgeneratedcontentafter;
1732 nodeInfo = mDocument->NodeInfoManager()->GetNodeInfo(elemName, nullptr,
1733 kNameSpaceID_None,
1734 nsIDOMNode::ELEMENT_NODE);
1735 nsCOMPtr<Element> container;
1736 nsresult rv = NS_NewXMLElement(getter_AddRefs(container), nodeInfo.forget());
1737 if (NS_FAILED(rv))
1738 return;
1739 container->SetIsNativeAnonymousRoot();
1741 rv = container->BindToTree(mDocument, aParentContent, aParentContent, true);
1742 if (NS_FAILED(rv)) {
1743 container->UnbindFromTree();
1744 return;
1745 }
1747 uint32_t contentCount = pseudoStyleContext->StyleContent()->ContentCount();
1748 for (uint32_t contentIndex = 0; contentIndex < contentCount; contentIndex++) {
1749 nsCOMPtr<nsIContent> content =
1750 CreateGeneratedContent(aState, aParentContent, pseudoStyleContext,
1751 contentIndex);
1752 if (content) {
1753 container->AppendChildTo(content, false);
1754 }
1755 }
1757 AddFrameConstructionItemsInternal(aState, container, aParentFrame, elemName,
1758 kNameSpaceID_None, true,
1759 pseudoStyleContext,
1760 ITEM_IS_GENERATED_CONTENT, nullptr,
1761 aItems);
1762 }
1764 /****************************************************
1765 ** BEGIN TABLE SECTION
1766 ****************************************************/
1768 // The term pseudo frame is being used instead of anonymous frame, since anonymous
1769 // frame has been used elsewhere to refer to frames that have generated content
1771 // Return whether the given frame is a table pseudo-frame. Note that
1772 // cell-content and table-outer frames have pseudo-types, but are always
1773 // created, even for non-anonymous cells and tables respectively. So for those
1774 // we have to examine the cell or table frame to see whether it's a pseudo
1775 // frame. In particular, a lone table caption will have an outer table as its
1776 // parent, but will also trigger construction of an empty inner table, which
1777 // will be the one we can examine to see whether the outer was a pseudo-frame.
1778 static bool
1779 IsTablePseudo(nsIFrame* aFrame)
1780 {
1781 nsIAtom* pseudoType = aFrame->StyleContext()->GetPseudo();
1782 return pseudoType &&
1783 (pseudoType == nsCSSAnonBoxes::table ||
1784 pseudoType == nsCSSAnonBoxes::inlineTable ||
1785 pseudoType == nsCSSAnonBoxes::tableColGroup ||
1786 pseudoType == nsCSSAnonBoxes::tableRowGroup ||
1787 pseudoType == nsCSSAnonBoxes::tableRow ||
1788 pseudoType == nsCSSAnonBoxes::tableCell ||
1789 (pseudoType == nsCSSAnonBoxes::cellContent &&
1790 aFrame->GetParent()->StyleContext()->GetPseudo() ==
1791 nsCSSAnonBoxes::tableCell) ||
1792 (pseudoType == nsCSSAnonBoxes::tableOuter &&
1793 (aFrame->GetFirstPrincipalChild()->StyleContext()->GetPseudo() ==
1794 nsCSSAnonBoxes::table ||
1795 aFrame->GetFirstPrincipalChild()->StyleContext()->GetPseudo() ==
1796 nsCSSAnonBoxes::inlineTable)));
1797 }
1799 /* static */
1800 nsCSSFrameConstructor::ParentType
1801 nsCSSFrameConstructor::GetParentType(nsIAtom* aFrameType)
1802 {
1803 if (aFrameType == nsGkAtoms::tableFrame) {
1804 return eTypeTable;
1805 }
1806 if (aFrameType == nsGkAtoms::tableRowGroupFrame) {
1807 return eTypeRowGroup;
1808 }
1809 if (aFrameType == nsGkAtoms::tableRowFrame) {
1810 return eTypeRow;
1811 }
1812 if (aFrameType == nsGkAtoms::tableColGroupFrame) {
1813 return eTypeColGroup;
1814 }
1816 return eTypeBlock;
1817 }
1819 static nsIFrame*
1820 AdjustCaptionParentFrame(nsIFrame* aParentFrame)
1821 {
1822 if (nsGkAtoms::tableFrame == aParentFrame->GetType()) {
1823 return aParentFrame->GetParent();;
1824 }
1825 return aParentFrame;
1826 }
1828 /**
1829 * If the parent frame is a |tableFrame| and the child is a
1830 * |captionFrame|, then we want to insert the frames beneath the
1831 * |tableFrame|'s parent frame. Returns |true| if the parent frame
1832 * needed to be fixed up.
1833 */
1834 static bool
1835 GetCaptionAdjustedParent(nsIFrame* aParentFrame,
1836 const nsIFrame* aChildFrame,
1837 nsIFrame** aAdjParentFrame)
1838 {
1839 *aAdjParentFrame = aParentFrame;
1840 bool haveCaption = false;
1842 if (nsGkAtoms::tableCaptionFrame == aChildFrame->GetType()) {
1843 haveCaption = true;
1844 *aAdjParentFrame = AdjustCaptionParentFrame(aParentFrame);
1845 }
1846 return haveCaption;
1847 }
1849 void
1850 nsCSSFrameConstructor::AdjustParentFrame(nsIFrame* & aParentFrame,
1851 const FrameConstructionData* aFCData,
1852 nsStyleContext* aStyleContext)
1853 {
1854 NS_PRECONDITION(aStyleContext, "Must have child's style context");
1855 NS_PRECONDITION(aFCData, "Must have frame construction data");
1857 bool tablePart = ((aFCData->mBits & FCDATA_IS_TABLE_PART) != 0);
1859 if (tablePart && aStyleContext->StyleDisplay()->mDisplay ==
1860 NS_STYLE_DISPLAY_TABLE_CAPTION) {
1861 aParentFrame = AdjustCaptionParentFrame(aParentFrame);
1862 }
1863 }
1865 // Pull all the captions present in aItems out into aCaptions
1866 static void
1867 PullOutCaptionFrames(nsFrameItems& aItems, nsFrameItems& aCaptions)
1868 {
1869 nsIFrame *child = aItems.FirstChild();
1870 while (child) {
1871 nsIFrame *nextSibling = child->GetNextSibling();
1872 if (nsGkAtoms::tableCaptionFrame == child->GetType()) {
1873 aItems.RemoveFrame(child);
1874 aCaptions.AddChild(child);
1875 }
1876 child = nextSibling;
1877 }
1878 }
1881 // Construct the outer, inner table frames and the children frames for the table.
1882 // XXX Page break frames for pseudo table frames are not constructed to avoid the risk
1883 // associated with revising the pseudo frame mechanism. The long term solution
1884 // of having frames handle page-break-before/after will solve the problem.
1885 nsIFrame*
1886 nsCSSFrameConstructor::ConstructTable(nsFrameConstructorState& aState,
1887 FrameConstructionItem& aItem,
1888 nsIFrame* aParentFrame,
1889 const nsStyleDisplay* aDisplay,
1890 nsFrameItems& aFrameItems)
1891 {
1892 NS_PRECONDITION(aDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE ||
1893 aDisplay->mDisplay == NS_STYLE_DISPLAY_INLINE_TABLE,
1894 "Unexpected call");
1896 nsIContent* const content = aItem.mContent;
1897 nsStyleContext* const styleContext = aItem.mStyleContext;
1898 const uint32_t nameSpaceID = aItem.mNameSpaceID;
1900 // create the pseudo SC for the outer table as a child of the inner SC
1901 nsRefPtr<nsStyleContext> outerStyleContext;
1902 outerStyleContext = mPresShell->StyleSet()->
1903 ResolveAnonymousBoxStyle(nsCSSAnonBoxes::tableOuter, styleContext);
1905 // Create the outer table frame which holds the caption and inner table frame
1906 nsIFrame* newFrame;
1907 if (kNameSpaceID_MathML == nameSpaceID)
1908 newFrame = NS_NewMathMLmtableOuterFrame(mPresShell, outerStyleContext);
1909 else
1910 newFrame = NS_NewTableOuterFrame(mPresShell, outerStyleContext);
1912 nsIFrame* geometricParent =
1913 aState.GetGeometricParent(outerStyleContext->StyleDisplay(),
1914 aParentFrame);
1916 // Init the table outer frame
1917 InitAndRestoreFrame(aState, content, geometricParent, newFrame);
1919 // Create the inner table frame
1920 nsIFrame* innerFrame;
1921 if (kNameSpaceID_MathML == nameSpaceID)
1922 innerFrame = NS_NewMathMLmtableFrame(mPresShell, styleContext);
1923 else
1924 innerFrame = NS_NewTableFrame(mPresShell, styleContext);
1926 InitAndRestoreFrame(aState, content, newFrame, innerFrame);
1928 // Put the newly created frames into the right child list
1929 SetInitialSingleChild(newFrame, innerFrame);
1931 aState.AddChild(newFrame, aFrameItems, content, styleContext, aParentFrame);
1933 if (!mRootElementFrame) {
1934 // The frame we're constructing will be the root element frame.
1935 // Set mRootElementFrame before processing children.
1936 mRootElementFrame = newFrame;
1937 }
1939 nsFrameItems childItems;
1941 // Process children
1942 nsFrameConstructorSaveState absoluteSaveState;
1943 const nsStyleDisplay* display = outerStyleContext->StyleDisplay();
1945 // Mark the table frame as an absolute container if needed
1946 newFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
1947 if (display->IsPositioned(newFrame)) {
1948 aState.PushAbsoluteContainingBlock(newFrame, newFrame, absoluteSaveState);
1949 }
1950 NS_ASSERTION(aItem.mAnonChildren.IsEmpty(),
1951 "nsIAnonymousContentCreator::CreateAnonymousContent "
1952 "implementations for table frames are not currently expected "
1953 "to output a list where the items have their own children");
1954 if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) {
1955 ConstructFramesFromItemList(aState, aItem.mChildItems,
1956 innerFrame, childItems);
1957 } else {
1958 ProcessChildren(aState, content, styleContext, innerFrame,
1959 true, childItems, false, aItem.mPendingBinding);
1960 }
1962 nsFrameItems captionItems;
1963 PullOutCaptionFrames(childItems, captionItems);
1965 // Set the inner table frame's initial primary list
1966 innerFrame->SetInitialChildList(kPrincipalList, childItems);
1968 // Set the outer table frame's secondary childlist lists
1969 if (captionItems.NotEmpty()) {
1970 newFrame->SetInitialChildList(nsIFrame::kCaptionList, captionItems);
1971 }
1973 return newFrame;
1974 }
1976 static void
1977 MakeTablePartAbsoluteContainingBlockIfNeeded(nsFrameConstructorState& aState,
1978 const nsStyleDisplay* aDisplay,
1979 nsFrameConstructorSaveState& aAbsSaveState,
1980 nsIFrame* aFrame)
1981 {
1982 // If we're positioned, then we need to become an absolute containing block
1983 // for any absolutely positioned children and register for post-reflow fixup.
1984 //
1985 // Note that usually if a frame type can be an absolute containing block, we
1986 // always set NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN, whether it actually is or not.
1987 // However, in this case flag serves the additional purpose of indicating that
1988 // the frame was registered with its table frame. This allows us to avoid the
1989 // overhead of unregistering the frame in most cases.
1990 if (aDisplay->IsPositioned(aFrame)) {
1991 aFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
1992 aState.PushAbsoluteContainingBlock(aFrame, aFrame, aAbsSaveState);
1993 nsTableFrame::RegisterPositionedTablePart(aFrame);
1994 }
1995 }
1997 nsIFrame*
1998 nsCSSFrameConstructor::ConstructTableRowOrRowGroup(nsFrameConstructorState& aState,
1999 FrameConstructionItem& aItem,
2000 nsIFrame* aParentFrame,
2001 const nsStyleDisplay* aDisplay,
2002 nsFrameItems& aFrameItems)
2003 {
2004 NS_PRECONDITION(aDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_ROW ||
2005 aDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_ROW_GROUP ||
2006 aDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP ||
2007 aDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_HEADER_GROUP,
2008 "Unexpected call");
2009 MOZ_ASSERT(aItem.mStyleContext->StyleDisplay() == aDisplay,
2010 "Display style doesn't match style context");
2011 nsIContent* const content = aItem.mContent;
2012 nsStyleContext* const styleContext = aItem.mStyleContext;
2013 const uint32_t nameSpaceID = aItem.mNameSpaceID;
2015 nsIFrame* newFrame;
2016 if (aDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_ROW) {
2017 if (kNameSpaceID_MathML == nameSpaceID)
2018 newFrame = NS_NewMathMLmtrFrame(mPresShell, styleContext);
2019 else
2020 newFrame = NS_NewTableRowFrame(mPresShell, styleContext);
2021 } else {
2022 newFrame = NS_NewTableRowGroupFrame(mPresShell, styleContext);
2023 }
2025 InitAndRestoreFrame(aState, content, aParentFrame, newFrame);
2027 nsFrameConstructorSaveState absoluteSaveState;
2028 MakeTablePartAbsoluteContainingBlockIfNeeded(aState, aDisplay,
2029 absoluteSaveState,
2030 newFrame);
2032 nsFrameItems childItems;
2033 NS_ASSERTION(aItem.mAnonChildren.IsEmpty(),
2034 "nsIAnonymousContentCreator::CreateAnonymousContent "
2035 "implementations for table frames are not currently expected "
2036 "to output a list where the items have their own children");
2037 if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) {
2038 ConstructFramesFromItemList(aState, aItem.mChildItems, newFrame,
2039 childItems);
2040 } else {
2041 ProcessChildren(aState, content, styleContext, newFrame,
2042 true, childItems, false, aItem.mPendingBinding);
2043 }
2045 newFrame->SetInitialChildList(kPrincipalList, childItems);
2046 aFrameItems.AddChild(newFrame);
2047 return newFrame;
2048 }
2050 nsIFrame*
2051 nsCSSFrameConstructor::ConstructTableCol(nsFrameConstructorState& aState,
2052 FrameConstructionItem& aItem,
2053 nsIFrame* aParentFrame,
2054 const nsStyleDisplay* aStyleDisplay,
2055 nsFrameItems& aFrameItems)
2056 {
2057 nsIContent* const content = aItem.mContent;
2058 nsStyleContext* const styleContext = aItem.mStyleContext;
2060 nsTableColFrame* colFrame = NS_NewTableColFrame(mPresShell, styleContext);
2061 InitAndRestoreFrame(aState, content, aParentFrame, colFrame);
2063 NS_ASSERTION(colFrame->StyleContext() == styleContext,
2064 "Unexpected style context");
2066 aFrameItems.AddChild(colFrame);
2068 // construct additional col frames if the col frame has a span > 1
2069 int32_t span = colFrame->GetSpan();
2070 for (int32_t spanX = 1; spanX < span; spanX++) {
2071 nsTableColFrame* newCol = NS_NewTableColFrame(mPresShell, styleContext);
2072 InitAndRestoreFrame(aState, content, aParentFrame, newCol, false);
2073 aFrameItems.LastChild()->SetNextContinuation(newCol);
2074 newCol->SetPrevContinuation(aFrameItems.LastChild());
2075 aFrameItems.AddChild(newCol);
2076 newCol->SetColType(eColAnonymousCol);
2077 }
2079 return colFrame;
2080 }
2082 nsIFrame*
2083 nsCSSFrameConstructor::ConstructTableCell(nsFrameConstructorState& aState,
2084 FrameConstructionItem& aItem,
2085 nsIFrame* aParentFrame,
2086 const nsStyleDisplay* aDisplay,
2087 nsFrameItems& aFrameItems)
2088 {
2089 NS_PRECONDITION(aDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_CELL,
2090 "Unexpected call");
2092 nsIContent* const content = aItem.mContent;
2093 nsStyleContext* const styleContext = aItem.mStyleContext;
2094 const uint32_t nameSpaceID = aItem.mNameSpaceID;
2096 bool borderCollapse = IsBorderCollapse(aParentFrame);
2097 nsIFrame* newFrame;
2098 // <mtable> is border separate in mathml.css and the MathML code doesn't implement
2099 // border collapse. For those users who style <mtable> with border collapse,
2100 // give them the default non-MathML table frames that understand border collapse.
2101 // This won't break us because MathML table frames are all subclasses of the default
2102 // table code, and so we can freely mix <mtable> with <mtr> or <tr>, <mtd> or <td>.
2103 // What will happen is just that non-MathML frames won't understand MathML attributes
2104 // and will therefore miss the special handling that the MathML code does.
2105 if (kNameSpaceID_MathML == nameSpaceID && !borderCollapse)
2106 newFrame = NS_NewMathMLmtdFrame(mPresShell, styleContext);
2107 else
2108 // Warning: If you change this and add a wrapper frame around table cell
2109 // frames, make sure Bug 368554 doesn't regress!
2110 // See IsInAutoWidthTableCellForQuirk() in nsImageFrame.cpp.
2111 newFrame = NS_NewTableCellFrame(mPresShell, styleContext, borderCollapse);
2113 // Initialize the table cell frame
2114 InitAndRestoreFrame(aState, content, aParentFrame, newFrame);
2116 // Resolve pseudo style and initialize the body cell frame
2117 nsRefPtr<nsStyleContext> innerPseudoStyle;
2118 innerPseudoStyle = mPresShell->StyleSet()->
2119 ResolveAnonymousBoxStyle(nsCSSAnonBoxes::cellContent, styleContext);
2121 // Create a block frame that will format the cell's content
2122 bool isBlock;
2123 nsIFrame* cellInnerFrame;
2124 if (kNameSpaceID_MathML == nameSpaceID) {
2125 cellInnerFrame = NS_NewMathMLmtdInnerFrame(mPresShell, innerPseudoStyle);
2126 isBlock = false;
2127 } else {
2128 cellInnerFrame = NS_NewBlockFormattingContext(mPresShell, innerPseudoStyle);
2129 isBlock = true;
2130 }
2132 InitAndRestoreFrame(aState, content, newFrame, cellInnerFrame);
2134 nsFrameConstructorSaveState absoluteSaveState;
2135 MakeTablePartAbsoluteContainingBlockIfNeeded(aState, aDisplay,
2136 absoluteSaveState,
2137 newFrame);
2139 nsFrameItems childItems;
2140 NS_ASSERTION(aItem.mAnonChildren.IsEmpty(),
2141 "nsIAnonymousContentCreator::CreateAnonymousContent "
2142 "implementations for table frames are not currently expected "
2143 "to output a list where the items have their own children");
2144 if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) {
2145 // Need to push ourselves as a float containing block.
2146 // XXXbz it might be nice to work on getting the parent
2147 // FrameConstructionItem down into ProcessChildren and just making use of
2148 // the push there, but that's a bit of work.
2149 nsFrameConstructorSaveState floatSaveState;
2150 if (!isBlock) { /* MathML case */
2151 aState.PushFloatContainingBlock(nullptr, floatSaveState);
2152 } else {
2153 aState.PushFloatContainingBlock(cellInnerFrame, floatSaveState);
2154 }
2156 ConstructFramesFromItemList(aState, aItem.mChildItems, cellInnerFrame,
2157 childItems);
2158 } else {
2159 // Process the child content
2160 ProcessChildren(aState, content, styleContext, cellInnerFrame,
2161 true, childItems, isBlock, aItem.mPendingBinding);
2162 }
2164 cellInnerFrame->SetInitialChildList(kPrincipalList, childItems);
2165 SetInitialSingleChild(newFrame, cellInnerFrame);
2166 aFrameItems.AddChild(newFrame);
2167 return newFrame;
2168 }
2170 static inline bool
2171 NeedFrameFor(const nsFrameConstructorState& aState,
2172 nsIFrame* aParentFrame,
2173 nsIContent* aChildContent)
2174 {
2175 // XXX the GetContent() != aChildContent check is needed due to bug 135040.
2176 // Remove it once that's fixed.
2177 NS_PRECONDITION(!aChildContent->GetPrimaryFrame() ||
2178 aState.mCreatingExtraFrames ||
2179 aChildContent->GetPrimaryFrame()->GetContent() != aChildContent,
2180 "Why did we get called?");
2182 // don't create a whitespace frame if aParentFrame doesn't want it.
2183 // always create frames for children in generated content. counter(),
2184 // quotes, and attr() content can easily change dynamically and we don't
2185 // want to be reconstructing frames. It's not even clear that these
2186 // should be considered ignorable just because they evaluate to
2187 // whitespace.
2189 // We could handle all this in CreateNeededTablePseudos or some other place
2190 // after we build our frame construction items, but that would involve
2191 // creating frame construction items for whitespace kids of
2192 // eExcludesIgnorableWhitespace frames, where we know we'll be dropping them
2193 // all anyway, and involve an extra walk down the frame construction item
2194 // list.
2195 if (!aParentFrame->IsFrameOfType(nsIFrame::eExcludesIgnorableWhitespace) ||
2196 aParentFrame->IsGeneratedContentFrame() ||
2197 !aChildContent->IsNodeOfType(nsINode::eTEXT)) {
2198 return true;
2199 }
2201 aChildContent->SetFlags(NS_CREATE_FRAME_IF_NON_WHITESPACE |
2202 NS_REFRAME_IF_WHITESPACE);
2203 return !aChildContent->TextIsOnlyWhitespace();
2204 }
2206 /***********************************************
2207 * END TABLE SECTION
2208 ***********************************************/
2210 static bool CheckOverflow(nsPresContext* aPresContext,
2211 const nsStyleDisplay* aDisplay)
2212 {
2213 if (aDisplay->mOverflowX == NS_STYLE_OVERFLOW_VISIBLE)
2214 return false;
2216 if (aDisplay->mOverflowX == NS_STYLE_OVERFLOW_CLIP)
2217 aPresContext->SetViewportOverflowOverride(NS_STYLE_OVERFLOW_HIDDEN,
2218 NS_STYLE_OVERFLOW_HIDDEN);
2219 else
2220 aPresContext->SetViewportOverflowOverride(aDisplay->mOverflowX,
2221 aDisplay->mOverflowY);
2222 return true;
2223 }
2225 /**
2226 * This checks the root element and the HTML BODY, if any, for an "overflow" property
2227 * that should be applied to the viewport. If one is found then we return the
2228 * element that we took the overflow from (which should then be treated as
2229 * "overflow:visible"), and we store the overflow style in the prescontext.
2230 * @return if scroll was propagated from some content node, the content node it
2231 * was propagated from.
2232 */
2233 nsIContent*
2234 nsCSSFrameConstructor::PropagateScrollToViewport()
2235 {
2236 // Set default
2237 nsPresContext* presContext = mPresShell->GetPresContext();
2238 presContext->SetViewportOverflowOverride(NS_STYLE_OVERFLOW_AUTO,
2239 NS_STYLE_OVERFLOW_AUTO);
2241 // We never mess with the viewport scroll state
2242 // when printing or in print preview
2243 if (presContext->IsPaginated()) {
2244 return nullptr;
2245 }
2247 Element* docElement = mDocument->GetRootElement();
2249 // Check the style on the document root element
2250 nsStyleSet *styleSet = mPresShell->StyleSet();
2251 nsRefPtr<nsStyleContext> rootStyle;
2252 rootStyle = styleSet->ResolveStyleFor(docElement, nullptr);
2253 if (CheckOverflow(presContext, rootStyle->StyleDisplay())) {
2254 // tell caller we stole the overflow style from the root element
2255 return docElement;
2256 }
2258 // Don't look in the BODY for non-HTML documents or HTML documents
2259 // with non-HTML roots
2260 // XXX this should be earlier; we shouldn't even look at the document root
2261 // for non-HTML documents. Fix this once we support explicit CSS styling
2262 // of the viewport
2263 // XXX what about XHTML?
2264 nsCOMPtr<nsIDOMHTMLDocument> htmlDoc(do_QueryInterface(mDocument));
2265 if (!htmlDoc || !docElement->IsHTML()) {
2266 return nullptr;
2267 }
2269 nsCOMPtr<nsIDOMHTMLElement> body;
2270 htmlDoc->GetBody(getter_AddRefs(body));
2271 nsCOMPtr<nsIContent> bodyElement = do_QueryInterface(body);
2273 if (!bodyElement ||
2274 !bodyElement->NodeInfo()->Equals(nsGkAtoms::body)) {
2275 // The body is not a <body> tag, it's a <frameset>.
2276 return nullptr;
2277 }
2279 nsRefPtr<nsStyleContext> bodyStyle;
2280 bodyStyle = styleSet->ResolveStyleFor(bodyElement->AsElement(), rootStyle);
2282 if (CheckOverflow(presContext, bodyStyle->StyleDisplay())) {
2283 // tell caller we stole the overflow style from the body element
2284 return bodyElement;
2285 }
2287 return nullptr;
2288 }
2290 nsIFrame*
2291 nsCSSFrameConstructor::ConstructDocElementFrame(Element* aDocElement,
2292 nsILayoutHistoryState* aFrameState)
2293 {
2294 NS_PRECONDITION(mFixedContainingBlock,
2295 "No viewport? Someone forgot to call ConstructRootFrame!");
2296 NS_PRECONDITION(mFixedContainingBlock == GetRootFrame(),
2297 "Unexpected mFixedContainingBlock");
2298 NS_PRECONDITION(!mDocElementContainingBlock,
2299 "Shouldn't have a doc element containing block here");
2301 // Make sure to call PropagateScrollToViewport before
2302 // SetUpDocElementContainingBlock, since it sets up our scrollbar state
2303 // properly.
2304 #ifdef DEBUG
2305 nsIContent* propagatedScrollFrom =
2306 #endif
2307 PropagateScrollToViewport();
2309 SetUpDocElementContainingBlock(aDocElement);
2311 NS_ASSERTION(mDocElementContainingBlock, "Should have parent by now");
2313 nsFrameConstructorState state(mPresShell, mFixedContainingBlock, nullptr,
2314 nullptr, aFrameState);
2315 // Initialize the ancestor filter with null for now; we'll push
2316 // aDocElement once we finish resolving style for it.
2317 state.mTreeMatchContext.InitAncestors(nullptr);
2319 // XXXbz why, exactly?
2320 if (!mTempFrameTreeState)
2321 state.mPresShell->CaptureHistoryState(getter_AddRefs(mTempFrameTreeState));
2323 // Make sure that we'll handle restyles for this document element in
2324 // the future. We need this, because the document element might
2325 // have stale restyle bits from a previous frame constructor for
2326 // this document. Unlike in AddFrameConstructionItems, it's safe to
2327 // unset all element restyle flags, since we don't have any
2328 // siblings.
2329 aDocElement->UnsetFlags(ELEMENT_ALL_RESTYLE_FLAGS);
2331 // --------- CREATE AREA OR BOX FRAME -------
2332 nsRefPtr<nsStyleContext> styleContext;
2333 styleContext = mPresShell->StyleSet()->ResolveStyleFor(aDocElement,
2334 nullptr);
2336 const nsStyleDisplay* display = styleContext->StyleDisplay();
2338 // Ensure that our XBL bindings are installed.
2339 if (display->mBinding) {
2340 // Get the XBL loader.
2341 nsresult rv;
2342 bool resolveStyle;
2344 nsXBLService* xblService = nsXBLService::GetInstance();
2345 if (!xblService) {
2346 return nullptr;
2347 }
2349 nsRefPtr<nsXBLBinding> binding;
2350 rv = xblService->LoadBindings(aDocElement, display->mBinding->GetURI(),
2351 display->mBinding->mOriginPrincipal,
2352 getter_AddRefs(binding), &resolveStyle);
2353 if (NS_FAILED(rv) && rv != NS_ERROR_XBL_BLOCKED)
2354 return nullptr; // Binding will load asynchronously.
2356 if (binding) {
2357 // For backwards compat, keep firing the root's constructor
2358 // after all of its kids' constructors. So tell the binding
2359 // manager about it right now.
2360 mDocument->BindingManager()->AddToAttachedQueue(binding);
2361 }
2363 if (resolveStyle) {
2364 styleContext = mPresShell->StyleSet()->ResolveStyleFor(aDocElement,
2365 nullptr);
2366 display = styleContext->StyleDisplay();
2367 }
2368 }
2370 // --------- IF SCROLLABLE WRAP IN SCROLLFRAME --------
2372 #ifdef DEBUG
2373 NS_ASSERTION(!display->IsScrollableOverflow() ||
2374 state.mPresContext->IsPaginated() ||
2375 propagatedScrollFrom == aDocElement,
2376 "Scrollbars should have been propagated to the viewport");
2377 #endif
2379 if (MOZ_UNLIKELY(display->mDisplay == NS_STYLE_DISPLAY_NONE)) {
2380 SetUndisplayedContent(aDocElement, styleContext);
2381 return nullptr;
2382 }
2384 TreeMatchContext::AutoAncestorPusher ancestorPusher(state.mTreeMatchContext);
2385 ancestorPusher.PushAncestorAndStyleScope(aDocElement);
2387 // Make sure to start any background image loads for the root element now.
2388 styleContext->StartBackgroundImageLoads();
2390 nsFrameConstructorSaveState absoluteSaveState;
2391 if (mHasRootAbsPosContainingBlock) {
2392 // Push the absolute containing block now so we can absolutely position
2393 // the root element
2394 mDocElementContainingBlock->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
2395 state.PushAbsoluteContainingBlock(mDocElementContainingBlock,
2396 mDocElementContainingBlock,
2397 absoluteSaveState);
2398 }
2400 // The rules from CSS 2.1, section 9.2.4, have already been applied
2401 // by the style system, so we can assume that display->mDisplay is
2402 // either NONE, BLOCK, or TABLE.
2404 // contentFrame is the primary frame for the root element. newFrame
2405 // is the frame that will be the child of the initial containing block.
2406 // These are usually the same frame but they can be different, in
2407 // particular if the root frame is positioned, in which case
2408 // contentFrame is the out-of-flow frame and newFrame is the
2409 // placeholder.
2410 nsIFrame* contentFrame;
2411 nsIFrame* newFrame;
2412 bool processChildren = false;
2414 // Check whether we need to build a XUL box or SVG root frame
2415 #ifdef MOZ_XUL
2416 if (aDocElement->IsXUL()) {
2417 contentFrame = NS_NewDocElementBoxFrame(mPresShell, styleContext);
2418 InitAndRestoreFrame(state, aDocElement, mDocElementContainingBlock,
2419 contentFrame);
2420 newFrame = contentFrame;
2421 processChildren = true;
2422 }
2423 else
2424 #endif
2425 if (aDocElement->IsSVG()) {
2426 if (aDocElement->Tag() != nsGkAtoms::svg) {
2427 return nullptr;
2428 }
2429 // We're going to call the right function ourselves, so no need to give a
2430 // function to this FrameConstructionData.
2432 // XXXbz on the other hand, if we converted this whole function to
2433 // FrameConstructionData/Item, then we'd need the right function
2434 // here... but would probably be able to get away with less code in this
2435 // function in general.
2436 // Use a null PendingBinding, since our binding is not in fact pending.
2437 static const FrameConstructionData rootSVGData = FCDATA_DECL(0, nullptr);
2438 already_AddRefed<nsStyleContext> extraRef =
2439 nsRefPtr<nsStyleContext>(styleContext).forget();
2440 FrameConstructionItem item(&rootSVGData, aDocElement,
2441 aDocElement->Tag(), kNameSpaceID_SVG,
2442 nullptr, extraRef, true, nullptr);
2444 nsFrameItems frameItems;
2445 contentFrame = ConstructOuterSVG(state, item, mDocElementContainingBlock,
2446 styleContext->StyleDisplay(),
2447 frameItems);
2448 newFrame = frameItems.FirstChild();
2449 NS_ASSERTION(frameItems.OnlyChild(), "multiple root element frames");
2450 } else if (display->mDisplay == NS_STYLE_DISPLAY_FLEX) {
2451 contentFrame = NS_NewFlexContainerFrame(mPresShell, styleContext);
2452 InitAndRestoreFrame(state, aDocElement, mDocElementContainingBlock,
2453 contentFrame);
2454 newFrame = contentFrame;
2455 processChildren = true;
2456 } else if (display->mDisplay == NS_STYLE_DISPLAY_GRID) {
2457 contentFrame = NS_NewGridContainerFrame(mPresShell, styleContext);
2458 InitAndRestoreFrame(state, aDocElement, mDocElementContainingBlock,
2459 contentFrame);
2460 newFrame = contentFrame;
2461 processChildren = true;
2462 } else if (display->mDisplay == NS_STYLE_DISPLAY_TABLE) {
2463 // We're going to call the right function ourselves, so no need to give a
2464 // function to this FrameConstructionData.
2466 // XXXbz on the other hand, if we converted this whole function to
2467 // FrameConstructionData/Item, then we'd need the right function
2468 // here... but would probably be able to get away with less code in this
2469 // function in general.
2470 // Use a null PendingBinding, since our binding is not in fact pending.
2471 static const FrameConstructionData rootTableData = FCDATA_DECL(0, nullptr);
2472 already_AddRefed<nsStyleContext> extraRef =
2473 nsRefPtr<nsStyleContext>(styleContext).forget();
2474 FrameConstructionItem item(&rootTableData, aDocElement,
2475 aDocElement->Tag(), kNameSpaceID_None,
2476 nullptr, extraRef, true, nullptr);
2478 nsFrameItems frameItems;
2479 // if the document is a table then just populate it.
2480 contentFrame = ConstructTable(state, item, mDocElementContainingBlock,
2481 styleContext->StyleDisplay(),
2482 frameItems);
2483 newFrame = frameItems.FirstChild();
2484 NS_ASSERTION(frameItems.OnlyChild(), "multiple root element frames");
2485 } else {
2486 MOZ_ASSERT(display->mDisplay == NS_STYLE_DISPLAY_BLOCK,
2487 "Unhandled display type for root element");
2488 contentFrame = NS_NewBlockFormattingContext(mPresShell, styleContext);
2489 nsFrameItems frameItems;
2490 // Use a null PendingBinding, since our binding is not in fact pending.
2491 ConstructBlock(state, display, aDocElement,
2492 state.GetGeometricParent(display,
2493 mDocElementContainingBlock),
2494 mDocElementContainingBlock, styleContext,
2495 &contentFrame, frameItems,
2496 display->IsPositioned(contentFrame) ? contentFrame : nullptr,
2497 nullptr);
2498 newFrame = frameItems.FirstChild();
2499 NS_ASSERTION(frameItems.OnlyChild(), "multiple root element frames");
2500 }
2502 MOZ_ASSERT(newFrame);
2503 MOZ_ASSERT(contentFrame);
2505 NS_ASSERTION(processChildren ? !mRootElementFrame :
2506 mRootElementFrame == contentFrame,
2507 "unexpected mRootElementFrame");
2508 mRootElementFrame = contentFrame;
2510 // Figure out which frame has the main style for the document element,
2511 // assigning it to mRootElementStyleFrame.
2512 // Backgrounds should be propagated from that frame to the viewport.
2513 mRootElementStyleFrame = contentFrame->GetParentStyleContextFrame();
2514 bool isChild = mRootElementStyleFrame &&
2515 mRootElementStyleFrame->GetParent() == contentFrame;
2516 if (!isChild) {
2517 mRootElementStyleFrame = mRootElementFrame;
2518 }
2520 if (processChildren) {
2521 // Still need to process the child content
2522 nsFrameItems childItems;
2524 NS_ASSERTION(!nsLayoutUtils::GetAsBlock(contentFrame) &&
2525 !contentFrame->IsFrameOfType(nsIFrame::eSVG),
2526 "Only XUL frames should reach here");
2527 // Use a null PendingBinding, since our binding is not in fact pending.
2528 ProcessChildren(state, aDocElement, styleContext, contentFrame, true,
2529 childItems, false, nullptr);
2531 // Set the initial child lists
2532 contentFrame->SetInitialChildList(kPrincipalList, childItems);
2533 }
2535 // set the primary frame
2536 aDocElement->SetPrimaryFrame(contentFrame);
2538 SetInitialSingleChild(mDocElementContainingBlock, newFrame);
2540 return newFrame;
2541 }
2544 nsIFrame*
2545 nsCSSFrameConstructor::ConstructRootFrame()
2546 {
2547 AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
2549 nsStyleSet *styleSet = mPresShell->StyleSet();
2551 // Set up our style rule observer.
2552 // XXXbz wouldn't this make more sense as part of presshell init?
2553 {
2554 styleSet->SetBindingManager(mDocument->BindingManager());
2555 }
2557 // --------- BUILD VIEWPORT -----------
2558 nsIFrame* viewportFrame = nullptr;
2559 nsRefPtr<nsStyleContext> viewportPseudoStyle;
2561 viewportPseudoStyle =
2562 styleSet->ResolveAnonymousBoxStyle(nsCSSAnonBoxes::viewport, nullptr);
2564 viewportFrame = NS_NewViewportFrame(mPresShell, viewportPseudoStyle);
2566 // XXXbz do we _have_ to pass a null content pointer to that frame?
2567 // Would it really kill us to pass in the root element or something?
2568 // What would that break?
2569 viewportFrame->Init(nullptr, nullptr, nullptr);
2571 // Bind the viewport frame to the root view
2572 nsView* rootView = mPresShell->GetViewManager()->GetRootView();
2573 viewportFrame->SetView(rootView);
2575 nsContainerFrame::SyncFrameViewProperties(mPresShell->GetPresContext(), viewportFrame,
2576 viewportPseudoStyle, rootView);
2577 nsContainerFrame::SyncWindowProperties(mPresShell->GetPresContext(), viewportFrame,
2578 rootView);
2580 // The viewport is the containing block for 'fixed' elements
2581 mFixedContainingBlock = viewportFrame;
2582 // Make it an absolute container for fixed-pos elements
2583 mFixedContainingBlock->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
2584 mFixedContainingBlock->MarkAsAbsoluteContainingBlock();
2586 return viewportFrame;
2587 }
2589 void
2590 nsCSSFrameConstructor::SetUpDocElementContainingBlock(nsIContent* aDocElement)
2591 {
2592 NS_PRECONDITION(aDocElement, "No element?");
2593 NS_PRECONDITION(!aDocElement->GetParent(), "Not root content?");
2594 NS_PRECONDITION(aDocElement->GetCurrentDoc(), "Not in a document?");
2595 NS_PRECONDITION(aDocElement->GetCurrentDoc()->GetRootElement() ==
2596 aDocElement, "Not the root of the document?");
2598 /*
2599 how the root frame hierarchy should look
2601 Galley presentation, non-XUL, with scrolling:
2603 ViewportFrame [fixed-cb]
2604 nsHTMLScrollFrame
2605 nsCanvasFrame [abs-cb]
2606 root element frame (nsBlockFrame, nsSVGOuterSVGFrame,
2607 nsTableOuterFrame, nsPlaceholderFrame)
2609 Galley presentation, XUL
2611 ViewportFrame [fixed-cb]
2612 nsRootBoxFrame
2613 root element frame (nsDocElementBoxFrame)
2615 Print presentation, non-XUL
2617 ViewportFrame
2618 nsSimplePageSequenceFrame
2619 nsPageFrame [fixed-cb]
2620 nsPageContentFrame
2621 nsCanvasFrame [abs-cb]
2622 root element frame (nsBlockFrame, nsSVGOuterSVGFrame,
2623 nsTableOuterFrame, nsPlaceholderFrame)
2625 Print-preview presentation, non-XUL
2627 ViewportFrame
2628 nsHTMLScrollFrame
2629 nsSimplePageSequenceFrame
2630 nsPageFrame [fixed-cb]
2631 nsPageContentFrame
2632 nsCanvasFrame [abs-cb]
2633 root element frame (nsBlockFrame, nsSVGOuterSVGFrame,
2634 nsTableOuterFrame, nsPlaceholderFrame)
2636 Print/print preview of XUL is not supported.
2637 [fixed-cb]: the default containing block for fixed-pos content
2638 [abs-cb]: the default containing block for abs-pos content
2640 Meaning of nsCSSFrameConstructor fields:
2641 mRootElementFrame is "root element frame". This is the primary frame for
2642 the root element.
2643 mDocElementContainingBlock is the parent of mRootElementFrame
2644 (i.e. nsCanvasFrame or nsRootBoxFrame)
2645 mFixedContainingBlock is the [fixed-cb]
2646 mGfxScrollFrame is the nsHTMLScrollFrame mentioned above, or null if there isn't one
2647 mPageSequenceFrame is the nsSimplePageSequenceFrame, or null if there isn't one
2648 */
2650 // --------- CREATE ROOT FRAME -------
2653 // Create the root frame. The document element's frame is a child of the
2654 // root frame.
2655 //
2656 // The root frame serves two purposes:
2657 // - reserves space for any margins needed for the document element's frame
2658 // - renders the document element's background. This ensures the background covers
2659 // the entire canvas as specified by the CSS2 spec
2661 nsPresContext* presContext = mPresShell->GetPresContext();
2662 bool isPaginated = presContext->IsRootPaginatedDocument();
2663 nsIFrame* viewportFrame = mFixedContainingBlock;
2664 nsStyleContext* viewportPseudoStyle = viewportFrame->StyleContext();
2666 nsIFrame* rootFrame = nullptr;
2667 nsIAtom* rootPseudo;
2669 if (!isPaginated) {
2670 #ifdef MOZ_XUL
2671 if (aDocElement->IsXUL())
2672 {
2673 // pass a temporary stylecontext, the correct one will be set later
2674 rootFrame = NS_NewRootBoxFrame(mPresShell, viewportPseudoStyle);
2675 } else
2676 #endif
2677 {
2678 // pass a temporary stylecontext, the correct one will be set later
2679 rootFrame = NS_NewCanvasFrame(mPresShell, viewportPseudoStyle);
2680 mHasRootAbsPosContainingBlock = true;
2681 }
2683 rootPseudo = nsCSSAnonBoxes::canvas;
2684 mDocElementContainingBlock = rootFrame;
2685 } else {
2686 // Create a page sequence frame
2687 rootFrame = NS_NewSimplePageSequenceFrame(mPresShell, viewportPseudoStyle);
2688 mPageSequenceFrame = rootFrame;
2689 rootPseudo = nsCSSAnonBoxes::pageSequence;
2690 }
2693 // --------- IF SCROLLABLE WRAP IN SCROLLFRAME --------
2695 // If the device supports scrolling (e.g., in galley mode on the screen and
2696 // for print-preview, but not when printing), then create a scroll frame that
2697 // will act as the scrolling mechanism for the viewport.
2698 // XXX Do we even need a viewport when printing to a printer?
2700 bool isHTML = aDocElement->IsHTML();
2701 bool isXUL = false;
2703 if (!isHTML) {
2704 isXUL = aDocElement->IsXUL();
2705 }
2707 // Never create scrollbars for XUL documents
2708 bool isScrollable = isPaginated ? presContext->HasPaginatedScrolling() : !isXUL;
2710 // We no longer need to do overflow propagation here. It's taken care of
2711 // when we construct frames for the element whose overflow might be
2712 // propagated
2713 NS_ASSERTION(!isScrollable || !isXUL,
2714 "XUL documents should never be scrollable - see above");
2716 nsIFrame* newFrame = rootFrame;
2717 nsRefPtr<nsStyleContext> rootPseudoStyle;
2718 // we must create a state because if the scrollbars are GFX it needs the
2719 // state to build the scrollbar frames.
2720 nsFrameConstructorState state(mPresShell, nullptr, nullptr, nullptr);
2722 // Start off with the viewport as parent; we'll adjust it as needed.
2723 nsIFrame* parentFrame = viewportFrame;
2725 nsStyleSet* styleSet = mPresShell->StyleSet();
2726 // If paginated, make sure we don't put scrollbars in
2727 if (!isScrollable) {
2728 rootPseudoStyle = styleSet->ResolveAnonymousBoxStyle(rootPseudo,
2729 viewportPseudoStyle);
2730 } else {
2731 if (rootPseudo == nsCSSAnonBoxes::canvas) {
2732 rootPseudo = nsCSSAnonBoxes::scrolledCanvas;
2733 } else {
2734 NS_ASSERTION(rootPseudo == nsCSSAnonBoxes::pageSequence,
2735 "Unknown root pseudo");
2736 rootPseudo = nsCSSAnonBoxes::scrolledPageSequence;
2737 }
2739 // Build the frame. We give it the content we are wrapping which is the
2740 // document element, the root frame, the parent view port frame, and we
2741 // should get back the new frame and the scrollable view if one was
2742 // created.
2744 // resolve a context for the scrollframe
2745 nsRefPtr<nsStyleContext> styleContext;
2746 styleContext = styleSet->ResolveAnonymousBoxStyle(nsCSSAnonBoxes::viewportScroll,
2747 viewportPseudoStyle);
2749 // Note that the viewport scrollframe is always built with
2750 // overflow:auto style. This forces the scroll frame to create
2751 // anonymous content for both scrollbars. This is necessary even
2752 // if the HTML or BODY elements are overriding the viewport
2753 // scroll style to 'hidden' --- dynamic style changes might put
2754 // scrollbars back on the viewport and we don't want to have to
2755 // reframe the viewport to create the scrollbar content.
2756 newFrame = nullptr;
2757 rootPseudoStyle = BeginBuildingScrollFrame( state,
2758 aDocElement,
2759 styleContext,
2760 viewportFrame,
2761 rootPseudo,
2762 true,
2763 newFrame);
2764 parentFrame = newFrame;
2765 mGfxScrollFrame = newFrame;
2766 }
2768 rootFrame->SetStyleContextWithoutNotification(rootPseudoStyle);
2769 rootFrame->Init(aDocElement, parentFrame, nullptr);
2771 if (isScrollable) {
2772 FinishBuildingScrollFrame(parentFrame, rootFrame);
2773 }
2775 if (isPaginated) { // paginated
2776 // Create the first page
2777 // Set the initial child lists
2778 nsIFrame* canvasFrame;
2779 nsIFrame* pageFrame =
2780 ConstructPageFrame(mPresShell, presContext, rootFrame, nullptr,
2781 canvasFrame);
2782 SetInitialSingleChild(rootFrame, pageFrame);
2784 // The eventual parent of the document element frame.
2785 // XXX should this be set for every new page (in ConstructPageFrame)?
2786 mDocElementContainingBlock = canvasFrame;
2787 mHasRootAbsPosContainingBlock = true;
2788 }
2790 if (viewportFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) {
2791 SetInitialSingleChild(viewportFrame, newFrame);
2792 } else {
2793 nsFrameList newFrameList(newFrame, newFrame);
2794 viewportFrame->AppendFrames(kPrincipalList, newFrameList);
2795 }
2796 }
2798 nsIFrame*
2799 nsCSSFrameConstructor::ConstructPageFrame(nsIPresShell* aPresShell,
2800 nsPresContext* aPresContext,
2801 nsIFrame* aParentFrame,
2802 nsIFrame* aPrevPageFrame,
2803 nsIFrame*& aCanvasFrame)
2804 {
2805 nsStyleContext* parentStyleContext = aParentFrame->StyleContext();
2806 nsStyleSet *styleSet = aPresShell->StyleSet();
2808 nsRefPtr<nsStyleContext> pagePseudoStyle;
2809 pagePseudoStyle = styleSet->ResolveAnonymousBoxStyle(nsCSSAnonBoxes::page,
2810 parentStyleContext);
2812 nsIFrame* pageFrame = NS_NewPageFrame(aPresShell, pagePseudoStyle);
2814 // Initialize the page frame and force it to have a view. This makes printing of
2815 // the pages easier and faster.
2816 pageFrame->Init(nullptr, aParentFrame, aPrevPageFrame);
2818 nsRefPtr<nsStyleContext> pageContentPseudoStyle;
2819 pageContentPseudoStyle =
2820 styleSet->ResolveAnonymousBoxStyle(nsCSSAnonBoxes::pageContent,
2821 pagePseudoStyle);
2823 nsIFrame* pageContentFrame = NS_NewPageContentFrame(aPresShell, pageContentPseudoStyle);
2825 // Initialize the page content frame and force it to have a view. Also make it the
2826 // containing block for fixed elements which are repeated on every page.
2827 nsIFrame* prevPageContentFrame = nullptr;
2828 if (aPrevPageFrame) {
2829 prevPageContentFrame = aPrevPageFrame->GetFirstPrincipalChild();
2830 NS_ASSERTION(prevPageContentFrame, "missing page content frame");
2831 }
2832 pageContentFrame->Init(nullptr, pageFrame, prevPageContentFrame);
2833 SetInitialSingleChild(pageFrame, pageContentFrame);
2834 mFixedContainingBlock = pageContentFrame;
2835 // Make it an absolute container for fixed-pos elements
2836 mFixedContainingBlock->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
2837 mFixedContainingBlock->MarkAsAbsoluteContainingBlock();
2839 nsRefPtr<nsStyleContext> canvasPseudoStyle;
2840 canvasPseudoStyle = styleSet->ResolveAnonymousBoxStyle(nsCSSAnonBoxes::canvas,
2841 pageContentPseudoStyle);
2843 aCanvasFrame = NS_NewCanvasFrame(aPresShell, canvasPseudoStyle);
2845 nsIFrame* prevCanvasFrame = nullptr;
2846 if (prevPageContentFrame) {
2847 prevCanvasFrame = prevPageContentFrame->GetFirstPrincipalChild();
2848 NS_ASSERTION(prevCanvasFrame, "missing canvas frame");
2849 }
2850 aCanvasFrame->Init(nullptr, pageContentFrame, prevCanvasFrame);
2851 SetInitialSingleChild(pageContentFrame, aCanvasFrame);
2852 return pageFrame;
2853 }
2855 /* static */
2856 nsIFrame*
2857 nsCSSFrameConstructor::CreatePlaceholderFrameFor(nsIPresShell* aPresShell,
2858 nsIContent* aContent,
2859 nsIFrame* aFrame,
2860 nsStyleContext* aStyleContext,
2861 nsIFrame* aParentFrame,
2862 nsIFrame* aPrevInFlow,
2863 nsFrameState aTypeBit)
2864 {
2865 nsRefPtr<nsStyleContext> placeholderStyle = aPresShell->StyleSet()->
2866 ResolveStyleForNonElement(aStyleContext->GetParent());
2868 // The placeholder frame gets a pseudo style context
2869 nsPlaceholderFrame* placeholderFrame =
2870 (nsPlaceholderFrame*)NS_NewPlaceholderFrame(aPresShell, placeholderStyle,
2871 aTypeBit);
2873 placeholderFrame->Init(aContent, aParentFrame, aPrevInFlow);
2875 // The placeholder frame has a pointer back to the out-of-flow frame
2876 placeholderFrame->SetOutOfFlowFrame(aFrame);
2878 aFrame->AddStateBits(NS_FRAME_OUT_OF_FLOW);
2880 // Add mapping from absolutely positioned frame to its placeholder frame
2881 aPresShell->FrameManager()->RegisterPlaceholderFrame(placeholderFrame);
2883 return placeholderFrame;
2884 }
2886 // Clears any lazy bits set in the range [aStartContent, aEndContent). If
2887 // aEndContent is null, that means to clear bits in all siblings starting with
2888 // aStartContent. aStartContent must not be null unless aEndContent is also
2889 // null. We do this so that when new children are inserted under elements whose
2890 // frame is a leaf the new children don't cause us to try to construct frames
2891 // for the existing children again.
2892 static inline void
2893 ClearLazyBits(nsIContent* aStartContent, nsIContent* aEndContent)
2894 {
2895 NS_PRECONDITION(aStartContent || !aEndContent,
2896 "Must have start child if we have an end child");
2897 for (nsIContent* cur = aStartContent; cur != aEndContent;
2898 cur = cur->GetNextSibling()) {
2899 cur->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
2900 }
2901 }
2903 nsIFrame*
2904 nsCSSFrameConstructor::ConstructSelectFrame(nsFrameConstructorState& aState,
2905 FrameConstructionItem& aItem,
2906 nsIFrame* aParentFrame,
2907 const nsStyleDisplay* aStyleDisplay,
2908 nsFrameItems& aFrameItems)
2909 {
2910 nsIContent* const content = aItem.mContent;
2911 nsStyleContext* const styleContext = aItem.mStyleContext;
2913 // Construct a frame-based listbox or combobox
2914 dom::HTMLSelectElement* sel = dom::HTMLSelectElement::FromContent(content);
2915 MOZ_ASSERT(sel);
2916 if (sel->IsCombobox()) {
2917 // Construct a frame-based combo box.
2918 // The frame-based combo box is built out of three parts. A display area, a button and
2919 // a dropdown list. The display area and button are created through anonymous content.
2920 // The drop-down list's frame is created explicitly. The combobox frame shares its content
2921 // with the drop-down list.
2922 nsFrameState flags = NS_BLOCK_FLOAT_MGR;
2923 nsIFrame* comboboxFrame = NS_NewComboboxControlFrame(mPresShell, styleContext, flags);
2925 // Save the history state so we don't restore during construction
2926 // since the complete tree is required before we restore.
2927 nsILayoutHistoryState *historyState = aState.mFrameState;
2928 aState.mFrameState = nullptr;
2929 // Initialize the combobox frame
2930 InitAndRestoreFrame(aState, content,
2931 aState.GetGeometricParent(aStyleDisplay, aParentFrame),
2932 comboboxFrame);
2934 aState.AddChild(comboboxFrame, aFrameItems, content, styleContext,
2935 aParentFrame);
2937 nsIComboboxControlFrame* comboBox = do_QueryFrame(comboboxFrame);
2938 NS_ASSERTION(comboBox, "NS_NewComboboxControlFrame returned frame that "
2939 "doesn't implement nsIComboboxControlFrame");
2941 // Resolve pseudo element style for the dropdown list
2942 nsRefPtr<nsStyleContext> listStyle;
2943 listStyle = mPresShell->StyleSet()->
2944 ResolveAnonymousBoxStyle(nsCSSAnonBoxes::dropDownList, styleContext);
2946 // Create a listbox
2947 nsIFrame* listFrame = NS_NewListControlFrame(mPresShell, listStyle);
2949 // Notify the listbox that it is being used as a dropdown list.
2950 nsIListControlFrame * listControlFrame = do_QueryFrame(listFrame);
2951 if (listControlFrame) {
2952 listControlFrame->SetComboboxFrame(comboboxFrame);
2953 }
2954 // Notify combobox that it should use the listbox as it's popup
2955 comboBox->SetDropDown(listFrame);
2957 NS_ASSERTION(!listFrame->IsPositioned(),
2958 "Ended up with positioned dropdown list somehow.");
2959 NS_ASSERTION(!listFrame->IsFloating(),
2960 "Ended up with floating dropdown list somehow.");
2962 // Initialize the scroll frame positioned. Note that it is NOT
2963 // initialized as absolutely positioned.
2964 nsIFrame* scrolledFrame = NS_NewSelectsAreaFrame(mPresShell, styleContext, flags);
2966 InitializeSelectFrame(aState, listFrame, scrolledFrame, content,
2967 comboboxFrame, listStyle, true,
2968 aItem.mPendingBinding, aFrameItems);
2970 NS_ASSERTION(listFrame->GetView(), "ListFrame's view is nullptr");
2972 // Create display and button frames from the combobox's anonymous content.
2973 // The anonymous content is appended to existing anonymous content for this
2974 // element (the scrollbars).
2976 nsFrameItems childItems;
2977 CreateAnonymousFrames(aState, content, comboboxFrame,
2978 aItem.mPendingBinding, childItems);
2980 comboboxFrame->SetInitialChildList(kPrincipalList, childItems);
2982 // Initialize the additional popup child list which contains the
2983 // dropdown list frame.
2984 nsFrameItems popupItems;
2985 popupItems.AddChild(listFrame);
2986 comboboxFrame->SetInitialChildList(nsIFrame::kSelectPopupList,
2987 popupItems);
2989 aState.mFrameState = historyState;
2990 if (aState.mFrameState) {
2991 // Restore frame state for the entire subtree of |comboboxFrame|.
2992 RestoreFrameState(comboboxFrame, aState.mFrameState);
2993 }
2994 return comboboxFrame;
2995 }
2997 // Listbox, not combobox
2998 nsIFrame* listFrame = NS_NewListControlFrame(mPresShell, styleContext);
3000 nsIFrame* scrolledFrame = NS_NewSelectsAreaFrame(
3001 mPresShell, styleContext, NS_BLOCK_FLOAT_MGR);
3003 // ******* this code stolen from Initialze ScrollFrame ********
3004 // please adjust this code to use BuildScrollFrame.
3006 InitializeSelectFrame(aState, listFrame, scrolledFrame, content,
3007 aParentFrame, styleContext, false,
3008 aItem.mPendingBinding, aFrameItems);
3010 return listFrame;
3011 }
3013 /**
3014 * Used to be InitializeScrollFrame but now it's only used for the select tag
3015 * But the select tag should really be fixed to use GFX scrollbars that can
3016 * be create with BuildScrollFrame.
3017 */
3018 nsresult
3019 nsCSSFrameConstructor::InitializeSelectFrame(nsFrameConstructorState& aState,
3020 nsIFrame* scrollFrame,
3021 nsIFrame* scrolledFrame,
3022 nsIContent* aContent,
3023 nsIFrame* aParentFrame,
3024 nsStyleContext* aStyleContext,
3025 bool aBuildCombobox,
3026 PendingBinding* aPendingBinding,
3027 nsFrameItems& aFrameItems)
3028 {
3029 const nsStyleDisplay* display = aStyleContext->StyleDisplay();
3031 // Initialize it
3032 nsIFrame* geometricParent = aState.GetGeometricParent(display, aParentFrame);
3034 // We don't call InitAndRestoreFrame for scrollFrame because we can only
3035 // restore the frame state after its parts have been created (in particular,
3036 // the scrollable view). So we have to split Init and Restore.
3038 // Initialize the frame
3039 scrollFrame->Init(aContent, geometricParent, nullptr);
3041 if (!aBuildCombobox) {
3042 aState.AddChild(scrollFrame, aFrameItems, aContent,
3043 aStyleContext, aParentFrame);
3044 }
3046 if (aBuildCombobox) {
3047 nsContainerFrame::CreateViewForFrame(scrollFrame, true);
3048 }
3050 BuildScrollFrame(aState, aContent, aStyleContext, scrolledFrame,
3051 geometricParent, scrollFrame);
3053 if (aState.mFrameState) {
3054 // Restore frame state for the scroll frame
3055 RestoreFrameStateFor(scrollFrame, aState.mFrameState);
3056 }
3058 // Process children
3059 nsFrameItems childItems;
3061 ProcessChildren(aState, aContent, aStyleContext, scrolledFrame, false,
3062 childItems, false, aPendingBinding);
3064 // Set the scrolled frame's initial child lists
3065 scrolledFrame->SetInitialChildList(kPrincipalList, childItems);
3066 return NS_OK;
3067 }
3069 nsIFrame*
3070 nsCSSFrameConstructor::ConstructFieldSetFrame(nsFrameConstructorState& aState,
3071 FrameConstructionItem& aItem,
3072 nsIFrame* aParentFrame,
3073 const nsStyleDisplay* aStyleDisplay,
3074 nsFrameItems& aFrameItems)
3075 {
3076 nsIContent* const content = aItem.mContent;
3077 nsStyleContext* const styleContext = aItem.mStyleContext;
3079 nsIFrame* fieldsetFrame = NS_NewFieldSetFrame(mPresShell, styleContext);
3081 // Initialize it
3082 InitAndRestoreFrame(aState, content,
3083 aState.GetGeometricParent(aStyleDisplay, aParentFrame),
3084 fieldsetFrame);
3086 // Resolve style and initialize the frame
3087 nsRefPtr<nsStyleContext> fieldsetContentStyle;
3088 fieldsetContentStyle = mPresShell->StyleSet()->
3089 ResolveAnonymousBoxStyle(nsCSSAnonBoxes::fieldsetContent, styleContext);
3091 const nsStyleDisplay* fieldsetContentDisplay = fieldsetContentStyle->StyleDisplay();
3092 bool isScrollable = fieldsetContentDisplay->IsScrollableOverflow();
3093 nsIFrame* scrollFrame = nullptr;
3094 if (isScrollable) {
3095 fieldsetContentStyle =
3096 BeginBuildingScrollFrame(aState, content, fieldsetContentStyle,
3097 fieldsetFrame, nsCSSAnonBoxes::scrolledContent,
3098 false, scrollFrame);
3099 }
3101 nsIFrame* blockFrame = NS_NewBlockFrame(mPresShell, fieldsetContentStyle,
3102 NS_BLOCK_FLOAT_MGR |
3103 NS_BLOCK_MARGIN_ROOT);
3104 InitAndRestoreFrame(aState, content,
3105 scrollFrame ? scrollFrame : fieldsetFrame, blockFrame);
3107 aState.AddChild(fieldsetFrame, aFrameItems, content, styleContext, aParentFrame);
3109 // Process children
3110 nsFrameConstructorSaveState absoluteSaveState;
3111 nsFrameItems childItems;
3113 blockFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
3114 if (fieldsetFrame->IsPositioned()) {
3115 aState.PushAbsoluteContainingBlock(blockFrame, fieldsetFrame, absoluteSaveState);
3116 }
3118 ProcessChildren(aState, content, styleContext, blockFrame, true,
3119 childItems, true, aItem.mPendingBinding);
3121 nsFrameItems fieldsetKids;
3122 fieldsetKids.AddChild(scrollFrame ? scrollFrame : blockFrame);
3124 for (nsFrameList::Enumerator e(childItems); !e.AtEnd(); e.Next()) {
3125 nsIFrame* child = e.get();
3126 if (child->GetContentInsertionFrame()->GetType() == nsGkAtoms::legendFrame) {
3127 // We want the legend to be the first frame in the fieldset child list.
3128 // That way the EventStateManager will do the right thing when tabbing
3129 // from a selection point within the legend (bug 236071), which is
3130 // used for implementing legend access keys (bug 81481).
3131 // GetAdjustedParentFrame() below depends on this frame order.
3132 childItems.RemoveFrame(child);
3133 // Make sure to reparent the legend so it has the fieldset as the parent.
3134 fieldsetKids.InsertFrame(fieldsetFrame, nullptr, child);
3135 if (scrollFrame) {
3136 StickyScrollContainer::NotifyReparentedFrameAcrossScrollFrameBoundary(
3137 child, blockFrame);
3138 }
3139 break;
3140 }
3141 }
3143 if (isScrollable) {
3144 FinishBuildingScrollFrame(scrollFrame, blockFrame);
3145 }
3147 // Set the inner frame's initial child lists
3148 blockFrame->SetInitialChildList(kPrincipalList, childItems);
3150 // Set the outer frame's initial child list
3151 fieldsetFrame->SetInitialChildList(kPrincipalList, fieldsetKids);
3153 fieldsetFrame->AddStateBits(NS_FRAME_MAY_HAVE_GENERATED_CONTENT);
3155 // Our new frame returned is the outer frame, which is the fieldset frame.
3156 return fieldsetFrame;
3157 }
3159 static nsIFrame*
3160 FindAncestorWithGeneratedContentPseudo(nsIFrame* aFrame)
3161 {
3162 for (nsIFrame* f = aFrame->GetParent(); f; f = f->GetParent()) {
3163 NS_ASSERTION(f->IsGeneratedContentFrame(),
3164 "should not have exited generated content");
3165 nsIAtom* pseudo = f->StyleContext()->GetPseudo();
3166 if (pseudo == nsCSSPseudoElements::before ||
3167 pseudo == nsCSSPseudoElements::after)
3168 return f;
3169 }
3170 return nullptr;
3171 }
3173 #define SIMPLE_FCDATA(_func) FCDATA_DECL(0, _func)
3174 #define FULL_CTOR_FCDATA(_flags, _func) \
3175 { _flags | FCDATA_FUNC_IS_FULL_CTOR, { nullptr }, _func, nullptr }
3177 /* static */
3178 const nsCSSFrameConstructor::FrameConstructionData*
3179 nsCSSFrameConstructor::FindTextData(nsIFrame* aParentFrame)
3180 {
3181 if (aParentFrame && IsFrameForSVG(aParentFrame)) {
3182 nsIFrame *ancestorFrame =
3183 nsSVGUtils::GetFirstNonAAncestorFrame(aParentFrame);
3184 if (ancestorFrame) {
3185 static const FrameConstructionData sSVGTextData =
3186 FCDATA_DECL(FCDATA_IS_LINE_PARTICIPANT | FCDATA_IS_SVG_TEXT,
3187 NS_NewTextFrame);
3188 if (ancestorFrame->IsSVGText()) {
3189 return &sSVGTextData;
3190 }
3191 }
3192 return nullptr;
3193 }
3195 static const FrameConstructionData sTextData =
3196 FCDATA_DECL(FCDATA_IS_LINE_PARTICIPANT, NS_NewTextFrame);
3197 return &sTextData;
3198 }
3200 void
3201 nsCSSFrameConstructor::ConstructTextFrame(const FrameConstructionData* aData,
3202 nsFrameConstructorState& aState,
3203 nsIContent* aContent,
3204 nsIFrame* aParentFrame,
3205 nsStyleContext* aStyleContext,
3206 nsFrameItems& aFrameItems)
3207 {
3208 NS_PRECONDITION(aData, "Must have frame construction data");
3210 nsIFrame* newFrame = (*aData->mFunc.mCreationFunc)(mPresShell, aStyleContext);
3212 InitAndRestoreFrame(aState, aContent, aParentFrame, newFrame);
3214 // We never need to create a view for a text frame.
3216 if (newFrame->IsGeneratedContentFrame()) {
3217 nsAutoPtr<nsGenConInitializer> initializer;
3218 initializer =
3219 static_cast<nsGenConInitializer*>(
3220 aContent->UnsetProperty(nsGkAtoms::genConInitializerProperty));
3221 if (initializer) {
3222 if (initializer->mNode->InitTextFrame(initializer->mList,
3223 FindAncestorWithGeneratedContentPseudo(newFrame), newFrame)) {
3224 (this->*(initializer->mDirtyAll))();
3225 }
3226 initializer->mNode.forget();
3227 }
3228 }
3230 // Add the newly constructed frame to the flow
3231 aFrameItems.AddChild(newFrame);
3233 if (!aState.mCreatingExtraFrames)
3234 aContent->SetPrimaryFrame(newFrame);
3235 }
3237 /* static */
3238 const nsCSSFrameConstructor::FrameConstructionData*
3239 nsCSSFrameConstructor::FindDataByInt(int32_t aInt,
3240 Element* aElement,
3241 nsStyleContext* aStyleContext,
3242 const FrameConstructionDataByInt* aDataPtr,
3243 uint32_t aDataLength)
3244 {
3245 for (const FrameConstructionDataByInt *curData = aDataPtr,
3246 *endData = aDataPtr + aDataLength;
3247 curData != endData;
3248 ++curData) {
3249 if (curData->mInt == aInt) {
3250 const FrameConstructionData* data = &curData->mData;
3251 if (data->mBits & FCDATA_FUNC_IS_DATA_GETTER) {
3252 return data->mFunc.mDataGetter(aElement, aStyleContext);
3253 }
3255 return data;
3256 }
3257 }
3259 return nullptr;
3260 }
3262 /* static */
3263 const nsCSSFrameConstructor::FrameConstructionData*
3264 nsCSSFrameConstructor::FindDataByTag(nsIAtom* aTag,
3265 Element* aElement,
3266 nsStyleContext* aStyleContext,
3267 const FrameConstructionDataByTag* aDataPtr,
3268 uint32_t aDataLength)
3269 {
3270 for (const FrameConstructionDataByTag *curData = aDataPtr,
3271 *endData = aDataPtr + aDataLength;
3272 curData != endData;
3273 ++curData) {
3274 if (*curData->mTag == aTag) {
3275 const FrameConstructionData* data = &curData->mData;
3276 if (data->mBits & FCDATA_FUNC_IS_DATA_GETTER) {
3277 return data->mFunc.mDataGetter(aElement, aStyleContext);
3278 }
3280 return data;
3281 }
3282 }
3284 return nullptr;
3285 }
3287 #define SUPPRESS_FCDATA() FCDATA_DECL(FCDATA_SUPPRESS_FRAME, nullptr)
3288 #define SIMPLE_INT_CREATE(_int, _func) { _int, SIMPLE_FCDATA(_func) }
3289 #define SIMPLE_INT_CHAIN(_int, _func) \
3290 { _int, FCDATA_DECL(FCDATA_FUNC_IS_DATA_GETTER, _func) }
3291 #define COMPLEX_INT_CREATE(_int, _func) \
3292 { _int, FULL_CTOR_FCDATA(0, _func) }
3294 #define SIMPLE_TAG_CREATE(_tag, _func) \
3295 { &nsGkAtoms::_tag, SIMPLE_FCDATA(_func) }
3296 #define SIMPLE_TAG_CHAIN(_tag, _func) \
3297 { &nsGkAtoms::_tag, FCDATA_DECL(FCDATA_FUNC_IS_DATA_GETTER, _func) }
3298 #define COMPLEX_TAG_CREATE(_tag, _func) \
3299 { &nsGkAtoms::_tag, FULL_CTOR_FCDATA(0, _func) }
3301 static bool
3302 IsFrameForFieldSet(nsIFrame* aFrame, nsIAtom* aFrameType)
3303 {
3304 nsIAtom* pseudo = aFrame->StyleContext()->GetPseudo();
3305 if (pseudo == nsCSSAnonBoxes::fieldsetContent ||
3306 pseudo == nsCSSAnonBoxes::scrolledContent) {
3307 return IsFrameForFieldSet(aFrame->GetParent(), aFrame->GetParent()->GetType());
3308 }
3309 return aFrameType == nsGkAtoms::fieldSetFrame;
3310 }
3312 /* static */
3313 const nsCSSFrameConstructor::FrameConstructionData*
3314 nsCSSFrameConstructor::FindHTMLData(Element* aElement,
3315 nsIAtom* aTag,
3316 int32_t aNameSpaceID,
3317 nsIFrame* aParentFrame,
3318 nsStyleContext* aStyleContext)
3319 {
3320 // Ignore the tag if it's not HTML content and if it doesn't extend (via XBL)
3321 // a valid HTML namespace. This check must match the one in
3322 // ShouldHaveFirstLineStyle.
3323 if (aNameSpaceID != kNameSpaceID_XHTML) {
3324 return nullptr;
3325 }
3327 NS_ASSERTION(!aParentFrame ||
3328 aParentFrame->StyleContext()->GetPseudo() !=
3329 nsCSSAnonBoxes::fieldsetContent ||
3330 aParentFrame->GetParent()->GetType() == nsGkAtoms::fieldSetFrame,
3331 "Unexpected parent for fieldset content anon box");
3332 if (aTag == nsGkAtoms::legend &&
3333 (!aParentFrame ||
3334 !IsFrameForFieldSet(aParentFrame, aParentFrame->GetType()) ||
3335 !aElement->GetParent() ||
3336 !aElement->GetParent()->IsHTML(nsGkAtoms::fieldset) ||
3337 aStyleContext->StyleDisplay()->IsFloatingStyle() ||
3338 aStyleContext->StyleDisplay()->IsAbsolutelyPositionedStyle())) {
3339 // <legend> is only special inside fieldset, check both the frame tree
3340 // parent and content tree parent due to XBL issues. For floated or
3341 // absolutely positioned legends we want to construct by display type and
3342 // not do special legend stuff.
3343 // XXXbz it would be nice if we could just decide this based on the parent
3344 // tag, and hence just use a SIMPLE_TAG_CHAIN for legend below, but the
3345 // fact that with XBL we could end up with this legend element in some
3346 // totally weird insertion point makes that chancy, I think.
3347 return nullptr;
3348 }
3350 static const FrameConstructionDataByTag sHTMLData[] = {
3351 SIMPLE_TAG_CHAIN(img, nsCSSFrameConstructor::FindImgData),
3352 SIMPLE_TAG_CHAIN(mozgeneratedcontentimage,
3353 nsCSSFrameConstructor::FindImgData),
3354 { &nsGkAtoms::br,
3355 FCDATA_DECL(FCDATA_IS_LINE_PARTICIPANT | FCDATA_IS_LINE_BREAK,
3356 NS_NewBRFrame) },
3357 SIMPLE_TAG_CREATE(wbr, NS_NewWBRFrame),
3358 SIMPLE_TAG_CHAIN(input, nsCSSFrameConstructor::FindInputData),
3359 SIMPLE_TAG_CREATE(textarea, NS_NewTextControlFrame),
3360 COMPLEX_TAG_CREATE(select, &nsCSSFrameConstructor::ConstructSelectFrame),
3361 SIMPLE_TAG_CHAIN(object, nsCSSFrameConstructor::FindObjectData),
3362 SIMPLE_TAG_CHAIN(applet, nsCSSFrameConstructor::FindObjectData),
3363 SIMPLE_TAG_CHAIN(embed, nsCSSFrameConstructor::FindObjectData),
3364 COMPLEX_TAG_CREATE(fieldset,
3365 &nsCSSFrameConstructor::ConstructFieldSetFrame),
3366 { &nsGkAtoms::legend,
3367 FCDATA_DECL(FCDATA_ALLOW_BLOCK_STYLES | FCDATA_MAY_NEED_SCROLLFRAME,
3368 NS_NewLegendFrame) },
3369 SIMPLE_TAG_CREATE(frameset, NS_NewHTMLFramesetFrame),
3370 SIMPLE_TAG_CREATE(iframe, NS_NewSubDocumentFrame),
3371 { &nsGkAtoms::button,
3372 FCDATA_WITH_WRAPPING_BLOCK(FCDATA_ALLOW_BLOCK_STYLES,
3373 NS_NewHTMLButtonControlFrame,
3374 nsCSSAnonBoxes::buttonContent) },
3375 SIMPLE_TAG_CHAIN(canvas, nsCSSFrameConstructor::FindCanvasData),
3376 SIMPLE_TAG_CREATE(video, NS_NewHTMLVideoFrame),
3377 SIMPLE_TAG_CREATE(audio, NS_NewHTMLVideoFrame),
3378 SIMPLE_TAG_CREATE(progress, NS_NewProgressFrame),
3379 SIMPLE_TAG_CREATE(meter, NS_NewMeterFrame)
3380 };
3382 return FindDataByTag(aTag, aElement, aStyleContext, sHTMLData,
3383 ArrayLength(sHTMLData));
3384 }
3386 /* static */
3387 const nsCSSFrameConstructor::FrameConstructionData*
3388 nsCSSFrameConstructor::FindImgData(Element* aElement,
3389 nsStyleContext* aStyleContext)
3390 {
3391 if (!nsImageFrame::ShouldCreateImageFrameFor(aElement, aStyleContext)) {
3392 return nullptr;
3393 }
3395 static const FrameConstructionData sImgData = SIMPLE_FCDATA(NS_NewImageFrame);
3396 return &sImgData;
3397 }
3399 /* static */
3400 const nsCSSFrameConstructor::FrameConstructionData*
3401 nsCSSFrameConstructor::FindImgControlData(Element* aElement,
3402 nsStyleContext* aStyleContext)
3403 {
3404 if (!nsImageFrame::ShouldCreateImageFrameFor(aElement, aStyleContext)) {
3405 return nullptr;
3406 }
3408 static const FrameConstructionData sImgControlData =
3409 SIMPLE_FCDATA(NS_NewImageControlFrame);
3410 return &sImgControlData;
3411 }
3413 /* static */
3414 const nsCSSFrameConstructor::FrameConstructionData*
3415 nsCSSFrameConstructor::FindInputData(Element* aElement,
3416 nsStyleContext* aStyleContext)
3417 {
3418 static const FrameConstructionDataByInt sInputData[] = {
3419 SIMPLE_INT_CREATE(NS_FORM_INPUT_CHECKBOX, NS_NewGfxCheckboxControlFrame),
3420 SIMPLE_INT_CREATE(NS_FORM_INPUT_RADIO, NS_NewGfxRadioControlFrame),
3421 SIMPLE_INT_CREATE(NS_FORM_INPUT_FILE, NS_NewFileControlFrame),
3422 SIMPLE_INT_CHAIN(NS_FORM_INPUT_IMAGE,
3423 nsCSSFrameConstructor::FindImgControlData),
3424 SIMPLE_INT_CREATE(NS_FORM_INPUT_EMAIL, NS_NewTextControlFrame),
3425 SIMPLE_INT_CREATE(NS_FORM_INPUT_SEARCH, NS_NewTextControlFrame),
3426 SIMPLE_INT_CREATE(NS_FORM_INPUT_TEXT, NS_NewTextControlFrame),
3427 SIMPLE_INT_CREATE(NS_FORM_INPUT_TEL, NS_NewTextControlFrame),
3428 SIMPLE_INT_CREATE(NS_FORM_INPUT_URL, NS_NewTextControlFrame),
3429 SIMPLE_INT_CREATE(NS_FORM_INPUT_RANGE, NS_NewRangeFrame),
3430 SIMPLE_INT_CREATE(NS_FORM_INPUT_PASSWORD, NS_NewTextControlFrame),
3431 { NS_FORM_INPUT_COLOR,
3432 FCDATA_WITH_WRAPPING_BLOCK(0, NS_NewColorControlFrame,
3433 nsCSSAnonBoxes::buttonContent) },
3434 // TODO: this is temporary until a frame is written: bug 635240.
3435 SIMPLE_INT_CREATE(NS_FORM_INPUT_NUMBER, NS_NewNumberControlFrame),
3436 // TODO: this is temporary until a frame is written: bug 773205.
3437 SIMPLE_INT_CREATE(NS_FORM_INPUT_DATE, NS_NewTextControlFrame),
3438 // TODO: this is temporary until a frame is written: bug 773205
3439 SIMPLE_INT_CREATE(NS_FORM_INPUT_TIME, NS_NewTextControlFrame),
3440 { NS_FORM_INPUT_SUBMIT,
3441 FCDATA_WITH_WRAPPING_BLOCK(0, NS_NewGfxButtonControlFrame,
3442 nsCSSAnonBoxes::buttonContent) },
3443 { NS_FORM_INPUT_RESET,
3444 FCDATA_WITH_WRAPPING_BLOCK(0, NS_NewGfxButtonControlFrame,
3445 nsCSSAnonBoxes::buttonContent) },
3446 { NS_FORM_INPUT_BUTTON,
3447 FCDATA_WITH_WRAPPING_BLOCK(0, NS_NewGfxButtonControlFrame,
3448 nsCSSAnonBoxes::buttonContent) }
3449 // Keeping hidden inputs out of here on purpose for so they get frames by
3450 // display (in practice, none).
3451 };
3453 nsCOMPtr<nsIFormControl> control = do_QueryInterface(aElement);
3454 NS_ASSERTION(control, "input doesn't implement nsIFormControl?");
3456 return FindDataByInt(control->GetType(), aElement, aStyleContext,
3457 sInputData, ArrayLength(sInputData));
3458 }
3460 /* static */
3461 const nsCSSFrameConstructor::FrameConstructionData*
3462 nsCSSFrameConstructor::FindObjectData(Element* aElement,
3463 nsStyleContext* aStyleContext)
3464 {
3465 // GetDisplayedType isn't necessarily nsIObjectLoadingContent::TYPE_NULL for
3466 // cases when the object is broken/suppressed/etc (e.g. a broken image), but
3467 // we want to treat those cases as TYPE_NULL
3468 uint32_t type;
3469 if (aElement->State().HasAtLeastOneOfStates(NS_EVENT_STATE_BROKEN |
3470 NS_EVENT_STATE_USERDISABLED |
3471 NS_EVENT_STATE_SUPPRESSED)) {
3472 type = nsIObjectLoadingContent::TYPE_NULL;
3473 } else {
3474 nsCOMPtr<nsIObjectLoadingContent> objContent(do_QueryInterface(aElement));
3475 NS_ASSERTION(objContent,
3476 "applet, embed and object must implement "
3477 "nsIObjectLoadingContent!");
3479 objContent->GetDisplayedType(&type);
3480 }
3482 static const FrameConstructionDataByInt sObjectData[] = {
3483 SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_LOADING,
3484 NS_NewEmptyFrame),
3485 SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_PLUGIN,
3486 NS_NewObjectFrame),
3487 SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_IMAGE,
3488 NS_NewImageFrame),
3489 SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_DOCUMENT,
3490 NS_NewSubDocumentFrame)
3491 // Nothing for TYPE_NULL so we'll construct frames by display there
3492 };
3494 return FindDataByInt((int32_t)type, aElement, aStyleContext,
3495 sObjectData, ArrayLength(sObjectData));
3496 }
3498 /* static */
3499 const nsCSSFrameConstructor::FrameConstructionData*
3500 nsCSSFrameConstructor::FindCanvasData(Element* aElement,
3501 nsStyleContext* aStyleContext)
3502 {
3503 // We want to check whether script is enabled on the document that
3504 // could be painting to the canvas. That's the owner document of
3505 // the canvas, except when the owner document is a static document,
3506 // in which case it's the original document it was cloned from.
3507 nsIDocument* doc = aElement->OwnerDoc();
3508 if (doc->IsStaticDocument()) {
3509 doc = doc->GetOriginalDocument();
3510 }
3511 if (!doc->IsScriptEnabled()) {
3512 return nullptr;
3513 }
3515 static const FrameConstructionData sCanvasData =
3516 FCDATA_WITH_WRAPPING_BLOCK(0, NS_NewHTMLCanvasFrame,
3517 nsCSSAnonBoxes::htmlCanvasContent);
3518 return &sCanvasData;
3519 }
3521 void
3522 nsCSSFrameConstructor::ConstructFrameFromItemInternal(FrameConstructionItem& aItem,
3523 nsFrameConstructorState& aState,
3524 nsIFrame* aParentFrame,
3525 nsFrameItems& aFrameItems)
3526 {
3527 const FrameConstructionData* data = aItem.mFCData;
3528 NS_ASSERTION(data, "Must have frame construction data");
3530 uint32_t bits = data->mBits;
3532 NS_ASSERTION(!(bits & FCDATA_FUNC_IS_DATA_GETTER),
3533 "Should have dealt with this inside the data finder");
3535 // Some sets of bits are not compatible with each other
3536 #define CHECK_ONLY_ONE_BIT(_bit1, _bit2) \
3537 NS_ASSERTION(!(bits & _bit1) || !(bits & _bit2), \
3538 "Only one of these bits should be set")
3539 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_FORCE_NULL_ABSPOS_CONTAINER);
3540 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_WRAP_KIDS_IN_BLOCKS);
3541 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_MAY_NEED_SCROLLFRAME);
3542 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_IS_POPUP);
3543 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_SKIP_ABSPOS_PUSH);
3544 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR,
3545 FCDATA_DISALLOW_GENERATED_CONTENT);
3546 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_ALLOW_BLOCK_STYLES);
3547 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR,
3548 FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS);
3549 CHECK_ONLY_ONE_BIT(FCDATA_WRAP_KIDS_IN_BLOCKS,
3550 FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS);
3551 #undef CHECK_ONLY_ONE_BIT
3552 NS_ASSERTION(!(bits & FCDATA_FORCED_NON_SCROLLABLE_BLOCK) ||
3553 ((bits & FCDATA_FUNC_IS_FULL_CTOR) &&
3554 data->mFullConstructor ==
3555 &nsCSSFrameConstructor::ConstructNonScrollableBlock),
3556 "Unexpected FCDATA_FORCED_NON_SCROLLABLE_BLOCK flag");
3558 // Don't create a subdocument frame for iframes if we're creating extra frames
3559 if (aState.mCreatingExtraFrames && aItem.mContent->IsHTML() &&
3560 aItem.mContent->Tag() == nsGkAtoms::iframe)
3561 {
3562 return;
3563 }
3565 nsStyleContext* const styleContext = aItem.mStyleContext;
3566 const nsStyleDisplay* display = styleContext->StyleDisplay();
3567 nsIContent* const content = aItem.mContent;
3569 // Get the parent of the content and check if it is a XBL children element.
3570 // Push the children element as an ancestor here because it does
3571 // not have a frame and would not otherwise be pushed as an ancestor. It is
3572 // necessary to do so in order to correctly handle style resolution on
3573 // descendants.
3574 nsIContent* parent = content->GetParent();
3575 TreeMatchContext::AutoAncestorPusher
3576 insertionPointPusher(aState.mTreeMatchContext);
3577 if (parent && nsContentUtils::IsContentInsertionPoint(parent)) {
3578 if (aState.mTreeMatchContext.mAncestorFilter.HasFilter()) {
3579 insertionPointPusher.PushAncestorAndStyleScope(parent);
3580 } else {
3581 insertionPointPusher.PushStyleScope(parent);
3582 }
3583 }
3585 // Push the content as a style ancestor now, so we don't have to do
3586 // it in our various full-constructor functions. In particular,
3587 // since a number of full-constructor functions don't actually call
3588 // ProcessChildren in some cases (e.g. for CSS anonymous table boxes
3589 // or for situations where only anonymouse children are having
3590 // frames constructed), this is the best place to bottleneck the
3591 // pushing of the content instead of having to do it in multiple
3592 // places.
3593 TreeMatchContext::AutoAncestorPusher
3594 ancestorPusher(aState.mTreeMatchContext);
3595 if (aState.mTreeMatchContext.mAncestorFilter.HasFilter()) {
3596 ancestorPusher.PushAncestorAndStyleScope(content);
3597 } else {
3598 ancestorPusher.PushStyleScope(content);
3599 }
3601 nsIFrame* newFrame;
3602 nsIFrame* primaryFrame;
3603 if (bits & FCDATA_FUNC_IS_FULL_CTOR) {
3604 newFrame =
3605 (this->*(data->mFullConstructor))(aState, aItem, aParentFrame,
3606 display, aFrameItems);
3607 MOZ_ASSERT(newFrame, "Full constructor failed");
3608 primaryFrame = newFrame;
3609 } else {
3610 newFrame =
3611 (*data->mFunc.mCreationFunc)(mPresShell, styleContext);
3613 bool allowOutOfFlow = !(bits & FCDATA_DISALLOW_OUT_OF_FLOW);
3614 bool isPopup = aItem.mIsPopup;
3615 NS_ASSERTION(!isPopup ||
3616 (aState.mPopupItems.containingBlock &&
3617 aState.mPopupItems.containingBlock->GetType() ==
3618 nsGkAtoms::popupSetFrame),
3619 "Should have a containing block here!");
3621 nsIFrame* geometricParent =
3622 isPopup ? aState.mPopupItems.containingBlock :
3623 (allowOutOfFlow ? aState.GetGeometricParent(display, aParentFrame)
3624 : aParentFrame);
3626 // Must init frameToAddToList to null, since it's inout
3627 nsIFrame* frameToAddToList = nullptr;
3628 if ((bits & FCDATA_MAY_NEED_SCROLLFRAME) &&
3629 display->IsScrollableOverflow()) {
3630 BuildScrollFrame(aState, content, styleContext, newFrame,
3631 geometricParent, frameToAddToList);
3632 } else {
3633 InitAndRestoreFrame(aState, content, geometricParent, newFrame);
3634 // See whether we need to create a view
3635 nsContainerFrame::CreateViewForFrame(newFrame, false);
3636 frameToAddToList = newFrame;
3637 }
3639 // Use frameToAddToList as the primary frame. In the non-scrollframe case
3640 // they're equal, but in the scrollframe case newFrame is the scrolled
3641 // frame, while frameToAddToList is the scrollframe (and should be the
3642 // primary frame).
3643 primaryFrame = frameToAddToList;
3645 // If we need to create a block formatting context to wrap our
3646 // kids, do it now.
3647 const nsStyleDisplay* maybeAbsoluteContainingBlockDisplay = display;
3648 nsIFrame* maybeAbsoluteContainingBlock = newFrame;
3649 nsIFrame* possiblyLeafFrame = newFrame;
3650 if (bits & FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS) {
3651 nsRefPtr<nsStyleContext> blockContext;
3652 blockContext =
3653 mPresShell->StyleSet()->ResolveAnonymousBoxStyle(*data->mAnonBoxPseudo,
3654 styleContext);
3655 nsIFrame* blockFrame =
3656 NS_NewBlockFormattingContext(mPresShell, blockContext);
3658 InitAndRestoreFrame(aState, content, newFrame, blockFrame);
3660 SetInitialSingleChild(newFrame, blockFrame);
3662 // Now figure out whether newFrame or blockFrame should be the
3663 // absolute container. It should be the latter if it's
3664 // positioned, otherwise the former.
3665 const nsStyleDisplay* blockDisplay = blockContext->StyleDisplay();
3666 if (blockDisplay->IsPositioned(blockFrame)) {
3667 maybeAbsoluteContainingBlockDisplay = blockDisplay;
3668 maybeAbsoluteContainingBlock = blockFrame;
3669 }
3671 // Our kids should go into the blockFrame
3672 newFrame = blockFrame;
3673 }
3675 aState.AddChild(frameToAddToList, aFrameItems, content, styleContext,
3676 aParentFrame, allowOutOfFlow, allowOutOfFlow, isPopup);
3678 #ifdef MOZ_XUL
3679 // Icky XUL stuff, sadly
3681 if (aItem.mIsRootPopupgroup) {
3682 NS_ASSERTION(nsIRootBox::GetRootBox(mPresShell) &&
3683 nsIRootBox::GetRootBox(mPresShell)->GetPopupSetFrame() ==
3684 newFrame,
3685 "Unexpected PopupSetFrame");
3686 aState.mPopupItems.containingBlock = newFrame;
3687 aState.mHavePendingPopupgroup = false;
3688 }
3689 #endif /* MOZ_XUL */
3691 // Process the child content if requested
3692 nsFrameItems childItems;
3693 nsFrameConstructorSaveState absoluteSaveState;
3695 if (bits & FCDATA_FORCE_NULL_ABSPOS_CONTAINER) {
3696 aState.PushAbsoluteContainingBlock(nullptr, nullptr, absoluteSaveState);
3697 } else if (!(bits & FCDATA_SKIP_ABSPOS_PUSH)) {
3698 nsIFrame* cb = maybeAbsoluteContainingBlock;
3699 cb->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
3700 // This check is identical to nsStyleDisplay::IsPositioned except without
3701 // the assertion that the style display and frame match. When constructing
3702 // scroll frames we intentionally use the style display for the outer, but
3703 // make the inner the containing block.
3704 if ((maybeAbsoluteContainingBlockDisplay->IsAbsolutelyPositionedStyle() ||
3705 maybeAbsoluteContainingBlockDisplay->IsRelativelyPositionedStyle() ||
3706 (maybeAbsoluteContainingBlockDisplay->HasTransformStyle() &&
3707 cb->IsFrameOfType(nsIFrame::eSupportsCSSTransforms)) ||
3708 maybeAbsoluteContainingBlockDisplay->HasPerspectiveStyle()) &&
3709 !cb->IsSVGText()) {
3710 aState.PushAbsoluteContainingBlock(cb, cb, absoluteSaveState);
3711 }
3712 }
3714 if (!aItem.mAnonChildren.IsEmpty()) {
3715 NS_ASSERTION(!(bits & FCDATA_USE_CHILD_ITEMS),
3716 "We should not have both anonymous and non-anonymous "
3717 "children in a given FrameConstructorItem");
3718 AddFCItemsForAnonymousContent(aState, newFrame, aItem.mAnonChildren,
3719 aItem.mChildItems);
3720 bits |= FCDATA_USE_CHILD_ITEMS;
3721 }
3723 if (bits & FCDATA_USE_CHILD_ITEMS) {
3724 nsFrameConstructorSaveState floatSaveState;
3726 if (ShouldSuppressFloatingOfDescendants(newFrame)) {
3727 aState.PushFloatContainingBlock(nullptr, floatSaveState);
3728 } else if (newFrame->IsFloatContainingBlock()) {
3729 aState.PushFloatContainingBlock(newFrame, floatSaveState);
3730 }
3731 ConstructFramesFromItemList(aState, aItem.mChildItems, newFrame,
3732 childItems);
3733 } else {
3734 // Process the child frames.
3735 ProcessChildren(aState, content, styleContext, newFrame,
3736 !(bits & FCDATA_DISALLOW_GENERATED_CONTENT),
3737 childItems,
3738 (bits & FCDATA_ALLOW_BLOCK_STYLES) != 0,
3739 aItem.mPendingBinding, possiblyLeafFrame);
3740 }
3742 #ifdef MOZ_XUL
3743 // More icky XUL stuff
3744 if (aItem.mNameSpaceID == kNameSpaceID_XUL &&
3745 (aItem.mTag == nsGkAtoms::treechildren || // trees always need titletips
3746 content->HasAttr(kNameSpaceID_None, nsGkAtoms::tooltiptext) ||
3747 content->HasAttr(kNameSpaceID_None, nsGkAtoms::tooltip))) {
3748 nsIRootBox* rootBox = nsIRootBox::GetRootBox(mPresShell);
3749 if (rootBox) {
3750 rootBox->AddTooltipSupport(content);
3751 }
3752 }
3753 #endif
3755 if (bits & FCDATA_WRAP_KIDS_IN_BLOCKS) {
3756 nsFrameItems newItems;
3757 nsFrameItems currentBlockItems;
3758 nsIFrame* f;
3759 while ((f = childItems.FirstChild()) != nullptr) {
3760 bool wrapFrame = IsInlineFrame(f) || IsFramePartOfIBSplit(f);
3761 if (!wrapFrame) {
3762 FlushAccumulatedBlock(aState, content, newFrame,
3763 currentBlockItems, newItems);
3764 }
3766 childItems.RemoveFrame(f);
3767 if (wrapFrame) {
3768 currentBlockItems.AddChild(f);
3769 } else {
3770 newItems.AddChild(f);
3771 }
3772 }
3773 FlushAccumulatedBlock(aState, content, newFrame,
3774 currentBlockItems, newItems);
3776 if (childItems.NotEmpty()) {
3777 // an error must have occurred, delete unprocessed frames
3778 childItems.DestroyFrames();
3779 }
3781 childItems = newItems;
3782 }
3784 // Set the frame's initial child list
3785 // Note that MathML depends on this being called even if
3786 // childItems is empty!
3787 newFrame->SetInitialChildList(kPrincipalList, childItems);
3788 }
3790 NS_ASSERTION(newFrame->IsFrameOfType(nsIFrame::eLineParticipant) ==
3791 ((bits & FCDATA_IS_LINE_PARTICIPANT) != 0),
3792 "Incorrectly set FCDATA_IS_LINE_PARTICIPANT bits");
3794 if (aItem.mIsAnonymousContentCreatorContent) {
3795 primaryFrame->AddStateBits(NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT);
3796 }
3798 // Even if mCreatingExtraFrames is set, we may need to SetPrimaryFrame for
3799 // generated content that doesn't have one yet. Note that we have to examine
3800 // the frame bit, because by this point mIsGeneratedContent has been cleared
3801 // on aItem.
3802 if ((!aState.mCreatingExtraFrames ||
3803 ((primaryFrame->GetStateBits() & NS_FRAME_GENERATED_CONTENT) &&
3804 !aItem.mContent->GetPrimaryFrame())) &&
3805 !(bits & FCDATA_SKIP_FRAMESET)) {
3806 aItem.mContent->SetPrimaryFrame(primaryFrame);
3807 }
3808 }
3810 // after the node has been constructed and initialized create any
3811 // anonymous content a node needs.
3812 nsresult
3813 nsCSSFrameConstructor::CreateAnonymousFrames(nsFrameConstructorState& aState,
3814 nsIContent* aParent,
3815 nsIFrame* aParentFrame,
3816 PendingBinding* aPendingBinding,
3817 nsFrameItems& aChildItems)
3818 {
3819 nsAutoTArray<nsIAnonymousContentCreator::ContentInfo, 4> newAnonymousItems;
3820 nsresult rv = GetAnonymousContent(aParent, aParentFrame, newAnonymousItems);
3821 NS_ENSURE_SUCCESS(rv, rv);
3823 uint32_t count = newAnonymousItems.Length();
3824 if (count == 0) {
3825 return NS_OK;
3826 }
3828 nsFrameConstructorState::PendingBindingAutoPusher pusher(aState,
3829 aPendingBinding);
3830 TreeMatchContext::AutoAncestorPusher ancestorPusher(aState.mTreeMatchContext);
3831 if (aState.mTreeMatchContext.mAncestorFilter.HasFilter()) {
3832 ancestorPusher.PushAncestorAndStyleScope(aParent->AsElement());
3833 } else {
3834 ancestorPusher.PushStyleScope(aParent->AsElement());
3835 }
3837 nsIAnonymousContentCreator* creator = do_QueryFrame(aParentFrame);
3838 NS_ASSERTION(creator,
3839 "How can that happen if we have nodes to construct frames for?");
3841 for (uint32_t i=0; i < count; i++) {
3842 nsIContent* content = newAnonymousItems[i].mContent;
3843 NS_ASSERTION(content, "null anonymous content?");
3844 NS_ASSERTION(!newAnonymousItems[i].mStyleContext, "Unexpected style context");
3845 NS_ASSERTION(newAnonymousItems[i].mChildren.IsEmpty(),
3846 "This method is not currently used with frames that implement "
3847 "nsIAnonymousContentCreator::CreateAnonymousContent to "
3848 "output a list where the items have their own children");
3850 nsIFrame* newFrame = creator->CreateFrameFor(content);
3851 if (newFrame) {
3852 NS_ASSERTION(content->GetPrimaryFrame(),
3853 "Content must have a primary frame now");
3854 newFrame->AddStateBits(NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT);
3855 aChildItems.AddChild(newFrame);
3856 } else {
3857 FrameConstructionItemList items;
3858 {
3859 // Skip flex item style-fixup during our AddFrameConstructionItems() call:
3860 TreeMatchContext::AutoFlexItemStyleFixupSkipper
3861 flexItemStyleFixupSkipper(aState.mTreeMatchContext);
3863 AddFrameConstructionItems(aState, content, true, aParentFrame, items);
3864 }
3865 ConstructFramesFromItemList(aState, items, aParentFrame, aChildItems);
3866 }
3867 }
3869 return NS_OK;
3870 }
3872 static void
3873 SetFlagsOnSubtree(nsIContent *aNode, uintptr_t aFlagsToSet)
3874 {
3875 #ifdef DEBUG
3876 // Make sure that the node passed to us doesn't have any XBL children
3877 {
3878 FlattenedChildIterator iter(aNode);
3879 NS_ASSERTION(!iter.XBLInvolved() || !iter.GetNextChild(),
3880 "The node should not have any XBL children");
3881 }
3882 #endif
3884 // Set the flag on the node itself
3885 aNode->SetFlags(aFlagsToSet);
3887 // Set the flag on all of its children recursively
3888 uint32_t count;
3889 nsIContent * const *children = aNode->GetChildArray(&count);
3891 for (uint32_t index = 0; index < count; ++index) {
3892 SetFlagsOnSubtree(children[index], aFlagsToSet);
3893 }
3894 }
3896 /**
3897 * This function takes a tree of nsIAnonymousContentCreator::ContentInfo
3898 * objects where the nsIContent nodes have just been created, and appends the
3899 * nsIContent children in the tree to their parent. The leaf nsIContent objects
3900 * are appended first to minimize the number of notifications that are sent
3901 * out (i.e. by appending as many descendants as posible while their parent is
3902 * not yet in the document tree).
3903 *
3904 * This function is used simply as a convenience so that implementations of
3905 * nsIAnonymousContentCreator::CreateAnonymousContent don't all have to have
3906 * their own code to connect the elements that they create.
3907 */
3908 static void
3909 ConnectAnonymousTreeDescendants(nsIContent* aParent,
3910 nsTArray<nsIAnonymousContentCreator::ContentInfo>& aContent)
3911 {
3912 uint32_t count = aContent.Length();
3913 for (uint32_t i=0; i < count; i++) {
3914 nsIContent* content = aContent[i].mContent;
3915 NS_ASSERTION(content, "null anonymous content?");
3917 ConnectAnonymousTreeDescendants(content, aContent[i].mChildren);
3919 aParent->AppendChildTo(content, false);
3920 }
3921 }
3923 nsresult
3924 nsCSSFrameConstructor::GetAnonymousContent(nsIContent* aParent,
3925 nsIFrame* aParentFrame,
3926 nsTArray<nsIAnonymousContentCreator::ContentInfo>& aContent)
3927 {
3928 nsIAnonymousContentCreator* creator = do_QueryFrame(aParentFrame);
3929 if (!creator)
3930 return NS_OK;
3932 nsresult rv = creator->CreateAnonymousContent(aContent);
3933 NS_ENSURE_SUCCESS(rv, rv);
3935 uint32_t count = aContent.Length();
3936 for (uint32_t i=0; i < count; i++) {
3937 // get our child's content and set its parent to our content
3938 nsIContent* content = aContent[i].mContent;
3939 NS_ASSERTION(content, "null anonymous content?");
3941 // least-surprise CSS binding until we do the SVG specified
3942 // cascading rules for <svg:use> - bug 265894
3943 if (aParentFrame->GetType() == nsGkAtoms::svgUseFrame) {
3944 content->SetFlags(NODE_IS_ANONYMOUS_ROOT);
3945 } else {
3946 content->SetIsNativeAnonymousRoot();
3947 }
3949 ConnectAnonymousTreeDescendants(content, aContent[i].mChildren);
3951 bool anonContentIsEditable = content->HasFlag(NODE_IS_EDITABLE);
3952 rv = content->BindToTree(mDocument, aParent, aParent, true);
3953 // If the anonymous content creator requested that the content should be
3954 // editable, honor its request.
3955 // We need to set the flag on the whole subtree, because existing
3956 // children's flags have already been set as part of the BindToTree operation.
3957 if (anonContentIsEditable) {
3958 NS_ASSERTION(aParentFrame->GetType() == nsGkAtoms::textInputFrame,
3959 "We only expect this for anonymous content under a text control frame");
3960 SetFlagsOnSubtree(content, NODE_IS_EDITABLE);
3961 }
3962 if (NS_FAILED(rv)) {
3963 content->UnbindFromTree();
3964 return rv;
3965 }
3966 }
3968 return NS_OK;
3969 }
3971 static
3972 bool IsXULDisplayType(const nsStyleDisplay* aDisplay)
3973 {
3974 return (aDisplay->mDisplay == NS_STYLE_DISPLAY_INLINE_BOX ||
3975 #ifdef MOZ_XUL
3976 aDisplay->mDisplay == NS_STYLE_DISPLAY_INLINE_XUL_GRID ||
3977 aDisplay->mDisplay == NS_STYLE_DISPLAY_INLINE_STACK ||
3978 #endif
3979 aDisplay->mDisplay == NS_STYLE_DISPLAY_BOX
3980 #ifdef MOZ_XUL
3981 || aDisplay->mDisplay == NS_STYLE_DISPLAY_XUL_GRID ||
3982 aDisplay->mDisplay == NS_STYLE_DISPLAY_STACK ||
3983 aDisplay->mDisplay == NS_STYLE_DISPLAY_XUL_GRID_GROUP ||
3984 aDisplay->mDisplay == NS_STYLE_DISPLAY_XUL_GRID_LINE ||
3985 aDisplay->mDisplay == NS_STYLE_DISPLAY_DECK ||
3986 aDisplay->mDisplay == NS_STYLE_DISPLAY_POPUP ||
3987 aDisplay->mDisplay == NS_STYLE_DISPLAY_GROUPBOX
3988 #endif
3989 );
3990 }
3993 // XUL frames are not allowed to be out of flow.
3994 #define SIMPLE_XUL_FCDATA(_func) \
3995 FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_SKIP_ABSPOS_PUSH, \
3996 _func)
3997 #define SCROLLABLE_XUL_FCDATA(_func) \
3998 FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_SKIP_ABSPOS_PUSH | \
3999 FCDATA_MAY_NEED_SCROLLFRAME, _func)
4000 // .. but we allow some XUL frames to be _containers_ for out-of-flow content
4001 // (This is the same as SCROLLABLE_XUL_FCDATA, but w/o FCDATA_SKIP_ABSPOS_PUSH)
4002 #define SCROLLABLE_ABSPOS_CONTAINER_XUL_FCDATA(_func) \
4003 FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW | \
4004 FCDATA_MAY_NEED_SCROLLFRAME, _func)
4006 #define SIMPLE_XUL_CREATE(_tag, _func) \
4007 { &nsGkAtoms::_tag, SIMPLE_XUL_FCDATA(_func) }
4008 #define SCROLLABLE_XUL_CREATE(_tag, _func) \
4009 { &nsGkAtoms::_tag, SCROLLABLE_XUL_FCDATA(_func) }
4010 #define SIMPLE_XUL_INT_CREATE(_int, _func) \
4011 { _int, SIMPLE_XUL_FCDATA(_func) }
4012 #define SCROLLABLE_XUL_INT_CREATE(_int, _func) \
4013 { _int, SCROLLABLE_XUL_FCDATA(_func) }
4014 #define SCROLLABLE_ABSPOS_CONTAINER_XUL_INT_CREATE(_int, _func) \
4015 { _int, SCROLLABLE_ABSPOS_CONTAINER_XUL_FCDATA(_func) }
4017 static
4018 nsIFrame* NS_NewGridBoxFrame(nsIPresShell* aPresShell,
4019 nsStyleContext* aStyleContext)
4020 {
4021 nsCOMPtr<nsBoxLayout> layout;
4022 NS_NewGridLayout2(aPresShell, getter_AddRefs(layout));
4023 return NS_NewBoxFrame(aPresShell, aStyleContext, false, layout);
4024 }
4026 /* static */
4027 const nsCSSFrameConstructor::FrameConstructionData*
4028 nsCSSFrameConstructor::FindXULTagData(Element* aElement,
4029 nsIAtom* aTag,
4030 int32_t aNameSpaceID,
4031 nsStyleContext* aStyleContext)
4032 {
4033 if (aNameSpaceID != kNameSpaceID_XUL) {
4034 return nullptr;
4035 }
4037 static const FrameConstructionDataByTag sXULTagData[] = {
4038 #ifdef MOZ_XUL
4039 SCROLLABLE_XUL_CREATE(button, NS_NewButtonBoxFrame),
4040 SCROLLABLE_XUL_CREATE(checkbox, NS_NewButtonBoxFrame),
4041 SCROLLABLE_XUL_CREATE(radio, NS_NewButtonBoxFrame),
4042 SCROLLABLE_XUL_CREATE(autorepeatbutton, NS_NewAutoRepeatBoxFrame),
4043 SCROLLABLE_XUL_CREATE(titlebar, NS_NewTitleBarFrame),
4044 SCROLLABLE_XUL_CREATE(resizer, NS_NewResizerFrame),
4045 SIMPLE_XUL_CREATE(image, NS_NewImageBoxFrame),
4046 SIMPLE_XUL_CREATE(spring, NS_NewLeafBoxFrame),
4047 SIMPLE_XUL_CREATE(spacer, NS_NewLeafBoxFrame),
4048 SIMPLE_XUL_CREATE(treechildren, NS_NewTreeBodyFrame),
4049 SIMPLE_XUL_CREATE(treecol, NS_NewTreeColFrame),
4050 SIMPLE_XUL_CREATE(text, NS_NewTextBoxFrame),
4051 SIMPLE_TAG_CHAIN(label, nsCSSFrameConstructor::FindXULLabelData),
4052 SIMPLE_TAG_CHAIN(description, nsCSSFrameConstructor::FindXULDescriptionData),
4053 SIMPLE_XUL_CREATE(menu, NS_NewMenuFrame),
4054 SIMPLE_XUL_CREATE(menubutton, NS_NewMenuFrame),
4055 SIMPLE_XUL_CREATE(menuitem, NS_NewMenuItemFrame),
4056 #ifdef XP_MACOSX
4057 SIMPLE_TAG_CHAIN(menubar, nsCSSFrameConstructor::FindXULMenubarData),
4058 #else
4059 SIMPLE_XUL_CREATE(menubar, NS_NewMenuBarFrame),
4060 #endif /* XP_MACOSX */
4061 SIMPLE_TAG_CHAIN(popupgroup, nsCSSFrameConstructor::FindPopupGroupData),
4062 SIMPLE_XUL_CREATE(iframe, NS_NewSubDocumentFrame),
4063 SIMPLE_XUL_CREATE(editor, NS_NewSubDocumentFrame),
4064 SIMPLE_XUL_CREATE(browser, NS_NewSubDocumentFrame),
4065 SIMPLE_XUL_CREATE(progressmeter, NS_NewProgressMeterFrame),
4066 SIMPLE_XUL_CREATE(splitter, NS_NewSplitterFrame),
4067 SIMPLE_TAG_CHAIN(listboxbody,
4068 nsCSSFrameConstructor::FindXULListBoxBodyData),
4069 SIMPLE_TAG_CHAIN(listitem, nsCSSFrameConstructor::FindXULListItemData),
4070 #endif /* MOZ_XUL */
4071 SIMPLE_XUL_CREATE(slider, NS_NewSliderFrame),
4072 SIMPLE_XUL_CREATE(scrollbar, NS_NewScrollbarFrame),
4073 SIMPLE_XUL_CREATE(scrollbarbutton, NS_NewScrollbarButtonFrame)
4074 };
4076 return FindDataByTag(aTag, aElement, aStyleContext, sXULTagData,
4077 ArrayLength(sXULTagData));
4078 }
4080 #ifdef MOZ_XUL
4081 /* static */
4082 const nsCSSFrameConstructor::FrameConstructionData*
4083 nsCSSFrameConstructor::FindPopupGroupData(Element* aElement,
4084 nsStyleContext* /* unused */)
4085 {
4086 if (!aElement->IsRootOfNativeAnonymousSubtree()) {
4087 return nullptr;
4088 }
4090 static const FrameConstructionData sPopupSetData =
4091 SIMPLE_XUL_FCDATA(NS_NewPopupSetFrame);
4092 return &sPopupSetData;
4093 }
4095 /* static */
4096 const nsCSSFrameConstructor::FrameConstructionData
4097 nsCSSFrameConstructor::sXULTextBoxData = SIMPLE_XUL_FCDATA(NS_NewTextBoxFrame);
4099 /* static */
4100 const nsCSSFrameConstructor::FrameConstructionData*
4101 nsCSSFrameConstructor::FindXULLabelData(Element* aElement,
4102 nsStyleContext* /* unused */)
4103 {
4104 if (aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::value)) {
4105 return &sXULTextBoxData;
4106 }
4108 static const FrameConstructionData sLabelData =
4109 SIMPLE_XUL_FCDATA(NS_NewXULLabelFrame);
4110 return &sLabelData;
4111 }
4113 static nsIFrame*
4114 NS_NewXULDescriptionFrame(nsIPresShell* aPresShell, nsStyleContext *aContext)
4115 {
4116 // XXXbz do we really need to set those flags? If the parent is not
4117 // a block we'll get them anyway, and if it is, do we want them?
4118 return NS_NewBlockFrame(aPresShell, aContext,
4119 NS_BLOCK_FLOAT_MGR | NS_BLOCK_MARGIN_ROOT);
4120 }
4122 /* static */
4123 const nsCSSFrameConstructor::FrameConstructionData*
4124 nsCSSFrameConstructor::FindXULDescriptionData(Element* aElement,
4125 nsStyleContext* /* unused */)
4126 {
4127 if (aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::value)) {
4128 return &sXULTextBoxData;
4129 }
4131 static const FrameConstructionData sDescriptionData =
4132 SIMPLE_XUL_FCDATA(NS_NewXULDescriptionFrame);
4133 return &sDescriptionData;
4134 }
4136 #ifdef XP_MACOSX
4137 /* static */
4138 const nsCSSFrameConstructor::FrameConstructionData*
4139 nsCSSFrameConstructor::FindXULMenubarData(Element* aElement,
4140 nsStyleContext* aStyleContext)
4141 {
4142 nsCOMPtr<nsIDocShell> treeItem =
4143 aStyleContext->PresContext()->GetDocShell();
4144 if (treeItem && nsIDocShellTreeItem::typeChrome == treeItem->ItemType()) {
4145 nsCOMPtr<nsIDocShellTreeItem> parent;
4146 treeItem->GetParent(getter_AddRefs(parent));
4147 if (!parent) {
4148 // This is the root. Suppress the menubar, since on Mac
4149 // window menus are not attached to the window.
4150 static const FrameConstructionData sSuppressData = SUPPRESS_FCDATA();
4151 return &sSuppressData;
4152 }
4153 }
4155 static const FrameConstructionData sMenubarData =
4156 SIMPLE_XUL_FCDATA(NS_NewMenuBarFrame);
4157 return &sMenubarData;
4158 }
4159 #endif /* XP_MACOSX */
4161 /* static */
4162 const nsCSSFrameConstructor::FrameConstructionData*
4163 nsCSSFrameConstructor::FindXULListBoxBodyData(Element* aElement,
4164 nsStyleContext* aStyleContext)
4165 {
4166 if (aStyleContext->StyleDisplay()->mDisplay !=
4167 NS_STYLE_DISPLAY_XUL_GRID_GROUP) {
4168 return nullptr;
4169 }
4171 static const FrameConstructionData sListBoxBodyData =
4172 SCROLLABLE_XUL_FCDATA(NS_NewListBoxBodyFrame);
4173 return &sListBoxBodyData;
4174 }
4176 /* static */
4177 const nsCSSFrameConstructor::FrameConstructionData*
4178 nsCSSFrameConstructor::FindXULListItemData(Element* aElement,
4179 nsStyleContext* aStyleContext)
4180 {
4181 if (aStyleContext->StyleDisplay()->mDisplay !=
4182 NS_STYLE_DISPLAY_XUL_GRID_LINE) {
4183 return nullptr;
4184 }
4186 static const FrameConstructionData sListItemData =
4187 SCROLLABLE_XUL_FCDATA(NS_NewListItemFrame);
4188 return &sListItemData;
4189 }
4191 #endif /* MOZ_XUL */
4193 /* static */
4194 const nsCSSFrameConstructor::FrameConstructionData*
4195 nsCSSFrameConstructor::FindXULDisplayData(const nsStyleDisplay* aDisplay,
4196 Element* aElement,
4197 nsStyleContext* aStyleContext)
4198 {
4199 static const FrameConstructionDataByInt sXULDisplayData[] = {
4200 SCROLLABLE_ABSPOS_CONTAINER_XUL_INT_CREATE(NS_STYLE_DISPLAY_INLINE_BOX,
4201 NS_NewBoxFrame),
4202 SCROLLABLE_ABSPOS_CONTAINER_XUL_INT_CREATE(NS_STYLE_DISPLAY_BOX,
4203 NS_NewBoxFrame),
4204 #ifdef MOZ_XUL
4205 SCROLLABLE_XUL_INT_CREATE(NS_STYLE_DISPLAY_INLINE_XUL_GRID, NS_NewGridBoxFrame),
4206 SCROLLABLE_XUL_INT_CREATE(NS_STYLE_DISPLAY_XUL_GRID, NS_NewGridBoxFrame),
4207 SCROLLABLE_XUL_INT_CREATE(NS_STYLE_DISPLAY_XUL_GRID_GROUP,
4208 NS_NewGridRowGroupFrame),
4209 SCROLLABLE_XUL_INT_CREATE(NS_STYLE_DISPLAY_XUL_GRID_LINE,
4210 NS_NewGridRowLeafFrame),
4211 SIMPLE_XUL_INT_CREATE(NS_STYLE_DISPLAY_DECK, NS_NewDeckFrame),
4212 SCROLLABLE_XUL_INT_CREATE(NS_STYLE_DISPLAY_GROUPBOX, NS_NewGroupBoxFrame),
4213 SCROLLABLE_XUL_INT_CREATE(NS_STYLE_DISPLAY_INLINE_STACK, NS_NewStackFrame),
4214 SCROLLABLE_XUL_INT_CREATE(NS_STYLE_DISPLAY_STACK, NS_NewStackFrame),
4215 { NS_STYLE_DISPLAY_POPUP,
4216 FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_IS_POPUP |
4217 FCDATA_SKIP_ABSPOS_PUSH, NS_NewMenuPopupFrame) }
4218 #endif /* MOZ_XUL */
4219 };
4221 // Processing by display here:
4222 return FindDataByInt(aDisplay->mDisplay, aElement, aStyleContext,
4223 sXULDisplayData, ArrayLength(sXULDisplayData));
4224 }
4226 already_AddRefed<nsStyleContext>
4227 nsCSSFrameConstructor::BeginBuildingScrollFrame(nsFrameConstructorState& aState,
4228 nsIContent* aContent,
4229 nsStyleContext* aContentStyle,
4230 nsIFrame* aParentFrame,
4231 nsIAtom* aScrolledPseudo,
4232 bool aIsRoot,
4233 nsIFrame*& aNewFrame)
4234 {
4235 nsIFrame* gfxScrollFrame = aNewFrame;
4237 nsFrameItems anonymousItems;
4239 nsRefPtr<nsStyleContext> contentStyle = aContentStyle;
4241 if (!gfxScrollFrame) {
4242 // Build a XULScrollFrame when the child is a box, otherwise an
4243 // HTMLScrollFrame
4244 // XXXbz this is the lone remaining consumer of IsXULDisplayType.
4245 // I wonder whether we can eliminate that somehow.
4246 const nsStyleDisplay* displayStyle = aContentStyle->StyleDisplay();
4247 if (IsXULDisplayType(displayStyle)) {
4248 gfxScrollFrame = NS_NewXULScrollFrame(mPresShell, contentStyle, aIsRoot,
4249 displayStyle->mDisplay == NS_STYLE_DISPLAY_STACK ||
4250 displayStyle->mDisplay == NS_STYLE_DISPLAY_INLINE_STACK);
4251 } else {
4252 gfxScrollFrame = NS_NewHTMLScrollFrame(mPresShell, contentStyle, aIsRoot);
4253 }
4255 InitAndRestoreFrame(aState, aContent, aParentFrame, gfxScrollFrame);
4256 }
4258 // if there are any anonymous children for the scroll frame, create
4259 // frames for them.
4260 // Pass a null pending binding: we don't care how constructors for any of
4261 // this anonymous content order with anything else. It's never been
4262 // consistent anyway.
4263 CreateAnonymousFrames(aState, aContent, gfxScrollFrame, nullptr,
4264 anonymousItems);
4266 aNewFrame = gfxScrollFrame;
4268 // we used the style that was passed in. So resolve another one.
4269 nsStyleSet *styleSet = mPresShell->StyleSet();
4270 nsRefPtr<nsStyleContext> scrolledChildStyle =
4271 styleSet->ResolveAnonymousBoxStyle(aScrolledPseudo, contentStyle);
4273 if (gfxScrollFrame) {
4274 gfxScrollFrame->SetInitialChildList(kPrincipalList, anonymousItems);
4275 }
4277 return scrolledChildStyle.forget();
4278 }
4280 void
4281 nsCSSFrameConstructor::FinishBuildingScrollFrame(nsIFrame* aScrollFrame,
4282 nsIFrame* aScrolledFrame)
4283 {
4284 nsFrameList scrolled(aScrolledFrame, aScrolledFrame);
4285 aScrollFrame->AppendFrames(kPrincipalList, scrolled);
4286 }
4289 /**
4290 * Called to wrap a gfx scrollframe around a frame. The hierarchy will look like this
4291 *
4292 * ------- for gfx scrollbars ------
4293 *
4294 *
4295 * ScrollFrame
4296 * ^
4297 * |
4298 * Frame (scrolled frame you passed in)
4299 *
4300 *
4301 *-----------------------------------
4302 * LEGEND:
4303 *
4304 * ScrollFrame: This is a frame that manages gfx cross platform frame based scrollbars.
4305 *
4306 * @param aContent the content node of the child to wrap.
4307 * @param aScrolledFrame The frame of the content to wrap. This should not be
4308 * Initialized. This method will initialize it with a scrolled pseudo
4309 * and no nsIContent. The content will be attached to the scrollframe
4310 * returned.
4311 * @param aContentStyle the style context that has already been resolved for the content being passed in.
4312 *
4313 * @param aParentFrame The parent to attach the scroll frame to
4314 *
4315 * @param aNewFrame The new scrollframe or gfx scrollframe that we create. It will contain the
4316 * scrolled frame you passed in. (returned)
4317 * If this is not null, we'll just use it
4318 * @param aScrolledContentStyle the style that was resolved for the scrolled frame. (returned)
4319 */
4320 nsresult
4321 nsCSSFrameConstructor::BuildScrollFrame(nsFrameConstructorState& aState,
4322 nsIContent* aContent,
4323 nsStyleContext* aContentStyle,
4324 nsIFrame* aScrolledFrame,
4325 nsIFrame* aParentFrame,
4326 nsIFrame*& aNewFrame)
4327 {
4328 nsRefPtr<nsStyleContext> scrolledContentStyle =
4329 BeginBuildingScrollFrame(aState, aContent, aContentStyle, aParentFrame,
4330 nsCSSAnonBoxes::scrolledContent,
4331 false, aNewFrame);
4333 aScrolledFrame->SetStyleContextWithoutNotification(scrolledContentStyle);
4334 InitAndRestoreFrame(aState, aContent, aNewFrame, aScrolledFrame);
4336 FinishBuildingScrollFrame(aNewFrame, aScrolledFrame);
4337 return NS_OK;
4338 }
4340 const nsCSSFrameConstructor::FrameConstructionData*
4341 nsCSSFrameConstructor::FindDisplayData(const nsStyleDisplay* aDisplay,
4342 Element* aElement,
4343 nsIFrame* aParentFrame,
4344 nsStyleContext* aStyleContext)
4345 {
4346 PR_STATIC_ASSERT(eParentTypeCount < (1 << (32 - FCDATA_PARENT_TYPE_OFFSET)));
4348 // The style system ensures that floated and positioned frames are
4349 // block-level.
4350 NS_ASSERTION(!(aDisplay->IsFloatingStyle() ||
4351 aDisplay->IsAbsolutelyPositionedStyle()) ||
4352 aDisplay->IsBlockOutsideStyle(),
4353 "Style system did not apply CSS2.1 section 9.7 fixups");
4355 // If this is "body", try propagating its scroll style to the viewport
4356 // Note that we need to do this even if the body is NOT scrollable;
4357 // it might have dynamically changed from scrollable to not scrollable,
4358 // and that might need to be propagated.
4359 // XXXbz is this the right place to do this? If this code moves,
4360 // make this function static.
4361 bool propagatedScrollToViewport = false;
4362 if (aElement->IsHTML(nsGkAtoms::body)) {
4363 propagatedScrollToViewport =
4364 PropagateScrollToViewport() == aElement;
4365 }
4367 NS_ASSERTION(!propagatedScrollToViewport ||
4368 !mPresShell->GetPresContext()->IsPaginated(),
4369 "Shouldn't propagate scroll in paginated contexts");
4371 // If the frame is a block-level frame and is scrollable, then wrap it in a
4372 // scroll frame.
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->IsBlockInsideStyle() &&
4377 aDisplay->IsScrollableOverflow() &&
4378 !propagatedScrollToViewport) {
4379 // Except we don't want to do that for paginated contexts for
4380 // frames that are block-outside and aren't frames for native
4381 // anonymous stuff.
4382 if (mPresShell->GetPresContext()->IsPaginated() &&
4383 aDisplay->IsBlockOutsideStyle() &&
4384 !aElement->IsInNativeAnonymousSubtree()) {
4385 static const FrameConstructionData sForcedNonScrollableBlockData =
4386 FULL_CTOR_FCDATA(FCDATA_FORCED_NON_SCROLLABLE_BLOCK,
4387 &nsCSSFrameConstructor::ConstructNonScrollableBlock);
4388 return &sForcedNonScrollableBlockData;
4389 }
4391 static const FrameConstructionData sScrollableBlockData =
4392 FULL_CTOR_FCDATA(0, &nsCSSFrameConstructor::ConstructScrollableBlock);
4393 return &sScrollableBlockData;
4394 }
4396 // Handle various non-scrollable blocks
4397 if (aDisplay->IsBlockInsideStyle()) {
4398 static const FrameConstructionData sNonScrollableBlockData =
4399 FULL_CTOR_FCDATA(0, &nsCSSFrameConstructor::ConstructNonScrollableBlock);
4400 return &sNonScrollableBlockData;
4401 }
4403 static const FrameConstructionDataByInt sDisplayData[] = {
4404 // To keep the hash table small don't add inline frames (they're
4405 // typically things like FONT and B), because we can quickly
4406 // find them if we need to.
4407 // XXXbz the "quickly" part is a bald-faced lie!
4408 { NS_STYLE_DISPLAY_INLINE,
4409 FULL_CTOR_FCDATA(FCDATA_IS_INLINE | FCDATA_IS_LINE_PARTICIPANT,
4410 &nsCSSFrameConstructor::ConstructInline) },
4411 { NS_STYLE_DISPLAY_FLEX,
4412 FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewFlexContainerFrame) },
4413 { NS_STYLE_DISPLAY_INLINE_FLEX,
4414 FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewFlexContainerFrame) },
4415 { NS_STYLE_DISPLAY_GRID,
4416 FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewGridContainerFrame) },
4417 { NS_STYLE_DISPLAY_INLINE_GRID,
4418 FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewGridContainerFrame) },
4419 { NS_STYLE_DISPLAY_TABLE,
4420 FULL_CTOR_FCDATA(0, &nsCSSFrameConstructor::ConstructTable) },
4421 { NS_STYLE_DISPLAY_INLINE_TABLE,
4422 FULL_CTOR_FCDATA(0, &nsCSSFrameConstructor::ConstructTable) },
4423 // NOTE: In the unlikely event that we add another table-part here that has
4424 // a desired-parent-type (& hence triggers table fixup), we'll need to also
4425 // update the flexbox chunk in nsStyleContext::ApplyStyleFixups().
4426 { NS_STYLE_DISPLAY_TABLE_CAPTION,
4427 FCDATA_DECL(FCDATA_IS_TABLE_PART | FCDATA_ALLOW_BLOCK_STYLES |
4428 FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_SKIP_ABSPOS_PUSH |
4429 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
4430 NS_NewTableCaptionFrame) },
4431 { NS_STYLE_DISPLAY_TABLE_ROW_GROUP,
4432 FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART |
4433 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
4434 &nsCSSFrameConstructor::ConstructTableRowOrRowGroup) },
4435 { NS_STYLE_DISPLAY_TABLE_HEADER_GROUP,
4436 FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART |
4437 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
4438 &nsCSSFrameConstructor::ConstructTableRowOrRowGroup) },
4439 { NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP,
4440 FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART |
4441 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
4442 &nsCSSFrameConstructor::ConstructTableRowOrRowGroup) },
4443 { NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP,
4444 FCDATA_DECL(FCDATA_IS_TABLE_PART | FCDATA_DISALLOW_OUT_OF_FLOW |
4445 FCDATA_SKIP_ABSPOS_PUSH |
4446 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
4447 NS_NewTableColGroupFrame) },
4448 { NS_STYLE_DISPLAY_TABLE_COLUMN,
4449 FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART |
4450 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeColGroup),
4451 &nsCSSFrameConstructor::ConstructTableCol) },
4452 { NS_STYLE_DISPLAY_TABLE_ROW,
4453 FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART |
4454 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRowGroup),
4455 &nsCSSFrameConstructor::ConstructTableRowOrRowGroup) },
4456 { NS_STYLE_DISPLAY_TABLE_CELL,
4457 FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART |
4458 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRow),
4459 &nsCSSFrameConstructor::ConstructTableCell) }
4460 };
4462 return FindDataByInt(aDisplay->mDisplay,
4463 aElement, aStyleContext, sDisplayData,
4464 ArrayLength(sDisplayData));
4465 }
4467 nsIFrame*
4468 nsCSSFrameConstructor::ConstructScrollableBlock(nsFrameConstructorState& aState,
4469 FrameConstructionItem& aItem,
4470 nsIFrame* aParentFrame,
4471 const nsStyleDisplay* aDisplay,
4472 nsFrameItems& aFrameItems)
4473 {
4474 nsIContent* const content = aItem.mContent;
4475 nsStyleContext* const styleContext = aItem.mStyleContext;
4477 nsIFrame* newFrame = nullptr;
4478 nsRefPtr<nsStyleContext> scrolledContentStyle
4479 = BeginBuildingScrollFrame(aState, content, styleContext,
4480 aState.GetGeometricParent(aDisplay, aParentFrame),
4481 nsCSSAnonBoxes::scrolledContent,
4482 false, newFrame);
4484 // Create our block frame
4485 // pass a temporary stylecontext, the correct one will be set later
4486 nsIFrame* scrolledFrame =
4487 NS_NewBlockFormattingContext(mPresShell, styleContext);
4489 // Make sure to AddChild before we call ConstructBlock so that we
4490 // end up before our descendants in fixed-pos lists as needed.
4491 aState.AddChild(newFrame, aFrameItems, content, styleContext, aParentFrame);
4493 nsFrameItems blockItem;
4494 ConstructBlock(aState, scrolledContentStyle->StyleDisplay(), content,
4495 newFrame, newFrame, scrolledContentStyle,
4496 &scrolledFrame, blockItem,
4497 aDisplay->IsPositioned(newFrame) ? newFrame : nullptr,
4498 aItem.mPendingBinding);
4500 NS_ASSERTION(blockItem.FirstChild() == scrolledFrame,
4501 "Scrollframe's frameItems should be exactly the scrolled frame");
4502 FinishBuildingScrollFrame(newFrame, scrolledFrame);
4504 return newFrame;
4505 }
4507 nsIFrame*
4508 nsCSSFrameConstructor::ConstructNonScrollableBlock(nsFrameConstructorState& aState,
4509 FrameConstructionItem& aItem,
4510 nsIFrame* aParentFrame,
4511 const nsStyleDisplay* aDisplay,
4512 nsFrameItems& aFrameItems)
4513 {
4514 nsStyleContext* const styleContext = aItem.mStyleContext;
4516 // We want a block formatting context root in paginated contexts for
4517 // every block that would be scrollable in a non-paginated context.
4518 // We mark our blocks with a bit here if this condition is true, so
4519 // we can check it later in nsFrame::ApplyPaginatedOverflowClipping.
4520 bool clipPaginatedOverflow =
4521 (aItem.mFCData->mBits & FCDATA_FORCED_NON_SCROLLABLE_BLOCK) != 0;
4522 nsIFrame* newFrame;
4523 if ((aDisplay->IsAbsolutelyPositionedStyle() ||
4524 aDisplay->IsFloatingStyle() ||
4525 NS_STYLE_DISPLAY_INLINE_BLOCK == aDisplay->mDisplay ||
4526 clipPaginatedOverflow) &&
4527 !aParentFrame->IsSVGText()) {
4528 newFrame = NS_NewBlockFormattingContext(mPresShell, styleContext);
4529 if (clipPaginatedOverflow) {
4530 newFrame->AddStateBits(NS_BLOCK_CLIP_PAGINATED_OVERFLOW);
4531 }
4532 } else {
4533 newFrame = NS_NewBlockFrame(mPresShell, styleContext);
4534 }
4536 ConstructBlock(aState, aDisplay, aItem.mContent,
4537 aState.GetGeometricParent(aDisplay, aParentFrame),
4538 aParentFrame, styleContext, &newFrame,
4539 aFrameItems,
4540 aDisplay->IsPositioned(newFrame) ? newFrame : nullptr,
4541 aItem.mPendingBinding);
4542 return newFrame;
4543 }
4546 void
4547 nsCSSFrameConstructor::InitAndRestoreFrame(const nsFrameConstructorState& aState,
4548 nsIContent* aContent,
4549 nsIFrame* aParentFrame,
4550 nsIFrame* aNewFrame,
4551 bool aAllowCounters)
4552 {
4553 NS_PRECONDITION(mUpdateCount != 0,
4554 "Should be in an update while creating frames");
4556 MOZ_ASSERT(aNewFrame, "Null frame cannot be initialized");
4558 // Initialize the frame
4559 aNewFrame->Init(aContent, aParentFrame, nullptr);
4560 aNewFrame->AddStateBits(aState.mAdditionalStateBits);
4562 if (aState.mFrameState) {
4563 // Restore frame state for just the newly created frame.
4564 RestoreFrameStateFor(aNewFrame, aState.mFrameState);
4565 }
4567 if (aAllowCounters &&
4568 mCounterManager.AddCounterResetsAndIncrements(aNewFrame)) {
4569 CountersDirty();
4570 }
4571 }
4573 already_AddRefed<nsStyleContext>
4574 nsCSSFrameConstructor::ResolveStyleContext(nsIFrame* aParentFrame,
4575 nsIContent* aContent,
4576 nsFrameConstructorState* aState)
4577 {
4578 nsStyleContext* parentStyleContext = nullptr;
4579 NS_ASSERTION(aContent->GetParent(), "Must have parent here");
4581 aParentFrame = nsFrame::CorrectStyleParentFrame(aParentFrame, nullptr);
4583 if (aParentFrame) {
4584 // Resolve the style context based on the content object and the parent
4585 // style context
4586 parentStyleContext = aParentFrame->StyleContext();
4587 } else {
4588 // Perhaps aParentFrame is a canvasFrame and we're replicating
4589 // fixed-pos frames.
4590 // XXX should we create a way to tell ConstructFrame which style
4591 // context to use, and pass it the style context for the
4592 // previous page's fixed-pos frame?
4593 }
4595 return ResolveStyleContext(parentStyleContext, aContent, aState);
4596 }
4598 already_AddRefed<nsStyleContext>
4599 nsCSSFrameConstructor::ResolveStyleContext(nsStyleContext* aParentStyleContext,
4600 nsIContent* aContent,
4601 nsFrameConstructorState* aState)
4602 {
4603 nsStyleSet *styleSet = mPresShell->StyleSet();
4604 aContent->OwnerDoc()->FlushPendingLinkUpdates();
4606 if (aContent->IsElement()) {
4607 if (aState) {
4608 return styleSet->ResolveStyleFor(aContent->AsElement(),
4609 aParentStyleContext,
4610 aState->mTreeMatchContext);
4611 }
4612 return styleSet->ResolveStyleFor(aContent->AsElement(), aParentStyleContext);
4614 }
4616 NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
4617 "shouldn't waste time creating style contexts for "
4618 "comments and processing instructions");
4620 return styleSet->ResolveStyleForNonElement(aParentStyleContext);
4621 }
4623 // MathML Mod - RBS
4624 void
4625 nsCSSFrameConstructor::FlushAccumulatedBlock(nsFrameConstructorState& aState,
4626 nsIContent* aContent,
4627 nsIFrame* aParentFrame,
4628 nsFrameItems& aBlockItems,
4629 nsFrameItems& aNewItems)
4630 {
4631 if (aBlockItems.IsEmpty()) {
4632 // Nothing to do
4633 return;
4634 }
4636 nsIAtom* anonPseudo = nsCSSAnonBoxes::mozMathMLAnonymousBlock;
4638 nsStyleContext* parentContext =
4639 nsFrame::CorrectStyleParentFrame(aParentFrame,
4640 anonPseudo)->StyleContext();
4641 nsStyleSet* styleSet = mPresShell->StyleSet();
4642 nsRefPtr<nsStyleContext> blockContext;
4643 blockContext = styleSet->
4644 ResolveAnonymousBoxStyle(anonPseudo, parentContext);
4647 // then, create a block frame that will wrap the child frames. Make it a
4648 // MathML frame so that Get(Absolute/Float)ContainingBlockFor know that this
4649 // is not a suitable block.
4650 nsIFrame* blockFrame =
4651 NS_NewMathMLmathBlockFrame(mPresShell, blockContext,
4652 NS_BLOCK_FLOAT_MGR | NS_BLOCK_MARGIN_ROOT);
4654 InitAndRestoreFrame(aState, aContent, aParentFrame, blockFrame);
4655 ReparentFrames(this, blockFrame, aBlockItems);
4656 // abs-pos and floats are disabled in MathML children so we don't have to
4657 // worry about messing up those.
4658 blockFrame->SetInitialChildList(kPrincipalList, aBlockItems);
4659 NS_ASSERTION(aBlockItems.IsEmpty(), "What happened?");
4660 aBlockItems.Clear();
4661 aNewItems.AddChild(blockFrame);
4662 }
4664 // Only <math> elements can be floated or positioned. All other MathML
4665 // should be in-flow.
4666 #define SIMPLE_MATHML_CREATE(_tag, _func) \
4667 { &nsGkAtoms::_tag, \
4668 FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW | \
4669 FCDATA_FORCE_NULL_ABSPOS_CONTAINER | \
4670 FCDATA_WRAP_KIDS_IN_BLOCKS, _func) }
4672 /* static */
4673 const nsCSSFrameConstructor::FrameConstructionData*
4674 nsCSSFrameConstructor::FindMathMLData(Element* aElement,
4675 nsIAtom* aTag,
4676 int32_t aNameSpaceID,
4677 nsStyleContext* aStyleContext)
4678 {
4679 // Make sure that we remain confined in the MathML world
4680 if (aNameSpaceID != kNameSpaceID_MathML)
4681 return nullptr;
4683 // Handle <math> specially, because it sometimes produces inlines
4684 if (aTag == nsGkAtoms::math) {
4685 // This needs to match the test in EnsureBlockDisplay in
4686 // nsRuleNode.cpp. Though the behavior here for the display:table
4687 // case is pretty weird...
4688 if (aStyleContext->StyleDisplay()->IsBlockOutsideStyle()) {
4689 static const FrameConstructionData sBlockMathData =
4690 FCDATA_DECL(FCDATA_FORCE_NULL_ABSPOS_CONTAINER |
4691 FCDATA_WRAP_KIDS_IN_BLOCKS,
4692 NS_CreateNewMathMLmathBlockFrame);
4693 return &sBlockMathData;
4694 }
4696 static const FrameConstructionData sInlineMathData =
4697 FCDATA_DECL(FCDATA_FORCE_NULL_ABSPOS_CONTAINER |
4698 FCDATA_IS_LINE_PARTICIPANT |
4699 FCDATA_WRAP_KIDS_IN_BLOCKS,
4700 NS_NewMathMLmathInlineFrame);
4701 return &sInlineMathData;
4702 }
4705 static const FrameConstructionDataByTag sMathMLData[] = {
4706 SIMPLE_MATHML_CREATE(annotation_, NS_NewMathMLTokenFrame),
4707 SIMPLE_MATHML_CREATE(annotation_xml_, NS_NewMathMLmrowFrame),
4708 SIMPLE_MATHML_CREATE(mi_, NS_NewMathMLTokenFrame),
4709 SIMPLE_MATHML_CREATE(mn_, NS_NewMathMLTokenFrame),
4710 SIMPLE_MATHML_CREATE(ms_, NS_NewMathMLTokenFrame),
4711 SIMPLE_MATHML_CREATE(mtext_, NS_NewMathMLTokenFrame),
4712 SIMPLE_MATHML_CREATE(mo_, NS_NewMathMLmoFrame),
4713 SIMPLE_MATHML_CREATE(mfrac_, NS_NewMathMLmfracFrame),
4714 SIMPLE_MATHML_CREATE(msup_, NS_NewMathMLmmultiscriptsFrame),
4715 SIMPLE_MATHML_CREATE(msub_, NS_NewMathMLmmultiscriptsFrame),
4716 SIMPLE_MATHML_CREATE(msubsup_, NS_NewMathMLmmultiscriptsFrame),
4717 SIMPLE_MATHML_CREATE(munder_, NS_NewMathMLmunderoverFrame),
4718 SIMPLE_MATHML_CREATE(mover_, NS_NewMathMLmunderoverFrame),
4719 SIMPLE_MATHML_CREATE(munderover_, NS_NewMathMLmunderoverFrame),
4720 SIMPLE_MATHML_CREATE(mphantom_, NS_NewMathMLmphantomFrame),
4721 SIMPLE_MATHML_CREATE(mpadded_, NS_NewMathMLmpaddedFrame),
4722 SIMPLE_MATHML_CREATE(mspace_, NS_NewMathMLmspaceFrame),
4723 SIMPLE_MATHML_CREATE(none, NS_NewMathMLmspaceFrame),
4724 SIMPLE_MATHML_CREATE(mprescripts_, NS_NewMathMLmspaceFrame),
4725 SIMPLE_MATHML_CREATE(mfenced_, NS_NewMathMLmfencedFrame),
4726 SIMPLE_MATHML_CREATE(mmultiscripts_, NS_NewMathMLmmultiscriptsFrame),
4727 SIMPLE_MATHML_CREATE(mstyle_, NS_NewMathMLmrowFrame),
4728 SIMPLE_MATHML_CREATE(msqrt_, NS_NewMathMLmsqrtFrame),
4729 SIMPLE_MATHML_CREATE(mroot_, NS_NewMathMLmrootFrame),
4730 SIMPLE_MATHML_CREATE(maction_, NS_NewMathMLmactionFrame),
4731 SIMPLE_MATHML_CREATE(mrow_, NS_NewMathMLmrowFrame),
4732 SIMPLE_MATHML_CREATE(merror_, NS_NewMathMLmrowFrame),
4733 SIMPLE_MATHML_CREATE(menclose_, NS_NewMathMLmencloseFrame),
4734 SIMPLE_MATHML_CREATE(semantics_, NS_NewMathMLsemanticsFrame)
4735 };
4737 return FindDataByTag(aTag, aElement, aStyleContext, sMathMLData,
4738 ArrayLength(sMathMLData));
4739 }
4742 nsIFrame*
4743 nsCSSFrameConstructor::ConstructFrameWithAnonymousChild(
4744 nsFrameConstructorState& aState,
4745 FrameConstructionItem& aItem,
4746 nsIFrame* aParentFrame,
4747 const nsStyleDisplay* aDisplay,
4748 nsFrameItems& aFrameItems,
4749 FrameCreationFunc aConstructor,
4750 FrameCreationFunc aInnerConstructor,
4751 nsICSSAnonBoxPseudo* aInnerPseudo,
4752 bool aCandidateRootFrame)
4753 {
4754 nsIContent* const content = aItem.mContent;
4755 nsStyleContext* const styleContext = aItem.mStyleContext;
4757 // Create the outer frame:
4758 nsIFrame* newFrame = aConstructor(mPresShell, styleContext);
4760 InitAndRestoreFrame(aState, content,
4761 aCandidateRootFrame ?
4762 aState.GetGeometricParent(styleContext->StyleDisplay(),
4763 aParentFrame) :
4764 aParentFrame,
4765 newFrame);
4767 // Create the pseudo SC for the anonymous wrapper child as a child of the SC:
4768 nsRefPtr<nsStyleContext> scForAnon;
4769 scForAnon = mPresShell->StyleSet()->
4770 ResolveAnonymousBoxStyle(aInnerPseudo, styleContext);
4772 // Create the anonymous inner wrapper frame
4773 nsIFrame* innerFrame = aInnerConstructor(mPresShell, scForAnon);
4775 InitAndRestoreFrame(aState, content, newFrame, innerFrame);
4777 // Put the newly created frames into the right child list
4778 SetInitialSingleChild(newFrame, innerFrame);
4780 aState.AddChild(newFrame, aFrameItems, content, styleContext, aParentFrame,
4781 aCandidateRootFrame, aCandidateRootFrame);
4783 if (!mRootElementFrame && aCandidateRootFrame) {
4784 // The frame we're constructing will be the root element frame.
4785 // Set mRootElementFrame before processing children.
4786 mRootElementFrame = newFrame;
4787 }
4789 nsFrameItems childItems;
4791 // Process children
4792 NS_ASSERTION(aItem.mAnonChildren.IsEmpty(),
4793 "nsIAnonymousContentCreator::CreateAnonymousContent should not "
4794 "be implemented for frames for which we explicitly create an "
4795 "anonymous child to wrap its child frames");
4796 if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) {
4797 ConstructFramesFromItemList(aState, aItem.mChildItems,
4798 innerFrame, childItems);
4799 } else {
4800 ProcessChildren(aState, content, styleContext, innerFrame,
4801 true, childItems, false, aItem.mPendingBinding);
4802 }
4804 // Set the inner wrapper frame's initial primary list
4805 innerFrame->SetInitialChildList(kPrincipalList, childItems);
4807 return newFrame;
4808 }
4810 nsIFrame*
4811 nsCSSFrameConstructor::ConstructOuterSVG(nsFrameConstructorState& aState,
4812 FrameConstructionItem& aItem,
4813 nsIFrame* aParentFrame,
4814 const nsStyleDisplay* aDisplay,
4815 nsFrameItems& aFrameItems)
4816 {
4817 return ConstructFrameWithAnonymousChild(
4818 aState, aItem, aParentFrame, aDisplay, aFrameItems,
4819 NS_NewSVGOuterSVGFrame, NS_NewSVGOuterSVGAnonChildFrame,
4820 nsCSSAnonBoxes::mozSVGOuterSVGAnonChild, true);
4821 }
4823 nsIFrame*
4824 nsCSSFrameConstructor::ConstructMarker(nsFrameConstructorState& aState,
4825 FrameConstructionItem& aItem,
4826 nsIFrame* aParentFrame,
4827 const nsStyleDisplay* aDisplay,
4828 nsFrameItems& aFrameItems)
4829 {
4830 return ConstructFrameWithAnonymousChild(
4831 aState, aItem, aParentFrame, aDisplay, aFrameItems,
4832 NS_NewSVGMarkerFrame, NS_NewSVGMarkerAnonChildFrame,
4833 nsCSSAnonBoxes::mozSVGMarkerAnonChild, false);
4834 }
4836 // Only outer <svg> elements can be floated or positioned. All other SVG
4837 // should be in-flow.
4838 #define SIMPLE_SVG_FCDATA(_func) \
4839 FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW | \
4840 FCDATA_SKIP_ABSPOS_PUSH | \
4841 FCDATA_DISALLOW_GENERATED_CONTENT, _func)
4842 #define SIMPLE_SVG_CREATE(_tag, _func) \
4843 { &nsGkAtoms::_tag, SIMPLE_SVG_FCDATA(_func) }
4845 static bool
4846 IsFilterPrimitiveChildTag(const nsIAtom* aTag)
4847 {
4848 return aTag == nsGkAtoms::feDistantLight ||
4849 aTag == nsGkAtoms::fePointLight ||
4850 aTag == nsGkAtoms::feSpotLight ||
4851 aTag == nsGkAtoms::feFuncR ||
4852 aTag == nsGkAtoms::feFuncG ||
4853 aTag == nsGkAtoms::feFuncB ||
4854 aTag == nsGkAtoms::feFuncA ||
4855 aTag == nsGkAtoms::feMergeNode;
4856 }
4858 /* static */
4859 const nsCSSFrameConstructor::FrameConstructionData*
4860 nsCSSFrameConstructor::FindSVGData(Element* aElement,
4861 nsIAtom* aTag,
4862 int32_t aNameSpaceID,
4863 nsIFrame* aParentFrame,
4864 bool aIsWithinSVGText,
4865 bool aAllowsTextPathChild,
4866 nsStyleContext* aStyleContext)
4867 {
4868 if (aNameSpaceID != kNameSpaceID_SVG) {
4869 return nullptr;
4870 }
4872 static const FrameConstructionData sSuppressData = SUPPRESS_FCDATA();
4873 static const FrameConstructionData sContainerData =
4874 SIMPLE_SVG_FCDATA(NS_NewSVGContainerFrame);
4876 bool parentIsSVG = aIsWithinSVGText;
4877 nsIContent* parentContent =
4878 aParentFrame ? aParentFrame->GetContent() : nullptr;
4879 // XXXbz should this really be based on the XBL-resolved tag of the parent
4880 // frame's content? Should it not be based on the type of the parent frame
4881 // (e.g. whether it's an SVG frame)?
4882 if (parentContent) {
4883 int32_t parentNSID;
4884 nsIAtom* parentTag =
4885 parentContent->OwnerDoc()->BindingManager()->
4886 ResolveTag(parentContent, &parentNSID);
4888 // It's not clear whether the SVG spec intends to allow any SVG
4889 // content within svg:foreignObject at all (SVG 1.1, section
4890 // 23.2), but if it does, it better be svg:svg. So given that
4891 // we're allowing it, treat it as a non-SVG parent.
4892 parentIsSVG = parentNSID == kNameSpaceID_SVG &&
4893 parentTag != nsGkAtoms::foreignObject;
4894 }
4896 if ((aTag != nsGkAtoms::svg && !parentIsSVG) ||
4897 (aTag == nsGkAtoms::desc || aTag == nsGkAtoms::title)) {
4898 // Sections 5.1 and G.4 of SVG 1.1 say that SVG elements other than
4899 // svg:svg not contained within svg:svg are incorrect, although they
4900 // don't seem to specify error handling. Ignore them, since many of
4901 // our frame classes can't deal. It *may* be that the document
4902 // should at that point be considered in error according to F.2, but
4903 // it's hard to tell.
4904 //
4905 // Style mutation can't change this situation, so don't bother
4906 // adding to the undisplayed content map.
4907 //
4908 // We don't currently handle any UI for desc/title
4909 return &sSuppressData;
4910 }
4912 // We don't need frames for animation elements
4913 if (aElement->IsNodeOfType(nsINode::eANIMATION)) {
4914 return &sSuppressData;
4915 }
4917 if (aTag == nsGkAtoms::svg && !parentIsSVG) {
4918 // We need outer <svg> elements to have an nsSVGOuterSVGFrame regardless
4919 // of whether they fail conditional processing attributes, since various
4920 // SVG frames assume that one exists. We handle the non-rendering
4921 // of failing outer <svg> element contents like <switch> statements,
4922 // and do the PassesConditionalProcessingTests call in
4923 // nsSVGOuterSVGFrame::Init.
4924 static const FrameConstructionData sOuterSVGData =
4925 FULL_CTOR_FCDATA(0, &nsCSSFrameConstructor::ConstructOuterSVG);
4926 return &sOuterSVGData;
4927 }
4929 if (aTag == nsGkAtoms::marker) {
4930 static const FrameConstructionData sMarkerSVGData =
4931 FULL_CTOR_FCDATA(0, &nsCSSFrameConstructor::ConstructMarker);
4932 return &sMarkerSVGData;
4933 }
4935 nsCOMPtr<SVGTests> tests(do_QueryInterface(aElement));
4936 if (tests && !tests->PassesConditionalProcessingTests()) {
4937 // Elements with failing conditional processing attributes never get
4938 // rendered. Note that this is not where we select which frame in a
4939 // <switch> to render! That happens in nsSVGSwitchFrame::PaintSVG.
4940 return &sContainerData;
4941 }
4943 // Prevent bad frame types being children of filters or parents of filter
4944 // primitives. If aParentFrame is null, we know that the frame that will
4945 // be created will be an nsInlineFrame, so it can never be a filter.
4946 bool parentIsFilter = aParentFrame &&
4947 aParentFrame->GetType() == nsGkAtoms::svgFilterFrame;
4948 bool filterPrimitive = aElement->IsNodeOfType(nsINode::eFILTER);
4949 if ((parentIsFilter && !filterPrimitive) ||
4950 (!parentIsFilter && filterPrimitive)) {
4951 return &sSuppressData;
4952 }
4954 // Prevent bad frame types being children of filter primitives or parents of
4955 // filter primitive children. If aParentFrame is null, we know that the frame
4956 // that will be created will be an nsInlineFrame, so it can never be a filter
4957 // primitive.
4958 bool parentIsFEContainerFrame = aParentFrame &&
4959 aParentFrame->GetType() == nsGkAtoms::svgFEContainerFrame;
4960 if ((parentIsFEContainerFrame && !IsFilterPrimitiveChildTag(aTag)) ||
4961 (!parentIsFEContainerFrame && IsFilterPrimitiveChildTag(aTag))) {
4962 return &sSuppressData;
4963 }
4965 // Special cases for text/tspan/textPath, because the kind of frame
4966 // they get depends on the parent frame. We ignore 'a' elements when
4967 // determining the parent, however.
4968 if (aIsWithinSVGText) {
4969 // If aIsWithinSVGText is true, then we know that the "SVG text uses
4970 // CSS frames" pref was true when this SVG fragment was first constructed.
4972 // We don't use ConstructInline because we want different behavior
4973 // for generated content.
4974 static const FrameConstructionData sTSpanData =
4975 FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW |
4976 FCDATA_SKIP_ABSPOS_PUSH |
4977 FCDATA_DISALLOW_GENERATED_CONTENT |
4978 FCDATA_IS_LINE_PARTICIPANT |
4979 FCDATA_IS_INLINE |
4980 FCDATA_USE_CHILD_ITEMS,
4981 NS_NewInlineFrame);
4982 if (aTag == nsGkAtoms::textPath) {
4983 if (aAllowsTextPathChild) {
4984 return &sTSpanData;
4985 }
4986 } else if (aTag == nsGkAtoms::tspan ||
4987 aTag == nsGkAtoms::altGlyph ||
4988 aTag == nsGkAtoms::a) {
4989 return &sTSpanData;
4990 }
4991 return &sSuppressData;
4992 } else if (aTag == nsGkAtoms::text) {
4993 static const FrameConstructionData sTextData =
4994 FCDATA_WITH_WRAPPING_BLOCK(FCDATA_DISALLOW_OUT_OF_FLOW |
4995 FCDATA_ALLOW_BLOCK_STYLES,
4996 NS_NewSVGTextFrame,
4997 nsCSSAnonBoxes::mozSVGText);
4998 return &sTextData;
4999 } else if (aTag == nsGkAtoms::tspan ||
5000 aTag == nsGkAtoms::altGlyph ||
5001 aTag == nsGkAtoms::textPath) {
5002 return &sSuppressData;
5003 }
5005 static const FrameConstructionDataByTag sSVGData[] = {
5006 SIMPLE_SVG_CREATE(svg, NS_NewSVGInnerSVGFrame),
5007 SIMPLE_SVG_CREATE(g, NS_NewSVGGFrame),
5008 SIMPLE_SVG_CREATE(svgSwitch, NS_NewSVGSwitchFrame),
5009 SIMPLE_SVG_CREATE(polygon, NS_NewSVGPathGeometryFrame),
5010 SIMPLE_SVG_CREATE(polyline, NS_NewSVGPathGeometryFrame),
5011 SIMPLE_SVG_CREATE(circle, NS_NewSVGPathGeometryFrame),
5012 SIMPLE_SVG_CREATE(ellipse, NS_NewSVGPathGeometryFrame),
5013 SIMPLE_SVG_CREATE(line, NS_NewSVGPathGeometryFrame),
5014 SIMPLE_SVG_CREATE(rect, NS_NewSVGPathGeometryFrame),
5015 SIMPLE_SVG_CREATE(path, NS_NewSVGPathGeometryFrame),
5016 SIMPLE_SVG_CREATE(defs, NS_NewSVGContainerFrame),
5017 SIMPLE_SVG_CREATE(generic_, NS_NewSVGGenericContainerFrame),
5018 { &nsGkAtoms::foreignObject,
5019 FCDATA_WITH_WRAPPING_BLOCK(FCDATA_DISALLOW_OUT_OF_FLOW,
5020 NS_NewSVGForeignObjectFrame,
5021 nsCSSAnonBoxes::mozSVGForeignContent) },
5022 SIMPLE_SVG_CREATE(a, NS_NewSVGAFrame),
5023 SIMPLE_SVG_CREATE(linearGradient, NS_NewSVGLinearGradientFrame),
5024 SIMPLE_SVG_CREATE(radialGradient, NS_NewSVGRadialGradientFrame),
5025 SIMPLE_SVG_CREATE(stop, NS_NewSVGStopFrame),
5026 SIMPLE_SVG_CREATE(use, NS_NewSVGUseFrame),
5027 SIMPLE_SVG_CREATE(view, NS_NewSVGViewFrame),
5028 SIMPLE_SVG_CREATE(image, NS_NewSVGImageFrame),
5029 SIMPLE_SVG_CREATE(clipPath, NS_NewSVGClipPathFrame),
5030 SIMPLE_SVG_CREATE(filter, NS_NewSVGFilterFrame),
5031 SIMPLE_SVG_CREATE(pattern, NS_NewSVGPatternFrame),
5032 SIMPLE_SVG_CREATE(mask, NS_NewSVGMaskFrame),
5033 SIMPLE_SVG_CREATE(feDistantLight, NS_NewSVGFEUnstyledLeafFrame),
5034 SIMPLE_SVG_CREATE(fePointLight, NS_NewSVGFEUnstyledLeafFrame),
5035 SIMPLE_SVG_CREATE(feSpotLight, NS_NewSVGFEUnstyledLeafFrame),
5036 SIMPLE_SVG_CREATE(feBlend, NS_NewSVGFELeafFrame),
5037 SIMPLE_SVG_CREATE(feColorMatrix, NS_NewSVGFELeafFrame),
5038 SIMPLE_SVG_CREATE(feFuncR, NS_NewSVGFEUnstyledLeafFrame),
5039 SIMPLE_SVG_CREATE(feFuncG, NS_NewSVGFEUnstyledLeafFrame),
5040 SIMPLE_SVG_CREATE(feFuncB, NS_NewSVGFEUnstyledLeafFrame),
5041 SIMPLE_SVG_CREATE(feFuncA, NS_NewSVGFEUnstyledLeafFrame),
5042 SIMPLE_SVG_CREATE(feComposite, NS_NewSVGFELeafFrame),
5043 SIMPLE_SVG_CREATE(feComponentTransfer, NS_NewSVGFEContainerFrame),
5044 SIMPLE_SVG_CREATE(feConvolveMatrix, NS_NewSVGFELeafFrame),
5045 SIMPLE_SVG_CREATE(feDiffuseLighting, NS_NewSVGFEContainerFrame),
5046 SIMPLE_SVG_CREATE(feDisplacementMap, NS_NewSVGFELeafFrame),
5047 SIMPLE_SVG_CREATE(feDropShadow, NS_NewSVGFELeafFrame),
5048 SIMPLE_SVG_CREATE(feFlood, NS_NewSVGFELeafFrame),
5049 SIMPLE_SVG_CREATE(feGaussianBlur, NS_NewSVGFELeafFrame),
5050 SIMPLE_SVG_CREATE(feImage, NS_NewSVGFEImageFrame),
5051 SIMPLE_SVG_CREATE(feMerge, NS_NewSVGFEContainerFrame),
5052 SIMPLE_SVG_CREATE(feMergeNode, NS_NewSVGFEUnstyledLeafFrame),
5053 SIMPLE_SVG_CREATE(feMorphology, NS_NewSVGFELeafFrame),
5054 SIMPLE_SVG_CREATE(feOffset, NS_NewSVGFELeafFrame),
5055 SIMPLE_SVG_CREATE(feSpecularLighting, NS_NewSVGFEContainerFrame),
5056 SIMPLE_SVG_CREATE(feTile, NS_NewSVGFELeafFrame),
5057 SIMPLE_SVG_CREATE(feTurbulence, NS_NewSVGFELeafFrame)
5058 };
5060 const FrameConstructionData* data =
5061 FindDataByTag(aTag, aElement, aStyleContext, sSVGData,
5062 ArrayLength(sSVGData));
5064 if (!data) {
5065 data = &sContainerData;
5066 }
5068 return data;
5069 }
5071 void
5072 nsCSSFrameConstructor::AddPageBreakItem(nsIContent* aContent,
5073 nsStyleContext* aMainStyleContext,
5074 FrameConstructionItemList& aItems)
5075 {
5076 // Use the same parent style context that |aMainStyleContext| has, since
5077 // that's easier to re-resolve and it doesn't matter in practice.
5078 // (Getting different parents can result in framechange hints, e.g.,
5079 // for user-modify.)
5080 nsRefPtr<nsStyleContext> pseudoStyle =
5081 mPresShell->StyleSet()->
5082 ResolveAnonymousBoxStyle(nsCSSAnonBoxes::pageBreak,
5083 aMainStyleContext->GetParent());
5085 NS_ASSERTION(pseudoStyle->StyleDisplay()->mDisplay ==
5086 NS_STYLE_DISPLAY_BLOCK, "Unexpected display");
5088 static const FrameConstructionData sPageBreakData =
5089 FCDATA_DECL(FCDATA_SKIP_FRAMESET, NS_NewPageBreakFrame);
5091 // Lie about the tag and namespace so we don't trigger anything
5092 // interesting during frame construction.
5093 aItems.AppendItem(&sPageBreakData, aContent, nsCSSAnonBoxes::pageBreak,
5094 kNameSpaceID_None, nullptr, pseudoStyle.forget(),
5095 true, nullptr);
5096 }
5098 void
5099 nsCSSFrameConstructor::AddFrameConstructionItems(nsFrameConstructorState& aState,
5100 nsIContent* aContent,
5101 bool aSuppressWhiteSpaceOptimizations,
5102 nsIFrame* aParentFrame,
5103 FrameConstructionItemList& aItems)
5104 {
5105 aContent->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
5106 if (aContent->IsElement()) {
5107 // We can't just remove our pending restyle flags, since we may
5108 // have restyle-later-siblings set on us. But we _can_ remove the
5109 // "is possible restyle root" flags, and need to. Otherwise we can
5110 // end up with stale such flags (e.g. if we used to have a
5111 // display:none parent when our last restyle was posted and
5112 // processed and now no longer do).
5113 aContent->UnsetFlags(ELEMENT_ALL_RESTYLE_FLAGS &
5114 ~ELEMENT_PENDING_RESTYLE_FLAGS);
5115 }
5117 // XXX the GetContent() != aContent check is needed due to bug 135040.
5118 // Remove it once that's fixed.
5119 if (aContent->GetPrimaryFrame() &&
5120 aContent->GetPrimaryFrame()->GetContent() == aContent &&
5121 !aState.mCreatingExtraFrames) {
5122 NS_ERROR("asked to create frame construction item for a node that already "
5123 "has a frame");
5124 return;
5125 }
5127 // don't create a whitespace frame if aParent doesn't want it
5128 if (!NeedFrameFor(aState, aParentFrame, aContent)) {
5129 return;
5130 }
5132 // never create frames for comments or PIs
5133 if (aContent->IsNodeOfType(nsINode::eCOMMENT) ||
5134 aContent->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION))
5135 return;
5137 nsRefPtr<nsStyleContext> styleContext;
5138 styleContext = ResolveStyleContext(aParentFrame, aContent, &aState);
5140 uint32_t flags = ITEM_ALLOW_XBL_BASE | ITEM_ALLOW_PAGE_BREAK;
5141 if (aParentFrame->IsSVGText()) {
5142 flags |= ITEM_IS_WITHIN_SVG_TEXT;
5143 }
5144 if (aParentFrame->GetType() == nsGkAtoms::blockFrame &&
5145 aParentFrame->GetParent() &&
5146 aParentFrame->GetParent()->GetType() == nsGkAtoms::svgTextFrame) {
5147 flags |= ITEM_ALLOWS_TEXT_PATH_CHILD;
5148 }
5149 AddFrameConstructionItemsInternal(aState, aContent, aParentFrame,
5150 aContent->Tag(), aContent->GetNameSpaceID(),
5151 aSuppressWhiteSpaceOptimizations,
5152 styleContext,
5153 flags, nullptr,
5154 aItems);
5155 }
5157 /* static */ void
5158 nsCSSFrameConstructor::SetAsUndisplayedContent(FrameConstructionItemList& aList,
5159 nsIContent* aContent,
5160 nsStyleContext* aStyleContext,
5161 bool aIsGeneratedContent)
5162 {
5163 if (aStyleContext->GetPseudo()) {
5164 if (aIsGeneratedContent) {
5165 aContent->UnbindFromTree();
5166 }
5167 return;
5168 }
5170 NS_ASSERTION(!aIsGeneratedContent, "Should have had pseudo type");
5171 aList.AppendUndisplayedItem(aContent, aStyleContext);
5172 }
5174 void
5175 nsCSSFrameConstructor::AddFrameConstructionItemsInternal(nsFrameConstructorState& aState,
5176 nsIContent* aContent,
5177 nsIFrame* aParentFrame,
5178 nsIAtom* aTag,
5179 int32_t aNameSpaceID,
5180 bool aSuppressWhiteSpaceOptimizations,
5181 nsStyleContext* aStyleContext,
5182 uint32_t aFlags,
5183 nsTArray<nsIAnonymousContentCreator::ContentInfo>* aAnonChildren,
5184 FrameConstructionItemList& aItems)
5185 {
5186 NS_PRECONDITION(aContent->IsNodeOfType(nsINode::eTEXT) ||
5187 aContent->IsElement(),
5188 "Shouldn't get anything else here!");
5190 // The following code allows the user to specify the base tag
5191 // of an element using XBL. XUL and HTML objects (like boxes, menus, etc.)
5192 // can then be extended arbitrarily.
5193 const nsStyleDisplay* display = aStyleContext->StyleDisplay();
5194 nsRefPtr<nsStyleContext> styleContext(aStyleContext);
5195 PendingBinding* pendingBinding = nullptr;
5196 if ((aFlags & ITEM_ALLOW_XBL_BASE) && display->mBinding)
5197 {
5198 // Ensure that our XBL bindings are installed.
5200 nsXBLService* xblService = nsXBLService::GetInstance();
5201 if (!xblService)
5202 return;
5204 bool resolveStyle;
5206 nsAutoPtr<PendingBinding> newPendingBinding(new PendingBinding());
5208 nsresult rv = xblService->LoadBindings(aContent, display->mBinding->GetURI(),
5209 display->mBinding->mOriginPrincipal,
5210 getter_AddRefs(newPendingBinding->mBinding),
5211 &resolveStyle);
5212 if (NS_FAILED(rv) && rv != NS_ERROR_XBL_BLOCKED)
5213 return;
5215 if (newPendingBinding->mBinding) {
5216 pendingBinding = newPendingBinding;
5217 // aState takes over owning newPendingBinding
5218 aState.AddPendingBinding(newPendingBinding.forget());
5219 }
5221 if (resolveStyle) {
5222 styleContext =
5223 ResolveStyleContext(styleContext->GetParent(), aContent, &aState);
5224 display = styleContext->StyleDisplay();
5225 aStyleContext = styleContext;
5226 }
5228 aTag = mDocument->BindingManager()->ResolveTag(aContent, &aNameSpaceID);
5229 }
5231 bool isGeneratedContent = ((aFlags & ITEM_IS_GENERATED_CONTENT) != 0);
5233 // Pre-check for display "none" - if we find that, don't create
5234 // any frame at all
5235 if (NS_STYLE_DISPLAY_NONE == display->mDisplay) {
5236 SetAsUndisplayedContent(aItems, aContent, styleContext, isGeneratedContent);
5237 return;
5238 }
5240 bool isText = !aContent->IsElement();
5242 // never create frames for non-option/optgroup kids of <select> and
5243 // non-option kids of <optgroup> inside a <select>.
5244 // XXXbz it's not clear how this should best work with XBL.
5245 nsIContent *parent = aContent->GetParent();
5246 if (parent) {
5247 // Check tag first, since that check will usually fail
5248 nsIAtom* parentTag = parent->Tag();
5249 if ((parentTag == nsGkAtoms::select || parentTag == nsGkAtoms::optgroup) &&
5250 parent->IsHTML() &&
5251 // <option> is ok no matter what
5252 !aContent->IsHTML(nsGkAtoms::option) &&
5253 // <optgroup> is OK in <select> but not in <optgroup>
5254 (!aContent->IsHTML(nsGkAtoms::optgroup) ||
5255 parentTag != nsGkAtoms::select) &&
5256 // Allow native anonymous content no matter what
5257 !aContent->IsRootOfNativeAnonymousSubtree()) {
5258 // No frame for aContent
5259 if (!isText) {
5260 SetAsUndisplayedContent(aItems, aContent, styleContext,
5261 isGeneratedContent);
5262 }
5263 return;
5264 }
5265 }
5267 bool isPopup = false;
5268 // Try to find frame construction data for this content
5269 const FrameConstructionData* data;
5270 if (isText) {
5271 data = FindTextData(aParentFrame);
5272 if (!data) {
5273 // Nothing to do here; suppressed text inside SVG
5274 return;
5275 }
5276 } else {
5277 Element* element = aContent->AsElement();
5279 // Don't create frames for non-SVG element children of SVG elements.
5280 if (aNameSpaceID != kNameSpaceID_SVG &&
5281 ((aParentFrame &&
5282 IsFrameForSVG(aParentFrame) &&
5283 !aParentFrame->IsFrameOfType(nsIFrame::eSVGForeignObject)) ||
5284 (aFlags & ITEM_IS_WITHIN_SVG_TEXT))) {
5285 SetAsUndisplayedContent(aItems, element, styleContext,
5286 isGeneratedContent);
5287 return;
5288 }
5290 data = FindHTMLData(element, aTag, aNameSpaceID, aParentFrame,
5291 styleContext);
5292 if (!data) {
5293 data = FindXULTagData(element, aTag, aNameSpaceID, styleContext);
5294 }
5295 if (!data) {
5296 data = FindMathMLData(element, aTag, aNameSpaceID, styleContext);
5297 }
5298 if (!data) {
5299 data = FindSVGData(element, aTag, aNameSpaceID, aParentFrame,
5300 aFlags & ITEM_IS_WITHIN_SVG_TEXT,
5301 aFlags & ITEM_ALLOWS_TEXT_PATH_CHILD,
5302 styleContext);
5303 }
5305 // Now check for XUL display types
5306 if (!data) {
5307 data = FindXULDisplayData(display, element, styleContext);
5308 }
5310 // And general display types
5311 if (!data) {
5312 data = FindDisplayData(display, element, aParentFrame, styleContext);
5313 }
5315 NS_ASSERTION(data, "Should have frame construction data now");
5317 if (data->mBits & FCDATA_SUPPRESS_FRAME) {
5318 SetAsUndisplayedContent(aItems, element, styleContext, isGeneratedContent);
5319 return;
5320 }
5322 #ifdef MOZ_XUL
5323 if ((data->mBits & FCDATA_IS_POPUP) &&
5324 (!aParentFrame || // Parent is inline
5325 aParentFrame->GetType() != nsGkAtoms::menuFrame)) {
5326 if (!aState.mPopupItems.containingBlock &&
5327 !aState.mHavePendingPopupgroup) {
5328 SetAsUndisplayedContent(aItems, element, styleContext,
5329 isGeneratedContent);
5330 return;
5331 }
5333 isPopup = true;
5334 }
5335 #endif /* MOZ_XUL */
5336 }
5338 uint32_t bits = data->mBits;
5340 // Inside colgroups, suppress everything except columns.
5341 if (aParentFrame &&
5342 aParentFrame->GetType() == nsGkAtoms::tableColGroupFrame &&
5343 (!(bits & FCDATA_IS_TABLE_PART) ||
5344 display->mDisplay != NS_STYLE_DISPLAY_TABLE_COLUMN)) {
5345 SetAsUndisplayedContent(aItems, aContent, styleContext, isGeneratedContent);
5346 return;
5347 }
5349 bool canHavePageBreak =
5350 (aFlags & ITEM_ALLOW_PAGE_BREAK) &&
5351 aState.mPresContext->IsPaginated() &&
5352 !display->IsAbsolutelyPositionedStyle() &&
5353 !(bits & FCDATA_IS_TABLE_PART) &&
5354 !(bits & FCDATA_IS_SVG_TEXT);
5356 if (canHavePageBreak && display->mBreakBefore) {
5357 AddPageBreakItem(aContent, aStyleContext, aItems);
5358 }
5360 FrameConstructionItem* item =
5361 aItems.AppendItem(data, aContent, aTag, aNameSpaceID,
5362 pendingBinding, styleContext.forget(),
5363 aSuppressWhiteSpaceOptimizations, aAnonChildren);
5364 if (!item) {
5365 if (isGeneratedContent) {
5366 aContent->UnbindFromTree();
5367 }
5368 return;
5369 }
5371 item->mIsText = isText;
5372 item->mIsGeneratedContent = isGeneratedContent;
5373 item->mIsAnonymousContentCreatorContent =
5374 aFlags & ITEM_IS_ANONYMOUSCONTENTCREATOR_CONTENT;
5375 if (isGeneratedContent) {
5376 NS_ADDREF(item->mContent);
5377 }
5378 item->mIsRootPopupgroup =
5379 aNameSpaceID == kNameSpaceID_XUL && aTag == nsGkAtoms::popupgroup &&
5380 aContent->IsRootOfNativeAnonymousSubtree();
5381 if (item->mIsRootPopupgroup) {
5382 aState.mHavePendingPopupgroup = true;
5383 }
5384 item->mIsPopup = isPopup;
5385 item->mIsForSVGAElement = aNameSpaceID == kNameSpaceID_SVG &&
5386 aTag == nsGkAtoms::a;
5388 if (canHavePageBreak && display->mBreakAfter) {
5389 AddPageBreakItem(aContent, aStyleContext, aItems);
5390 }
5392 if (bits & FCDATA_IS_INLINE) {
5393 // To correctly set item->mIsAllInline we need to build up our child items
5394 // right now.
5395 BuildInlineChildItems(aState, *item,
5396 aFlags & ITEM_IS_WITHIN_SVG_TEXT,
5397 aFlags & ITEM_ALLOWS_TEXT_PATH_CHILD);
5398 item->mHasInlineEnds = true;
5399 item->mIsBlock = false;
5400 } else {
5401 // Compute a boolean isInline which is guaranteed to be false for blocks
5402 // (but may also be false for some inlines).
5403 bool isInline =
5404 // Table-internal things are inline-outside if and only if they're kids of
5405 // inlines, since they'll trigger construction of inline-table
5406 // pseudos.
5407 ((bits & FCDATA_IS_TABLE_PART) &&
5408 (!aParentFrame || // No aParentFrame means inline
5409 aParentFrame->StyleDisplay()->mDisplay == NS_STYLE_DISPLAY_INLINE)) ||
5410 // Things that are inline-outside but aren't inline frames are inline
5411 display->IsInlineOutsideStyle() ||
5412 // Popups that are certainly out of flow.
5413 isPopup;
5415 // Set mIsAllInline conservatively. It just might be that even an inline
5416 // that has mIsAllInline false doesn't need an {ib} split. So this is just
5417 // an optimization to keep from doing too much work in cases when we can
5418 // show that mIsAllInline is true..
5419 item->mIsAllInline = item->mHasInlineEnds = isInline ||
5420 // Figure out whether we're guaranteed this item will be out of flow.
5421 // This is not a precise test, since one of our ancestor inlines might add
5422 // an absolute containing block (if it's relatively positioned) when there
5423 // wasn't such a containing block before. But it's conservative in the
5424 // sense that anything that will really end up as an in-flow non-inline
5425 // will test false here. In other words, if this test is true we're
5426 // guaranteed to be inline; if it's false we don't know what we'll end up
5427 // as.
5428 //
5429 // If we make this test precise, we can remove some of the code dealing
5430 // with the imprecision in ConstructInline and adjust the comments on
5431 // mIsAllInline and mIsBlock in the header. And probably remove mIsBlock
5432 // altogether, since then it will always be equal to !mHasInlineEnds.
5433 (!(bits & FCDATA_DISALLOW_OUT_OF_FLOW) &&
5434 aState.GetGeometricParent(display, nullptr));
5436 // Set mIsBlock conservatively. It's OK to set it false for some real
5437 // blocks, but not OK to set it true for things that aren't blocks. Since
5438 // isOutOfFlow might be false even in cases when the frame will end up
5439 // out-of-flow, we can't use it here. But we _can_ say that the frame will
5440 // for sure end up in-flow if it's not floated or absolutely positioned.
5441 item->mIsBlock = !isInline &&
5442 !display->IsAbsolutelyPositionedStyle() &&
5443 !display->IsFloatingStyle() &&
5444 !(bits & FCDATA_IS_SVG_TEXT);
5445 }
5447 if (item->mIsAllInline) {
5448 aItems.InlineItemAdded();
5449 } else if (item->mIsBlock) {
5450 aItems.BlockItemAdded();
5451 }
5453 // Our item should be treated as a line participant if we have the relevant
5454 // bit and are going to be in-flow. Note that this really only matters if
5455 // our ancestor is a box or some such, so the fact that we might have an
5456 // inline ancestor that might become a containing block is not relevant here.
5457 if ((bits & FCDATA_IS_LINE_PARTICIPANT) &&
5458 ((bits & FCDATA_DISALLOW_OUT_OF_FLOW) ||
5459 !aState.GetGeometricParent(display, nullptr))) {
5460 item->mIsLineParticipant = true;
5461 aItems.LineParticipantItemAdded();
5462 }
5463 }
5465 static void
5466 DestroyContent(void* aPropertyValue)
5467 {
5468 nsIContent* content = static_cast<nsIContent*>(aPropertyValue);
5469 content->UnbindFromTree();
5470 NS_RELEASE(content);
5471 }
5473 NS_DECLARE_FRAME_PROPERTY(BeforeProperty, DestroyContent)
5474 NS_DECLARE_FRAME_PROPERTY(AfterProperty, DestroyContent)
5476 static const FramePropertyDescriptor*
5477 GenConPseudoToProperty(nsIAtom* aPseudo)
5478 {
5479 NS_ASSERTION(aPseudo == nsCSSPseudoElements::before ||
5480 aPseudo == nsCSSPseudoElements::after,
5481 "Bad gen-con pseudo");
5482 return aPseudo == nsCSSPseudoElements::before ? BeforeProperty()
5483 : AfterProperty();
5484 }
5486 /**
5487 * Return true if the frame construction item pointed to by aIter will
5488 * create a frame adjacent to a line boundary in the frame tree, and that
5489 * line boundary is induced by a content node adjacent to the frame's
5490 * content node in the content tree. The latter condition is necessary so
5491 * that ContentAppended/ContentInserted/ContentRemoved can easily find any
5492 * text nodes that were suppressed here.
5493 */
5494 bool
5495 nsCSSFrameConstructor::AtLineBoundary(FCItemIterator& aIter)
5496 {
5497 if (aIter.item().mSuppressWhiteSpaceOptimizations) {
5498 return false;
5499 }
5501 if (aIter.AtStart()) {
5502 if (aIter.List()->HasLineBoundaryAtStart() &&
5503 !aIter.item().mContent->GetPreviousSibling())
5504 return true;
5505 } else {
5506 FCItemIterator prev = aIter;
5507 prev.Prev();
5508 if (prev.item().IsLineBoundary() &&
5509 !prev.item().mSuppressWhiteSpaceOptimizations &&
5510 aIter.item().mContent->GetPreviousSibling() == prev.item().mContent)
5511 return true;
5512 }
5514 FCItemIterator next = aIter;
5515 next.Next();
5516 if (next.IsDone()) {
5517 if (aIter.List()->HasLineBoundaryAtEnd() &&
5518 !aIter.item().mContent->GetNextSibling())
5519 return true;
5520 } else {
5521 if (next.item().IsLineBoundary() &&
5522 !next.item().mSuppressWhiteSpaceOptimizations &&
5523 aIter.item().mContent->GetNextSibling() == next.item().mContent)
5524 return true;
5525 }
5527 return false;
5528 }
5530 void
5531 nsCSSFrameConstructor::ConstructFramesFromItem(nsFrameConstructorState& aState,
5532 FCItemIterator& aIter,
5533 nsIFrame* aParentFrame,
5534 nsFrameItems& aFrameItems)
5535 {
5536 nsIFrame* adjParentFrame = aParentFrame;
5537 FrameConstructionItem& item = aIter.item();
5538 nsStyleContext* styleContext = item.mStyleContext;
5539 AdjustParentFrame(adjParentFrame, item.mFCData, styleContext);
5541 if (item.mIsText) {
5542 // If this is collapsible whitespace next to a line boundary,
5543 // don't create a frame. item.IsWhitespace() also sets the
5544 // NS_CREATE_FRAME_IF_NON_WHITESPACE flag in the text node. (If we
5545 // end up creating a frame, nsTextFrame::Init will clear the flag.)
5546 // We don't do this for generated content, because some generated
5547 // text content is empty text nodes that are about to be initialized.
5548 // (We check mAdditionalStateBits because only the generated content
5549 // container's frame construction item is marked with
5550 // mIsGeneratedContent, and we might not have an aParentFrame.)
5551 // We don't do it for content that may have XBL anonymous siblings,
5552 // because they make it difficult to correctly create the frame
5553 // due to dynamic changes.
5554 // We don't do it for SVG text, since we might need to position and
5555 // measure the white space glyphs due to x/y/dx/dy attributes.
5556 if (AtLineBoundary(aIter) &&
5557 !styleContext->StyleText()->WhiteSpaceOrNewlineIsSignificant() &&
5558 aIter.List()->ParentHasNoXBLChildren() &&
5559 !(aState.mAdditionalStateBits & NS_FRAME_GENERATED_CONTENT) &&
5560 (item.mFCData->mBits & FCDATA_IS_LINE_PARTICIPANT) &&
5561 !(item.mFCData->mBits & FCDATA_IS_SVG_TEXT) &&
5562 !mAlwaysCreateFramesForIgnorableWhitespace &&
5563 item.IsWhitespace(aState))
5564 return;
5566 ConstructTextFrame(item.mFCData, aState, item.mContent,
5567 adjParentFrame, styleContext,
5568 aFrameItems);
5569 return;
5570 }
5572 // Start background loads during frame construction so that we're
5573 // guaranteed that they will be started before onload fires.
5574 styleContext->StartBackgroundImageLoads();
5576 nsFrameState savedStateBits = aState.mAdditionalStateBits;
5577 if (item.mIsGeneratedContent) {
5578 // Ensure that frames created here are all tagged with
5579 // NS_FRAME_GENERATED_CONTENT.
5580 aState.mAdditionalStateBits |= NS_FRAME_GENERATED_CONTENT;
5582 // Note that we're not necessarily setting this property on the primary
5583 // frame for the content for which this is generated content. We might be
5584 // setting it on a table pseudo-frame inserted under that instead. That's
5585 // OK, though; we just need to do the property set so that the content will
5586 // get cleaned up when the frame is destroyed.
5587 aParentFrame->Properties().Set(GenConPseudoToProperty(styleContext->GetPseudo()),
5588 item.mContent);
5590 // Now that we've passed ownership of item.mContent to the frame, unset
5591 // our generated content flag so we don't release or unbind it ourselves.
5592 item.mIsGeneratedContent = false;
5593 }
5595 // XXXbz maybe just inline ConstructFrameFromItemInternal here or something?
5596 ConstructFrameFromItemInternal(item, aState, adjParentFrame, aFrameItems);
5598 aState.mAdditionalStateBits = savedStateBits;
5599 }
5602 inline bool
5603 IsRootBoxFrame(nsIFrame *aFrame)
5604 {
5605 return (aFrame->GetType() == nsGkAtoms::rootFrame);
5606 }
5608 nsresult
5609 nsCSSFrameConstructor::ReconstructDocElementHierarchy()
5610 {
5611 Element* rootElement = mDocument->GetRootElement();
5612 if (!rootElement) {
5613 /* nothing to do */
5614 return NS_OK;
5615 }
5616 return RecreateFramesForContent(rootElement, false);
5617 }
5619 nsIFrame*
5620 nsCSSFrameConstructor::GetFrameFor(nsIContent* aContent)
5621 {
5622 // Get the primary frame associated with the content
5623 nsIFrame* frame = aContent->GetPrimaryFrame();
5625 if (!frame)
5626 return nullptr;
5628 // If the content of the frame is not the desired content then this is not
5629 // really a frame for the desired content.
5630 // XXX This check is needed due to bug 135040. Remove it once that's fixed.
5631 if (frame->GetContent() != aContent) {
5632 return nullptr;
5633 }
5635 nsIFrame* insertionFrame = frame->GetContentInsertionFrame();
5637 NS_ASSERTION(insertionFrame == frame || !frame->IsLeaf(),
5638 "The insertion frame is the primary frame or the primary frame isn't a leaf");
5640 return insertionFrame;
5641 }
5643 nsIFrame*
5644 nsCSSFrameConstructor::GetAbsoluteContainingBlock(nsIFrame* aFrame,
5645 ContainingBlockType aType)
5646 {
5647 NS_PRECONDITION(nullptr != mRootElementFrame, "no root element frame");
5649 // Starting with aFrame, look for a frame that is absolutely positioned or
5650 // relatively positioned (and transformed, if aType is FIXED)
5651 for (nsIFrame* frame = aFrame; frame; frame = frame->GetParent()) {
5652 if (frame->IsFrameOfType(nsIFrame::eMathML)) {
5653 // If it's mathml, bail out -- no absolute positioning out from inside
5654 // mathml frames. Note that we don't make this part of the loop
5655 // condition because of the stuff at the end of this method...
5656 return nullptr;
5657 }
5659 // If the frame is positioned, we will probably return it as the containing
5660 // block (see the exceptions below). Otherwise, we'll start looking at the
5661 // parent frame, unless we're dealing with a scrollframe.
5662 // Scrollframes are special since they're not positioned, but their
5663 // scrolledframe might be. So, we need to check this special case to return
5664 // the correct containing block (the scrolledframe) in that case.
5665 // If we're looking for a fixed-pos containing block and the frame is
5666 // not transformed, skip it.
5667 if (!frame->IsPositioned() ||
5668 (aType == FIXED_POS &&
5669 !frame->StyleDisplay()->HasTransform(frame) &&
5670 !frame->StyleDisplay()->HasPerspectiveStyle())) {
5671 continue;
5672 }
5673 nsIFrame* absPosCBCandidate = frame;
5674 nsIAtom* type = absPosCBCandidate->GetType();
5675 if (type == nsGkAtoms::fieldSetFrame) {
5676 absPosCBCandidate = static_cast<nsFieldSetFrame*>(absPosCBCandidate)->GetInner();
5677 if (!absPosCBCandidate) {
5678 continue;
5679 }
5680 type = absPosCBCandidate->GetType();
5681 }
5682 if (type == nsGkAtoms::scrollFrame) {
5683 nsIScrollableFrame* scrollFrame = do_QueryFrame(absPosCBCandidate);
5684 absPosCBCandidate = scrollFrame->GetScrolledFrame();
5685 if (!absPosCBCandidate) {
5686 continue;
5687 }
5688 type = absPosCBCandidate->GetType();
5689 }
5690 // Only first continuations can be containing blocks.
5691 absPosCBCandidate = absPosCBCandidate->FirstContinuation();
5692 // Is the frame really an absolute container?
5693 if (!absPosCBCandidate->IsAbsoluteContainer()) {
5694 continue;
5695 }
5697 // For tables, skip the inner frame and consider the outer table frame.
5698 if (type == nsGkAtoms::tableFrame) {
5699 continue;
5700 }
5701 // For outer table frames, we can just return absPosCBCandidate.
5702 return absPosCBCandidate;
5703 }
5705 // It is possible for the search for the containing block to fail, because
5706 // no absolute container can be found in the parent chain. In those cases,
5707 // we fall back to the document element's containing block.
5708 if (aType == FIXED_POS) {
5709 return mFixedContainingBlock;
5710 }
5711 return mHasRootAbsPosContainingBlock ? mDocElementContainingBlock : nullptr;
5712 }
5714 nsIFrame*
5715 nsCSSFrameConstructor::GetFloatContainingBlock(nsIFrame* aFrame)
5716 {
5717 // Starting with aFrame, look for a frame that is a float containing block.
5718 // IF we hit a mathml frame, bail out; we don't allow floating out of mathml
5719 // frames, because they don't seem to be able to deal.
5720 // The logic here needs to match the logic in ProcessChildren()
5721 for (nsIFrame* containingBlock = aFrame;
5722 containingBlock &&
5723 !ShouldSuppressFloatingOfDescendants(containingBlock);
5724 containingBlock = containingBlock->GetParent()) {
5725 if (containingBlock->IsFloatContainingBlock()) {
5726 return containingBlock;
5727 }
5728 }
5730 // If we didn't find a containing block, then there just isn't
5731 // one.... return null
5732 return nullptr;
5733 }
5735 /**
5736 * This function will check whether aContainer has :after generated content.
5737 * If so, appending to it should actually insert. The return value is the
5738 * parent to use for newly-appended content. *aAfterFrame points to the :after
5739 * frame before which appended content should go, if there is one.
5740 */
5741 static nsIFrame*
5742 AdjustAppendParentForAfterContent(nsPresContext* aPresContext,
5743 nsIContent* aContainer,
5744 nsIFrame* aParentFrame,
5745 nsIFrame** aAfterFrame)
5746 {
5747 // See if the parent has an :after pseudo-element. Check for the presence
5748 // of style first, since nsLayoutUtils::GetAfterFrame is sorta expensive.
5749 nsStyleContext* parentStyle = aParentFrame->StyleContext();
5750 if (nsLayoutUtils::HasPseudoStyle(aContainer, parentStyle,
5751 nsCSSPseudoElements::ePseudo_after,
5752 aPresContext)) {
5753 // Ensure that the :after frame is on the principal child list.
5754 aParentFrame->DrainSelfOverflowList();
5756 nsIFrame* afterFrame = nsLayoutUtils::GetAfterFrame(aParentFrame);
5757 if (afterFrame) {
5758 *aAfterFrame = afterFrame;
5759 return afterFrame->GetParent();
5760 }
5761 }
5763 *aAfterFrame = nullptr;
5765 if (IsFramePartOfIBSplit(aParentFrame)) {
5766 // We might be in a situation where the last part of the {ib} split was
5767 // empty. Since we have no ::after pseudo-element, we do in fact want to be
5768 // appending to that last part, so advance to it if needed. Note that here
5769 // aParentFrame is the result of a GetLastIBSplitSibling call, so must be
5770 // either the last or next to last ib-split sibling.
5771 nsIFrame* trailingInline = GetIBSplitSibling(aParentFrame);
5772 if (trailingInline) {
5773 aParentFrame = trailingInline;
5774 }
5776 // Always make sure to look at the last continuation of the frame
5777 // for the {ib} case, even if that continuation is empty. We
5778 // don't do this for the non-ib-split-frame case, since in the
5779 // other cases appending to the last nonempty continuation is fine
5780 // and in fact not doing that can confuse code that doesn't know
5781 // to pull kids from continuations other than its next one.
5782 aParentFrame = aParentFrame->LastContinuation();
5783 }
5785 return aParentFrame;
5786 }
5788 /**
5789 * This function will get the previous sibling to use for an append operation.
5790 * it takes a parent frame (must not be null) and its :after frame (may be
5791 * null).
5792 */
5793 static nsIFrame*
5794 FindAppendPrevSibling(nsIFrame* aParentFrame, nsIFrame* aAfterFrame)
5795 {
5796 if (aAfterFrame) {
5797 NS_ASSERTION(aAfterFrame->GetParent() == aParentFrame, "Wrong parent");
5798 NS_ASSERTION(aAfterFrame->GetPrevSibling() ||
5799 aParentFrame->GetFirstPrincipalChild() == aAfterFrame,
5800 ":after frame must be on the principal child list here");
5801 return aAfterFrame->GetPrevSibling();
5802 }
5804 aParentFrame->DrainSelfOverflowList();
5806 return aParentFrame->GetLastChild(kPrincipalList);
5807 }
5809 /**
5810 * This function will get the next sibling for a frame insert operation given
5811 * the parent and previous sibling. aPrevSibling may be null.
5812 */
5813 static nsIFrame*
5814 GetInsertNextSibling(nsIFrame* aParentFrame, nsIFrame* aPrevSibling)
5815 {
5816 if (aPrevSibling) {
5817 return aPrevSibling->GetNextSibling();
5818 }
5820 return aParentFrame->GetFirstPrincipalChild();
5821 }
5823 /**
5824 * This function is called by ContentAppended() and ContentInserted() when
5825 * appending flowed frames to a parent's principal child list. It handles the
5826 * case where the parent is the trailing inline of an {ib} split.
5827 */
5828 nsresult
5829 nsCSSFrameConstructor::AppendFramesToParent(nsFrameConstructorState& aState,
5830 nsIFrame* aParentFrame,
5831 nsFrameItems& aFrameList,
5832 nsIFrame* aPrevSibling,
5833 bool aIsRecursiveCall)
5834 {
5835 NS_PRECONDITION(!IsFramePartOfIBSplit(aParentFrame) ||
5836 !GetIBSplitSibling(aParentFrame) ||
5837 !GetIBSplitSibling(aParentFrame)->GetFirstPrincipalChild(),
5838 "aParentFrame has a ib-split sibling with kids?");
5839 NS_PRECONDITION(!aPrevSibling || aPrevSibling->GetParent() == aParentFrame,
5840 "Parent and prevsibling don't match");
5842 nsIFrame* nextSibling = ::GetInsertNextSibling(aParentFrame, aPrevSibling);
5844 NS_ASSERTION(nextSibling ||
5845 !aParentFrame->GetNextContinuation() ||
5846 !aParentFrame->GetNextContinuation()->GetFirstPrincipalChild() ||
5847 aIsRecursiveCall,
5848 "aParentFrame has later continuations with kids?");
5849 NS_ASSERTION(nextSibling ||
5850 !IsFramePartOfIBSplit(aParentFrame) ||
5851 (IsInlineFrame(aParentFrame) &&
5852 !GetIBSplitSibling(aParentFrame) &&
5853 !aParentFrame->GetNextContinuation()) ||
5854 aIsRecursiveCall,
5855 "aParentFrame is not last?");
5857 // If we're inserting a list of frames at the end of the trailing inline
5858 // of an {ib} split, we may need to create additional {ib} siblings to parent
5859 // them.
5860 if (!nextSibling && IsFramePartOfIBSplit(aParentFrame)) {
5861 // When we get here, our frame list might start with a block. If it does
5862 // so, and aParentFrame is an inline, and it and all its previous
5863 // continuations have no siblings, then put the initial blocks from the
5864 // frame list into the previous block of the {ib} split. Note that we
5865 // didn't want to stop at the block part of the split when figuring out
5866 // initial parent, because that could screw up float parenting; it's easier
5867 // to do this little fixup here instead.
5868 if (aFrameList.NotEmpty() && !aFrameList.FirstChild()->IsInlineOutside()) {
5869 // See whether our trailing inline is empty
5870 nsIFrame* firstContinuation = aParentFrame->FirstContinuation();
5871 if (firstContinuation->PrincipalChildList().IsEmpty()) {
5872 // Our trailing inline is empty. Collect our starting blocks from
5873 // aFrameList, get the right parent frame for them, and put them in.
5874 nsFrameList::FrameLinkEnumerator firstNonBlockEnumerator =
5875 FindFirstNonBlock(aFrameList);
5876 nsFrameList blockKids = aFrameList.ExtractHead(firstNonBlockEnumerator);
5877 NS_ASSERTION(blockKids.NotEmpty(), "No blocks?");
5879 nsIFrame* prevBlock =
5880 GetIBSplitPrevSibling(firstContinuation)->LastContinuation();
5881 NS_ASSERTION(prevBlock, "Should have previous block here");
5883 MoveChildrenTo(aState.mPresContext, aParentFrame, prevBlock, blockKids);
5884 }
5885 }
5887 // We want to put some of the frames into this inline frame.
5888 nsFrameList::FrameLinkEnumerator firstBlockEnumerator(aFrameList);
5889 FindFirstBlock(firstBlockEnumerator);
5891 nsFrameList inlineKids = aFrameList.ExtractHead(firstBlockEnumerator);
5892 if (!inlineKids.IsEmpty()) {
5893 AppendFrames(aParentFrame, kPrincipalList, inlineKids);
5894 }
5896 if (!aFrameList.IsEmpty()) {
5897 bool positioned = aParentFrame->IsRelativelyPositioned();
5898 nsFrameItems ibSiblings;
5899 CreateIBSiblings(aState, aParentFrame, positioned, aFrameList,
5900 ibSiblings);
5902 // Make sure to trigger reflow of the inline that used to be our
5903 // last one and now isn't anymore, since its GetSkipSides() has
5904 // changed.
5905 mPresShell->FrameNeedsReflow(aParentFrame,
5906 nsIPresShell::eTreeChange,
5907 NS_FRAME_HAS_DIRTY_CHILDREN);
5909 // Recurse so we create new ib siblings as needed for aParentFrame's parent
5910 return AppendFramesToParent(aState, aParentFrame->GetParent(), ibSiblings,
5911 aParentFrame, true);
5912 }
5914 return NS_OK;
5915 }
5917 // Insert the frames after our aPrevSibling
5918 return InsertFrames(aParentFrame, kPrincipalList, aPrevSibling, aFrameList);
5919 }
5921 #define UNSET_DISPLAY 255
5923 // This gets called to see if the frames corresponding to aSibling and aContent
5924 // should be siblings in the frame tree. Although (1) rows and cols, (2) row
5925 // groups and col groups, (3) row groups and captions, (4) legends and content
5926 // inside fieldsets, (5) popups and other kids of the menu are siblings from a
5927 // content perspective, they are not considered siblings in the frame tree.
5928 bool
5929 nsCSSFrameConstructor::IsValidSibling(nsIFrame* aSibling,
5930 nsIContent* aContent,
5931 uint8_t& aDisplay)
5932 {
5933 nsIFrame* parentFrame = aSibling->GetParent();
5934 nsIAtom* parentType = nullptr;
5935 if (parentFrame) {
5936 parentType = parentFrame->GetType();
5937 }
5939 uint8_t siblingDisplay = aSibling->GetDisplay();
5940 if ((NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == siblingDisplay) ||
5941 (NS_STYLE_DISPLAY_TABLE_COLUMN == siblingDisplay) ||
5942 (NS_STYLE_DISPLAY_TABLE_CAPTION == siblingDisplay) ||
5943 (NS_STYLE_DISPLAY_TABLE_HEADER_GROUP == siblingDisplay) ||
5944 (NS_STYLE_DISPLAY_TABLE_ROW_GROUP == siblingDisplay) ||
5945 (NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP == siblingDisplay) ||
5946 nsGkAtoms::menuFrame == parentType) {
5947 // if we haven't already, construct a style context to find the display type of aContent
5948 if (UNSET_DISPLAY == aDisplay) {
5949 nsRefPtr<nsStyleContext> styleContext;
5950 nsIFrame* styleParent = aSibling->GetParentStyleContextFrame();
5951 if (!styleParent) {
5952 NS_NOTREACHED("Shouldn't happen");
5953 return false;
5954 }
5955 // XXXbz when this code is killed, the state argument to
5956 // ResolveStyleContext can be made non-optional.
5957 styleContext = ResolveStyleContext(styleParent, aContent, nullptr);
5958 const nsStyleDisplay* display = styleContext->StyleDisplay();
5959 aDisplay = display->mDisplay;
5960 }
5961 if (nsGkAtoms::menuFrame == parentType) {
5962 return
5963 (NS_STYLE_DISPLAY_POPUP == aDisplay) ==
5964 (NS_STYLE_DISPLAY_POPUP == siblingDisplay);
5965 }
5966 // To have decent performance we want to return false in cases in which
5967 // reordering the two siblings has no effect on display. To ensure
5968 // correctness, we MUST return false in cases where the two siblings have
5969 // the same desired parent type and live on different display lists.
5970 // Specificaly, columns and column groups should only consider columns and
5971 // column groups as valid siblings. Captions should only consider other
5972 // captions. All other things should consider each other as valid
5973 // siblings. The restriction in the |if| above on siblingDisplay is ok,
5974 // because for correctness the only part that really needs to happen is to
5975 // not consider captions, column groups, and row/header/footer groups
5976 // siblings of each other. Treating a column or colgroup as a valid
5977 // sibling of a non-table-related frame will just mean we end up reframing.
5978 if ((siblingDisplay == NS_STYLE_DISPLAY_TABLE_CAPTION) !=
5979 (aDisplay == NS_STYLE_DISPLAY_TABLE_CAPTION)) {
5980 // One's a caption and the other is not. Not valid siblings.
5981 return false;
5982 }
5984 if ((siblingDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP ||
5985 siblingDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN) !=
5986 (aDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP ||
5987 aDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN)) {
5988 // One's a column or column group and the other is not. Not valid
5989 // siblings.
5990 return false;
5991 }
5992 // Fall through; it's possible that the display type was overridden and
5993 // a different sort of frame was constructed, so we may need to return false
5994 // below.
5995 }
5997 if (IsFrameForFieldSet(parentFrame, parentType)) {
5998 // Legends can be sibling of legends but not of other content in the fieldset
5999 nsIAtom* sibType = aSibling->GetContentInsertionFrame()->GetType();
6000 bool legendContent = aContent->IsHTML(nsGkAtoms::legend);
6002 if ((legendContent && (nsGkAtoms::legendFrame != sibType)) ||
6003 (!legendContent && (nsGkAtoms::legendFrame == sibType)))
6004 return false;
6005 }
6007 return true;
6008 }
6010 nsIFrame*
6011 nsCSSFrameConstructor::FindFrameForContentSibling(nsIContent* aContent,
6012 nsIContent* aTargetContent,
6013 uint8_t& aTargetContentDisplay,
6014 bool aPrevSibling)
6015 {
6016 nsIFrame* sibling = aContent->GetPrimaryFrame();
6017 if (!sibling || sibling->GetContent() != aContent) {
6018 // XXX the GetContent() != aContent check is needed due to bug 135040.
6019 // Remove it once that's fixed.
6020 return nullptr;
6021 }
6023 // If the frame is out-of-flow, GetPrimaryFrame() will have returned the
6024 // out-of-flow frame; we want the placeholder.
6025 if (sibling->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
6026 nsIFrame* placeholderFrame = GetPlaceholderFrameFor(sibling);
6027 NS_ASSERTION(placeholderFrame, "no placeholder for out-of-flow frame");
6028 sibling = placeholderFrame;
6029 }
6031 // The frame we have now should never be a continuation
6032 NS_ASSERTION(!sibling->GetPrevContinuation(), "How did that happen?");
6034 if (aPrevSibling) {
6035 // The frame may be a ib-split frame (a split inline frame that
6036 // contains a block). Get the last part of that split.
6037 if (IsFramePartOfIBSplit(sibling)) {
6038 sibling = GetLastIBSplitSibling(sibling, true);
6039 }
6041 // The frame may have a continuation. If so, we want the last
6042 // non-overflow-container continuation as our previous sibling.
6043 sibling = sibling->GetTailContinuation();
6044 }
6046 if (aTargetContent &&
6047 !IsValidSibling(sibling, aTargetContent, aTargetContentDisplay)) {
6048 sibling = nullptr;
6049 }
6051 return sibling;
6052 }
6054 nsIFrame*
6055 nsCSSFrameConstructor::FindPreviousSibling(FlattenedChildIterator aIter,
6056 uint8_t& aTargetContentDisplay)
6057 {
6058 nsIContent* child = aIter.Get();
6060 // Note: not all content objects are associated with a frame (e.g., if it's
6061 // `display: none') so keep looking until we find a previous frame
6062 while (nsIContent* sibling = aIter.GetPreviousChild()) {
6063 nsIFrame* prevSibling =
6064 FindFrameForContentSibling(sibling, child, aTargetContentDisplay, true);
6066 if (prevSibling) {
6067 // Found a previous sibling, we're done!
6068 return prevSibling;
6069 }
6070 }
6072 return nullptr;
6073 }
6075 nsIFrame*
6076 nsCSSFrameConstructor::FindNextSibling(FlattenedChildIterator aIter,
6077 uint8_t& aTargetContentDisplay)
6078 {
6079 nsIContent* child = aIter.Get();
6081 while (nsIContent* sibling = aIter.GetNextChild()) {
6082 nsIFrame* nextSibling =
6083 FindFrameForContentSibling(sibling, child, aTargetContentDisplay, false);
6085 if (nextSibling) {
6086 // We found a next sibling, we're done!
6087 return nextSibling;
6088 }
6089 }
6091 return nullptr;
6092 }
6094 // For fieldsets, returns the area frame, if the child is not a legend.
6095 static nsIFrame*
6096 GetAdjustedParentFrame(nsIFrame* aParentFrame,
6097 nsIAtom* aParentFrameType,
6098 nsIContent* aChildContent)
6099 {
6100 NS_PRECONDITION(nsGkAtoms::tableOuterFrame != aParentFrameType,
6101 "Shouldn't be happening!");
6103 nsIFrame* newParent = nullptr;
6105 if (nsGkAtoms::fieldSetFrame == aParentFrameType) {
6106 // If the parent is a fieldSet, use the fieldSet's area frame as the
6107 // parent unless the new content is a legend.
6108 if (!aChildContent->IsHTML(nsGkAtoms::legend)) {
6109 newParent = GetFieldSetBlockFrame(aParentFrame);
6110 }
6111 }
6112 return (newParent) ? newParent : aParentFrame;
6113 }
6115 nsIFrame*
6116 nsCSSFrameConstructor::GetInsertionPrevSibling(nsIFrame*& aParentFrame,
6117 nsIContent* aContainer,
6118 nsIContent* aChild,
6119 bool* aIsAppend,
6120 bool* aIsRangeInsertSafe,
6121 nsIContent* aStartSkipChild,
6122 nsIContent* aEndSkipChild)
6123 {
6124 *aIsAppend = false;
6126 // Find the frame that precedes the insertion point. Walk backwards
6127 // from the parent frame to get the parent content, because if an
6128 // XBL insertion point is involved, we'll need to use _that_ to find
6129 // the preceding frame.
6131 NS_PRECONDITION(aParentFrame, "Must have parent frame to start with");
6132 nsIContent* container = aParentFrame->GetContent();
6134 FlattenedChildIterator iter(container);
6135 bool xblCase = iter.XBLInvolved() || container != aContainer;
6136 if (xblCase || !aChild->IsRootOfAnonymousSubtree()) {
6137 // The check for IsRootOfAnonymousSubtree() is because editor is
6138 // severely broken and calls us directly for native anonymous
6139 // nodes that it creates.
6140 if (aStartSkipChild) {
6141 iter.Seek(aStartSkipChild);
6142 } else {
6143 iter.Seek(aChild);
6144 }
6145 } else {
6146 // Prime the iterator for the call to FindPreviousSibling.
6147 iter.GetNextChild();
6148 NS_WARNING("Someone passed native anonymous content directly into frame "
6149 "construction. Stop doing that!");
6150 }
6152 // Note that FindPreviousSibling is passed the iterator by value, so that
6153 // the later usage of the iterator starts from the same place.
6154 uint8_t childDisplay = UNSET_DISPLAY;
6155 nsIFrame* prevSibling = FindPreviousSibling(iter, childDisplay);
6157 // Now, find the geometric parent so that we can handle
6158 // continuations properly. Use the prev sibling if we have it;
6159 // otherwise use the next sibling.
6160 if (prevSibling) {
6161 aParentFrame = prevSibling->GetParent()->GetContentInsertionFrame();
6162 }
6163 else {
6164 // If there is no previous sibling, then find the frame that follows
6165 if (aEndSkipChild) {
6166 iter.Seek(aEndSkipChild);
6167 iter.GetPreviousChild();
6168 }
6169 nsIFrame* nextSibling = FindNextSibling(iter, childDisplay);
6171 if (nextSibling) {
6172 aParentFrame = nextSibling->GetParent()->GetContentInsertionFrame();
6173 }
6174 else {
6175 // No previous or next sibling, so treat this like an appended frame.
6176 *aIsAppend = true;
6177 if (IsFramePartOfIBSplit(aParentFrame)) {
6178 // Since we're appending, we'll walk to the last anonymous frame
6179 // that was created for the broken inline frame. But don't walk
6180 // to the trailing inline if it's empty; stop at the block.
6181 aParentFrame = GetLastIBSplitSibling(aParentFrame, false);
6182 }
6183 // Get continuation that parents the last child. This MUST be done
6184 // before the AdjustAppendParentForAfterContent call.
6185 aParentFrame = nsLayoutUtils::LastContinuationWithChild(aParentFrame);
6186 // Deal with fieldsets
6187 aParentFrame = ::GetAdjustedParentFrame(aParentFrame,
6188 aParentFrame->GetType(),
6189 aChild);
6190 nsIFrame* appendAfterFrame;
6191 aParentFrame =
6192 ::AdjustAppendParentForAfterContent(mPresShell->GetPresContext(),
6193 container, aParentFrame,
6194 &appendAfterFrame);
6195 prevSibling = ::FindAppendPrevSibling(aParentFrame, appendAfterFrame);
6196 }
6197 }
6199 *aIsRangeInsertSafe = (childDisplay == UNSET_DISPLAY);
6200 return prevSibling;
6201 }
6203 static bool
6204 IsSpecialFramesetChild(nsIContent* aContent)
6205 {
6206 // IMPORTANT: This must match the conditions in nsHTMLFramesetFrame::Init.
6207 return aContent->IsHTML() &&
6208 (aContent->Tag() == nsGkAtoms::frameset ||
6209 aContent->Tag() == nsGkAtoms::frame);
6210 }
6212 static void
6213 InvalidateCanvasIfNeeded(nsIPresShell* presShell, nsIContent* node);
6215 #ifdef MOZ_XUL
6217 static
6218 bool
6219 IsXULListBox(nsIContent* aContainer)
6220 {
6221 return (aContainer->IsXUL() && aContainer->Tag() == nsGkAtoms::listbox);
6222 }
6224 static
6225 nsListBoxBodyFrame*
6226 MaybeGetListBoxBodyFrame(nsIContent* aContainer, nsIContent* aChild)
6227 {
6228 if (!aContainer)
6229 return nullptr;
6231 if (IsXULListBox(aContainer) &&
6232 aChild->IsXUL() && aChild->Tag() == nsGkAtoms::listitem) {
6233 nsCOMPtr<nsIDOMXULElement> xulElement = do_QueryInterface(aContainer);
6234 nsCOMPtr<nsIBoxObject> boxObject;
6235 xulElement->GetBoxObject(getter_AddRefs(boxObject));
6236 nsCOMPtr<nsPIListBoxObject> listBoxObject = do_QueryInterface(boxObject);
6237 if (listBoxObject) {
6238 return listBoxObject->GetListBoxBody(false);
6239 }
6240 }
6242 return nullptr;
6243 }
6244 #endif
6246 void
6247 nsCSSFrameConstructor::AddTextItemIfNeeded(nsFrameConstructorState& aState,
6248 nsIFrame* aParentFrame,
6249 nsIContent* aPossibleTextContent,
6250 FrameConstructionItemList& aItems)
6251 {
6252 NS_PRECONDITION(aPossibleTextContent, "Must have node");
6253 if (!aPossibleTextContent->IsNodeOfType(nsINode::eTEXT) ||
6254 !aPossibleTextContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE)) {
6255 // Not text, or not suppressed due to being all-whitespace (if it
6256 // were being suppressed, it would have the
6257 // NS_CREATE_FRAME_IF_NON_WHITESPACE flag)
6258 return;
6259 }
6260 NS_ASSERTION(!aPossibleTextContent->GetPrimaryFrame(),
6261 "Text node has a frame and NS_CREATE_FRAME_IF_NON_WHITESPACE");
6262 AddFrameConstructionItems(aState, aPossibleTextContent, false,
6263 aParentFrame, aItems);
6264 }
6266 void
6267 nsCSSFrameConstructor::ReframeTextIfNeeded(nsIContent* aParentContent,
6268 nsIContent* aContent)
6269 {
6270 if (!aContent->IsNodeOfType(nsINode::eTEXT) ||
6271 !aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE)) {
6272 // Not text, or not suppressed due to being all-whitespace (if it
6273 // were being suppressed, it would have the
6274 // NS_CREATE_FRAME_IF_NON_WHITESPACE flag)
6275 return;
6276 }
6277 NS_ASSERTION(!aContent->GetPrimaryFrame(),
6278 "Text node has a frame and NS_CREATE_FRAME_IF_NON_WHITESPACE");
6279 ContentInserted(aParentContent, aContent, nullptr, false);
6280 }
6282 // For inserts aChild should be valid, for appends it should be null.
6283 // Returns true if this operation can be lazy, false if not.
6284 bool
6285 nsCSSFrameConstructor::MaybeConstructLazily(Operation aOperation,
6286 nsIContent* aContainer,
6287 nsIContent* aChild)
6288 {
6289 if (mPresShell->GetPresContext()->IsChrome() || !aContainer ||
6290 aContainer->IsInNativeAnonymousSubtree() || aContainer->IsXUL()) {
6291 return false;
6292 }
6294 if (aOperation == CONTENTINSERT) {
6295 if (aChild->IsRootOfAnonymousSubtree() ||
6296 aChild->HasFlag(NODE_IS_IN_SHADOW_TREE) ||
6297 aChild->IsEditable() || aChild->IsXUL()) {
6298 return false;
6299 }
6300 } else { // CONTENTAPPEND
6301 NS_ASSERTION(aOperation == CONTENTAPPEND,
6302 "operation should be either insert or append");
6303 for (nsIContent* child = aChild; child; child = child->GetNextSibling()) {
6304 NS_ASSERTION(!child->IsRootOfAnonymousSubtree(),
6305 "Should be coming through the CONTENTAPPEND case");
6306 if (child->IsXUL() || child->IsEditable()) {
6307 return false;
6308 }
6309 }
6310 }
6312 // We can construct lazily; just need to set suitable bits in the content
6313 // tree.
6315 // Walk up the tree setting the NODE_DESCENDANTS_NEED_FRAMES bit as we go.
6316 nsIContent* content = aContainer;
6317 #ifdef DEBUG
6318 // If we hit a node with no primary frame, or the NODE_NEEDS_FRAME bit set
6319 // we want to assert, but leaf frames that process their own children and may
6320 // ignore anonymous children (eg framesets) make this complicated. So we set
6321 // these two booleans if we encounter these situations and unset them if we
6322 // hit a node with a leaf frame.
6323 bool noPrimaryFrame = false;
6324 bool needsFrameBitSet = false;
6325 #endif
6326 while (content &&
6327 !content->HasFlag(NODE_DESCENDANTS_NEED_FRAMES)) {
6328 #ifdef DEBUG
6329 if (content->GetPrimaryFrame() && content->GetPrimaryFrame()->IsLeaf()) {
6330 noPrimaryFrame = needsFrameBitSet = false;
6331 }
6332 if (!noPrimaryFrame && !content->GetPrimaryFrame()) {
6333 noPrimaryFrame = true;
6334 }
6335 if (!needsFrameBitSet && content->HasFlag(NODE_NEEDS_FRAME)) {
6336 needsFrameBitSet = true;
6337 }
6338 #endif
6339 content->SetFlags(NODE_DESCENDANTS_NEED_FRAMES);
6340 content = content->GetFlattenedTreeParent();
6341 }
6342 #ifdef DEBUG
6343 if (content && content->GetPrimaryFrame() &&
6344 content->GetPrimaryFrame()->IsLeaf()) {
6345 noPrimaryFrame = needsFrameBitSet = false;
6346 }
6347 NS_ASSERTION(!noPrimaryFrame, "Ancestors of nodes with frames to be "
6348 "constructed lazily should have frames");
6349 NS_ASSERTION(!needsFrameBitSet, "Ancestors of nodes with frames to be "
6350 "constructed lazily should not have NEEDS_FRAME bit set");
6351 #endif
6353 // Set NODE_NEEDS_FRAME on the new nodes.
6354 if (aOperation == CONTENTINSERT) {
6355 NS_ASSERTION(!aChild->GetPrimaryFrame() ||
6356 aChild->GetPrimaryFrame()->GetContent() != aChild,
6357 //XXX the aChild->GetPrimaryFrame()->GetContent() != aChild
6358 // check is needed due to bug 135040. Remove it once that's
6359 // fixed.
6360 "setting NEEDS_FRAME on a node that already has a frame?");
6361 aChild->SetFlags(NODE_NEEDS_FRAME);
6362 } else { // CONTENTAPPEND
6363 for (nsIContent* child = aChild; child; child = child->GetNextSibling()) {
6364 NS_ASSERTION(!child->GetPrimaryFrame() ||
6365 child->GetPrimaryFrame()->GetContent() != child,
6366 //XXX the child->GetPrimaryFrame()->GetContent() != child
6367 // check is needed due to bug 135040. Remove it once that's
6368 // fixed.
6369 "setting NEEDS_FRAME on a node that already has a frame?");
6370 child->SetFlags(NODE_NEEDS_FRAME);
6371 }
6372 }
6374 RestyleManager()->PostRestyleEventForLazyConstruction();
6375 return true;
6376 }
6378 void
6379 nsCSSFrameConstructor::CreateNeededFrames(nsIContent* aContent)
6380 {
6381 NS_ASSERTION(!aContent->HasFlag(NODE_NEEDS_FRAME),
6382 "shouldn't get here with a content node that has needs frame bit set");
6383 NS_ASSERTION(aContent->HasFlag(NODE_DESCENDANTS_NEED_FRAMES),
6384 "should only get here with a content node that has descendants needing frames");
6386 aContent->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES);
6388 // We could either descend first (on nodes that don't have NODE_NEEDS_FRAME
6389 // set) or issue content notifications for our kids first. In absence of
6390 // anything definitive either way we'll go with the latter.
6392 // It might be better to use GetChildArray and scan it completely first and
6393 // then issue all notifications. (We have to scan it completely first because
6394 // constructing frames can set attributes, which can change the storage of
6395 // child lists).
6397 // Scan the children of aContent to see what operations (if any) we need to
6398 // perform.
6399 uint32_t childCount = aContent->GetChildCount();
6400 bool inRun = false;
6401 nsIContent* firstChildInRun = nullptr;
6402 for (uint32_t i = 0; i < childCount; i++) {
6403 nsIContent* child = aContent->GetChildAt(i);
6404 if (child->HasFlag(NODE_NEEDS_FRAME)) {
6405 NS_ASSERTION(!child->GetPrimaryFrame() ||
6406 child->GetPrimaryFrame()->GetContent() != child,
6407 //XXX the child->GetPrimaryFrame()->GetContent() != child
6408 // check is needed due to bug 135040. Remove it once that's
6409 // fixed.
6410 "NEEDS_FRAME set on a node that already has a frame?");
6411 if (!inRun) {
6412 inRun = true;
6413 firstChildInRun = child;
6414 }
6415 } else {
6416 if (inRun) {
6417 inRun = false;
6418 // generate a ContentRangeInserted for [startOfRun,i)
6419 ContentRangeInserted(aContent, firstChildInRun, child, nullptr,
6420 false);
6421 }
6422 }
6423 }
6424 if (inRun) {
6425 ContentAppended(aContent, firstChildInRun, false);
6426 }
6428 // Now descend.
6429 FlattenedChildIterator iter(aContent);
6430 for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
6431 if (child->HasFlag(NODE_DESCENDANTS_NEED_FRAMES)) {
6432 CreateNeededFrames(child);
6433 }
6434 }
6435 }
6437 void nsCSSFrameConstructor::CreateNeededFrames()
6438 {
6439 NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
6440 "Someone forgot a script blocker");
6442 Element* rootElement = mDocument->GetRootElement();
6443 NS_ASSERTION(!rootElement || !rootElement->HasFlag(NODE_NEEDS_FRAME),
6444 "root element should not have frame created lazily");
6445 if (rootElement && rootElement->HasFlag(NODE_DESCENDANTS_NEED_FRAMES)) {
6446 BeginUpdate();
6447 CreateNeededFrames(rootElement);
6448 EndUpdate();
6449 }
6450 }
6452 void
6453 nsCSSFrameConstructor::IssueSingleInsertNofications(nsIContent* aContainer,
6454 nsIContent* aStartChild,
6455 nsIContent* aEndChild,
6456 bool aAllowLazyConstruction)
6457 {
6458 for (nsIContent* child = aStartChild;
6459 child != aEndChild;
6460 child = child->GetNextSibling()) {
6461 if ((child->GetPrimaryFrame() ||
6462 GetUndisplayedContent(child))
6463 #ifdef MOZ_XUL
6464 // Except listboxes suck, so do NOT skip anything here if
6465 // we plan to notify a listbox.
6466 && !MaybeGetListBoxBodyFrame(aContainer, child)
6467 #endif
6468 ) {
6469 // Already have a frame or undisplayed entry for this content; a
6470 // previous ContentInserted in this loop must have reconstructed
6471 // its insertion parent. Skip it.
6472 continue;
6473 }
6474 // Call ContentInserted with this node.
6475 ContentInserted(aContainer, child, mTempFrameTreeState,
6476 aAllowLazyConstruction);
6477 }
6478 }
6480 nsIFrame*
6481 nsCSSFrameConstructor::GetRangeInsertionPoint(nsIContent* aContainer,
6482 nsIContent* aStartChild,
6483 nsIContent* aEndChild,
6484 bool aAllowLazyConstruction)
6485 {
6486 // See if we have an XBL insertion point. If so, then that's our
6487 // real parent frame; if not, then the frame hasn't been built yet
6488 // and we just bail.
6489 bool multiple = false;
6490 nsIFrame* insertionPoint = GetInsertionPoint(aContainer, nullptr, &multiple);
6491 if (!insertionPoint && !multiple)
6492 return nullptr; // Don't build the frames.
6494 bool hasInsertion = false;
6495 if (!multiple) {
6496 // XXXbz XBL2/sXBL issue
6497 nsIDocument* document = aStartChild->GetDocument();
6498 // XXXbz how would |document| be null here?
6499 if (document && aStartChild->GetXBLInsertionParent()) {
6500 hasInsertion = true;
6501 }
6502 }
6504 if (multiple || hasInsertion) {
6505 // We have an insertion point. There are some additional tests we need to do
6506 // in order to ensure that an append is a safe operation.
6507 uint32_t childCount = 0;
6509 if (!multiple) {
6510 // We may need to make multiple ContentInserted calls instead. A
6511 // reasonable heuristic to employ (in order to maintain good performance)
6512 // is to find out if the insertion point's content node contains any
6513 // explicit children. If it does not, then it is highly likely that
6514 // an append is occurring. (Note it is not definite, and there are insane
6515 // cases we will not deal with by employing this heuristic, but it beats
6516 // always falling back to multiple ContentInserted calls).
6517 //
6518 // In the multiple insertion point case, we know we're going to need to do
6519 // multiple ContentInserted calls anyway.
6520 // XXXndeakin This test doesn't work in the new world. Or rather, it works, but
6521 // it's slow
6522 childCount = insertionPoint->GetContent()->GetChildCount();
6523 }
6525 // If we have multiple insertion points or if we have an insertion point
6526 // and the operation is not a true append or if the insertion point already
6527 // has explicit children, then we must fall back.
6528 if (multiple || aEndChild != nullptr || childCount > 0) {
6529 // Now comes the fun part. For each inserted child, make a
6530 // ContentInserted call as if it had just gotten inserted and
6531 // let ContentInserted handle the mess.
6532 IssueSingleInsertNofications(aContainer, aStartChild, aEndChild,
6533 aAllowLazyConstruction);
6534 return nullptr;
6535 }
6536 }
6538 return insertionPoint;
6539 }
6541 bool
6542 nsCSSFrameConstructor::MaybeRecreateForFrameset(nsIFrame* aParentFrame,
6543 nsIContent* aStartChild,
6544 nsIContent* aEndChild)
6545 {
6546 if (aParentFrame->GetType() == nsGkAtoms::frameSetFrame) {
6547 // Check whether we have any kids we care about.
6548 for (nsIContent* cur = aStartChild;
6549 cur != aEndChild;
6550 cur = cur->GetNextSibling()) {
6551 if (IsSpecialFramesetChild(cur)) {
6552 // Just reframe the parent, since framesets are weird like that.
6553 RecreateFramesForContent(aParentFrame->GetContent(), false);
6554 return true;
6555 }
6556 }
6557 }
6558 return false;
6559 }
6561 nsresult
6562 nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer,
6563 nsIContent* aFirstNewContent,
6564 bool aAllowLazyConstruction)
6565 {
6566 AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
6567 NS_PRECONDITION(mUpdateCount != 0,
6568 "Should be in an update while creating frames");
6570 #ifdef DEBUG
6571 if (gNoisyContentUpdates) {
6572 printf("nsCSSFrameConstructor::ContentAppended container=%p "
6573 "first-child=%p lazy=%d\n",
6574 static_cast<void*>(aContainer), aFirstNewContent,
6575 aAllowLazyConstruction);
6576 if (gReallyNoisyContentUpdates && aContainer) {
6577 aContainer->List(stdout, 0);
6578 }
6579 }
6580 #endif
6582 #ifdef DEBUG
6583 for (nsIContent* child = aFirstNewContent;
6584 child;
6585 child = child->GetNextSibling()) {
6586 // XXX the GetContent() != child check is needed due to bug 135040.
6587 // Remove it once that's fixed.
6588 NS_ASSERTION(!child->GetPrimaryFrame() ||
6589 child->GetPrimaryFrame()->GetContent() != child,
6590 "asked to construct a frame for a node that already has a frame");
6591 }
6592 #endif
6594 #ifdef MOZ_XUL
6595 if (aContainer) {
6596 int32_t namespaceID;
6597 nsIAtom* tag =
6598 mDocument->BindingManager()->ResolveTag(aContainer, &namespaceID);
6600 // Just ignore tree tags, anyway we don't create any frames for them.
6601 if (tag == nsGkAtoms::treechildren ||
6602 tag == nsGkAtoms::treeitem ||
6603 tag == nsGkAtoms::treerow)
6604 return NS_OK;
6606 }
6607 #endif // MOZ_XUL
6609 if (aContainer && aContainer->HasFlag(NODE_IS_IN_SHADOW_TREE)) {
6610 // Recreate frames if content is appended into a ShadowRoot
6611 // because children of ShadowRoot are rendered in place of children
6612 // of the host.
6613 nsIContent* bindingParent = aContainer->GetBindingParent();
6614 LAYOUT_PHASE_TEMP_EXIT();
6615 nsresult rv = RecreateFramesForContent(bindingParent, false);
6616 LAYOUT_PHASE_TEMP_REENTER();
6617 return rv;
6618 }
6620 // Get the frame associated with the content
6621 nsIFrame* parentFrame = GetFrameFor(aContainer);
6623 // See comment in ContentRangeInserted for why this is necessary.
6624 if (!parentFrame && !aContainer->IsActiveChildrenElement()) {
6625 return NS_OK;
6626 }
6628 if (aAllowLazyConstruction &&
6629 MaybeConstructLazily(CONTENTAPPEND, aContainer, aFirstNewContent)) {
6630 return NS_OK;
6631 }
6633 LAYOUT_PHASE_TEMP_EXIT();
6634 parentFrame = GetRangeInsertionPoint(aContainer,
6635 aFirstNewContent, nullptr,
6636 aAllowLazyConstruction);
6637 LAYOUT_PHASE_TEMP_REENTER();
6638 if (!parentFrame) {
6639 return NS_OK;
6640 }
6642 LAYOUT_PHASE_TEMP_EXIT();
6643 if (MaybeRecreateForFrameset(parentFrame, aFirstNewContent, nullptr)) {
6644 LAYOUT_PHASE_TEMP_REENTER();
6645 return NS_OK;
6646 }
6647 LAYOUT_PHASE_TEMP_REENTER();
6649 if (parentFrame->IsLeaf()) {
6650 // Nothing to do here; we shouldn't be constructing kids of leaves
6651 // Clear lazy bits so we don't try to construct again.
6652 ClearLazyBits(aFirstNewContent, nullptr);
6653 return NS_OK;
6654 }
6656 if (parentFrame->IsFrameOfType(nsIFrame::eMathML)) {
6657 LAYOUT_PHASE_TEMP_EXIT();
6658 nsresult rv = RecreateFramesForContent(parentFrame->GetContent(), false);
6659 LAYOUT_PHASE_TEMP_REENTER();
6660 return rv;
6661 }
6663 // If the frame we are manipulating is a ib-split frame (that is, one
6664 // that's been created as a result of a block-in-inline situation) then we
6665 // need to append to the last ib-split sibling, not to the frame itself.
6666 bool parentIBSplit = IsFramePartOfIBSplit(parentFrame);
6667 if (parentIBSplit) {
6668 #ifdef DEBUG
6669 if (gNoisyContentUpdates) {
6670 printf("nsCSSFrameConstructor::ContentAppended: parentFrame=");
6671 nsFrame::ListTag(stdout, parentFrame);
6672 printf(" is ib-split\n");
6673 }
6674 #endif
6676 // Since we're appending, we'll walk to the last anonymous frame
6677 // that was created for the broken inline frame. But don't walk
6678 // to the trailing inline if it's empty; stop at the block.
6679 parentFrame = GetLastIBSplitSibling(parentFrame, false);
6680 }
6682 // Get continuation that parents the last child. This MUST be done
6683 // before the AdjustAppendParentForAfterContent call.
6684 parentFrame = nsLayoutUtils::LastContinuationWithChild(parentFrame);
6686 // We should never get here with fieldsets, since they have multiple
6687 // insertion points.
6688 NS_ASSERTION(parentFrame->GetType() != nsGkAtoms::fieldSetFrame,
6689 "Unexpected parent");
6691 // Deal with possible :after generated content on the parent
6692 nsIFrame* parentAfterFrame;
6693 parentFrame =
6694 ::AdjustAppendParentForAfterContent(mPresShell->GetPresContext(),
6695 aContainer, parentFrame,
6696 &parentAfterFrame);
6698 // Create some new frames
6699 nsFrameConstructorState state(mPresShell,
6700 GetAbsoluteContainingBlock(parentFrame, FIXED_POS),
6701 GetAbsoluteContainingBlock(parentFrame, ABS_POS),
6702 GetFloatContainingBlock(parentFrame));
6703 state.mTreeMatchContext.InitAncestors(aContainer->AsElement());
6705 // See if the containing block has :first-letter style applied.
6706 bool haveFirstLetterStyle = false, haveFirstLineStyle = false;
6707 nsIFrame* containingBlock = state.mFloatedItems.containingBlock;
6708 if (containingBlock) {
6709 haveFirstLetterStyle = HasFirstLetterStyle(containingBlock);
6710 haveFirstLineStyle =
6711 ShouldHaveFirstLineStyle(containingBlock->GetContent(),
6712 containingBlock->StyleContext());
6713 }
6715 if (haveFirstLetterStyle) {
6716 // Before we get going, remove the current letter frames
6717 RemoveLetterFrames(state.mPresContext, state.mPresShell,
6718 containingBlock);
6719 }
6721 nsIAtom* frameType = parentFrame->GetType();
6723 FlattenedChildIterator iter(aContainer);
6724 bool haveNoXBLChildren = (!iter.XBLInvolved() || !iter.GetNextChild());
6725 FrameConstructionItemList items;
6726 if (aFirstNewContent->GetPreviousSibling() &&
6727 GetParentType(frameType) == eTypeBlock &&
6728 haveNoXBLChildren) {
6729 // If there's a text node in the normal content list just before the new
6730 // items, and it has no frame, make a frame construction item for it. If it
6731 // doesn't need a frame, ConstructFramesFromItemList below won't give it
6732 // one. No need to do all this if our parent type is not block, though,
6733 // since WipeContainingBlock already handles that situation.
6734 //
6735 // Because we're appending, we don't need to worry about any text
6736 // after the appended content; there can only be XBL anonymous content
6737 // (text in an XBL binding is not suppressed) or generated content
6738 // (and bare text nodes are not generated). Native anonymous content
6739 // generated by frames never participates in inline layout.
6740 AddTextItemIfNeeded(state, parentFrame,
6741 aFirstNewContent->GetPreviousSibling(), items);
6742 }
6743 for (nsIContent* child = aFirstNewContent;
6744 child;
6745 child = child->GetNextSibling()) {
6746 AddFrameConstructionItems(state, child, false, parentFrame, items);
6747 }
6749 nsIFrame* prevSibling = ::FindAppendPrevSibling(parentFrame, parentAfterFrame);
6751 // Perform special check for diddling around with the frames in
6752 // a ib-split inline frame.
6753 // If we're appending before :after content, then we're not really
6754 // appending, so let WipeContainingBlock know that.
6755 LAYOUT_PHASE_TEMP_EXIT();
6756 if (WipeContainingBlock(state, containingBlock, parentFrame, items,
6757 true, prevSibling)) {
6758 LAYOUT_PHASE_TEMP_REENTER();
6759 return NS_OK;
6760 }
6761 LAYOUT_PHASE_TEMP_REENTER();
6763 // If the parent is a block frame, and we're not in a special case
6764 // where frames can be moved around, determine if the list is for the
6765 // start or end of the block.
6766 if (nsLayoutUtils::GetAsBlock(parentFrame) && !haveFirstLetterStyle &&
6767 !haveFirstLineStyle && !parentIBSplit) {
6768 items.SetLineBoundaryAtStart(!prevSibling ||
6769 !prevSibling->IsInlineOutside() ||
6770 prevSibling->GetType() == nsGkAtoms::brFrame);
6771 // :after content can't be <br> so no need to check it
6772 items.SetLineBoundaryAtEnd(!parentAfterFrame ||
6773 !parentAfterFrame->IsInlineOutside());
6774 }
6775 // To suppress whitespace-only text frames, we have to verify that
6776 // our container's DOM child list matches its flattened tree child list.
6777 items.SetParentHasNoXBLChildren(haveNoXBLChildren);
6779 nsFrameItems frameItems;
6780 ConstructFramesFromItemList(state, items, parentFrame, frameItems);
6782 for (nsIContent* child = aFirstNewContent;
6783 child;
6784 child = child->GetNextSibling()) {
6785 // Invalidate now instead of before the WipeContainingBlock call, just in
6786 // case we do wipe; in that case we don't need to do this walk at all.
6787 // XXXbz does that matter? Would it make more sense to save some virtual
6788 // GetChildAt calls instead and do this during construction of our
6789 // FrameConstructionItemList?
6790 InvalidateCanvasIfNeeded(mPresShell, child);
6791 }
6793 // if the container is a table and a caption was appended, it needs to be put
6794 // in the outer table frame's additional child list.
6795 nsFrameItems captionItems;
6796 if (nsGkAtoms::tableFrame == frameType) {
6797 // Pull out the captions. Note that we don't want to do that as we go,
6798 // because processing a single caption can add a whole bunch of things to
6799 // the frame items due to pseudoframe processing. So we'd have to pull
6800 // captions from a list anyway; might as well do that here.
6801 // XXXbz this is no longer true; we could pull captions directly out of the
6802 // FrameConstructionItemList now.
6803 PullOutCaptionFrames(frameItems, captionItems);
6804 }
6806 if (haveFirstLineStyle && parentFrame == containingBlock) {
6807 // It's possible that some of the new frames go into a
6808 // first-line frame. Look at them and see...
6809 AppendFirstLineFrames(state, containingBlock->GetContent(),
6810 containingBlock, frameItems);
6811 }
6813 // Notify the parent frame passing it the list of new frames
6814 // Append the flowed frames to the principal child list; captions
6815 // need special treatment
6816 if (captionItems.NotEmpty()) { // append the caption to the outer table
6817 NS_ASSERTION(nsGkAtoms::tableFrame == frameType, "how did that happen?");
6818 nsIFrame* outerTable = parentFrame->GetParent();
6819 AppendFrames(outerTable, nsIFrame::kCaptionList, captionItems);
6820 }
6822 if (frameItems.NotEmpty()) { // append the in-flow kids
6823 AppendFramesToParent(state, parentFrame, frameItems, prevSibling);
6824 }
6826 // Recover first-letter frames
6827 if (haveFirstLetterStyle) {
6828 RecoverLetterFrames(containingBlock);
6829 }
6831 #ifdef DEBUG
6832 if (gReallyNoisyContentUpdates) {
6833 printf("nsCSSFrameConstructor::ContentAppended: resulting frame model:\n");
6834 parentFrame->List(stdout, 0);
6835 }
6836 #endif
6838 #ifdef ACCESSIBILITY
6839 nsAccessibilityService* accService = nsIPresShell::AccService();
6840 if (accService) {
6841 accService->ContentRangeInserted(mPresShell, aContainer,
6842 aFirstNewContent, nullptr);
6843 }
6844 #endif
6846 return NS_OK;
6847 }
6849 #ifdef MOZ_XUL
6851 enum content_operation
6852 {
6853 CONTENT_INSERTED,
6854 CONTENT_REMOVED
6855 };
6857 // Helper function to lookup the listbox body frame and send a notification
6858 // for insertion or removal of content
6859 static
6860 bool NotifyListBoxBody(nsPresContext* aPresContext,
6861 nsIContent* aContainer,
6862 nsIContent* aChild,
6863 // Only used for the removed notification
6864 nsIContent* aOldNextSibling,
6865 nsIDocument* aDocument,
6866 nsIFrame* aChildFrame,
6867 content_operation aOperation)
6868 {
6869 nsListBoxBodyFrame* listBoxBodyFrame =
6870 MaybeGetListBoxBodyFrame(aContainer, aChild);
6871 if (listBoxBodyFrame) {
6872 if (aOperation == CONTENT_REMOVED) {
6873 // Except if we have an aChildFrame and its parent is not the right
6874 // thing, then we don't do this. Pseudo frames are so much fun....
6875 if (!aChildFrame || aChildFrame->GetParent() == listBoxBodyFrame) {
6876 listBoxBodyFrame->OnContentRemoved(aPresContext, aContainer,
6877 aChildFrame, aOldNextSibling);
6878 return true;
6879 }
6880 } else {
6881 listBoxBodyFrame->OnContentInserted(aPresContext, aChild);
6882 return true;
6883 }
6884 }
6886 return false;
6887 }
6888 #endif // MOZ_XUL
6890 nsresult
6891 nsCSSFrameConstructor::ContentInserted(nsIContent* aContainer,
6892 nsIContent* aChild,
6893 nsILayoutHistoryState* aFrameState,
6894 bool aAllowLazyConstruction)
6895 {
6896 return ContentRangeInserted(aContainer,
6897 aChild,
6898 aChild->GetNextSibling(),
6899 aFrameState,
6900 aAllowLazyConstruction);
6901 }
6903 // ContentRangeInserted handles creating frames for a range of nodes that
6904 // aren't at the end of their childlist. ContentRangeInserted isn't a real
6905 // content notification, but rather it handles regular ContentInserted calls
6906 // for a single node as well as the lazy construction of frames for a range of
6907 // nodes when called from CreateNeededFrames. For a range of nodes to be
6908 // suitable to have its frames constructed all at once they must meet the same
6909 // conditions that ContentAppended imposes (GetRangeInsertionPoint checks
6910 // these), plus more. Namely when finding the insertion prevsibling we must not
6911 // need to consult something specific to any one node in the range, so that the
6912 // insertion prevsibling would be the same for each node in the range. So we
6913 // pass the first node in the range to GetInsertionPrevSibling, and if
6914 // IsValidSibling (the only place GetInsertionPrevSibling might look at the
6915 // passed in node itself) needs to resolve style on the node we record this and
6916 // return that this range needs to be split up and inserted separately. Table
6917 // captions need extra attention as we need to determine where to insert them
6918 // in the caption list, while skipping any nodes in the range being inserted
6919 // (because when we treat the caption frames the other nodes have had their
6920 // frames constructed but not yet inserted into the frame tree).
6921 nsresult
6922 nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer,
6923 nsIContent* aStartChild,
6924 nsIContent* aEndChild,
6925 nsILayoutHistoryState* aFrameState,
6926 bool aAllowLazyConstruction)
6927 {
6928 AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
6929 NS_PRECONDITION(mUpdateCount != 0,
6930 "Should be in an update while creating frames");
6932 NS_PRECONDITION(aStartChild, "must always pass a child");
6934 // XXXldb Do we need to re-resolve style to handle the CSS2 + combinator and
6935 // the :empty pseudo-class?
6936 #ifdef DEBUG
6937 if (gNoisyContentUpdates) {
6938 printf("nsCSSFrameConstructor::ContentRangeInserted container=%p "
6939 "start-child=%p end-child=%p lazy=%d\n",
6940 static_cast<void*>(aContainer),
6941 static_cast<void*>(aStartChild), static_cast<void*>(aEndChild),
6942 aAllowLazyConstruction);
6943 if (gReallyNoisyContentUpdates) {
6944 if (aContainer) {
6945 aContainer->List(stdout,0);
6946 } else {
6947 aStartChild->List(stdout, 0);
6948 }
6949 }
6950 }
6951 #endif
6953 #ifdef DEBUG
6954 for (nsIContent* child = aStartChild;
6955 child != aEndChild;
6956 child = child->GetNextSibling()) {
6957 // XXX the GetContent() != child check is needed due to bug 135040.
6958 // Remove it once that's fixed.
6959 NS_ASSERTION(!child->GetPrimaryFrame() ||
6960 child->GetPrimaryFrame()->GetContent() != child,
6961 "asked to construct a frame for a node that already has a frame");
6962 }
6963 #endif
6965 bool isSingleInsert = (aStartChild->GetNextSibling() == aEndChild);
6966 NS_ASSERTION(isSingleInsert || !aAllowLazyConstruction,
6967 "range insert shouldn't be lazy");
6968 NS_ASSERTION(isSingleInsert || aEndChild,
6969 "range should not include all nodes after aStartChild");
6971 #ifdef MOZ_XUL
6972 if (aContainer && IsXULListBox(aContainer)) {
6973 if (isSingleInsert) {
6974 if (NotifyListBoxBody(mPresShell->GetPresContext(), aContainer,
6975 // The insert case in NotifyListBoxBody
6976 // doesn't use "old next sibling".
6977 aStartChild, nullptr,
6978 mDocument, nullptr, CONTENT_INSERTED)) {
6979 return NS_OK;
6980 }
6981 } else {
6982 // We don't handle a range insert to a listbox parent, issue single
6983 // ContertInserted calls for each node inserted.
6984 LAYOUT_PHASE_TEMP_EXIT();
6985 IssueSingleInsertNofications(aContainer, aStartChild, aEndChild,
6986 aAllowLazyConstruction);
6987 LAYOUT_PHASE_TEMP_REENTER();
6988 return NS_OK;
6989 }
6990 }
6991 #endif // MOZ_XUL
6993 // If we have a null parent, then this must be the document element being
6994 // inserted, or some other child of the document in the DOM (might be a PI,
6995 // say).
6996 if (! aContainer) {
6997 NS_ASSERTION(isSingleInsert,
6998 "root node insertion should be a single insertion");
6999 Element *docElement = mDocument->GetRootElement();
7001 if (aStartChild != docElement) {
7002 // Not the root element; just bail out
7003 return NS_OK;
7004 }
7006 NS_PRECONDITION(nullptr == mRootElementFrame,
7007 "root element frame already created");
7009 // Create frames for the document element and its child elements
7010 nsIFrame* docElementFrame =
7011 ConstructDocElementFrame(docElement, aFrameState);
7013 if (docElementFrame) {
7014 InvalidateCanvasIfNeeded(mPresShell, aStartChild);
7015 #ifdef DEBUG
7016 if (gReallyNoisyContentUpdates) {
7017 printf("nsCSSFrameConstructor::ContentRangeInserted: resulting frame "
7018 "model:\n");
7019 mFixedContainingBlock->List(stdout, 0);
7020 }
7021 #endif
7022 }
7024 if (aFrameState) {
7025 // Restore frame state for the root scroll frame if there is one
7026 nsIFrame* rootScrollFrame = mPresShell->GetRootScrollFrame();
7027 if (rootScrollFrame) {
7028 RestoreFrameStateFor(rootScrollFrame, aFrameState);
7029 }
7030 }
7032 #ifdef ACCESSIBILITY
7033 nsAccessibilityService* accService = nsIPresShell::AccService();
7034 if (accService) {
7035 accService->ContentRangeInserted(mPresShell, aContainer,
7036 aStartChild, aEndChild);
7037 }
7038 #endif
7040 return NS_OK;
7041 }
7043 if (aContainer->HasFlag(NODE_IS_IN_SHADOW_TREE)) {
7044 // Recreate frames if content is inserted into a ShadowRoot
7045 // because children of ShadowRoot are rendered in place of
7046 // the children of the host.
7047 nsIContent* bindingParent = aContainer->GetBindingParent();
7048 LAYOUT_PHASE_TEMP_EXIT();
7049 nsresult rv = RecreateFramesForContent(bindingParent, false);
7050 LAYOUT_PHASE_TEMP_REENTER();
7051 return rv;
7052 }
7054 nsIFrame* parentFrame = GetFrameFor(aContainer);
7055 // The xbl:children element won't have a frame, but default content can have the children as
7056 // a parent. While its uncommon to change the structure of the default content itself, a label,
7057 // for example, can be reframed by having its value attribute set or removed.
7058 if (!parentFrame && !aContainer->IsActiveChildrenElement()) {
7059 return NS_OK;
7060 }
7062 // Otherwise, we've got parent content. Find its frame.
7063 NS_ASSERTION(!parentFrame || parentFrame->GetContent() == aContainer, "New XBL code is possibly wrong!");
7065 if (aAllowLazyConstruction &&
7066 MaybeConstructLazily(CONTENTINSERT, aContainer, aStartChild)) {
7067 return NS_OK;
7068 }
7070 if (isSingleInsert) {
7071 // See if we have an XBL insertion point. If so, then that's our
7072 // real parent frame; if not, then the frame hasn't been built yet
7073 // and we just bail.
7074 parentFrame = GetInsertionPoint(aContainer, aStartChild);
7075 } else {
7076 // Get our insertion point. If we need to issue single ContentInserted's
7077 // GetRangeInsertionPoint will take care of that for us.
7078 LAYOUT_PHASE_TEMP_EXIT();
7079 parentFrame = GetRangeInsertionPoint(aContainer, aStartChild, aEndChild,
7080 aAllowLazyConstruction);
7081 LAYOUT_PHASE_TEMP_REENTER();
7082 }
7084 if (!parentFrame) {
7085 return NS_OK;
7086 }
7088 bool isAppend, isRangeInsertSafe;
7089 nsIFrame* prevSibling =
7090 GetInsertionPrevSibling(parentFrame, aContainer, aStartChild,
7091 &isAppend, &isRangeInsertSafe);
7093 // check if range insert is safe
7094 if (!isSingleInsert && !isRangeInsertSafe) {
7095 // must fall back to a single ContertInserted for each child in the range
7096 LAYOUT_PHASE_TEMP_EXIT();
7097 IssueSingleInsertNofications(aContainer, aStartChild, aEndChild,
7098 aAllowLazyConstruction);
7099 LAYOUT_PHASE_TEMP_REENTER();
7100 return NS_OK;
7101 }
7103 nsIContent* container = parentFrame->GetContent();
7105 nsIAtom* frameType = parentFrame->GetType();
7106 LAYOUT_PHASE_TEMP_EXIT();
7107 if (MaybeRecreateForFrameset(parentFrame, aStartChild, aEndChild)) {
7108 LAYOUT_PHASE_TEMP_REENTER();
7109 return NS_OK;
7110 }
7111 LAYOUT_PHASE_TEMP_REENTER();
7113 // We should only get here with fieldsets when doing a single insert, because
7114 // fieldsets have multiple insertion points.
7115 NS_ASSERTION(isSingleInsert || frameType != nsGkAtoms::fieldSetFrame,
7116 "Unexpected parent");
7117 if (IsFrameForFieldSet(parentFrame, frameType) &&
7118 aStartChild->Tag() == nsGkAtoms::legend) {
7119 // Just reframe the parent, since figuring out whether this
7120 // should be the new legend and then handling it is too complex.
7121 // We could do a little better here --- check if the fieldset already
7122 // has a legend which occurs earlier in its child list than this node,
7123 // and if so, proceed. But we'd have to extend nsFieldSetFrame
7124 // to locate this legend in the inserted frames and extract it.
7125 LAYOUT_PHASE_TEMP_EXIT();
7126 nsresult rv = RecreateFramesForContent(parentFrame->GetContent(), false);
7127 LAYOUT_PHASE_TEMP_REENTER();
7128 return rv;
7129 }
7131 // Don't construct kids of leaves
7132 if (parentFrame->IsLeaf()) {
7133 // Clear lazy bits so we don't try to construct again.
7134 ClearLazyBits(aStartChild, aEndChild);
7135 return NS_OK;
7136 }
7138 if (parentFrame->IsFrameOfType(nsIFrame::eMathML)) {
7139 LAYOUT_PHASE_TEMP_EXIT();
7140 nsresult rv = RecreateFramesForContent(parentFrame->GetContent(), false);
7141 LAYOUT_PHASE_TEMP_REENTER();
7142 return rv;
7143 }
7145 nsFrameConstructorState state(mPresShell,
7146 GetAbsoluteContainingBlock(parentFrame, FIXED_POS),
7147 GetAbsoluteContainingBlock(parentFrame, ABS_POS),
7148 GetFloatContainingBlock(parentFrame),
7149 aFrameState);
7150 state.mTreeMatchContext.InitAncestors(aContainer ?
7151 aContainer->AsElement() :
7152 nullptr);
7154 // Recover state for the containing block - we need to know if
7155 // it has :first-letter or :first-line style applied to it. The
7156 // reason we care is that the internal structure in these cases
7157 // is not the normal structure and requires custom updating
7158 // logic.
7159 nsIFrame* containingBlock = state.mFloatedItems.containingBlock;
7160 bool haveFirstLetterStyle = false;
7161 bool haveFirstLineStyle = false;
7163 // In order to shave off some cycles, we only dig up the
7164 // containing block haveFirst* flags if the parent frame where
7165 // the insertion/append is occurring is an inline or block
7166 // container. For other types of containers this isn't relevant.
7167 uint8_t parentDisplay = parentFrame->GetDisplay();
7169 // Examine the parentFrame where the insertion is taking
7170 // place. If it's a certain kind of container then some special
7171 // processing is done.
7172 if ((NS_STYLE_DISPLAY_BLOCK == parentDisplay) ||
7173 (NS_STYLE_DISPLAY_LIST_ITEM == parentDisplay) ||
7174 (NS_STYLE_DISPLAY_INLINE == parentDisplay) ||
7175 (NS_STYLE_DISPLAY_INLINE_BLOCK == parentDisplay)) {
7176 // Recover the special style flags for the containing block
7177 if (containingBlock) {
7178 haveFirstLetterStyle = HasFirstLetterStyle(containingBlock);
7179 haveFirstLineStyle =
7180 ShouldHaveFirstLineStyle(containingBlock->GetContent(),
7181 containingBlock->StyleContext());
7182 }
7184 if (haveFirstLetterStyle) {
7185 // If our current parentFrame is a Letter frame, use its parent as our
7186 // new parent hint
7187 if (parentFrame->GetType() == nsGkAtoms::letterFrame) {
7188 // If parentFrame is out of flow, then we actually want the parent of
7189 // the placeholder frame.
7190 if (parentFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
7191 nsPlaceholderFrame* placeholderFrame =
7192 GetPlaceholderFrameFor(parentFrame);
7193 NS_ASSERTION(placeholderFrame, "No placeholder for out-of-flow?");
7194 parentFrame = placeholderFrame->GetParent();
7195 } else {
7196 parentFrame = parentFrame->GetParent();
7197 }
7198 }
7200 // Remove the old letter frames before doing the insertion
7201 RemoveLetterFrames(state.mPresContext, mPresShell,
7202 state.mFloatedItems.containingBlock);
7204 // Removing the letterframes messes around with the frame tree, removing
7205 // and creating frames. We need to reget our prevsibling, parent frame,
7206 // etc.
7207 prevSibling = GetInsertionPrevSibling(parentFrame, aContainer,
7208 aStartChild, &isAppend,
7209 &isRangeInsertSafe);
7211 // Need check whether a range insert is still safe.
7212 if (!isSingleInsert && !isRangeInsertSafe) {
7213 // Need to recover the letter frames first.
7214 RecoverLetterFrames(state.mFloatedItems.containingBlock);
7216 // must fall back to a single ContertInserted for each child in the range
7217 LAYOUT_PHASE_TEMP_EXIT();
7218 IssueSingleInsertNofications(aContainer, aStartChild, aEndChild,
7219 aAllowLazyConstruction);
7220 LAYOUT_PHASE_TEMP_REENTER();
7221 return NS_OK;
7222 }
7224 container = parentFrame->GetContent();
7225 frameType = parentFrame->GetType();
7226 }
7227 }
7229 if (!prevSibling) {
7230 // We're inserting the new frames as the first child. See if the
7231 // parent has a :before pseudo-element
7232 nsIFrame* firstChild = parentFrame->GetFirstPrincipalChild();
7234 if (firstChild &&
7235 nsLayoutUtils::IsGeneratedContentFor(container, firstChild,
7236 nsCSSPseudoElements::before)) {
7237 // Insert the new frames after the last continuation of the :before
7238 prevSibling = firstChild->GetTailContinuation();
7239 parentFrame = prevSibling->GetParent()->GetContentInsertionFrame();
7240 // Don't change isAppend here; we'll can call AppendFrames as needed, and
7241 // the change to our prevSibling doesn't affect that.
7242 }
7243 }
7245 FrameConstructionItemList items;
7246 ParentType parentType = GetParentType(frameType);
7247 FlattenedChildIterator iter(aContainer);
7248 bool haveNoXBLChildren = (!iter.XBLInvolved() || !iter.GetNextChild());
7249 if (aStartChild->GetPreviousSibling() &&
7250 parentType == eTypeBlock && haveNoXBLChildren) {
7251 // If there's a text node in the normal content list just before the
7252 // new nodes, and it has no frame, make a frame construction item for
7253 // it, because it might need a frame now. No need to do this if our
7254 // parent type is not block, though, since WipeContainingBlock
7255 // already handles that sitation.
7256 AddTextItemIfNeeded(state, parentFrame, aStartChild->GetPreviousSibling(),
7257 items);
7258 }
7260 if (isSingleInsert) {
7261 AddFrameConstructionItems(state, aStartChild,
7262 aStartChild->IsRootOfAnonymousSubtree(),
7263 parentFrame, items);
7264 } else {
7265 for (nsIContent* child = aStartChild;
7266 child != aEndChild;
7267 child = child->GetNextSibling()){
7268 AddFrameConstructionItems(state, child, false, parentFrame, items);
7269 }
7270 }
7272 if (aEndChild && parentType == eTypeBlock && haveNoXBLChildren) {
7273 // If there's a text node in the normal content list just after the
7274 // new nodes, and it has no frame, make a frame construction item for
7275 // it, because it might need a frame now. No need to do this if our
7276 // parent type is not block, though, since WipeContainingBlock
7277 // already handles that sitation.
7278 AddTextItemIfNeeded(state, parentFrame, aEndChild, items);
7279 }
7281 // Perform special check for diddling around with the frames in
7282 // a special inline frame.
7283 // If we're appending before :after content, then we're not really
7284 // appending, so let WipeContainingBlock know that.
7285 LAYOUT_PHASE_TEMP_EXIT();
7286 if (WipeContainingBlock(state, containingBlock, parentFrame, items,
7287 isAppend, prevSibling)) {
7288 LAYOUT_PHASE_TEMP_REENTER();
7289 return NS_OK;
7290 }
7291 LAYOUT_PHASE_TEMP_REENTER();
7293 // If the container is a table and a caption will be appended, it needs to be
7294 // put in the outer table frame's additional child list.
7295 // We make no attempt here to set flags to indicate whether the list
7296 // will be at the start or end of a block. It doesn't seem worthwhile.
7297 nsFrameItems frameItems, captionItems;
7298 ConstructFramesFromItemList(state, items, parentFrame, frameItems);
7300 if (frameItems.NotEmpty()) {
7301 for (nsIContent* child = aStartChild;
7302 child != aEndChild;
7303 child = child->GetNextSibling()){
7304 InvalidateCanvasIfNeeded(mPresShell, child);
7305 }
7307 if (nsGkAtoms::tableFrame == frameType ||
7308 nsGkAtoms::tableOuterFrame == frameType) {
7309 PullOutCaptionFrames(frameItems, captionItems);
7310 }
7311 }
7313 // If the parent of our current prevSibling is different from the frame we'll
7314 // actually use as the parent, then the calculated insertion point is now
7315 // invalid and as it is unknown where to insert correctly we append instead
7316 // (bug 341858).
7317 // This can affect our prevSibling and isAppend, but should not have any
7318 // effect on the WipeContainingBlock above, since this should only happen
7319 // when neither parent is a ib-split frame and should not affect whitespace
7320 // handling inside table-related frames (and in fact, can only happen when
7321 // one of the parents is an outer table and one is an inner table or when the
7322 // parent is a fieldset or fieldset content frame). So it won't affect the
7323 // {ib} or XUL box cases in WipeContainingBlock(), and the table pseudo
7324 // handling will only be affected by us maybe thinking we're not inserting
7325 // at the beginning, whereas we really are. That would have made us reframe
7326 // unnecessarily, but that's ok.
7327 // XXXbz we should push our frame construction item code up higher, so we
7328 // know what our items are by the time we start figuring out previous
7329 // siblings
7330 if (prevSibling && frameItems.NotEmpty() &&
7331 frameItems.FirstChild()->GetParent() != prevSibling->GetParent()) {
7332 #ifdef DEBUG
7333 nsIFrame* frame1 = frameItems.FirstChild()->GetParent();
7334 nsIFrame* frame2 = prevSibling->GetParent();
7335 NS_ASSERTION(!IsFramePartOfIBSplit(frame1) &&
7336 !IsFramePartOfIBSplit(frame2),
7337 "Neither should be ib-split");
7338 NS_ASSERTION((frame1->GetType() == nsGkAtoms::tableFrame &&
7339 frame2->GetType() == nsGkAtoms::tableOuterFrame) ||
7340 (frame1->GetType() == nsGkAtoms::tableOuterFrame &&
7341 frame2->GetType() == nsGkAtoms::tableFrame) ||
7342 frame1->GetType() == nsGkAtoms::fieldSetFrame ||
7343 (frame1->GetParent() &&
7344 frame1->GetParent()->GetType() == nsGkAtoms::fieldSetFrame),
7345 "Unexpected frame types");
7346 #endif
7347 isAppend = true;
7348 nsIFrame* appendAfterFrame;
7349 parentFrame =
7350 ::AdjustAppendParentForAfterContent(mPresShell->GetPresContext(),
7351 container,
7352 frameItems.FirstChild()->GetParent(),
7353 &appendAfterFrame);
7354 prevSibling = ::FindAppendPrevSibling(parentFrame, appendAfterFrame);
7355 }
7357 if (haveFirstLineStyle && parentFrame == containingBlock) {
7358 // It's possible that the new frame goes into a first-line
7359 // frame. Look at it and see...
7360 if (isAppend) {
7361 // Use append logic when appending
7362 AppendFirstLineFrames(state, containingBlock->GetContent(),
7363 containingBlock, frameItems);
7364 }
7365 else {
7366 // Use more complicated insert logic when inserting
7367 // XXXbz this method is a no-op, so it's easy for the args being passed
7368 // here to make no sense without anyone noticing... If it ever stops
7369 // being a no-op, vet them carefully!
7370 InsertFirstLineFrames(state, container, containingBlock, &parentFrame,
7371 prevSibling, frameItems);
7372 }
7373 }
7375 // We might have captions; put them into the caption list of the
7376 // outer table frame.
7377 if (captionItems.NotEmpty()) {
7378 NS_ASSERTION(nsGkAtoms::tableFrame == frameType ||
7379 nsGkAtoms::tableOuterFrame == frameType,
7380 "parent for caption is not table?");
7381 // We need to determine where to put the caption items; start with the
7382 // the parent frame that has already been determined and get the insertion
7383 // prevsibling of the first caption item.
7384 nsIFrame* captionParent = parentFrame;
7385 bool captionIsAppend;
7386 nsIFrame* captionPrevSibling = nullptr;
7388 // aIsRangeInsertSafe is ignored on purpose because it is irrelevant here.
7389 bool ignored;
7390 if (isSingleInsert) {
7391 captionPrevSibling =
7392 GetInsertionPrevSibling(captionParent, aContainer, aStartChild,
7393 &captionIsAppend, &ignored);
7394 } else {
7395 nsIContent* firstCaption = captionItems.FirstChild()->GetContent();
7396 // It is very important here that we skip the children in
7397 // [aStartChild,aEndChild) when looking for a
7398 // prevsibling.
7399 captionPrevSibling =
7400 GetInsertionPrevSibling(captionParent, aContainer, firstCaption,
7401 &captionIsAppend, &ignored,
7402 aStartChild, aEndChild);
7403 }
7405 nsIFrame* outerTable = nullptr;
7406 if (GetCaptionAdjustedParent(captionParent, captionItems.FirstChild(),
7407 &outerTable)) {
7408 // If the parent is not an outer table frame we will try to add frames
7409 // to a named child list that the parent does not honour and the frames
7410 // will get lost
7411 NS_ASSERTION(nsGkAtoms::tableOuterFrame == outerTable->GetType(),
7412 "Pseudo frame construction failure; "
7413 "a caption can be only a child of an outer table frame");
7415 // If the parent of our current prevSibling is different from the frame
7416 // we'll actually use as the parent, then the calculated insertion
7417 // point is now invalid (bug 341382).
7418 if (captionPrevSibling &&
7419 captionPrevSibling->GetParent() != outerTable) {
7420 captionPrevSibling = nullptr;
7421 }
7422 if (captionIsAppend) {
7423 AppendFrames(outerTable, nsIFrame::kCaptionList, captionItems);
7424 } else {
7425 InsertFrames(outerTable, nsIFrame::kCaptionList,
7426 captionPrevSibling, captionItems);
7427 }
7428 }
7429 }
7431 if (frameItems.NotEmpty()) {
7432 // Notify the parent frame
7433 if (isAppend) {
7434 AppendFramesToParent(state, parentFrame, frameItems, prevSibling);
7435 } else {
7436 InsertFrames(parentFrame, kPrincipalList, prevSibling, frameItems);
7437 }
7438 }
7440 if (haveFirstLetterStyle) {
7441 // Recover the letter frames for the containing block when
7442 // it has first-letter style.
7443 RecoverLetterFrames(state.mFloatedItems.containingBlock);
7444 }
7446 #ifdef DEBUG
7447 if (gReallyNoisyContentUpdates && parentFrame) {
7448 printf("nsCSSFrameConstructor::ContentRangeInserted: resulting frame model:\n");
7449 parentFrame->List(stdout, 0);
7450 }
7451 #endif
7453 #ifdef ACCESSIBILITY
7454 nsAccessibilityService* accService = nsIPresShell::AccService();
7455 if (accService) {
7456 accService->ContentRangeInserted(mPresShell, aContainer,
7457 aStartChild, aEndChild);
7458 }
7459 #endif
7461 return NS_OK;
7462 }
7464 nsresult
7465 nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer,
7466 nsIContent* aChild,
7467 nsIContent* aOldNextSibling,
7468 RemoveFlags aFlags,
7469 bool* aDidReconstruct)
7470 {
7471 AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
7472 NS_PRECONDITION(mUpdateCount != 0,
7473 "Should be in an update while destroying frames");
7475 *aDidReconstruct = false;
7477 // XXXldb Do we need to re-resolve style to handle the CSS2 + combinator and
7478 // the :empty pseudo-class?
7480 #ifdef DEBUG
7481 if (gNoisyContentUpdates) {
7482 printf("nsCSSFrameConstructor::ContentRemoved container=%p child=%p "
7483 "old-next-sibling=%p\n",
7484 static_cast<void*>(aContainer),
7485 static_cast<void*>(aChild),
7486 static_cast<void*>(aOldNextSibling));
7487 if (gReallyNoisyContentUpdates) {
7488 aContainer->List(stdout, 0);
7489 }
7490 }
7491 #endif
7493 nsPresContext *presContext = mPresShell->GetPresContext();
7494 nsresult rv = NS_OK;
7496 // Find the child frame that maps the content
7497 nsIFrame* childFrame = aChild->GetPrimaryFrame();
7499 if (!childFrame || childFrame->GetContent() != aChild) {
7500 // XXXbz the GetContent() != aChild check is needed due to bug 135040.
7501 // Remove it once that's fixed.
7502 ClearUndisplayedContentIn(aChild, aContainer);
7503 }
7505 #ifdef MOZ_XUL
7506 if (NotifyListBoxBody(presContext, aContainer, aChild, aOldNextSibling,
7507 mDocument, childFrame, CONTENT_REMOVED))
7508 return NS_OK;
7510 #endif // MOZ_XUL
7512 // If we're removing the root, then make sure to remove things starting at
7513 // the viewport's child instead of the primary frame (which might even be
7514 // null if the root had an XBL binding or display:none, even though the
7515 // frames above it got created). We do the adjustment after the childFrame
7516 // check above, because we do want to clear any undisplayed content we might
7517 // have for the root. Detecting removal of a root is a little exciting; in
7518 // particular, having a null aContainer is necessary but NOT sufficient. Due
7519 // to how we process reframes, the content node might not even be in our
7520 // document by now. So explicitly check whether the viewport's first kid's
7521 // content node is aChild.
7522 bool isRoot = false;
7523 if (!aContainer) {
7524 nsIFrame* viewport = GetRootFrame();
7525 if (viewport) {
7526 nsIFrame* firstChild = viewport->GetFirstPrincipalChild();
7527 if (firstChild && firstChild->GetContent() == aChild) {
7528 isRoot = true;
7529 childFrame = firstChild;
7530 NS_ASSERTION(!childFrame->GetNextSibling(), "How did that happen?");
7531 }
7532 }
7533 }
7535 if (aContainer && aContainer->HasFlag(NODE_IS_IN_SHADOW_TREE)) {
7536 // Recreate frames if content is removed from a ShadowRoot
7537 // because it may contain an insertion point which can change
7538 // how the host is rendered.
7539 nsIContent* bindingParent = aContainer->GetBindingParent();
7540 *aDidReconstruct = true;
7541 LAYOUT_PHASE_TEMP_EXIT();
7542 nsresult rv = RecreateFramesForContent(bindingParent, false);
7543 LAYOUT_PHASE_TEMP_REENTER();
7544 return rv;
7545 }
7547 if (childFrame) {
7548 InvalidateCanvasIfNeeded(mPresShell, aChild);
7550 // See whether we need to remove more than just childFrame
7551 LAYOUT_PHASE_TEMP_EXIT();
7552 if (MaybeRecreateContainerForFrameRemoval(childFrame, &rv)) {
7553 LAYOUT_PHASE_TEMP_REENTER();
7554 *aDidReconstruct = true;
7555 return rv;
7556 }
7557 LAYOUT_PHASE_TEMP_REENTER();
7559 // Get the childFrame's parent frame
7560 nsIFrame* parentFrame = childFrame->GetParent();
7561 nsIAtom* parentType = parentFrame->GetType();
7563 if (parentType == nsGkAtoms::frameSetFrame &&
7564 IsSpecialFramesetChild(aChild)) {
7565 // Just reframe the parent, since framesets are weird like that.
7566 *aDidReconstruct = true;
7567 LAYOUT_PHASE_TEMP_EXIT();
7568 nsresult rv = RecreateFramesForContent(parentFrame->GetContent(), false);
7569 LAYOUT_PHASE_TEMP_REENTER();
7570 return rv;
7571 }
7573 // If we're a child of MathML, then we should reframe the MathML content.
7574 // If we're non-MathML, then we would be wrapped in a block so we need to
7575 // check our grandparent in that case.
7576 nsIFrame* possibleMathMLAncestor = parentType == nsGkAtoms::blockFrame ?
7577 parentFrame->GetParent() : parentFrame;
7578 if (possibleMathMLAncestor->IsFrameOfType(nsIFrame::eMathML)) {
7579 *aDidReconstruct = true;
7580 LAYOUT_PHASE_TEMP_EXIT();
7581 nsresult rv = RecreateFramesForContent(possibleMathMLAncestor->GetContent(), false);
7582 LAYOUT_PHASE_TEMP_REENTER();
7583 return rv;
7584 }
7586 // Undo XUL wrapping if it's no longer needed.
7587 // (If we're in the XUL block-wrapping situation, parentFrame is the
7588 // wrapper frame.)
7589 nsIFrame* grandparentFrame = parentFrame->GetParent();
7590 if (grandparentFrame && grandparentFrame->IsBoxFrame() &&
7591 (grandparentFrame->GetStateBits() & NS_STATE_BOX_WRAPS_KIDS_IN_BLOCK) &&
7592 // check if this frame is the only one needing wrapping
7593 aChild == AnyKidsNeedBlockParent(parentFrame->GetFirstPrincipalChild()) &&
7594 !AnyKidsNeedBlockParent(childFrame->GetNextSibling())) {
7595 *aDidReconstruct = true;
7596 LAYOUT_PHASE_TEMP_EXIT();
7597 nsresult rv = RecreateFramesForContent(grandparentFrame->GetContent(), true);
7598 LAYOUT_PHASE_TEMP_REENTER();
7599 return rv;
7600 }
7602 #ifdef ACCESSIBILITY
7603 nsAccessibilityService* accService = nsIPresShell::AccService();
7604 if (accService) {
7605 accService->ContentRemoved(mPresShell, aContainer, aChild);
7606 }
7607 #endif
7609 // Examine the containing-block for the removed content and see if
7610 // :first-letter style applies.
7611 nsIFrame* inflowChild = childFrame;
7612 if (childFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
7613 inflowChild = GetPlaceholderFrameFor(childFrame);
7614 NS_ASSERTION(inflowChild, "No placeholder for out-of-flow?");
7615 }
7616 nsIFrame* containingBlock =
7617 GetFloatContainingBlock(inflowChild->GetParent());
7618 bool haveFLS = containingBlock && HasFirstLetterStyle(containingBlock);
7619 if (haveFLS) {
7620 // Trap out to special routine that handles adjusting a blocks
7621 // frame tree when first-letter style is present.
7622 #ifdef NOISY_FIRST_LETTER
7623 printf("ContentRemoved: containingBlock=");
7624 nsFrame::ListTag(stdout, containingBlock);
7625 printf(" parentFrame=");
7626 nsFrame::ListTag(stdout, parentFrame);
7627 printf(" childFrame=");
7628 nsFrame::ListTag(stdout, childFrame);
7629 printf("\n");
7630 #endif
7632 // First update the containing blocks structure by removing the
7633 // existing letter frames. This makes the subsequent logic
7634 // simpler.
7635 RemoveLetterFrames(presContext, mPresShell, containingBlock);
7637 // Recover childFrame and parentFrame
7638 childFrame = aChild->GetPrimaryFrame();
7639 if (!childFrame || childFrame->GetContent() != aChild) {
7640 // XXXbz the GetContent() != aChild check is needed due to bug 135040.
7641 // Remove it once that's fixed.
7642 ClearUndisplayedContentIn(aChild, aContainer);
7643 return NS_OK;
7644 }
7645 parentFrame = childFrame->GetParent();
7646 parentType = parentFrame->GetType();
7648 #ifdef NOISY_FIRST_LETTER
7649 printf(" ==> revised parentFrame=");
7650 nsFrame::ListTag(stdout, parentFrame);
7651 printf(" childFrame=");
7652 nsFrame::ListTag(stdout, childFrame);
7653 printf("\n");
7654 #endif
7655 }
7657 #ifdef DEBUG
7658 if (gReallyNoisyContentUpdates) {
7659 printf("nsCSSFrameConstructor::ContentRemoved: childFrame=");
7660 nsFrame::ListTag(stdout, childFrame);
7661 putchar('\n');
7662 parentFrame->List(stdout, 0);
7663 }
7664 #endif
7667 // Notify the parent frame that it should delete the frame
7668 if (childFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
7669 childFrame = GetPlaceholderFrameFor(childFrame);
7670 NS_ASSERTION(childFrame, "Missing placeholder frame for out of flow.");
7671 parentFrame = childFrame->GetParent();
7672 }
7673 rv = RemoveFrame(nsLayoutUtils::GetChildListNameFor(childFrame),
7674 childFrame);
7675 //XXXfr NS_ENSURE_SUCCESS(rv, rv) ?
7677 if (isRoot) {
7678 mRootElementFrame = nullptr;
7679 mRootElementStyleFrame = nullptr;
7680 mDocElementContainingBlock = nullptr;
7681 mPageSequenceFrame = nullptr;
7682 mGfxScrollFrame = nullptr;
7683 mHasRootAbsPosContainingBlock = false;
7684 mFixedContainingBlock = GetRootFrame();
7685 }
7687 if (haveFLS && mRootElementFrame) {
7688 RecoverLetterFrames(containingBlock);
7689 }
7691 // If we're just reconstructing frames for the element, then the
7692 // following ContentInserted notification on the element will
7693 // take care of fixing up any adjacent text nodes. We don't need
7694 // to do this if the table parent type of our parent type is not
7695 // eTypeBlock, though, because in that case the whitespace isn't
7696 // being suppressed due to us anyway.
7697 if (aContainer && !aChild->IsRootOfAnonymousSubtree() &&
7698 aFlags != REMOVE_FOR_RECONSTRUCTION &&
7699 GetParentType(parentType) == eTypeBlock) {
7700 // Adjacent whitespace-only text nodes might have been suppressed if
7701 // this node does not have inline ends. Create frames for them now
7702 // if necessary.
7703 // Reframe any text node just before the node being removed, if there is
7704 // one, and if it's not the last child or the first child. If a whitespace
7705 // textframe was being suppressed and it's now the last child or first
7706 // child then it can stay suppressed since the parent must be a block
7707 // and hence it's adjacent to a block end.
7708 // If aOldNextSibling is null, then the text node before the node being
7709 // removed is the last node, and we don't need to worry about it.
7710 if (aOldNextSibling) {
7711 nsIContent* prevSibling = aOldNextSibling->GetPreviousSibling();
7712 if (prevSibling && prevSibling->GetPreviousSibling()) {
7713 LAYOUT_PHASE_TEMP_EXIT();
7714 ReframeTextIfNeeded(aContainer, prevSibling);
7715 LAYOUT_PHASE_TEMP_REENTER();
7716 }
7717 }
7718 // Reframe any text node just after the node being removed, if there is
7719 // one, and if it's not the last child or the first child.
7720 if (aOldNextSibling && aOldNextSibling->GetNextSibling() &&
7721 aOldNextSibling->GetPreviousSibling()) {
7722 LAYOUT_PHASE_TEMP_EXIT();
7723 ReframeTextIfNeeded(aContainer, aOldNextSibling);
7724 LAYOUT_PHASE_TEMP_REENTER();
7725 }
7726 }
7728 #ifdef DEBUG
7729 if (gReallyNoisyContentUpdates && parentFrame) {
7730 printf("nsCSSFrameConstructor::ContentRemoved: resulting frame model:\n");
7731 parentFrame->List(stdout, 0);
7732 }
7733 #endif
7734 }
7736 return rv;
7737 }
7739 /**
7740 * This method invalidates the canvas when frames are removed or added for a
7741 * node that might have its background propagated to the canvas, i.e., a
7742 * document root node or an HTML BODY which is a child of the root node.
7743 *
7744 * @param aFrame a frame for a content node about to be removed or a frame that
7745 * was just created for a content node that was inserted.
7746 */
7747 static void
7748 InvalidateCanvasIfNeeded(nsIPresShell* presShell, nsIContent* node)
7749 {
7750 NS_PRECONDITION(presShell->GetRootFrame(), "What happened here?");
7751 NS_PRECONDITION(presShell->GetPresContext(), "Say what?");
7753 // Note that both in ContentRemoved and ContentInserted the content node
7754 // will still have the right parent pointer, so looking at that is ok.
7756 nsIContent* parent = node->GetParent();
7757 if (parent) {
7758 // Has a parent; might not be what we want
7759 nsIContent* grandParent = parent->GetParent();
7760 if (grandParent) {
7761 // Has a grandparent, so not what we want
7762 return;
7763 }
7765 // Check whether it's an HTML body
7766 if (node->Tag() != nsGkAtoms::body ||
7767 !node->IsHTML()) {
7768 return;
7769 }
7770 }
7772 // At this point the node has no parent or it's an HTML <body> child of the
7773 // root. We might not need to invalidate in this case (eg we might be in
7774 // XHTML or something), but chances are we want to. Play it safe.
7775 // Invalidate the viewport.
7777 nsIFrame* rootFrame = presShell->GetRootFrame();
7778 rootFrame->InvalidateFrameSubtree();
7779 }
7781 nsIFrame*
7782 nsCSSFrameConstructor::EnsureFrameForTextNode(nsGenericDOMDataNode* aContent)
7783 {
7784 if (aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE) &&
7785 !mAlwaysCreateFramesForIgnorableWhitespace) {
7786 // Text frame may have been suppressed. Disable suppression and signal
7787 // that a flush should be performed. We do this on a document-wide
7788 // basis so that pages that repeatedly query metrics for
7789 // collapsed-whitespace text nodes don't trigger pathological behavior.
7790 mAlwaysCreateFramesForIgnorableWhitespace = true;
7791 nsAutoScriptBlocker blocker;
7792 BeginUpdate();
7793 ReconstructDocElementHierarchy();
7794 EndUpdate();
7795 }
7796 return aContent->GetPrimaryFrame();
7797 }
7799 nsresult
7800 nsCSSFrameConstructor::CharacterDataChanged(nsIContent* aContent,
7801 CharacterDataChangeInfo* aInfo)
7802 {
7803 AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
7804 nsresult rv = NS_OK;
7806 if ((aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE) &&
7807 !aContent->TextIsOnlyWhitespace()) ||
7808 (aContent->HasFlag(NS_REFRAME_IF_WHITESPACE) &&
7809 aContent->TextIsOnlyWhitespace())) {
7810 #ifdef DEBUG
7811 nsIFrame* frame = aContent->GetPrimaryFrame();
7812 NS_ASSERTION(!frame || !frame->IsGeneratedContentFrame(),
7813 "Bit should never be set on generated content");
7814 #endif
7815 LAYOUT_PHASE_TEMP_EXIT();
7816 nsresult rv = RecreateFramesForContent(aContent, false);
7817 LAYOUT_PHASE_TEMP_REENTER();
7818 return rv;
7819 }
7821 // Find the child frame
7822 nsIFrame* frame = aContent->GetPrimaryFrame();
7824 // Notify the first frame that maps the content. It will generate a reflow
7825 // command
7827 // It's possible the frame whose content changed isn't inserted into the
7828 // frame hierarchy yet, or that there is no frame that maps the content
7829 if (nullptr != frame) {
7830 #if 0
7831 NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
7832 ("nsCSSFrameConstructor::CharacterDataChanged: content=%p[%s] subcontent=%p frame=%p",
7833 aContent, ContentTag(aContent, 0),
7834 aSubContent, frame));
7835 #endif
7837 // Special check for text content that is a child of a letter frame. If
7838 // this happens, we should remove the letter frame, do whatever we're
7839 // planning to do with this notification, then put the letter frame back.
7840 // Note that this is basically what RecreateFramesForContent ends up doing;
7841 // the reason we dont' want to call that here is that our text content
7842 // could be native anonymous, in which case RecreateFramesForContent would
7843 // completely barf on it. And recreating the non-anonymous ancestor would
7844 // just lead us to come back into this notification (e.g. if quotes or
7845 // counters are involved), leading to a loop.
7846 nsIFrame* block = GetFloatContainingBlock(frame);
7847 bool haveFirstLetterStyle = false;
7848 if (block) {
7849 // See if the block has first-letter style applied to it.
7850 haveFirstLetterStyle = HasFirstLetterStyle(block);
7851 if (haveFirstLetterStyle) {
7852 RemoveLetterFrames(mPresShell->GetPresContext(), mPresShell,
7853 block);
7854 // Reget |frame|, since we might have killed it.
7855 // Do we really need to call CharacterDataChanged in this case, though?
7856 frame = aContent->GetPrimaryFrame();
7857 NS_ASSERTION(frame, "Should have frame here!");
7858 }
7859 }
7861 frame->CharacterDataChanged(aInfo);
7863 if (haveFirstLetterStyle) {
7864 RecoverLetterFrames(block);
7865 }
7866 }
7868 return rv;
7869 }
7871 void
7872 nsCSSFrameConstructor::BeginUpdate() {
7873 NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
7874 "Someone forgot a script blocker");
7876 nsRootPresContext* rootPresContext =
7877 mPresShell->GetPresContext()->GetRootPresContext();
7878 if (rootPresContext) {
7879 rootPresContext->IncrementDOMGeneration();
7880 }
7882 ++sGlobalGenerationNumber;
7883 ++mUpdateCount;
7884 }
7886 void
7887 nsCSSFrameConstructor::EndUpdate()
7888 {
7889 if (mUpdateCount == 1) {
7890 // This is the end of our last update. Before we decrement
7891 // mUpdateCount, recalc quotes and counters as needed.
7893 RecalcQuotesAndCounters();
7894 NS_ASSERTION(mUpdateCount == 1, "Odd update count");
7895 }
7896 NS_ASSERTION(mUpdateCount, "Negative mUpdateCount!");
7897 --mUpdateCount;
7898 }
7900 void
7901 nsCSSFrameConstructor::RecalcQuotesAndCounters()
7902 {
7903 if (mQuotesDirty) {
7904 mQuotesDirty = false;
7905 mQuoteList.RecalcAll();
7906 }
7908 if (mCountersDirty) {
7909 mCountersDirty = false;
7910 mCounterManager.RecalcAll();
7911 }
7913 NS_ASSERTION(!mQuotesDirty, "Quotes updates will be lost");
7914 NS_ASSERTION(!mCountersDirty, "Counter updates will be lost");
7915 }
7917 void
7918 nsCSSFrameConstructor::WillDestroyFrameTree()
7919 {
7920 #if defined(DEBUG_dbaron_off)
7921 mCounterManager.Dump();
7922 #endif
7924 mIsDestroyingFrameTree = true;
7926 // Prevent frame tree destruction from being O(N^2)
7927 mQuoteList.Clear();
7928 mCounterManager.Clear();
7930 // Remove our presshell as a style flush observer. But leave
7931 // RestyleManager::mObservingRefreshDriver true so we don't readd to
7932 // it even if someone tries to post restyle events on us from this
7933 // point on for some reason.
7934 mPresShell->GetPresContext()->RefreshDriver()->
7935 RemoveStyleFlushObserver(mPresShell);
7937 nsFrameManager::Destroy();
7938 }
7940 //STATIC
7942 // XXXbz I'd really like this method to go away. Once we have inline-block and
7943 // I can just use that for sized broken images, that can happen, maybe.
7944 void nsCSSFrameConstructor::GetAlternateTextFor(nsIContent* aContent,
7945 nsIAtom* aTag, // content object's tag
7946 nsXPIDLString& aAltText)
7947 {
7948 // The "alt" attribute specifies alternate text that is rendered
7949 // when the image can not be displayed
7951 // If there's no "alt" attribute, and aContent is an input
7952 // element, then use the value of the "value" attribute
7953 if (!aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::alt, aAltText) &&
7954 nsGkAtoms::input == aTag) {
7955 // If there's no "value" attribute either, then use the localized string
7956 // for "Submit" as the alternate text.
7957 if (!aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::value, aAltText)) {
7958 nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
7959 "Submit", aAltText);
7960 }
7961 }
7962 }
7964 nsIFrame*
7965 nsCSSFrameConstructor::CreateContinuingOuterTableFrame(nsIPresShell* aPresShell,
7966 nsPresContext* aPresContext,
7967 nsIFrame* aFrame,
7968 nsIFrame* aParentFrame,
7969 nsIContent* aContent,
7970 nsStyleContext* aStyleContext)
7971 {
7972 nsIFrame* newFrame = NS_NewTableOuterFrame(aPresShell, aStyleContext);
7974 newFrame->Init(aContent, aParentFrame, aFrame);
7976 // Create a continuing inner table frame, and if there's a caption then
7977 // replicate the caption
7978 nsFrameItems newChildFrames;
7980 nsIFrame* childFrame = aFrame->GetFirstPrincipalChild();
7981 if (childFrame) {
7982 nsIFrame* continuingTableFrame =
7983 CreateContinuingFrame(aPresContext, childFrame, newFrame);
7984 newChildFrames.AddChild(continuingTableFrame);
7986 NS_ASSERTION(!childFrame->GetNextSibling(),"there can be only one inner table frame");
7987 }
7989 // Set the outer table's initial child list
7990 newFrame->SetInitialChildList(kPrincipalList, newChildFrames);
7992 return newFrame;
7993 }
7995 nsIFrame*
7996 nsCSSFrameConstructor::CreateContinuingTableFrame(nsIPresShell* aPresShell,
7997 nsPresContext* aPresContext,
7998 nsIFrame* aFrame,
7999 nsIFrame* aParentFrame,
8000 nsIContent* aContent,
8001 nsStyleContext* aStyleContext)
8002 {
8003 nsIFrame* newFrame = NS_NewTableFrame(aPresShell, aStyleContext);
8005 newFrame->Init(aContent, aParentFrame, aFrame);
8007 // Replicate any header/footer frames
8008 nsFrameItems childFrames;
8009 nsIFrame* childFrame = aFrame->GetFirstPrincipalChild();
8010 for ( ; childFrame; childFrame = childFrame->GetNextSibling()) {
8011 // See if it's a header/footer, possibly wrapped in a scroll frame.
8012 nsTableRowGroupFrame* rowGroupFrame =
8013 static_cast<nsTableRowGroupFrame*>(childFrame);
8014 // If the row group was continued, then don't replicate it.
8015 nsIFrame* rgNextInFlow = rowGroupFrame->GetNextInFlow();
8016 if (rgNextInFlow) {
8017 rowGroupFrame->SetRepeatable(false);
8018 }
8019 else if (rowGroupFrame->IsRepeatable()) {
8020 // Replicate the header/footer frame.
8021 nsTableRowGroupFrame* headerFooterFrame;
8022 nsFrameItems childItems;
8023 nsFrameConstructorState state(mPresShell,
8024 GetAbsoluteContainingBlock(newFrame, FIXED_POS),
8025 GetAbsoluteContainingBlock(newFrame, ABS_POS),
8026 nullptr);
8027 state.mCreatingExtraFrames = true;
8029 nsStyleContext* const headerFooterStyleContext = rowGroupFrame->StyleContext();
8030 headerFooterFrame = static_cast<nsTableRowGroupFrame*>
8031 (NS_NewTableRowGroupFrame(aPresShell, headerFooterStyleContext));
8033 nsIContent* headerFooter = rowGroupFrame->GetContent();
8034 headerFooterFrame->Init(headerFooter, newFrame, nullptr);
8036 nsFrameConstructorSaveState absoluteSaveState;
8037 MakeTablePartAbsoluteContainingBlockIfNeeded(state,
8038 headerFooterStyleContext->StyleDisplay(),
8039 absoluteSaveState,
8040 headerFooterFrame);
8042 ProcessChildren(state, headerFooter, rowGroupFrame->StyleContext(),
8043 headerFooterFrame, true, childItems, false,
8044 nullptr);
8045 NS_ASSERTION(state.mFloatedItems.IsEmpty(), "unexpected floated element");
8046 headerFooterFrame->SetInitialChildList(kPrincipalList, childItems);
8047 headerFooterFrame->SetRepeatable(true);
8049 // Table specific initialization
8050 headerFooterFrame->InitRepeatedFrame(aPresContext, rowGroupFrame);
8052 // XXX Deal with absolute and fixed frames...
8053 childFrames.AddChild(headerFooterFrame);
8054 }
8055 }
8057 // Set the table frame's initial child list
8058 newFrame->SetInitialChildList(kPrincipalList, childFrames);
8060 return newFrame;
8061 }
8063 nsIFrame*
8064 nsCSSFrameConstructor::CreateContinuingFrame(nsPresContext* aPresContext,
8065 nsIFrame* aFrame,
8066 nsIFrame* aParentFrame,
8067 bool aIsFluid)
8068 {
8069 nsIPresShell* shell = aPresContext->PresShell();
8070 nsStyleContext* styleContext = aFrame->StyleContext();
8071 nsIFrame* newFrame = nullptr;
8072 nsIFrame* nextContinuation = aFrame->GetNextContinuation();
8073 nsIFrame* nextInFlow = aFrame->GetNextInFlow();
8075 // Use the frame type to determine what type of frame to create
8076 nsIAtom* frameType = aFrame->GetType();
8077 nsIContent* content = aFrame->GetContent();
8079 NS_ASSERTION(aFrame->GetSplittableType() != NS_FRAME_NOT_SPLITTABLE,
8080 "why CreateContinuingFrame for a non-splittable frame?");
8082 if (nsGkAtoms::textFrame == frameType) {
8083 newFrame = NS_NewContinuingTextFrame(shell, styleContext);
8084 newFrame->Init(content, aParentFrame, aFrame);
8085 } else if (nsGkAtoms::inlineFrame == frameType) {
8086 newFrame = NS_NewInlineFrame(shell, styleContext);
8087 newFrame->Init(content, aParentFrame, aFrame);
8088 } else if (nsGkAtoms::blockFrame == frameType) {
8089 newFrame = NS_NewBlockFrame(shell, styleContext);
8090 newFrame->Init(content, aParentFrame, aFrame);
8091 #ifdef MOZ_XUL
8092 } else if (nsGkAtoms::XULLabelFrame == frameType) {
8093 newFrame = NS_NewXULLabelFrame(shell, styleContext);
8094 newFrame->Init(content, aParentFrame, aFrame);
8095 #endif
8096 } else if (nsGkAtoms::columnSetFrame == frameType) {
8097 newFrame = NS_NewColumnSetFrame(shell, styleContext, nsFrameState(0));
8098 newFrame->Init(content, aParentFrame, aFrame);
8099 } else if (nsGkAtoms::pageFrame == frameType) {
8100 nsIFrame* canvasFrame;
8101 newFrame = ConstructPageFrame(shell, aPresContext, aParentFrame, aFrame,
8102 canvasFrame);
8103 } else if (nsGkAtoms::tableOuterFrame == frameType) {
8104 newFrame =
8105 CreateContinuingOuterTableFrame(shell, aPresContext, aFrame, aParentFrame,
8106 content, styleContext);
8108 } else if (nsGkAtoms::tableFrame == frameType) {
8109 newFrame =
8110 CreateContinuingTableFrame(shell, aPresContext, aFrame, aParentFrame,
8111 content, styleContext);
8113 } else if (nsGkAtoms::tableRowGroupFrame == frameType) {
8114 newFrame = NS_NewTableRowGroupFrame(shell, styleContext);
8115 newFrame->Init(content, aParentFrame, aFrame);
8116 if (newFrame->GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN) {
8117 nsTableFrame::RegisterPositionedTablePart(newFrame);
8118 }
8119 } else if (nsGkAtoms::tableRowFrame == frameType) {
8120 newFrame = NS_NewTableRowFrame(shell, styleContext);
8122 newFrame->Init(content, aParentFrame, aFrame);
8123 if (newFrame->GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN) {
8124 nsTableFrame::RegisterPositionedTablePart(newFrame);
8125 }
8127 // Create a continuing frame for each table cell frame
8128 nsFrameItems newChildList;
8129 nsIFrame* cellFrame = aFrame->GetFirstPrincipalChild();
8130 while (cellFrame) {
8131 // See if it's a table cell frame
8132 if (IS_TABLE_CELL(cellFrame->GetType())) {
8133 nsIFrame* continuingCellFrame =
8134 CreateContinuingFrame(aPresContext, cellFrame, newFrame);
8135 newChildList.AddChild(continuingCellFrame);
8136 }
8137 cellFrame = cellFrame->GetNextSibling();
8138 }
8140 // Set the table cell's initial child list
8141 newFrame->SetInitialChildList(kPrincipalList, newChildList);
8143 } else if (IS_TABLE_CELL(frameType)) {
8144 // Warning: If you change this and add a wrapper frame around table cell
8145 // frames, make sure Bug 368554 doesn't regress!
8146 // See IsInAutoWidthTableCellForQuirk() in nsImageFrame.cpp.
8147 newFrame = NS_NewTableCellFrame(shell, styleContext, IsBorderCollapse(aParentFrame));
8149 newFrame->Init(content, aParentFrame, aFrame);
8150 if (newFrame->GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN) {
8151 nsTableFrame::RegisterPositionedTablePart(newFrame);
8152 }
8154 // Create a continuing area frame
8155 nsIFrame* blockFrame = aFrame->GetFirstPrincipalChild();
8156 nsIFrame* continuingBlockFrame =
8157 CreateContinuingFrame(aPresContext, blockFrame, newFrame);
8159 // Set the table cell's initial child list
8160 SetInitialSingleChild(newFrame, continuingBlockFrame);
8161 } else if (nsGkAtoms::lineFrame == frameType) {
8162 newFrame = NS_NewFirstLineFrame(shell, styleContext);
8163 newFrame->Init(content, aParentFrame, aFrame);
8164 } else if (nsGkAtoms::letterFrame == frameType) {
8165 newFrame = NS_NewFirstLetterFrame(shell, styleContext);
8166 newFrame->Init(content, aParentFrame, aFrame);
8167 } else if (nsGkAtoms::imageFrame == frameType) {
8168 newFrame = NS_NewImageFrame(shell, styleContext);
8169 newFrame->Init(content, aParentFrame, aFrame);
8170 } else if (nsGkAtoms::imageControlFrame == frameType) {
8171 newFrame = NS_NewImageControlFrame(shell, styleContext);
8172 newFrame->Init(content, aParentFrame, aFrame);
8173 } else if (nsGkAtoms::placeholderFrame == frameType) {
8174 // create a continuing out of flow frame
8175 nsIFrame* oofFrame = nsPlaceholderFrame::GetRealFrameForPlaceholder(aFrame);
8176 nsIFrame* oofContFrame =
8177 CreateContinuingFrame(aPresContext, oofFrame, aParentFrame);
8178 newFrame =
8179 CreatePlaceholderFrameFor(shell, content, oofContFrame, styleContext,
8180 aParentFrame, aFrame,
8181 aFrame->GetStateBits() & PLACEHOLDER_TYPE_MASK);
8182 } else if (nsGkAtoms::fieldSetFrame == frameType) {
8183 newFrame = NS_NewFieldSetFrame(shell, styleContext);
8185 newFrame->Init(content, aParentFrame, aFrame);
8187 // Create a continuing area frame
8188 // XXXbz we really shouldn't have to do this by hand!
8189 nsIFrame* blockFrame = GetFieldSetBlockFrame(aFrame);
8190 if (blockFrame) {
8191 nsIFrame* continuingBlockFrame =
8192 CreateContinuingFrame(aPresContext, blockFrame, newFrame);
8193 // Set the fieldset's initial child list
8194 SetInitialSingleChild(newFrame, continuingBlockFrame);
8195 } else {
8196 MOZ_ASSERT(aFrame->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER,
8197 "FieldSet block may only be null for overflow containers");
8198 }
8199 } else if (nsGkAtoms::legendFrame == frameType) {
8200 newFrame = NS_NewLegendFrame(shell, styleContext);
8201 newFrame->Init(content, aParentFrame, aFrame);
8202 } else if (nsGkAtoms::flexContainerFrame == frameType) {
8203 newFrame = NS_NewFlexContainerFrame(shell, styleContext);
8204 newFrame->Init(content, aParentFrame, aFrame);
8205 } else {
8206 NS_RUNTIMEABORT("unexpected frame type");
8207 }
8209 // Init() set newFrame to be a fluid continuation of aFrame.
8210 // If we want a non-fluid continuation, we need to call SetPrevContinuation()
8211 // to reset NS_FRAME_IS_FLUID_CONTINUATION.
8212 if (!aIsFluid) {
8213 newFrame->SetPrevContinuation(aFrame);
8214 }
8216 // A continuation of generated content is also generated content
8217 if (aFrame->GetStateBits() & NS_FRAME_GENERATED_CONTENT) {
8218 newFrame->AddStateBits(NS_FRAME_GENERATED_CONTENT);
8219 }
8221 // A continuation of nsIAnonymousContentCreator content is also
8222 // nsIAnonymousContentCreator created content
8223 if (aFrame->GetStateBits() & NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT) {
8224 newFrame->AddStateBits(NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT);
8225 }
8227 // A continuation of an out-of-flow is also an out-of-flow
8228 if (aFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
8229 newFrame->AddStateBits(NS_FRAME_OUT_OF_FLOW);
8230 }
8232 if (nextInFlow) {
8233 nextInFlow->SetPrevInFlow(newFrame);
8234 newFrame->SetNextInFlow(nextInFlow);
8235 } else if (nextContinuation) {
8236 nextContinuation->SetPrevContinuation(newFrame);
8237 newFrame->SetNextContinuation(nextContinuation);
8238 }
8240 NS_POSTCONDITION(!newFrame->GetNextSibling(), "unexpected sibling");
8241 return newFrame;
8242 }
8244 nsresult
8245 nsCSSFrameConstructor::ReplicateFixedFrames(nsPageContentFrame* aParentFrame)
8246 {
8247 // Now deal with fixed-pos things.... They should appear on all pages,
8248 // so we want to move over the placeholders when processing the child
8249 // of the pageContentFrame.
8251 nsIFrame* prevPageContentFrame = aParentFrame->GetPrevInFlow();
8252 if (!prevPageContentFrame) {
8253 return NS_OK;
8254 }
8255 nsIFrame* canvasFrame = aParentFrame->GetFirstPrincipalChild();
8256 nsIFrame* prevCanvasFrame = prevPageContentFrame->GetFirstPrincipalChild();
8257 if (!canvasFrame || !prevCanvasFrame) {
8258 // document's root element frame missing
8259 return NS_ERROR_UNEXPECTED;
8260 }
8262 nsFrameItems fixedPlaceholders;
8263 nsIFrame* firstFixed = prevPageContentFrame->GetFirstChild(nsIFrame::kFixedList);
8264 if (!firstFixed) {
8265 return NS_OK;
8266 }
8268 // Don't allow abs-pos descendants of the fixed content to escape the content.
8269 // This should not normally be possible (because fixed-pos elements should
8270 // be absolute containers) but fixed-pos tables currently aren't abs-pos
8271 // containers.
8272 nsFrameConstructorState state(mPresShell, aParentFrame,
8273 nullptr,
8274 mRootElementFrame);
8275 state.mCreatingExtraFrames = true;
8277 // We can't use an ancestor filter here, because we're not going to
8278 // be usefully recurring down the tree. This means that other
8279 // places in frame construction can't assume a filter is
8280 // initialized!
8282 // Iterate across fixed frames and replicate each whose placeholder is a
8283 // descendant of aFrame. (We don't want to explicitly copy placeholders that
8284 // are within fixed frames, because that would cause duplicates on the new
8285 // page - bug 389619)
8286 for (nsIFrame* fixed = firstFixed; fixed; fixed = fixed->GetNextSibling()) {
8287 nsIFrame* prevPlaceholder = GetPlaceholderFrameFor(fixed);
8288 if (prevPlaceholder &&
8289 nsLayoutUtils::IsProperAncestorFrame(prevCanvasFrame, prevPlaceholder)) {
8290 // We want to use the same style as the primary style frame for
8291 // our content
8292 nsIContent* content = fixed->GetContent();
8293 nsStyleContext* styleContext =
8294 nsLayoutUtils::GetStyleFrame(content)->StyleContext();
8295 FrameConstructionItemList items;
8296 AddFrameConstructionItemsInternal(state, content, canvasFrame,
8297 content->Tag(),
8298 content->GetNameSpaceID(),
8299 true,
8300 styleContext,
8301 ITEM_ALLOW_XBL_BASE |
8302 ITEM_ALLOW_PAGE_BREAK,
8303 nullptr, items);
8304 ConstructFramesFromItemList(state, items, canvasFrame, fixedPlaceholders);
8305 }
8306 }
8308 // Add the placeholders to our primary child list.
8309 // XXXbz this is a little screwed up, since the fixed frames will have
8310 // broken auto-positioning. Oh, well.
8311 NS_ASSERTION(!canvasFrame->GetFirstPrincipalChild(),
8312 "leaking frames; doc root continuation must be empty");
8313 canvasFrame->SetInitialChildList(kPrincipalList, fixedPlaceholders);
8314 return NS_OK;
8315 }
8317 nsIFrame*
8318 nsCSSFrameConstructor::GetInsertionPoint(nsIContent* aContainer,
8319 nsIContent* aChildContent,
8320 bool* aMultiple)
8321 {
8322 nsBindingManager *bindingManager = mDocument->BindingManager();
8324 nsIContent* insertionElement;
8325 if (aChildContent) {
8326 // We've got an explicit insertion child. Check to see if it's
8327 // anonymous.
8328 if (aChildContent->GetBindingParent() == aContainer) {
8329 // This child content is anonymous. Don't use the insertion
8330 // point, since that's only for the explicit kids.
8331 return GetFrameFor(aContainer);
8332 }
8334 insertionElement = bindingManager->FindNestedInsertionPoint(aContainer, aChildContent);
8335 }
8336 else {
8337 bool multiple;
8338 insertionElement = bindingManager->FindNestedSingleInsertionPoint(aContainer, &multiple);
8340 if (multiple) {
8341 if (aMultiple) {
8342 *aMultiple = true;
8343 }
8344 return nullptr;
8345 }
8346 }
8348 if (!insertionElement) {
8349 insertionElement = aContainer;
8350 }
8352 nsIFrame* insertionPoint = GetFrameFor(insertionElement);
8354 // fieldsets have multiple insertion points. Note that we might
8355 // have to look at insertionElement here...
8356 if (aMultiple && insertionElement->IsHTML(nsGkAtoms::fieldset)) {
8357 *aMultiple = true;
8358 }
8360 return insertionPoint;
8361 }
8363 // Capture state for the frame tree rooted at the frame associated with the
8364 // content object, aContent
8365 void
8366 nsCSSFrameConstructor::CaptureStateForFramesOf(nsIContent* aContent,
8367 nsILayoutHistoryState* aHistoryState)
8368 {
8369 if (!aHistoryState) {
8370 return;
8371 }
8372 nsIFrame* frame = aContent->GetPrimaryFrame();
8373 if (frame == mRootElementFrame) {
8374 frame = mFixedContainingBlock;
8375 }
8376 for ( ; frame;
8377 frame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(frame)) {
8378 CaptureFrameState(frame, aHistoryState);
8379 }
8380 }
8382 static bool EqualURIs(mozilla::css::URLValue *aURI1,
8383 mozilla::css::URLValue *aURI2)
8384 {
8385 return aURI1 == aURI2 || // handle null==null, and optimize
8386 (aURI1 && aURI2 && aURI1->URIEquals(*aURI2));
8387 }
8389 nsresult
8390 nsCSSFrameConstructor::MaybeRecreateFramesForElement(Element* aElement)
8391 {
8392 nsRefPtr<nsStyleContext> oldContext = GetUndisplayedContent(aElement);
8393 if (!oldContext) {
8394 return NS_OK;
8395 }
8397 // The parent has a frame, so try resolving a new context.
8398 nsRefPtr<nsStyleContext> newContext = mPresShell->StyleSet()->
8399 ResolveStyleFor(aElement, oldContext->GetParent());
8401 ChangeUndisplayedContent(aElement, newContext);
8402 const nsStyleDisplay* disp = newContext->StyleDisplay();
8403 if (disp->mDisplay == NS_STYLE_DISPLAY_NONE) {
8404 // We can skip trying to recreate frames here, but only if our style
8405 // context does not have a binding URI that differs from our old one.
8406 // Otherwise, we should try to recreate, because we may want to apply the
8407 // new binding
8408 if (!disp->mBinding) {
8409 return NS_OK;
8410 }
8411 const nsStyleDisplay* oldDisp = oldContext->PeekStyleDisplay();
8412 if (oldDisp && EqualURIs(disp->mBinding, oldDisp->mBinding)) {
8413 return NS_OK;
8414 }
8415 }
8417 return RecreateFramesForContent(aElement, false);
8418 }
8420 static nsIFrame*
8421 FindFirstNonWhitespaceChild(nsIFrame* aParentFrame)
8422 {
8423 nsIFrame* f = aParentFrame->GetFirstPrincipalChild();
8424 while (f && f->GetType() == nsGkAtoms::textFrame &&
8425 f->GetContent()->TextIsOnlyWhitespace()) {
8426 f = f->GetNextSibling();
8427 }
8428 return f;
8429 }
8431 static nsIFrame*
8432 FindNextNonWhitespaceSibling(nsIFrame* aFrame)
8433 {
8434 nsIFrame* f = aFrame;
8435 do {
8436 f = f->GetNextSibling();
8437 } while (f && f->GetType() == nsGkAtoms::textFrame &&
8438 f->GetContent()->TextIsOnlyWhitespace());
8439 return f;
8440 }
8442 static nsIFrame*
8443 FindPreviousNonWhitespaceSibling(nsIFrame* aFrame)
8444 {
8445 nsIFrame* f = aFrame;
8446 do {
8447 f = f->GetPrevSibling();
8448 } while (f && f->GetType() == nsGkAtoms::textFrame &&
8449 f->GetContent()->TextIsOnlyWhitespace());
8450 return f;
8451 }
8453 bool
8454 nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame,
8455 nsresult* aResult)
8456 {
8457 NS_PRECONDITION(aFrame, "Must have a frame");
8458 NS_PRECONDITION(aFrame->GetParent(), "Frame shouldn't be root");
8459 NS_PRECONDITION(aResult, "Null out param?");
8460 NS_PRECONDITION(aFrame == aFrame->FirstContinuation(),
8461 "aFrame not the result of GetPrimaryFrame()?");
8463 if (IsFramePartOfIBSplit(aFrame)) {
8464 // The removal functions can't handle removal of an {ib} split directly; we
8465 // need to rebuild the containing block.
8466 #ifdef DEBUG
8467 if (gNoisyContentUpdates) {
8468 printf("nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval: "
8469 "frame=");
8470 nsFrame::ListTag(stdout, aFrame);
8471 printf(" is ib-split\n");
8472 }
8473 #endif
8475 *aResult = ReframeContainingBlock(aFrame);
8476 return true;
8477 }
8479 if (aFrame->GetContentInsertionFrame()->GetType() == nsGkAtoms::legendFrame &&
8480 aFrame->GetParent()->GetType() == nsGkAtoms::fieldSetFrame) {
8481 // When we remove the legend for a fieldset, we should reframe
8482 // the fieldset to ensure another legend is used, if there is one
8483 *aResult = RecreateFramesForContent(aFrame->GetParent()->GetContent(), false);
8484 return true;
8485 }
8487 // Now check for possibly needing to reconstruct due to a pseudo parent
8488 nsIFrame* inFlowFrame =
8489 (aFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) ?
8490 GetPlaceholderFrameFor(aFrame) : aFrame;
8491 MOZ_ASSERT(inFlowFrame, "How did that happen?");
8492 MOZ_ASSERT(inFlowFrame == inFlowFrame->FirstContinuation(),
8493 "placeholder for primary frame has previous continuations?");
8494 nsIFrame* parent = inFlowFrame->GetParent();
8495 if (IsTablePseudo(parent)) {
8496 if (FindFirstNonWhitespaceChild(parent) == inFlowFrame ||
8497 !FindNextNonWhitespaceSibling(inFlowFrame->LastContinuation()) ||
8498 // If we're a table-column-group, then the GetFirstChild check above is
8499 // not going to catch cases when we're the first child.
8500 (inFlowFrame->GetType() == nsGkAtoms::tableColGroupFrame &&
8501 parent->GetFirstChild(nsIFrame::kColGroupList) == inFlowFrame) ||
8502 // Similar if we're a table-caption.
8503 (inFlowFrame->GetType() == nsGkAtoms::tableCaptionFrame &&
8504 parent->GetFirstChild(nsIFrame::kCaptionList) == inFlowFrame)) {
8505 // We're the first or last frame in the pseudo. Need to reframe.
8506 // Good enough to recreate frames for |parent|'s content
8507 *aResult = RecreateFramesForContent(parent->GetContent(), true);
8508 return true;
8509 }
8510 }
8512 // Might need to reconstruct things if this frame's nextSibling is a table
8513 // pseudo, since removal of this frame might mean that this pseudo needs to
8514 // get merged with the frame's prevSibling if that's also a table pseudo.
8515 nsIFrame* nextSibling =
8516 FindNextNonWhitespaceSibling(inFlowFrame->LastContinuation());
8517 NS_ASSERTION(!IsTablePseudo(inFlowFrame), "Shouldn't happen here");
8518 if (nextSibling && IsTablePseudo(nextSibling)) {
8519 nsIFrame* prevSibling = FindPreviousNonWhitespaceSibling(inFlowFrame);
8520 if (prevSibling && IsTablePseudo(prevSibling)) {
8521 #ifdef DEBUG
8522 if (gNoisyContentUpdates) {
8523 printf("nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval: "
8524 "frame=");
8525 nsFrame::ListTag(stdout, aFrame);
8526 printf(" has a table pseudo next sibling of different type and a "
8527 "table pseudo prevsibling\n");
8528 }
8529 #endif
8530 // Good enough to recreate frames for aFrame's parent's content; even if
8531 // aFrame's parent is a table pseudo, that'll be the right content node.
8532 *aResult = RecreateFramesForContent(parent->GetContent(), true);
8533 return true;
8534 }
8535 }
8537 // Might need to reconstruct things if the removed frame's nextSibling is an
8538 // anonymous flex item. The removed frame might've been what divided two
8539 // runs of inline content into two anonymous flex items, which would now
8540 // need to be merged.
8541 // NOTE: It's fine that we've advanced nextSibling past whitespace (up above);
8542 // we're only interested in anonymous flex items here, and those can never
8543 // be adjacent to whitespace, since they absorb contiguous runs of inline
8544 // non-replaced content (including whitespace).
8545 if (nextSibling && IsAnonymousFlexItem(nextSibling)) {
8546 NS_ABORT_IF_FALSE(parent->GetType() == nsGkAtoms::flexContainerFrame,
8547 "anonymous flex items should only exist as children "
8548 "of flex container frames");
8549 #ifdef DEBUG
8550 if (gNoisyContentUpdates) {
8551 printf("nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval: "
8552 "frame=");
8553 nsFrame::ListTag(stdout, aFrame);
8554 printf(" has an anonymous flex item as its next sibling\n");
8555 }
8556 #endif // DEBUG
8557 // Recreate frames for the flex container (the removed frame's parent)
8558 *aResult = RecreateFramesForContent(parent->GetContent(), true);
8559 return true;
8560 }
8562 // Might need to reconstruct things if the removed frame's nextSibling is
8563 // null and its parent is an anonymous flex item. (This might be the last
8564 // remaining child of that anonymous flex item, which can then go away.)
8565 if (!nextSibling && IsAnonymousFlexItem(parent)) {
8566 NS_ABORT_IF_FALSE(parent->GetParent() &&
8567 parent->GetParent()->GetType() == nsGkAtoms::flexContainerFrame,
8568 "anonymous flex items should only exist as children "
8569 "of flex container frames");
8570 #ifdef DEBUG
8571 if (gNoisyContentUpdates) {
8572 printf("nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval: "
8573 "frame=");
8574 nsFrame::ListTag(stdout, aFrame);
8575 printf(" has an anonymous flex item as its parent\n");
8576 }
8577 #endif // DEBUG
8578 // Recreate frames for the flex container (the removed frame's grandparent)
8579 *aResult = RecreateFramesForContent(parent->GetParent()->GetContent(),
8580 true);
8581 return true;
8582 }
8584 #ifdef MOZ_XUL
8585 if (aFrame->GetType() == nsGkAtoms::popupSetFrame) {
8586 nsIRootBox* rootBox = nsIRootBox::GetRootBox(mPresShell);
8587 if (rootBox && rootBox->GetPopupSetFrame() == aFrame) {
8588 *aResult = ReconstructDocElementHierarchy();
8589 return true;
8590 }
8591 }
8592 #endif
8594 // Reconstruct if inflowFrame is parent's only child, and parent is, or has,
8595 // a non-fluid continuation, i.e. it was split by bidi resolution
8596 if (!inFlowFrame->GetPrevSibling() &&
8597 !inFlowFrame->GetNextSibling() &&
8598 ((parent->GetPrevContinuation() && !parent->GetPrevInFlow()) ||
8599 (parent->GetNextContinuation() && !parent->GetNextInFlow()))) {
8600 *aResult = RecreateFramesForContent(parent->GetContent(), true);
8601 return true;
8602 }
8604 // We might still need to reconstruct things if the parent of inFlowFrame is
8605 // ib-split, since in that case the removal of aFrame might affect the
8606 // splitting of its parent.
8607 if (!IsFramePartOfIBSplit(parent)) {
8608 return false;
8609 }
8611 // If inFlowFrame is not the only in-flow child of |parent|, then removing
8612 // it will change nothing about the {ib} split.
8613 if (inFlowFrame != parent->GetFirstPrincipalChild() ||
8614 inFlowFrame->LastContinuation()->GetNextSibling()) {
8615 return false;
8616 }
8618 // If the parent is the first or last part of the {ib} split, then
8619 // removing one of its kids will have no effect on the splitting.
8620 // Get the first continuation up front so we don't have to do it twice.
8621 nsIFrame* parentFirstContinuation = parent->FirstContinuation();
8622 if (!GetIBSplitSibling(parentFirstContinuation) ||
8623 !GetIBSplitPrevSibling(parentFirstContinuation)) {
8624 return false;
8625 }
8627 #ifdef DEBUG
8628 if (gNoisyContentUpdates) {
8629 printf("nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval: "
8630 "frame=");
8631 nsFrame::ListTag(stdout, parent);
8632 printf(" is ib-split\n");
8633 }
8634 #endif
8636 *aResult = ReframeContainingBlock(parent);
8637 return true;
8638 }
8640 nsresult
8641 nsCSSFrameConstructor::RecreateFramesForContent(nsIContent* aContent,
8642 bool aAsyncInsert)
8643 {
8644 NS_PRECONDITION(!aAsyncInsert || aContent->IsElement(),
8645 "Can only insert elements async");
8646 // If there is no document, we don't want to recreate frames for it. (You
8647 // shouldn't generally be giving this method content without a document
8648 // anyway).
8649 // Rebuilding the frame tree can have bad effects, especially if it's the
8650 // frame tree for chrome (see bug 157322).
8651 NS_ENSURE_TRUE(aContent->GetDocument(), NS_ERROR_FAILURE);
8653 // Is the frame ib-split? If so, we need to reframe the containing
8654 // block *here*, rather than trying to remove and re-insert the
8655 // content (which would otherwise result in *two* nested reframe
8656 // containing block from ContentRemoved() and ContentInserted(),
8657 // below!). We'd really like to optimize away one of those
8658 // containing block reframes, hence the code here.
8660 nsIFrame* frame = aContent->GetPrimaryFrame();
8661 if (frame && frame->IsFrameOfType(nsIFrame::eMathML)) {
8662 // Reframe the topmost MathML element to prevent exponential blowup
8663 // (see bug 397518)
8664 while (true) {
8665 nsIContent* parentContent = aContent->GetParent();
8666 nsIFrame* parentContentFrame = parentContent->GetPrimaryFrame();
8667 if (!parentContentFrame || !parentContentFrame->IsFrameOfType(nsIFrame::eMathML))
8668 break;
8669 aContent = parentContent;
8670 frame = parentContentFrame;
8671 }
8672 }
8674 if (frame) {
8675 nsIFrame* nonGeneratedAncestor = nsLayoutUtils::GetNonGeneratedAncestor(frame);
8676 if (nonGeneratedAncestor->GetContent() != aContent) {
8677 return RecreateFramesForContent(nonGeneratedAncestor->GetContent(), aAsyncInsert);
8678 }
8680 if (frame->GetStateBits() & NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT) {
8681 // Recreate the frames for the entire nsIAnonymousContentCreator tree
8682 // since |frame| or one of its descendants may need an nsStyleContext
8683 // that associates it to a CSS pseudo-element, and only the
8684 // nsIAnonymousContentCreator that created this content knows how to make
8685 // that happen.
8686 nsIAnonymousContentCreator* acc = nullptr;
8687 nsIFrame* ancestor = frame->GetParent();
8688 while (!(acc = do_QueryFrame(ancestor))) {
8689 ancestor = ancestor->GetParent();
8690 }
8691 NS_ASSERTION(acc, "Where is the nsIAnonymousContentCreator? We may fail "
8692 "to recreate its content correctly");
8693 // nsSVGUseFrame is special, and we know this is unnecessary for it.
8694 if (ancestor->GetType() != nsGkAtoms::svgUseFrame) {
8695 NS_ASSERTION(aContent->IsInNativeAnonymousSubtree(),
8696 "Why is NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT set?");
8697 return RecreateFramesForContent(ancestor->GetContent(), aAsyncInsert);
8698 }
8699 }
8701 nsIFrame* parent = frame->GetParent();
8702 nsIContent* parentContent = parent ? parent->GetContent() : nullptr;
8703 // If the parent frame is a leaf then the subsequent insert will fail to
8704 // create a frame, so we need to recreate the parent content. This happens
8705 // with native anonymous content from the editor.
8706 if (parent && parent->IsLeaf() && parentContent &&
8707 parentContent != aContent) {
8708 return RecreateFramesForContent(parentContent, aAsyncInsert);
8709 }
8710 }
8712 nsresult rv = NS_OK;
8714 if (frame && MaybeRecreateContainerForFrameRemoval(frame, &rv)) {
8715 return rv;
8716 }
8718 nsINode* containerNode = aContent->GetParentNode();
8719 // XXXbz how can containerNode be null here?
8720 if (containerNode) {
8721 // Before removing the frames associated with the content object,
8722 // ask them to save their state onto a temporary state object.
8723 CaptureStateForFramesOf(aContent, mTempFrameTreeState);
8725 // Need the nsIContent parent, which might be null here, since we need to
8726 // pass it to ContentInserted and ContentRemoved.
8727 nsCOMPtr<nsIContent> container = aContent->GetParent();
8729 // Remove the frames associated with the content object.
8730 bool didReconstruct;
8731 rv = ContentRemoved(container, aContent,
8732 aContent->IsRootOfAnonymousSubtree() ?
8733 nullptr :
8734 aContent->GetNextSibling(),
8735 REMOVE_FOR_RECONSTRUCTION, &didReconstruct);
8737 if (NS_SUCCEEDED(rv) && !didReconstruct) {
8738 // Now, recreate the frames associated with this content object. If
8739 // ContentRemoved triggered reconstruction, then we don't need to do this
8740 // because the frames will already have been built.
8741 if (aAsyncInsert) {
8742 RestyleManager()->PostRestyleEvent(
8743 aContent->AsElement(), nsRestyleHint(0), nsChangeHint_ReconstructFrame);
8744 } else {
8745 rv = ContentInserted(container, aContent, mTempFrameTreeState, false);
8746 }
8747 }
8748 }
8750 return rv;
8751 }
8753 //////////////////////////////////////////////////////////////////////
8755 // Block frame construction code
8757 already_AddRefed<nsStyleContext>
8758 nsCSSFrameConstructor::GetFirstLetterStyle(nsIContent* aContent,
8759 nsStyleContext* aStyleContext)
8760 {
8761 if (aContent) {
8762 return mPresShell->StyleSet()->
8763 ResolvePseudoElementStyle(aContent->AsElement(),
8764 nsCSSPseudoElements::ePseudo_firstLetter,
8765 aStyleContext,
8766 nullptr);
8767 }
8768 return nullptr;
8769 }
8771 already_AddRefed<nsStyleContext>
8772 nsCSSFrameConstructor::GetFirstLineStyle(nsIContent* aContent,
8773 nsStyleContext* aStyleContext)
8774 {
8775 if (aContent) {
8776 return mPresShell->StyleSet()->
8777 ResolvePseudoElementStyle(aContent->AsElement(),
8778 nsCSSPseudoElements::ePseudo_firstLine,
8779 aStyleContext,
8780 nullptr);
8781 }
8782 return nullptr;
8783 }
8785 // Predicate to see if a given content (block element) has
8786 // first-letter style applied to it.
8787 bool
8788 nsCSSFrameConstructor::ShouldHaveFirstLetterStyle(nsIContent* aContent,
8789 nsStyleContext* aStyleContext)
8790 {
8791 return nsLayoutUtils::HasPseudoStyle(aContent, aStyleContext,
8792 nsCSSPseudoElements::ePseudo_firstLetter,
8793 mPresShell->GetPresContext());
8794 }
8796 bool
8797 nsCSSFrameConstructor::HasFirstLetterStyle(nsIFrame* aBlockFrame)
8798 {
8799 NS_PRECONDITION(aBlockFrame, "Need a frame");
8800 NS_ASSERTION(nsLayoutUtils::GetAsBlock(aBlockFrame),
8801 "Not a block frame?");
8803 return (aBlockFrame->GetStateBits() & NS_BLOCK_HAS_FIRST_LETTER_STYLE) != 0;
8804 }
8806 bool
8807 nsCSSFrameConstructor::ShouldHaveFirstLineStyle(nsIContent* aContent,
8808 nsStyleContext* aStyleContext)
8809 {
8810 bool hasFirstLine =
8811 nsLayoutUtils::HasPseudoStyle(aContent, aStyleContext,
8812 nsCSSPseudoElements::ePseudo_firstLine,
8813 mPresShell->GetPresContext());
8814 if (hasFirstLine) {
8815 // But disable for fieldsets
8816 int32_t namespaceID;
8817 nsIAtom* tag = mDocument->BindingManager()->ResolveTag(aContent,
8818 &namespaceID);
8819 // This check must match the one in FindHTMLData.
8820 hasFirstLine = tag != nsGkAtoms::fieldset ||
8821 namespaceID != kNameSpaceID_XHTML;
8822 }
8824 return hasFirstLine;
8825 }
8827 void
8828 nsCSSFrameConstructor::ShouldHaveSpecialBlockStyle(nsIContent* aContent,
8829 nsStyleContext* aStyleContext,
8830 bool* aHaveFirstLetterStyle,
8831 bool* aHaveFirstLineStyle)
8832 {
8833 *aHaveFirstLetterStyle =
8834 ShouldHaveFirstLetterStyle(aContent, aStyleContext);
8835 *aHaveFirstLineStyle =
8836 ShouldHaveFirstLineStyle(aContent, aStyleContext);
8837 }
8839 /* static */
8840 const nsCSSFrameConstructor::PseudoParentData
8841 nsCSSFrameConstructor::sPseudoParentData[eParentTypeCount] = {
8842 { // Cell
8843 FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET |
8844 FCDATA_USE_CHILD_ITEMS |
8845 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRow),
8846 &nsCSSFrameConstructor::ConstructTableCell),
8847 &nsCSSAnonBoxes::tableCell
8848 },
8849 { // Row
8850 FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET |
8851 FCDATA_USE_CHILD_ITEMS |
8852 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRowGroup),
8853 &nsCSSFrameConstructor::ConstructTableRowOrRowGroup),
8854 &nsCSSAnonBoxes::tableRow
8855 },
8856 { // Row group
8857 FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET |
8858 FCDATA_USE_CHILD_ITEMS |
8859 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
8860 &nsCSSFrameConstructor::ConstructTableRowOrRowGroup),
8861 &nsCSSAnonBoxes::tableRowGroup
8862 },
8863 { // Column group
8864 FCDATA_DECL(FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET |
8865 FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_USE_CHILD_ITEMS |
8866 FCDATA_SKIP_ABSPOS_PUSH |
8867 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
8868 NS_NewTableColGroupFrame),
8869 &nsCSSAnonBoxes::tableColGroup
8870 },
8871 { // Table
8872 FULL_CTOR_FCDATA(FCDATA_SKIP_FRAMESET | FCDATA_USE_CHILD_ITEMS,
8873 &nsCSSFrameConstructor::ConstructTable),
8874 &nsCSSAnonBoxes::table
8875 }
8876 };
8878 void
8879 nsCSSFrameConstructor::CreateNeededAnonFlexItems(
8880 nsFrameConstructorState& aState,
8881 FrameConstructionItemList& aItems,
8882 nsIFrame* aParentFrame)
8883 {
8884 if (aItems.IsEmpty() ||
8885 aParentFrame->GetType() != nsGkAtoms::flexContainerFrame) {
8886 return;
8887 }
8889 FCItemIterator iter(aItems);
8890 do {
8891 // Advance iter past children that don't want to be wrapped
8892 if (iter.SkipItemsThatDontNeedAnonFlexItem(aState)) {
8893 // Hit the end of the items without finding any remaining children that
8894 // need to be wrapped. We're finished!
8895 return;
8896 }
8898 // If our next potentially-wrappable child is whitespace, then see if
8899 // there's anything wrappable immediately after it. If not, we just drop
8900 // the whitespace and move on. (We're not supposed to create any anonymous
8901 // flex items that _only_ contain whitespace).
8902 // (BUT if this is generated content, then we don't give whitespace nodes
8903 // any special treatment, because they're probably not really whitespace --
8904 // they're just temporarily empty, waiting for their generated text.)
8905 // XXXdholbert If this node's generated text will *actually end up being
8906 // entirely whitespace*, then we technically should still skip over it, per
8907 // the flexbox spec. I'm not bothering with that at this point, since it's
8908 // a pretty extreme edge case.
8909 if (!aParentFrame->IsGeneratedContentFrame() &&
8910 iter.item().IsWhitespace(aState)) {
8911 FCItemIterator afterWhitespaceIter(iter);
8912 bool hitEnd = afterWhitespaceIter.SkipWhitespace(aState);
8913 bool nextChildNeedsAnonFlexItem =
8914 !hitEnd && afterWhitespaceIter.item().NeedsAnonFlexItem(aState);
8916 if (!nextChildNeedsAnonFlexItem) {
8917 // There's nothing after the whitespace that we need to wrap, so we
8918 // just drop this run of whitespace.
8919 iter.DeleteItemsTo(afterWhitespaceIter);
8920 if (hitEnd) {
8921 // Nothing left to do -- we're finished!
8922 return;
8923 }
8924 // else, we have a next child and it does not want to be wrapped. So,
8925 // we jump back to the beginning of the loop to skip over that child
8926 // (and anything else non-wrappable after it)
8927 NS_ABORT_IF_FALSE(!iter.IsDone() &&
8928 !iter.item().NeedsAnonFlexItem(aState),
8929 "hitEnd and/or nextChildNeedsAnonFlexItem lied");
8930 continue;
8931 }
8932 }
8934 // Now |iter| points to the first child that needs to be wrapped in an
8935 // anonymous flex item. Now we see how many children after it also want
8936 // to be wrapped in an anonymous flex item.
8937 FCItemIterator endIter(iter); // iterator to find the end of the group
8938 endIter.SkipItemsThatNeedAnonFlexItem(aState);
8940 NS_ASSERTION(iter != endIter,
8941 "Should've had at least one wrappable child to seek past");
8943 // Now, we create the anonymous flex item to contain the children
8944 // between |iter| and |endIter|.
8945 nsIAtom* pseudoType = nsCSSAnonBoxes::anonymousFlexItem;
8946 nsStyleContext* parentStyle = aParentFrame->StyleContext();
8947 nsIContent* parentContent = aParentFrame->GetContent();
8948 already_AddRefed<nsStyleContext> wrapperStyle =
8949 mPresShell->StyleSet()->ResolveAnonymousBoxStyle(pseudoType, parentStyle);
8951 static const FrameConstructionData sBlockFormattingContextFCData =
8952 FCDATA_DECL(FCDATA_SKIP_FRAMESET | FCDATA_USE_CHILD_ITEMS,
8953 NS_NewBlockFormattingContext);
8955 FrameConstructionItem* newItem =
8956 new FrameConstructionItem(&sBlockFormattingContextFCData,
8957 // Use the content of our parent frame
8958 parentContent,
8959 // Lie about the tag; it doesn't matter anyway
8960 pseudoType,
8961 iter.item().mNameSpaceID,
8962 // no pending binding
8963 nullptr,
8964 wrapperStyle,
8965 true, nullptr);
8967 newItem->mIsAllInline = newItem->mHasInlineEnds =
8968 newItem->mStyleContext->StyleDisplay()->IsInlineOutsideStyle();
8969 newItem->mIsBlock = !newItem->mIsAllInline;
8971 NS_ABORT_IF_FALSE(!newItem->mIsAllInline && newItem->mIsBlock,
8972 "expecting anonymous flex items to be block-level "
8973 "(this will make a difference when we encounter "
8974 "'flex-align: baseline')");
8976 // Anonymous flex items induce line boundaries around their
8977 // contents.
8978 newItem->mChildItems.SetLineBoundaryAtStart(true);
8979 newItem->mChildItems.SetLineBoundaryAtEnd(true);
8980 // The parent of the items in aItems is also the parent of the items
8981 // in mChildItems
8982 newItem->mChildItems.SetParentHasNoXBLChildren(
8983 aItems.ParentHasNoXBLChildren());
8985 // Eat up all items between |iter| and |endIter| and put them in our
8986 // wrapper. This advances |iter| to point to |endIter|.
8987 iter.AppendItemsToList(endIter, newItem->mChildItems);
8989 iter.InsertItem(newItem);
8990 } while (!iter.IsDone());
8991 }
8993 /*
8994 * This function works as follows: we walk through the child list (aItems) and
8995 * find items that cannot have aParentFrame as their parent. We wrap
8996 * continuous runs of such items into a FrameConstructionItem for a frame that
8997 * gets them closer to their desired parents. For example, a run of non-row
8998 * children of a row-group will get wrapped in a row. When we later construct
8999 * the frame for this wrapper (in this case for the row), it'll be the correct
9000 * parent for the cells in the set of items we wrapped or we'll wrap cells
9001 * around everything else. At the end of this method, aItems is guaranteed to
9002 * contain only items for frames that can be direct kids of aParentFrame.
9003 */
9004 void
9005 nsCSSFrameConstructor::CreateNeededTablePseudos(nsFrameConstructorState& aState,
9006 FrameConstructionItemList& aItems,
9007 nsIFrame* aParentFrame)
9008 {
9009 ParentType ourParentType = GetParentType(aParentFrame);
9010 if (aItems.AllWantParentType(ourParentType)) {
9011 // Nothing to do here
9012 return;
9013 }
9015 FCItemIterator iter(aItems);
9016 do {
9017 if (iter.SkipItemsWantingParentType(ourParentType)) {
9018 // Nothing else to do here; we're finished
9019 return;
9020 }
9022 // Now we're pointing to the first child that wants a different parent
9023 // type.
9025 // Now try to figure out what kids we can group together. We can generally
9026 // group everything that has a different desired parent type from us. Two
9027 // exceptions to this:
9028 // 1) If our parent type is table, we can't group columns with anything
9029 // else other than whitespace.
9030 // 2) Whitespace that lies between two things we can group which both want
9031 // a non-block parent should be dropped, even if we can't group them
9032 // with each other and even if the whitespace wants a parent of
9033 // ourParentType. Ends of the list count as things that don't want a
9034 // block parent (so that for example we'll drop a whitespace-only list).
9036 FCItemIterator endIter(iter); /* iterator to find the end of the group */
9037 ParentType groupingParentType = endIter.item().DesiredParentType();
9038 if (aItems.AllWantParentType(groupingParentType) &&
9039 groupingParentType != eTypeBlock) {
9040 // Just group them all and be done with it. We need the check for
9041 // eTypeBlock here to catch the "all the items are whitespace" case
9042 // described above.
9043 endIter.SetToEnd();
9044 } else {
9045 // Locate the end of the group.
9047 // Keep track of the type the previous item wanted, in case we have to
9048 // deal with whitespace. Start it off with ourParentType, since that's
9049 // the last thing |iter| would have skipped over.
9050 ParentType prevParentType = ourParentType;
9051 do {
9052 /* Walk an iterator past any whitespace that we might be able to drop from the list */
9053 FCItemIterator spaceEndIter(endIter);
9054 if (prevParentType != eTypeBlock &&
9055 !aParentFrame->IsGeneratedContentFrame() &&
9056 spaceEndIter.item().IsWhitespace(aState)) {
9057 bool trailingSpaces = spaceEndIter.SkipWhitespace(aState);
9059 // We drop the whitespace if these are not trailing spaces and the next item
9060 // does not want a block parent (see case 2 above)
9061 // if these are trailing spaces and aParentFrame is a tabular container
9062 // according to rule 1.3 of CSS 2.1 Sec 17.2.1. (Being a tabular container
9063 // pretty much means ourParentType != eTypeBlock besides the eTypeColGroup case,
9064 // which won't reach here.)
9065 if ((trailingSpaces && ourParentType != eTypeBlock) ||
9066 (!trailingSpaces && spaceEndIter.item().DesiredParentType() !=
9067 eTypeBlock)) {
9068 bool updateStart = (iter == endIter);
9069 endIter.DeleteItemsTo(spaceEndIter);
9070 NS_ASSERTION(trailingSpaces == endIter.IsDone(), "These should match");
9072 if (updateStart) {
9073 iter = endIter;
9074 }
9076 if (trailingSpaces) {
9077 break; /* Found group end */
9078 }
9080 if (updateStart) {
9081 // Update groupingParentType, since it might have been eTypeBlock
9082 // just because of the whitespace.
9083 groupingParentType = iter.item().DesiredParentType();
9084 }
9085 }
9086 }
9088 // Now endIter points to a non-whitespace item or a non-droppable
9089 // whitespace item. In the latter case, if this is the end of the group
9090 // we'll traverse this whitespace again. But it'll all just be quick
9091 // DesiredParentType() checks which will match ourParentType (that's
9092 // what it means that this is the group end), so it's OK.
9093 prevParentType = endIter.item().DesiredParentType();
9094 if (prevParentType == ourParentType) {
9095 // End the group at endIter.
9096 break;
9097 }
9099 if (ourParentType == eTypeTable &&
9100 (prevParentType == eTypeColGroup) !=
9101 (groupingParentType == eTypeColGroup)) {
9102 // Either we started with columns and now found something else, or vice
9103 // versa. In any case, end the grouping.
9104 break;
9105 }
9107 // Include the whitespace we didn't drop (if any) in the group, since
9108 // this is not the end of the group. Note that this doesn't change
9109 // prevParentType, since if we didn't drop the whitespace then we ended
9110 // at something that wants a block parent.
9111 endIter = spaceEndIter;
9113 endIter.Next();
9114 } while (!endIter.IsDone());
9115 }
9117 if (iter == endIter) {
9118 // Nothing to wrap here; just skipped some whitespace
9119 continue;
9120 }
9122 // Now group together all the items between iter and endIter. The right
9123 // parent type to use depends on ourParentType.
9124 ParentType wrapperType;
9125 switch (ourParentType) {
9126 case eTypeBlock:
9127 wrapperType = eTypeTable;
9128 break;
9129 case eTypeRow:
9130 // The parent type for a cell is eTypeBlock, since that's what a cell
9131 // looks like to its kids.
9132 wrapperType = eTypeBlock;
9133 break;
9134 case eTypeRowGroup:
9135 wrapperType = eTypeRow;
9136 break;
9137 case eTypeTable:
9138 // Either colgroup or rowgroup, depending on what we're grouping.
9139 wrapperType = groupingParentType == eTypeColGroup ?
9140 eTypeColGroup : eTypeRowGroup;
9141 break;
9142 default:
9143 MOZ_CRASH("Colgroups should be suppresing non-col child items");
9144 }
9146 const PseudoParentData& pseudoData = sPseudoParentData[wrapperType];
9147 nsIAtom* pseudoType = *pseudoData.mPseudoType;
9148 nsStyleContext* parentStyle = aParentFrame->StyleContext();
9149 nsIContent* parentContent = aParentFrame->GetContent();
9151 if (pseudoType == nsCSSAnonBoxes::table &&
9152 parentStyle->StyleDisplay()->mDisplay == NS_STYLE_DISPLAY_INLINE) {
9153 pseudoType = nsCSSAnonBoxes::inlineTable;
9154 }
9156 already_AddRefed<nsStyleContext> wrapperStyle =
9157 mPresShell->StyleSet()->ResolveAnonymousBoxStyle(pseudoType, parentStyle);
9158 FrameConstructionItem* newItem =
9159 new FrameConstructionItem(&pseudoData.mFCData,
9160 // Use the content of our parent frame
9161 parentContent,
9162 // Lie about the tag; it doesn't matter anyway
9163 pseudoType,
9164 // The namespace does matter, however; it needs
9165 // to match that of our first child item to
9166 // match the old behavior
9167 iter.item().mNameSpaceID,
9168 // no pending binding
9169 nullptr,
9170 wrapperStyle,
9171 true, nullptr);
9173 // Here we're cheating a tad... technically, table-internal items should be
9174 // inline if aParentFrame is inline, but they'll get wrapped in an
9175 // inline-table in the end, so it'll all work out. In any case, arguably
9176 // we don't need to maintain this state at this point... but it's better
9177 // to, I guess.
9178 newItem->mIsAllInline = newItem->mHasInlineEnds =
9179 newItem->mStyleContext->StyleDisplay()->IsInlineOutsideStyle();
9181 // Table pseudo frames always induce line boundaries around their
9182 // contents.
9183 newItem->mChildItems.SetLineBoundaryAtStart(true);
9184 newItem->mChildItems.SetLineBoundaryAtEnd(true);
9185 // The parent of the items in aItems is also the parent of the items
9186 // in mChildItems
9187 newItem->mChildItems.SetParentHasNoXBLChildren(
9188 aItems.ParentHasNoXBLChildren());
9190 // Eat up all items between |iter| and |endIter| and put them in our wrapper
9191 // Advances |iter| to point to |endIter|.
9192 iter.AppendItemsToList(endIter, newItem->mChildItems);
9194 iter.InsertItem(newItem);
9196 // Now |iter| points to the item that was the first one we didn't wrap;
9197 // loop and see whether we need to skip it or wrap it in something
9198 // different.
9199 } while (!iter.IsDone());
9200 }
9202 inline void
9203 nsCSSFrameConstructor::ConstructFramesFromItemList(nsFrameConstructorState& aState,
9204 FrameConstructionItemList& aItems,
9205 nsIFrame* aParentFrame,
9206 nsFrameItems& aFrameItems)
9207 {
9208 CreateNeededTablePseudos(aState, aItems, aParentFrame);
9209 CreateNeededAnonFlexItems(aState, aItems, aParentFrame);
9211 aItems.SetTriedConstructingFrames();
9212 for (FCItemIterator iter(aItems); !iter.IsDone(); iter.Next()) {
9213 NS_ASSERTION(iter.item().DesiredParentType() == GetParentType(aParentFrame),
9214 "Needed pseudos didn't get created; expect bad things");
9215 ConstructFramesFromItem(aState, iter, aParentFrame, aFrameItems);
9216 }
9218 NS_ASSERTION(!aState.mHavePendingPopupgroup,
9219 "Should have proccessed it by now");
9220 }
9222 void
9223 nsCSSFrameConstructor::AddFCItemsForAnonymousContent(
9224 nsFrameConstructorState& aState,
9225 nsIFrame* aFrame,
9226 nsTArray<nsIAnonymousContentCreator::ContentInfo>& aAnonymousItems,
9227 FrameConstructionItemList& aItemsToConstruct,
9228 uint32_t aExtraFlags)
9229 {
9230 for (uint32_t i = 0; i < aAnonymousItems.Length(); ++i) {
9231 nsIContent* content = aAnonymousItems[i].mContent;
9232 #ifdef DEBUG
9233 nsIAnonymousContentCreator* creator = do_QueryFrame(aFrame);
9234 NS_ASSERTION(!creator || !creator->CreateFrameFor(content),
9235 "If you need to use CreateFrameFor, you need to call "
9236 "CreateAnonymousFrames manually and not follow the standard "
9237 "ProcessChildren() codepath for this frame");
9238 #endif
9239 // Assert some things about this content
9240 NS_ABORT_IF_FALSE(!(content->GetFlags() &
9241 (NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME)),
9242 "Should not be marked as needing frames");
9243 NS_ABORT_IF_FALSE(!content->IsElement() ||
9244 !(content->GetFlags() & ELEMENT_ALL_RESTYLE_FLAGS),
9245 "Should have no pending restyle flags");
9246 NS_ABORT_IF_FALSE(!content->GetPrimaryFrame(),
9247 "Should have no existing frame");
9248 NS_ABORT_IF_FALSE(!content->IsNodeOfType(nsINode::eCOMMENT) &&
9249 !content->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION),
9250 "Why is someone creating garbage anonymous content");
9252 nsRefPtr<nsStyleContext> styleContext;
9253 TreeMatchContext::AutoFlexItemStyleFixupSkipper
9254 flexItemStyleFixupSkipper(aState.mTreeMatchContext);
9255 if (aAnonymousItems[i].mStyleContext) {
9256 styleContext = aAnonymousItems[i].mStyleContext.forget();
9257 } else {
9258 styleContext = ResolveStyleContext(aFrame, content, &aState);
9259 }
9261 nsTArray<nsIAnonymousContentCreator::ContentInfo>* anonChildren = nullptr;
9262 if (!aAnonymousItems[i].mChildren.IsEmpty()) {
9263 anonChildren = &aAnonymousItems[i].mChildren;
9264 }
9266 uint32_t flags = ITEM_ALLOW_XBL_BASE | ITEM_ALLOW_PAGE_BREAK |
9267 ITEM_IS_ANONYMOUSCONTENTCREATOR_CONTENT | aExtraFlags;
9269 AddFrameConstructionItemsInternal(aState, content, aFrame,
9270 content->Tag(), content->GetNameSpaceID(),
9271 true, styleContext, flags,
9272 anonChildren, aItemsToConstruct);
9273 }
9274 }
9276 void
9277 nsCSSFrameConstructor::ProcessChildren(nsFrameConstructorState& aState,
9278 nsIContent* aContent,
9279 nsStyleContext* aStyleContext,
9280 nsIFrame* aFrame,
9281 const bool aCanHaveGeneratedContent,
9282 nsFrameItems& aFrameItems,
9283 const bool aAllowBlockStyles,
9284 PendingBinding* aPendingBinding,
9285 nsIFrame* aPossiblyLeafFrame)
9286 {
9287 NS_PRECONDITION(aFrame, "Must have parent frame here");
9288 NS_PRECONDITION(aFrame->GetContentInsertionFrame() == aFrame,
9289 "Parent frame in ProcessChildren should be its own "
9290 "content insertion frame");
9291 const uint32_t kMaxDepth = 2 * MAX_REFLOW_DEPTH;
9292 static_assert(kMaxDepth <= UINT16_MAX, "mCurrentDepth type is too narrow");
9293 AutoRestore<uint16_t> savedDepth(mCurrentDepth);
9294 if (mCurrentDepth != UINT16_MAX) {
9295 ++mCurrentDepth;
9296 }
9298 if (!aPossiblyLeafFrame) {
9299 aPossiblyLeafFrame = aFrame;
9300 }
9302 // XXXbz ideally, this would do all the pushing of various
9303 // containing blocks as needed, so callers don't have to do it...
9305 bool haveFirstLetterStyle = false, haveFirstLineStyle = false;
9306 if (aAllowBlockStyles) {
9307 ShouldHaveSpecialBlockStyle(aContent, aStyleContext, &haveFirstLetterStyle,
9308 &haveFirstLineStyle);
9309 }
9311 // The logic here needs to match the logic in GetFloatContainingBlock()
9312 nsFrameConstructorSaveState floatSaveState;
9313 if (ShouldSuppressFloatingOfDescendants(aFrame)) {
9314 aState.PushFloatContainingBlock(nullptr, floatSaveState);
9315 } else if (aFrame->IsFloatContainingBlock()) {
9316 aState.PushFloatContainingBlock(aFrame, floatSaveState);
9317 }
9319 nsFrameConstructorState::PendingBindingAutoPusher pusher(aState,
9320 aPendingBinding);
9322 FrameConstructionItemList itemsToConstruct;
9324 // If we have first-letter or first-line style then frames can get
9325 // moved around so don't set these flags.
9326 if (aAllowBlockStyles && !haveFirstLetterStyle && !haveFirstLineStyle) {
9327 itemsToConstruct.SetLineBoundaryAtStart(true);
9328 itemsToConstruct.SetLineBoundaryAtEnd(true);
9329 }
9331 // Create any anonymous frames we need here. This must happen before the
9332 // non-anonymous children are processed to ensure that popups are never
9333 // constructed before the popupset.
9334 nsAutoTArray<nsIAnonymousContentCreator::ContentInfo, 4> anonymousItems;
9335 GetAnonymousContent(aContent, aPossiblyLeafFrame, anonymousItems);
9336 #ifdef DEBUG
9337 for (uint32_t i = 0; i < anonymousItems.Length(); ++i) {
9338 NS_ABORT_IF_FALSE(anonymousItems[i].mContent->IsRootOfAnonymousSubtree(),
9339 "Content should know it's an anonymous subtree");
9340 }
9341 #endif
9342 AddFCItemsForAnonymousContent(aState, aFrame, anonymousItems,
9343 itemsToConstruct);
9345 if (!aPossiblyLeafFrame->IsLeaf()) {
9346 // :before/:after content should have the same style context parent
9347 // as normal kids.
9348 // Note that we don't use this style context for looking up things like
9349 // special block styles because in some cases involving table pseudo-frames
9350 // it has nothing to do with the parent frame's desired behavior.
9351 nsStyleContext* styleContext;
9353 if (aCanHaveGeneratedContent) {
9354 aFrame->AddStateBits(NS_FRAME_MAY_HAVE_GENERATED_CONTENT);
9355 styleContext =
9356 nsFrame::CorrectStyleParentFrame(aFrame, nullptr)->StyleContext();
9357 // Probe for generated content before
9358 CreateGeneratedContentItem(aState, aFrame, aContent, styleContext,
9359 nsCSSPseudoElements::ePseudo_before,
9360 itemsToConstruct);
9361 }
9363 const bool addChildItems = MOZ_LIKELY(mCurrentDepth < kMaxDepth);
9364 if (!addChildItems) {
9365 NS_WARNING("ProcessChildren max depth exceeded");
9366 }
9368 FlattenedChildIterator iter(aContent);
9369 for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
9370 // Get the parent of the content and check if it is a XBL children element
9371 // (if the content is a children element then parent != aContent because the
9372 // FlattenedChildIterator will transitively iterate through <xbl:children>
9373 // for default content). Push the children element as an ancestor here because
9374 // it does not have a frame and would not otherwise be pushed as an ancestor.
9375 nsIContent* parent = child->GetParent();
9376 MOZ_ASSERT(parent, "Parent must be non-null because we are iterating children.");
9377 TreeMatchContext::AutoAncestorPusher ancestorPusher(aState.mTreeMatchContext);
9378 if (parent != aContent && parent->IsElement()) {
9379 if (aState.mTreeMatchContext.mAncestorFilter.HasFilter()) {
9380 ancestorPusher.PushAncestorAndStyleScope(parent->AsElement());
9381 } else {
9382 ancestorPusher.PushStyleScope(parent->AsElement());
9383 }
9384 }
9386 // Frame construction item construction should not post
9387 // restyles, so removing restyle flags here is safe.
9388 if (child->IsElement()) {
9389 child->UnsetFlags(ELEMENT_ALL_RESTYLE_FLAGS);
9390 }
9391 if (addChildItems) {
9392 AddFrameConstructionItems(aState, child, iter.XBLInvolved(), aFrame,
9393 itemsToConstruct);
9394 } else {
9395 ClearLazyBits(child, child->GetNextSibling());
9396 }
9397 }
9398 itemsToConstruct.SetParentHasNoXBLChildren(!iter.XBLInvolved());
9400 if (aCanHaveGeneratedContent) {
9401 // Probe for generated content after
9402 CreateGeneratedContentItem(aState, aFrame, aContent, styleContext,
9403 nsCSSPseudoElements::ePseudo_after,
9404 itemsToConstruct);
9405 }
9406 } else {
9407 ClearLazyBits(aContent->GetFirstChild(), nullptr);
9408 }
9410 ConstructFramesFromItemList(aState, itemsToConstruct, aFrame, aFrameItems);
9412 NS_ASSERTION(!aAllowBlockStyles || !aFrame->IsBoxFrame(),
9413 "can't be both block and box");
9415 if (haveFirstLetterStyle) {
9416 WrapFramesInFirstLetterFrame(aContent, aFrame, aFrameItems);
9417 }
9418 if (haveFirstLineStyle) {
9419 WrapFramesInFirstLineFrame(aState, aContent, aFrame, nullptr,
9420 aFrameItems);
9421 }
9423 // We might end up with first-line frames that change
9424 // AnyKidsNeedBlockParent() without changing itemsToConstruct, but that
9425 // should never happen for cases whan aFrame->IsBoxFrame().
9426 NS_ASSERTION(!haveFirstLineStyle || !aFrame->IsBoxFrame(),
9427 "Shouldn't have first-line style if we're a box");
9428 NS_ASSERTION(!aFrame->IsBoxFrame() ||
9429 itemsToConstruct.AnyItemsNeedBlockParent() ==
9430 (AnyKidsNeedBlockParent(aFrameItems.FirstChild()) != nullptr),
9431 "Something went awry in our block parent calculations");
9433 if (aFrame->IsBoxFrame() && itemsToConstruct.AnyItemsNeedBlockParent()) {
9434 // XXXbz we could do this on the FrameConstructionItemList level,
9435 // no? And if we cared we could look through the item list
9436 // instead of groveling through the framelist here..
9437 nsStyleContext *frameStyleContext = aFrame->StyleContext();
9438 // Report a warning for non-GC frames, for chrome:
9439 if (!aFrame->IsGeneratedContentFrame() &&
9440 mPresShell->GetPresContext()->IsChrome()) {
9441 nsIContent *badKid = AnyKidsNeedBlockParent(aFrameItems.FirstChild());
9442 nsDependentAtomString parentTag(aContent->Tag()), kidTag(badKid->Tag());
9443 const char16_t* params[] = { parentTag.get(), kidTag.get() };
9444 const nsStyleDisplay *display = frameStyleContext->StyleDisplay();
9445 const char *message =
9446 (display->mDisplay == NS_STYLE_DISPLAY_INLINE_BOX)
9447 ? "NeededToWrapXULInlineBox" : "NeededToWrapXUL";
9448 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
9449 NS_LITERAL_CSTRING("FrameConstructor"),
9450 mDocument,
9451 nsContentUtils::eXUL_PROPERTIES,
9452 message,
9453 params, ArrayLength(params));
9454 }
9456 nsRefPtr<nsStyleContext> blockSC = mPresShell->StyleSet()->
9457 ResolveAnonymousBoxStyle(nsCSSAnonBoxes::mozXULAnonymousBlock,
9458 frameStyleContext);
9459 nsIFrame *blockFrame = NS_NewBlockFrame(mPresShell, blockSC);
9460 // We might, in theory, want to set NS_BLOCK_FLOAT_MGR and
9461 // NS_BLOCK_MARGIN_ROOT, but I think it's a bad idea given that
9462 // a real block placed here wouldn't get those set on it.
9464 InitAndRestoreFrame(aState, aContent, aFrame, blockFrame, false);
9466 NS_ASSERTION(!blockFrame->HasView(), "need to do view reparenting");
9467 ReparentFrames(this, blockFrame, aFrameItems);
9469 blockFrame->SetInitialChildList(kPrincipalList, aFrameItems);
9470 NS_ASSERTION(aFrameItems.IsEmpty(), "How did that happen?");
9471 aFrameItems.Clear();
9472 aFrameItems.AddChild(blockFrame);
9474 aFrame->AddStateBits(NS_STATE_BOX_WRAPS_KIDS_IN_BLOCK);
9475 }
9476 }
9478 //----------------------------------------------------------------------
9480 // Support for :first-line style
9482 // Special routine to handle placing a list of frames into a block
9483 // frame that has first-line style. The routine ensures that the first
9484 // collection of inline frames end up in a first-line frame.
9485 // NOTE: aState may have containing block information related to a
9486 // different part of the frame tree than where the first line occurs.
9487 // In particular aState may be set up for where ContentInserted or
9488 // ContentAppended is inserting content, which may be some
9489 // non-first-in-flow continuation of the block to which the first-line
9490 // belongs. So this function needs to be careful about how it uses
9491 // aState.
9492 void
9493 nsCSSFrameConstructor::WrapFramesInFirstLineFrame(
9494 nsFrameConstructorState& aState,
9495 nsIContent* aBlockContent,
9496 nsIFrame* aBlockFrame,
9497 nsIFrame* aLineFrame,
9498 nsFrameItems& aFrameItems)
9499 {
9500 // Find the part of aFrameItems that we want to put in the first-line
9501 nsFrameList::FrameLinkEnumerator link(aFrameItems);
9502 while (!link.AtEnd() && link.NextFrame()->IsInlineOutside()) {
9503 link.Next();
9504 }
9506 nsFrameList firstLineChildren = aFrameItems.ExtractHead(link);
9508 if (firstLineChildren.IsEmpty()) {
9509 // Nothing is supposed to go into the first-line; nothing to do
9510 return;
9511 }
9513 if (!aLineFrame) {
9514 // Create line frame
9515 nsStyleContext* parentStyle =
9516 nsFrame::CorrectStyleParentFrame(aBlockFrame,
9517 nsCSSPseudoElements::firstLine)->
9518 StyleContext();
9519 nsRefPtr<nsStyleContext> firstLineStyle = GetFirstLineStyle(aBlockContent,
9520 parentStyle);
9522 aLineFrame = NS_NewFirstLineFrame(mPresShell, firstLineStyle);
9524 // Initialize the line frame
9525 InitAndRestoreFrame(aState, aBlockContent, aBlockFrame, aLineFrame);
9527 // The lineFrame will be the block's first child; the rest of the
9528 // frame list (after lastInlineFrame) will be the second and
9529 // subsequent children; insert lineFrame into aFrameItems.
9530 aFrameItems.InsertFrame(nullptr, nullptr, aLineFrame);
9532 NS_ASSERTION(aLineFrame->StyleContext() == firstLineStyle,
9533 "Bogus style context on line frame");
9534 }
9536 // Give the inline frames to the lineFrame <b>after</b> reparenting them
9537 ReparentFrames(this, aLineFrame, firstLineChildren);
9538 if (aLineFrame->PrincipalChildList().IsEmpty() &&
9539 (aLineFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
9540 aLineFrame->SetInitialChildList(kPrincipalList, firstLineChildren);
9541 } else {
9542 AppendFrames(aLineFrame, kPrincipalList, firstLineChildren);
9543 }
9544 }
9546 // Special routine to handle appending a new frame to a block frame's
9547 // child list. Takes care of placing the new frame into the right
9548 // place when first-line style is present.
9549 void
9550 nsCSSFrameConstructor::AppendFirstLineFrames(
9551 nsFrameConstructorState& aState,
9552 nsIContent* aBlockContent,
9553 nsIFrame* aBlockFrame,
9554 nsFrameItems& aFrameItems)
9555 {
9556 // It's possible that aBlockFrame needs to have a first-line frame
9557 // created because it doesn't currently have any children.
9558 const nsFrameList& blockKids = aBlockFrame->PrincipalChildList();
9559 if (blockKids.IsEmpty()) {
9560 WrapFramesInFirstLineFrame(aState, aBlockContent,
9561 aBlockFrame, nullptr, aFrameItems);
9562 return;
9563 }
9565 // Examine the last block child - if it's a first-line frame then
9566 // appended frames need special treatment.
9567 nsIFrame* lastBlockKid = blockKids.LastChild();
9568 if (lastBlockKid->GetType() != nsGkAtoms::lineFrame) {
9569 // No first-line frame at the end of the list, therefore there is
9570 // an intervening block between any first-line frame the frames
9571 // we are appending. Therefore, we don't need any special
9572 // treatment of the appended frames.
9573 return;
9574 }
9576 WrapFramesInFirstLineFrame(aState, aBlockContent, aBlockFrame,
9577 lastBlockKid, aFrameItems);
9578 }
9580 // Special routine to handle inserting a new frame into a block
9581 // frame's child list. Takes care of placing the new frame into the
9582 // right place when first-line style is present.
9583 nsresult
9584 nsCSSFrameConstructor::InsertFirstLineFrames(
9585 nsFrameConstructorState& aState,
9586 nsIContent* aContent,
9587 nsIFrame* aBlockFrame,
9588 nsIFrame** aParentFrame,
9589 nsIFrame* aPrevSibling,
9590 nsFrameItems& aFrameItems)
9591 {
9592 nsresult rv = NS_OK;
9593 // XXXbz If you make this method actually do something, check to
9594 // make sure that the caller is passing what you expect. In
9595 // particular, which content is aContent? And audit the rest of
9596 // this code too; it makes bogus assumptions and may not build.
9597 #if 0
9598 nsIFrame* parentFrame = *aParentFrame;
9599 nsIFrame* newFrame = aFrameItems.childList;
9600 bool isInline = IsInlineOutside(newFrame);
9602 if (!aPrevSibling) {
9603 // Insertion will become the first frame. Two cases: we either
9604 // already have a first-line frame or we don't.
9605 nsIFrame* firstBlockKid = aBlockFrame->GetFirstPrincipalChild();
9606 if (firstBlockKid->GetType() == nsGkAtoms::lineFrame) {
9607 // We already have a first-line frame
9608 nsIFrame* lineFrame = firstBlockKid;
9610 if (isInline) {
9611 // Easy case: the new inline frame will go into the lineFrame.
9612 ReparentFrame(this, lineFrame, newFrame);
9613 InsertFrames(lineFrame, kPrincipalList, nullptr, newFrame);
9615 // Since the frame is going into the lineFrame, don't let it
9616 // go into the block too.
9617 aFrameItems.childList = nullptr;
9618 aFrameItems.lastChild = nullptr;
9619 }
9620 else {
9621 // Harder case: We are about to insert a block level element
9622 // before the first-line frame.
9623 // XXX need a method to steal away frames from the line-frame
9624 }
9625 }
9626 else {
9627 // We do not have a first-line frame
9628 if (isInline) {
9629 // We now need a first-line frame to contain the inline frame.
9630 nsIFrame* lineFrame = NS_NewFirstLineFrame(firstLineStyle);
9632 if (NS_SUCCEEDED(rv)) {
9633 // Lookup first-line style context
9634 nsStyleContext* parentStyle =
9635 nsFrame::CorrectStyleParentFrame(aBlockFrame,
9636 nsCSSPseudoElements::firstLine)->
9637 StyleContext();
9638 nsRefPtr<nsStyleContext> firstLineStyle =
9639 GetFirstLineStyle(aContent, parentStyle);
9641 // Initialize the line frame
9642 InitAndRestoreFrame(aState, aContent, aBlockFrame, lineFrame);
9644 // Make sure the caller inserts the lineFrame into the
9645 // blocks list of children.
9646 aFrameItems.childList = lineFrame;
9647 aFrameItems.lastChild = lineFrame;
9649 // Give the inline frames to the lineFrame <b>after</b>
9650 // reparenting them
9651 NS_ASSERTION(lineFrame->StyleContext() == firstLineStyle,
9652 "Bogus style context on line frame");
9653 ReparentFrame(aPresContext, lineFrame, newFrame);
9654 lineFrame->SetInitialChildList(kPrincipalList, newFrame);
9655 }
9656 }
9657 else {
9658 // Easy case: the regular insertion logic can insert the new
9659 // frame because it's a block frame.
9660 }
9661 }
9662 }
9663 else {
9664 // Insertion will not be the first frame.
9665 nsIFrame* prevSiblingParent = aPrevSibling->GetParent();
9666 if (prevSiblingParent == aBlockFrame) {
9667 // Easy case: The prev-siblings parent is the block
9668 // frame. Therefore the prev-sibling is not currently in a
9669 // line-frame. Therefore the new frame which is going after it,
9670 // regardless of type, is not going into a line-frame.
9671 }
9672 else {
9673 // If the prevSiblingParent is not the block-frame then it must
9674 // be a line-frame (if it were a letter-frame, that logic would
9675 // already have adjusted the prev-sibling to be the
9676 // letter-frame).
9677 if (isInline) {
9678 // Easy case: the insertion can go where the caller thinks it
9679 // should go (which is into prevSiblingParent).
9680 }
9681 else {
9682 // Block elements don't end up in line-frames, therefore
9683 // change the insertion point to aBlockFrame. However, there
9684 // might be more inline elements following aPrevSibling that
9685 // need to be pulled out of the line-frame and become children
9686 // of the block.
9687 nsIFrame* nextSibling = aPrevSibling->GetNextSibling();
9688 nsIFrame* nextLineFrame = prevSiblingParent->GetNextInFlow();
9689 if (nextSibling || nextLineFrame) {
9690 // Oy. We have work to do. Create a list of the new frames
9691 // that are going into the block by stripping them away from
9692 // the line-frame(s).
9693 if (nextSibling) {
9694 nsLineFrame* lineFrame = (nsLineFrame*) prevSiblingParent;
9695 nsFrameList tail = lineFrame->StealFramesAfter(aPrevSibling);
9696 // XXX do something with 'tail'
9697 }
9699 nsLineFrame* nextLineFrame = (nsLineFrame*) lineFrame;
9700 for (;;) {
9701 nextLineFrame = nextLineFrame->GetNextInFlow();
9702 if (!nextLineFrame) {
9703 break;
9704 }
9705 nsIFrame* kids = nextLineFrame->GetFirstPrincipalChild();
9706 }
9707 }
9708 else {
9709 // We got lucky: aPrevSibling was the last inline frame in
9710 // the line-frame.
9711 ReparentFrame(this, aBlockFrame, newFrame);
9712 InsertFrames(aBlockFrame, kPrincipalList,
9713 prevSiblingParent, newFrame);
9714 aFrameItems.childList = nullptr;
9715 aFrameItems.lastChild = nullptr;
9716 }
9717 }
9718 }
9719 }
9721 #endif
9722 return rv;
9723 }
9725 //----------------------------------------------------------------------
9727 // First-letter support
9729 // Determine how many characters in the text fragment apply to the
9730 // first letter
9731 static int32_t
9732 FirstLetterCount(const nsTextFragment* aFragment)
9733 {
9734 int32_t count = 0;
9735 int32_t firstLetterLength = 0;
9737 int32_t i, n = aFragment->GetLength();
9738 for (i = 0; i < n; i++) {
9739 char16_t ch = aFragment->CharAt(i);
9740 // FIXME: take content language into account when deciding whitespace.
9741 if (dom::IsSpaceCharacter(ch)) {
9742 if (firstLetterLength) {
9743 break;
9744 }
9745 count++;
9746 continue;
9747 }
9748 // XXX I18n
9749 if ((ch == '\'') || (ch == '\"')) {
9750 if (firstLetterLength) {
9751 break;
9752 }
9753 // keep looping
9754 firstLetterLength = 1;
9755 }
9756 else {
9757 count++;
9758 break;
9759 }
9760 }
9762 return count;
9763 }
9765 static bool
9766 NeedFirstLetterContinuation(nsIContent* aContent)
9767 {
9768 NS_PRECONDITION(aContent, "null ptr");
9770 bool result = false;
9771 if (aContent) {
9772 const nsTextFragment* frag = aContent->GetText();
9773 if (frag) {
9774 int32_t flc = FirstLetterCount(frag);
9775 int32_t tl = frag->GetLength();
9776 if (flc < tl) {
9777 result = true;
9778 }
9779 }
9780 }
9781 return result;
9782 }
9784 static bool IsFirstLetterContent(nsIContent* aContent)
9785 {
9786 return aContent->TextLength() &&
9787 !aContent->TextIsOnlyWhitespace();
9788 }
9790 /**
9791 * Create a letter frame, only make it a floating frame.
9792 */
9793 void
9794 nsCSSFrameConstructor::CreateFloatingLetterFrame(
9795 nsFrameConstructorState& aState,
9796 nsIFrame* aBlockFrame,
9797 nsIContent* aTextContent,
9798 nsIFrame* aTextFrame,
9799 nsIContent* aBlockContent,
9800 nsIFrame* aParentFrame,
9801 nsStyleContext* aStyleContext,
9802 nsFrameItems& aResult)
9803 {
9804 // Create the first-letter-frame
9805 nsIFrame* letterFrame;
9806 nsStyleSet *styleSet = mPresShell->StyleSet();
9808 letterFrame = NS_NewFirstLetterFrame(mPresShell, aStyleContext);
9809 // We don't want to use a text content for a non-text frame (because we want
9810 // its primary frame to be a text frame). So use its parent for the
9811 // first-letter.
9812 nsIContent* letterContent = aTextContent->GetParent();
9813 nsIFrame* containingBlock = aState.GetGeometricParent(
9814 aStyleContext->StyleDisplay(), aParentFrame);
9815 InitAndRestoreFrame(aState, letterContent, containingBlock, letterFrame);
9817 // Init the text frame to refer to the letter frame. Make sure we
9818 // get a proper style context for it (the one passed in is for the
9819 // letter frame and will have the float property set on it; the text
9820 // frame shouldn't have that set).
9821 nsRefPtr<nsStyleContext> textSC;
9822 textSC = styleSet->ResolveStyleForNonElement(aStyleContext);
9823 aTextFrame->SetStyleContextWithoutNotification(textSC);
9824 InitAndRestoreFrame(aState, aTextContent, letterFrame, aTextFrame);
9826 // And then give the text frame to the letter frame
9827 SetInitialSingleChild(letterFrame, aTextFrame);
9829 // See if we will need to continue the text frame (does it contain
9830 // more than just the first-letter text or not?) If it does, then we
9831 // create (in advance) a continuation frame for it.
9832 nsIFrame* nextTextFrame = nullptr;
9833 if (NeedFirstLetterContinuation(aTextContent)) {
9834 // Create continuation
9835 nextTextFrame =
9836 CreateContinuingFrame(aState.mPresContext, aTextFrame, aParentFrame);
9837 // Repair the continuations style context
9838 nsStyleContext* parentStyleContext = aStyleContext->GetParent();
9839 if (parentStyleContext) {
9840 nsRefPtr<nsStyleContext> newSC;
9841 newSC = styleSet->ResolveStyleForNonElement(parentStyleContext);
9842 nextTextFrame->SetStyleContext(newSC);
9843 }
9844 }
9846 NS_ASSERTION(aResult.IsEmpty(), "aResult should be an empty nsFrameItems!");
9847 // Put the new float before any of the floats in the block we're doing
9848 // first-letter for, that is, before any floats whose parent is
9849 // containingBlock.
9850 nsFrameList::FrameLinkEnumerator link(aState.mFloatedItems);
9851 while (!link.AtEnd() && link.NextFrame()->GetParent() != containingBlock) {
9852 link.Next();
9853 }
9855 aState.AddChild(letterFrame, aResult, letterContent, aStyleContext,
9856 aParentFrame, false, true, false, true,
9857 link.PrevFrame());
9859 if (nextTextFrame) {
9860 aResult.AddChild(nextTextFrame);
9861 }
9862 }
9864 /**
9865 * Create a new letter frame for aTextFrame. The letter frame will be
9866 * a child of aParentFrame.
9867 */
9868 void
9869 nsCSSFrameConstructor::CreateLetterFrame(nsIFrame* aBlockFrame,
9870 nsIFrame* aBlockContinuation,
9871 nsIContent* aTextContent,
9872 nsIFrame* aParentFrame,
9873 nsFrameItems& aResult)
9874 {
9875 NS_PRECONDITION(aTextContent->IsNodeOfType(nsINode::eTEXT),
9876 "aTextContent isn't text");
9877 NS_ASSERTION(nsLayoutUtils::GetAsBlock(aBlockFrame),
9878 "Not a block frame?");
9880 // Get style context for the first-letter-frame
9881 nsStyleContext* parentStyleContext =
9882 nsFrame::CorrectStyleParentFrame(aParentFrame,
9883 nsCSSPseudoElements::firstLetter)->
9884 StyleContext();
9886 // Use content from containing block so that we can actually
9887 // find a matching style rule.
9888 nsIContent* blockContent = aBlockFrame->GetContent();
9890 // Create first-letter style rule
9891 nsRefPtr<nsStyleContext> sc = GetFirstLetterStyle(blockContent,
9892 parentStyleContext);
9893 if (sc) {
9894 nsRefPtr<nsStyleContext> textSC;
9895 textSC = mPresShell->StyleSet()->ResolveStyleForNonElement(sc);
9897 // Create a new text frame (the original one will be discarded)
9898 // pass a temporary stylecontext, the correct one will be set
9899 // later. Start off by unsetting the primary frame for
9900 // aTextContent, so it's no longer pointing to the to-be-destroyed
9901 // frame.
9902 // XXXbz it would be really nice to destroy the old frame _first_,
9903 // then create the new one, so we could avoid this hack.
9904 aTextContent->SetPrimaryFrame(nullptr);
9905 nsIFrame* textFrame = NS_NewTextFrame(mPresShell, textSC);
9907 NS_ASSERTION(aBlockContinuation == GetFloatContainingBlock(aParentFrame),
9908 "Containing block is confused");
9909 nsFrameConstructorState state(mPresShell,
9910 GetAbsoluteContainingBlock(aParentFrame, FIXED_POS),
9911 GetAbsoluteContainingBlock(aParentFrame, ABS_POS),
9912 aBlockContinuation);
9914 // Create the right type of first-letter frame
9915 const nsStyleDisplay* display = sc->StyleDisplay();
9916 if (display->IsFloatingStyle() && !aParentFrame->IsSVGText()) {
9917 // Make a floating first-letter frame
9918 CreateFloatingLetterFrame(state, aBlockFrame, aTextContent, textFrame,
9919 blockContent, aParentFrame, sc, aResult);
9920 }
9921 else {
9922 // Make an inflow first-letter frame
9923 nsIFrame* letterFrame = NS_NewFirstLetterFrame(mPresShell, sc);
9925 // Initialize the first-letter-frame. We don't want to use a text
9926 // content for a non-text frame (because we want its primary frame to
9927 // be a text frame). So use its parent for the first-letter.
9928 nsIContent* letterContent = aTextContent->GetParent();
9929 letterFrame->Init(letterContent, aParentFrame, nullptr);
9931 InitAndRestoreFrame(state, aTextContent, letterFrame, textFrame);
9933 SetInitialSingleChild(letterFrame, textFrame);
9934 aResult.Clear();
9935 aResult.AddChild(letterFrame);
9936 NS_ASSERTION(!aBlockFrame->GetPrevContinuation(),
9937 "should have the first continuation here");
9938 aBlockFrame->AddStateBits(NS_BLOCK_HAS_FIRST_LETTER_CHILD);
9939 }
9940 aTextContent->SetPrimaryFrame(textFrame);
9941 }
9942 }
9944 void
9945 nsCSSFrameConstructor::WrapFramesInFirstLetterFrame(
9946 nsIContent* aBlockContent,
9947 nsIFrame* aBlockFrame,
9948 nsFrameItems& aBlockFrames)
9949 {
9950 aBlockFrame->AddStateBits(NS_BLOCK_HAS_FIRST_LETTER_STYLE);
9952 nsIFrame* parentFrame = nullptr;
9953 nsIFrame* textFrame = nullptr;
9954 nsIFrame* prevFrame = nullptr;
9955 nsFrameItems letterFrames;
9956 bool stopLooking = false;
9957 WrapFramesInFirstLetterFrame(aBlockFrame, aBlockFrame, aBlockFrame,
9958 aBlockFrames.FirstChild(),
9959 &parentFrame, &textFrame, &prevFrame,
9960 letterFrames, &stopLooking);
9961 if (parentFrame) {
9962 if (parentFrame == aBlockFrame) {
9963 // Take textFrame out of the block's frame list and substitute the
9964 // letter frame(s) instead.
9965 aBlockFrames.DestroyFrame(textFrame);
9966 aBlockFrames.InsertFrames(nullptr, prevFrame, letterFrames);
9967 }
9968 else {
9969 // Take the old textFrame out of the inline parent's child list
9970 RemoveFrame(kPrincipalList, textFrame);
9972 // Insert in the letter frame(s)
9973 parentFrame->InsertFrames(kPrincipalList, prevFrame, letterFrames);
9974 }
9975 }
9976 }
9978 void
9979 nsCSSFrameConstructor::WrapFramesInFirstLetterFrame(
9980 nsIFrame* aBlockFrame,
9981 nsIFrame* aBlockContinuation,
9982 nsIFrame* aParentFrame,
9983 nsIFrame* aParentFrameList,
9984 nsIFrame** aModifiedParent,
9985 nsIFrame** aTextFrame,
9986 nsIFrame** aPrevFrame,
9987 nsFrameItems& aLetterFrames,
9988 bool* aStopLooking)
9989 {
9990 nsIFrame* prevFrame = nullptr;
9991 nsIFrame* frame = aParentFrameList;
9993 while (frame) {
9994 nsIFrame* nextFrame = frame->GetNextSibling();
9996 nsIAtom* frameType = frame->GetType();
9997 if (nsGkAtoms::textFrame == frameType) {
9998 // Wrap up first-letter content in a letter frame
9999 nsIContent* textContent = frame->GetContent();
10000 if (IsFirstLetterContent(textContent)) {
10001 // Create letter frame to wrap up the text
10002 CreateLetterFrame(aBlockFrame, aBlockContinuation, textContent,
10003 aParentFrame, aLetterFrames);
10005 // Provide adjustment information for parent
10006 *aModifiedParent = aParentFrame;
10007 *aTextFrame = frame;
10008 *aPrevFrame = prevFrame;
10009 *aStopLooking = true;
10010 return;
10011 }
10012 }
10013 else if (IsInlineFrame(frame) && frameType != nsGkAtoms::brFrame) {
10014 nsIFrame* kids = frame->GetFirstPrincipalChild();
10015 WrapFramesInFirstLetterFrame(aBlockFrame, aBlockContinuation, frame,
10016 kids, aModifiedParent, aTextFrame,
10017 aPrevFrame, aLetterFrames, aStopLooking);
10018 if (*aStopLooking) {
10019 return;
10020 }
10021 }
10022 else {
10023 // This will stop us looking to create more letter frames. For
10024 // example, maybe the frame-type is "letterFrame" or
10025 // "placeholderFrame". This keeps us from creating extra letter
10026 // frames, and also prevents us from creating letter frames when
10027 // the first real content child of a block is not text (e.g. an
10028 // image, hr, etc.)
10029 *aStopLooking = true;
10030 break;
10031 }
10033 prevFrame = frame;
10034 frame = nextFrame;
10035 }
10036 }
10038 static nsIFrame*
10039 FindFirstLetterFrame(nsIFrame* aFrame, nsIFrame::ChildListID aListID)
10040 {
10041 nsFrameList list = aFrame->GetChildList(aListID);
10042 for (nsFrameList::Enumerator e(list); !e.AtEnd(); e.Next()) {
10043 if (nsGkAtoms::letterFrame == e.get()->GetType()) {
10044 return e.get();
10045 }
10046 }
10047 return nullptr;
10048 }
10050 nsresult
10051 nsCSSFrameConstructor::RemoveFloatingFirstLetterFrames(
10052 nsPresContext* aPresContext,
10053 nsIPresShell* aPresShell,
10054 nsIFrame* aBlockFrame,
10055 bool* aStopLooking)
10056 {
10057 // Look for the first letter frame on the kFloatList, then kPushedFloatsList.
10058 nsIFrame* floatFrame =
10059 ::FindFirstLetterFrame(aBlockFrame, nsIFrame::kFloatList);
10060 if (!floatFrame) {
10061 floatFrame =
10062 ::FindFirstLetterFrame(aBlockFrame, nsIFrame::kPushedFloatsList);
10063 if (!floatFrame) {
10064 return NS_OK;
10065 }
10066 }
10068 // Take the text frame away from the letter frame (so it isn't
10069 // destroyed when we destroy the letter frame).
10070 nsIFrame* textFrame = floatFrame->GetFirstPrincipalChild();
10071 if (!textFrame) {
10072 return NS_OK;
10073 }
10075 // Discover the placeholder frame for the letter frame
10076 nsIFrame* parentFrame;
10077 nsPlaceholderFrame* placeholderFrame = GetPlaceholderFrameFor(floatFrame);
10079 if (!placeholderFrame) {
10080 // Somethings really wrong
10081 return NS_OK;
10082 }
10083 parentFrame = placeholderFrame->GetParent();
10084 if (!parentFrame) {
10085 // Somethings really wrong
10086 return NS_OK;
10087 }
10089 // Create a new text frame with the right style context that maps
10090 // all of the content that was previously part of the letter frame
10091 // (and probably continued elsewhere).
10092 nsStyleContext* parentSC = parentFrame->StyleContext();
10093 nsIContent* textContent = textFrame->GetContent();
10094 if (!textContent) {
10095 return NS_OK;
10096 }
10097 nsRefPtr<nsStyleContext> newSC;
10098 newSC = aPresShell->StyleSet()->ResolveStyleForNonElement(parentSC);
10099 nsIFrame* newTextFrame = NS_NewTextFrame(aPresShell, newSC);
10100 newTextFrame->Init(textContent, parentFrame, nullptr);
10102 // Destroy the old text frame's continuations (the old text frame
10103 // will be destroyed when its letter frame is destroyed).
10104 nsIFrame* frameToDelete = textFrame->LastContinuation();
10105 while (frameToDelete != textFrame) {
10106 nsIFrame* nextFrameToDelete = frameToDelete->GetPrevContinuation();
10107 RemoveFrame(kPrincipalList, frameToDelete);
10108 frameToDelete = nextFrameToDelete;
10109 }
10111 nsIFrame* prevSibling = placeholderFrame->GetPrevSibling();
10113 // Now that everything is set...
10114 #ifdef NOISY_FIRST_LETTER
10115 printf("RemoveFloatingFirstLetterFrames: textContent=%p oldTextFrame=%p newTextFrame=%p\n",
10116 textContent.get(), textFrame, newTextFrame);
10117 #endif
10119 // Remove placeholder frame and the float
10120 RemoveFrame(kPrincipalList, placeholderFrame);
10122 // Now that the old frames are gone, we can start pointing to our
10123 // new primary frame.
10124 textContent->SetPrimaryFrame(newTextFrame);
10126 // Wallpaper bug 822910.
10127 bool offsetsNeedFixing =
10128 prevSibling && prevSibling->GetType() == nsGkAtoms::textFrame;
10129 if (offsetsNeedFixing) {
10130 prevSibling->AddStateBits(TEXT_OFFSETS_NEED_FIXING);
10131 }
10133 // Insert text frame in its place
10134 nsFrameList textList(newTextFrame, newTextFrame);
10135 InsertFrames(parentFrame, kPrincipalList, prevSibling, textList);
10137 if (offsetsNeedFixing) {
10138 prevSibling->RemoveStateBits(TEXT_OFFSETS_NEED_FIXING);
10139 }
10141 return NS_OK;
10142 }
10144 nsresult
10145 nsCSSFrameConstructor::RemoveFirstLetterFrames(nsPresContext* aPresContext,
10146 nsIPresShell* aPresShell,
10147 nsIFrame* aFrame,
10148 nsIFrame* aBlockFrame,
10149 bool* aStopLooking)
10150 {
10151 nsIFrame* prevSibling = nullptr;
10152 nsIFrame* kid = aFrame->GetFirstPrincipalChild();
10154 while (kid) {
10155 if (nsGkAtoms::letterFrame == kid->GetType()) {
10156 // Bingo. Found it. First steal away the text frame.
10157 nsIFrame* textFrame = kid->GetFirstPrincipalChild();
10158 if (!textFrame) {
10159 break;
10160 }
10162 // Create a new textframe
10163 nsStyleContext* parentSC = aFrame->StyleContext();
10164 if (!parentSC) {
10165 break;
10166 }
10167 nsIContent* textContent = textFrame->GetContent();
10168 if (!textContent) {
10169 break;
10170 }
10171 nsRefPtr<nsStyleContext> newSC;
10172 newSC = aPresShell->StyleSet()->ResolveStyleForNonElement(parentSC);
10173 textFrame = NS_NewTextFrame(aPresShell, newSC);
10174 textFrame->Init(textContent, aFrame, nullptr);
10176 // Next rip out the kid and replace it with the text frame
10177 RemoveFrame(kPrincipalList, kid);
10179 // Now that the old frames are gone, we can start pointing to our
10180 // new primary frame.
10181 textContent->SetPrimaryFrame(textFrame);
10183 // Wallpaper bug 822910.
10184 bool offsetsNeedFixing =
10185 prevSibling && prevSibling->GetType() == nsGkAtoms::textFrame;
10186 if (offsetsNeedFixing) {
10187 prevSibling->AddStateBits(TEXT_OFFSETS_NEED_FIXING);
10188 }
10190 // Insert text frame in its place
10191 nsFrameList textList(textFrame, textFrame);
10192 InsertFrames(aFrame, kPrincipalList, prevSibling, textList);
10194 if (offsetsNeedFixing) {
10195 prevSibling->RemoveStateBits(TEXT_OFFSETS_NEED_FIXING);
10196 }
10198 *aStopLooking = true;
10199 NS_ASSERTION(!aBlockFrame->GetPrevContinuation(),
10200 "should have the first continuation here");
10201 aBlockFrame->RemoveStateBits(NS_BLOCK_HAS_FIRST_LETTER_CHILD);
10202 break;
10203 }
10204 else if (IsInlineFrame(kid)) {
10205 // Look inside child inline frame for the letter frame
10206 RemoveFirstLetterFrames(aPresContext, aPresShell,
10207 kid, aBlockFrame, aStopLooking);
10208 if (*aStopLooking) {
10209 break;
10210 }
10211 }
10212 prevSibling = kid;
10213 kid = kid->GetNextSibling();
10214 }
10216 return NS_OK;
10217 }
10219 nsresult
10220 nsCSSFrameConstructor::RemoveLetterFrames(nsPresContext* aPresContext,
10221 nsIPresShell* aPresShell,
10222 nsIFrame* aBlockFrame)
10223 {
10224 aBlockFrame = aBlockFrame->FirstContinuation();
10225 nsIFrame* continuation = aBlockFrame;
10227 bool stopLooking = false;
10228 nsresult rv;
10229 do {
10230 rv = RemoveFloatingFirstLetterFrames(aPresContext, aPresShell,
10231 continuation, &stopLooking);
10232 if (NS_SUCCEEDED(rv) && !stopLooking) {
10233 rv = RemoveFirstLetterFrames(aPresContext, aPresShell,
10234 continuation, aBlockFrame, &stopLooking);
10235 }
10236 if (stopLooking) {
10237 break;
10238 }
10239 continuation = continuation->GetNextContinuation();
10240 } while (continuation);
10241 return rv;
10242 }
10244 // Fixup the letter frame situation for the given block
10245 void
10246 nsCSSFrameConstructor::RecoverLetterFrames(nsIFrame* aBlockFrame)
10247 {
10248 aBlockFrame = aBlockFrame->FirstContinuation();
10249 nsIFrame* continuation = aBlockFrame;
10251 nsIFrame* parentFrame = nullptr;
10252 nsIFrame* textFrame = nullptr;
10253 nsIFrame* prevFrame = nullptr;
10254 nsFrameItems letterFrames;
10255 bool stopLooking = false;
10256 do {
10257 // XXX shouldn't this bit be set already (bug 408493), assert instead?
10258 continuation->AddStateBits(NS_BLOCK_HAS_FIRST_LETTER_STYLE);
10259 WrapFramesInFirstLetterFrame(aBlockFrame, continuation, continuation,
10260 continuation->GetFirstPrincipalChild(),
10261 &parentFrame, &textFrame, &prevFrame,
10262 letterFrames, &stopLooking);
10263 if (stopLooking) {
10264 break;
10265 }
10266 continuation = continuation->GetNextContinuation();
10267 } while (continuation);
10269 if (parentFrame) {
10270 // Take the old textFrame out of the parents child list
10271 RemoveFrame(kPrincipalList, textFrame);
10273 // Insert in the letter frame(s)
10274 parentFrame->InsertFrames(kPrincipalList, prevFrame, letterFrames);
10275 }
10276 }
10278 //----------------------------------------------------------------------
10280 // listbox Widget Routines
10282 nsresult
10283 nsCSSFrameConstructor::CreateListBoxContent(nsPresContext* aPresContext,
10284 nsIFrame* aParentFrame,
10285 nsIFrame* aPrevFrame,
10286 nsIContent* aChild,
10287 nsIFrame** aNewFrame,
10288 bool aIsAppend,
10289 bool aIsScrollbar,
10290 nsILayoutHistoryState* aFrameState)
10291 {
10292 #ifdef MOZ_XUL
10293 nsresult rv = NS_OK;
10295 // Construct a new frame
10296 if (nullptr != aParentFrame) {
10297 nsFrameItems frameItems;
10298 nsFrameConstructorState state(mPresShell, GetAbsoluteContainingBlock(aParentFrame, FIXED_POS),
10299 GetAbsoluteContainingBlock(aParentFrame, ABS_POS),
10300 GetFloatContainingBlock(aParentFrame),
10301 mTempFrameTreeState);
10303 // If we ever initialize the ancestor filter on |state|, make sure
10304 // to push the right parent!
10306 nsRefPtr<nsStyleContext> styleContext;
10307 styleContext = ResolveStyleContext(aParentFrame, aChild, &state);
10309 // Pre-check for display "none" - only if we find that, do we create
10310 // any frame at all
10311 const nsStyleDisplay* display = styleContext->StyleDisplay();
10313 if (NS_STYLE_DISPLAY_NONE == display->mDisplay) {
10314 *aNewFrame = nullptr;
10315 return NS_OK;
10316 }
10318 BeginUpdate();
10320 FrameConstructionItemList items;
10321 AddFrameConstructionItemsInternal(state, aChild, aParentFrame,
10322 aChild->Tag(), aChild->GetNameSpaceID(),
10323 true, styleContext,
10324 ITEM_ALLOW_XBL_BASE, nullptr, items);
10325 ConstructFramesFromItemList(state, items, aParentFrame, frameItems);
10327 nsIFrame* newFrame = frameItems.FirstChild();
10328 *aNewFrame = newFrame;
10330 if (newFrame) {
10331 // Notify the parent frame
10332 if (aIsAppend)
10333 rv = ((nsListBoxBodyFrame*)aParentFrame)->ListBoxAppendFrames(frameItems);
10334 else
10335 rv = ((nsListBoxBodyFrame*)aParentFrame)->ListBoxInsertFrames(aPrevFrame, frameItems);
10336 }
10338 EndUpdate();
10340 #ifdef ACCESSIBILITY
10341 if (newFrame) {
10342 nsAccessibilityService* accService = nsIPresShell::AccService();
10343 if (accService) {
10344 accService->ContentRangeInserted(mPresShell, aChild->GetParent(),
10345 aChild, aChild->GetNextSibling());
10346 }
10347 }
10348 #endif
10349 }
10351 return rv;
10352 #else
10353 return NS_ERROR_FAILURE;
10354 #endif
10355 }
10357 //----------------------------------------
10359 void
10360 nsCSSFrameConstructor::ConstructBlock(nsFrameConstructorState& aState,
10361 const nsStyleDisplay* aDisplay,
10362 nsIContent* aContent,
10363 nsIFrame* aParentFrame,
10364 nsIFrame* aContentParentFrame,
10365 nsStyleContext* aStyleContext,
10366 nsIFrame** aNewFrame,
10367 nsFrameItems& aFrameItems,
10368 nsIFrame* aPositionedFrameForAbsPosContainer,
10369 PendingBinding* aPendingBinding)
10370 {
10371 // Create column wrapper if necessary
10372 nsIFrame* blockFrame = *aNewFrame;
10373 NS_ASSERTION(blockFrame->GetType() == nsGkAtoms::blockFrame, "not a block frame?");
10374 nsIFrame* parent = aParentFrame;
10375 nsRefPtr<nsStyleContext> blockStyle = aStyleContext;
10376 const nsStyleColumn* columns = aStyleContext->StyleColumn();
10378 if (columns->mColumnCount != NS_STYLE_COLUMN_COUNT_AUTO
10379 || columns->mColumnWidth.GetUnit() != eStyleUnit_Auto) {
10380 nsIFrame* columnSetFrame = nullptr;
10381 columnSetFrame =
10382 NS_NewColumnSetFrame(mPresShell, aStyleContext, nsFrameState(0));
10384 InitAndRestoreFrame(aState, aContent, aParentFrame, columnSetFrame);
10385 blockStyle = mPresShell->StyleSet()->
10386 ResolveAnonymousBoxStyle(nsCSSAnonBoxes::columnContent, aStyleContext);
10387 parent = columnSetFrame;
10388 *aNewFrame = columnSetFrame;
10390 SetInitialSingleChild(columnSetFrame, blockFrame);
10391 }
10393 blockFrame->SetStyleContextWithoutNotification(blockStyle);
10394 InitAndRestoreFrame(aState, aContent, parent, blockFrame);
10396 aState.AddChild(*aNewFrame, aFrameItems, aContent, aStyleContext,
10397 aContentParentFrame ? aContentParentFrame :
10398 aParentFrame);
10399 if (!mRootElementFrame) {
10400 // The frame we're constructing will be the root element frame.
10401 // Set mRootElementFrame before processing children.
10402 mRootElementFrame = *aNewFrame;
10403 }
10405 // We should make the outer frame be the absolute containing block,
10406 // if one is required. We have to do this because absolute
10407 // positioning must be computed with respect to the CSS dimensions
10408 // of the element, which are the dimensions of the outer block. But
10409 // we can't really do that because only blocks can have absolute
10410 // children. So use the block and try to compensate with hacks
10411 // in nsBlockFrame::CalculateContainingBlockSizeForAbsolutes.
10412 nsFrameConstructorSaveState absoluteSaveState;
10413 (*aNewFrame)->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
10414 if (aPositionedFrameForAbsPosContainer) {
10415 // NS_ASSERTION(aRelPos, "should have made area frame for this");
10416 aState.PushAbsoluteContainingBlock(*aNewFrame, aPositionedFrameForAbsPosContainer, absoluteSaveState);
10417 }
10419 // Process the child content
10420 nsFrameItems childItems;
10421 ProcessChildren(aState, aContent, aStyleContext, blockFrame, true,
10422 childItems, true, aPendingBinding);
10424 // Set the frame's initial child list
10425 blockFrame->SetInitialChildList(kPrincipalList, childItems);
10426 }
10428 nsIFrame*
10429 nsCSSFrameConstructor::ConstructInline(nsFrameConstructorState& aState,
10430 FrameConstructionItem& aItem,
10431 nsIFrame* aParentFrame,
10432 const nsStyleDisplay* aDisplay,
10433 nsFrameItems& aFrameItems)
10434 {
10435 // If an inline frame has non-inline kids, then we chop up the child list
10436 // into runs of blocks and runs of inlines, create anonymous block frames to
10437 // contain the runs of blocks, inline frames with our style context for the
10438 // runs of inlines, and put all these frames, in order, into aFrameItems. We
10439 // return the the first one. The whole setup is called an {ib}
10440 // split; in what follows "frames in the split" refers to the anonymous blocks
10441 // and inlines that contain our children.
10442 //
10443 // {ib} splits maintain the following invariants:
10444 // 1) All frames in the split have the NS_FRAME_PART_OF_IBSPLIT bit
10445 // set.
10446 // 2) Each frame in the split has the nsIFrame::IBSplitSibling
10447 // property pointing to the next frame in the split, except for the last
10448 // one, which does not have it set.
10449 // 3) Each frame in the split has the nsIFrame::IBSplitPrevSibling
10450 // property pointing to the previous frame in the split, except for the
10451 // first one, which does not have it set.
10452 // 4) The first and last frame in the split are always inlines.
10453 //
10454 // An invariant that is NOT maintained is that the wrappers are actually
10455 // linked via GetNextSibling linkage. A simple example is an inline
10456 // containing an inline that contains a block. The three parts of the inner
10457 // inline end up with three different parents.
10458 //
10459 // For example, this HTML:
10460 // <span>
10461 // <div>a</div>
10462 // <span>
10463 // b
10464 // <div>c</div>
10465 // </span>
10466 // d
10467 // <div>e</div>
10468 // f
10469 // </span>
10470 // Gives the following frame tree:
10471 //
10472 // Inline (outer span)
10473 // Block (anonymous, outer span)
10474 // Block (div)
10475 // Text("a")
10476 // Inline (outer span)
10477 // Inline (inner span)
10478 // Text("b")
10479 // Block (anonymous, outer span)
10480 // Block (anonymous, inner span)
10481 // Block (div)
10482 // Text("c")
10483 // Inline (outer span)
10484 // Inline (inner span)
10485 // Text("d")
10486 // Block (anonymous, outer span)
10487 // Block (div)
10488 // Text("e")
10489 // Inline (outer span)
10490 // Text("f")
10492 nsIContent* const content = aItem.mContent;
10493 nsStyleContext* const styleContext = aItem.mStyleContext;
10495 bool positioned =
10496 NS_STYLE_DISPLAY_INLINE == aDisplay->mDisplay &&
10497 aDisplay->IsRelativelyPositionedStyle() &&
10498 !aParentFrame->IsSVGText();
10500 nsIFrame* newFrame = NS_NewInlineFrame(mPresShell, styleContext);
10502 // Initialize the frame
10503 InitAndRestoreFrame(aState, content, aParentFrame, newFrame);
10505 // Inline frames can always have generated content
10506 newFrame->AddStateBits(NS_FRAME_MAY_HAVE_GENERATED_CONTENT);
10508 nsFrameConstructorSaveState absoluteSaveState; // definition cannot be inside next block
10509 // because the object's destructor is significant
10510 // this is part of the fix for bug 42372
10512 newFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
10513 if (positioned) {
10514 // Relatively positioned frames becomes a container for child
10515 // frames that are positioned
10516 aState.PushAbsoluteContainingBlock(newFrame, newFrame, absoluteSaveState);
10517 }
10519 // Process the child content
10520 nsFrameItems childItems;
10521 ConstructFramesFromItemList(aState, aItem.mChildItems, newFrame, childItems);
10523 nsFrameList::FrameLinkEnumerator firstBlockEnumerator(childItems);
10524 if (!aItem.mIsAllInline) {
10525 FindFirstBlock(firstBlockEnumerator);
10526 }
10528 if (aItem.mIsAllInline || firstBlockEnumerator.AtEnd()) {
10529 // This part is easy. We either already know we have no non-inline kids,
10530 // or haven't found any when constructing actual frames (the latter can
10531 // happen only if out-of-flows that we thought had no containing block
10532 // acquired one when ancestor inline frames and {ib} splits got
10533 // constructed). Just put all the kids into the single inline frame and
10534 // bail.
10535 newFrame->SetInitialChildList(kPrincipalList, childItems);
10536 aState.AddChild(newFrame, aFrameItems, content, styleContext, aParentFrame);
10537 return newFrame;
10538 }
10540 // This inline frame contains several types of children. Therefore this frame
10541 // has to be chopped into several pieces, as described above.
10543 // Grab the first inline's kids
10544 nsFrameList firstInlineKids = childItems.ExtractHead(firstBlockEnumerator);
10545 newFrame->SetInitialChildList(kPrincipalList, firstInlineKids);
10547 aFrameItems.AddChild(newFrame);
10549 CreateIBSiblings(aState, newFrame, positioned, childItems, aFrameItems);
10551 return newFrame;
10552 }
10554 void
10555 nsCSSFrameConstructor::CreateIBSiblings(nsFrameConstructorState& aState,
10556 nsIFrame* aInitialInline,
10557 bool aIsPositioned,
10558 nsFrameItems& aChildItems,
10559 nsFrameItems& aSiblings)
10560 {
10561 nsIContent* content = aInitialInline->GetContent();
10562 nsStyleContext* styleContext = aInitialInline->StyleContext();
10563 nsIFrame* parentFrame = aInitialInline->GetParent();
10565 // Resolve the right style context for our anonymous blocks.
10566 // The distinction in styles is needed because of CSS 2.1, section
10567 // 9.2.1.1, which says:
10568 // When such an inline box is affected by relative positioning, any
10569 // resulting translation also affects the block-level box contained
10570 // in the inline box.
10571 nsRefPtr<nsStyleContext> blockSC =
10572 mPresShell->StyleSet()->
10573 ResolveAnonymousBoxStyle(aIsPositioned ?
10574 nsCSSAnonBoxes::mozAnonymousPositionedBlock :
10575 nsCSSAnonBoxes::mozAnonymousBlock,
10576 styleContext);
10578 nsIFrame* lastNewInline = aInitialInline->FirstContinuation();
10579 do {
10580 // On entry to this loop aChildItems is not empty and the first frame in it
10581 // is block-level.
10582 NS_PRECONDITION(aChildItems.NotEmpty(), "Should have child items");
10583 NS_PRECONDITION(!aChildItems.FirstChild()->IsInlineOutside(),
10584 "Must have list starting with block");
10586 // The initial run of blocks belongs to an anonymous block that we create
10587 // right now. The anonymous block will be the parent of these block
10588 // children of the inline.
10589 nsIFrame* blockFrame;
10590 blockFrame = NS_NewBlockFrame(mPresShell, blockSC);
10592 InitAndRestoreFrame(aState, content, parentFrame, blockFrame, false);
10594 // Find the first non-block child which defines the end of our block kids
10595 // and the start of our next inline's kids
10596 nsFrameList::FrameLinkEnumerator firstNonBlock =
10597 FindFirstNonBlock(aChildItems);
10598 nsFrameList blockKids = aChildItems.ExtractHead(firstNonBlock);
10600 MoveChildrenTo(aState.mPresContext, aInitialInline, blockFrame, blockKids);
10602 SetFrameIsIBSplit(lastNewInline, blockFrame);
10603 aSiblings.AddChild(blockFrame);
10605 // Now grab the initial inlines in aChildItems and put them into an inline
10606 // frame
10607 nsIFrame* inlineFrame = NS_NewInlineFrame(mPresShell, styleContext);
10609 InitAndRestoreFrame(aState, content, parentFrame, inlineFrame, false);
10611 inlineFrame->AddStateBits(NS_FRAME_MAY_HAVE_GENERATED_CONTENT |
10612 NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
10613 if (aIsPositioned) {
10614 inlineFrame->MarkAsAbsoluteContainingBlock();
10615 }
10617 if (aChildItems.NotEmpty()) {
10618 nsFrameList::FrameLinkEnumerator firstBlock(aChildItems);
10619 FindFirstBlock(firstBlock);
10620 nsFrameList inlineKids = aChildItems.ExtractHead(firstBlock);
10622 MoveChildrenTo(aState.mPresContext, aInitialInline, inlineFrame,
10623 inlineKids);
10624 }
10626 SetFrameIsIBSplit(blockFrame, inlineFrame);
10627 aSiblings.AddChild(inlineFrame);
10628 lastNewInline = inlineFrame;
10629 } while (aChildItems.NotEmpty());
10631 SetFrameIsIBSplit(lastNewInline, nullptr);
10632 }
10634 void
10635 nsCSSFrameConstructor::BuildInlineChildItems(nsFrameConstructorState& aState,
10636 FrameConstructionItem& aParentItem,
10637 bool aItemIsWithinSVGText,
10638 bool aItemAllowsTextPathChild)
10639 {
10640 // XXXbz should we preallocate aParentItem.mChildItems to some sane
10641 // length? Maybe even to parentContent->GetChildCount()?
10642 nsFrameConstructorState::PendingBindingAutoPusher
10643 pusher(aState, aParentItem.mPendingBinding);
10645 nsStyleContext* const parentStyleContext = aParentItem.mStyleContext;
10646 nsIContent* const parentContent = aParentItem.mContent;
10648 TreeMatchContext::AutoAncestorPusher ancestorPusher(aState.mTreeMatchContext);
10649 if (aState.mTreeMatchContext.mAncestorFilter.HasFilter()) {
10650 ancestorPusher.PushAncestorAndStyleScope(parentContent->AsElement());
10651 } else {
10652 ancestorPusher.PushStyleScope(parentContent->AsElement());
10653 }
10655 if (!aItemIsWithinSVGText) {
10656 // Probe for generated content before
10657 CreateGeneratedContentItem(aState, nullptr, parentContent, parentStyleContext,
10658 nsCSSPseudoElements::ePseudo_before,
10659 aParentItem.mChildItems);
10660 }
10662 uint32_t flags = ITEM_ALLOW_XBL_BASE | ITEM_ALLOW_PAGE_BREAK;
10663 if (aItemIsWithinSVGText) {
10664 flags |= ITEM_IS_WITHIN_SVG_TEXT;
10665 }
10666 if (aItemAllowsTextPathChild && aParentItem.mIsForSVGAElement) {
10667 flags |= ITEM_ALLOWS_TEXT_PATH_CHILD;
10668 }
10670 if (!aParentItem.mAnonChildren.IsEmpty()) {
10671 // Use the anon-children list instead of the content tree child list so
10672 // that we use any special style context that should be associated with
10673 // the children, and so that we won't try to construct grandchildren frame
10674 // constructor items before the frame is available for their parent.
10675 AddFCItemsForAnonymousContent(aState, nullptr, aParentItem.mAnonChildren,
10676 aParentItem.mChildItems, flags);
10677 } else {
10678 // Use the content tree child list:
10679 FlattenedChildIterator iter(parentContent);
10680 for (nsIContent* content = iter.GetNextChild(); content; content = iter.GetNextChild()) {
10681 // Get the parent of the content and check if it is a XBL children element
10682 // (if the content is a children element then contentParent != parentContent because the
10683 // FlattenedChildIterator will transitively iterate through <xbl:children>
10684 // for default content). Push the children element as an ancestor here because
10685 // it does not have a frame and would not otherwise be pushed as an ancestor.
10686 nsIContent* contentParent = content->GetParent();
10687 MOZ_ASSERT(contentParent, "Parent must be non-null because we are iterating children.");
10688 TreeMatchContext::AutoAncestorPusher insertionPointPusher(aState.mTreeMatchContext);
10689 if (contentParent != parentContent && contentParent->IsElement()) {
10690 if (aState.mTreeMatchContext.mAncestorFilter.HasFilter()) {
10691 insertionPointPusher.PushAncestorAndStyleScope(contentParent->AsElement());
10692 } else {
10693 insertionPointPusher.PushStyleScope(contentParent->AsElement());
10694 }
10695 }
10697 // Manually check for comments/PIs, since we don't have a frame to pass to
10698 // AddFrameConstructionItems. We know our parent is a non-replaced inline,
10699 // so there is no need to do the NeedFrameFor check.
10700 content->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
10701 if (content->IsNodeOfType(nsINode::eCOMMENT) ||
10702 content->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION)) {
10703 continue;
10704 }
10705 if (content->IsElement()) {
10706 // See comment explaining why we need to remove the "is possible
10707 // restyle root" flags in AddFrameConstructionItems. But note
10708 // that we can remove all restyle flags, just like in
10709 // ProcessChildren and for the same reason.
10710 content->UnsetFlags(ELEMENT_ALL_RESTYLE_FLAGS);
10711 }
10713 nsRefPtr<nsStyleContext> childContext =
10714 ResolveStyleContext(parentStyleContext, content, &aState);
10716 AddFrameConstructionItemsInternal(aState, content, nullptr, content->Tag(),
10717 content->GetNameSpaceID(),
10718 iter.XBLInvolved(), childContext,
10719 flags, nullptr,
10720 aParentItem.mChildItems);
10721 }
10722 }
10724 if (!aItemIsWithinSVGText) {
10725 // Probe for generated content after
10726 CreateGeneratedContentItem(aState, nullptr, parentContent, parentStyleContext,
10727 nsCSSPseudoElements::ePseudo_after,
10728 aParentItem.mChildItems);
10729 }
10731 aParentItem.mIsAllInline = aParentItem.mChildItems.AreAllItemsInline();
10732 }
10734 // return whether it's ok to append (in the AppendFrames sense) to
10735 // aParentFrame if our nextSibling is aNextSibling. aParentFrame must
10736 // be an ib-split inline.
10737 static bool
10738 IsSafeToAppendToIBSplitInline(nsIFrame* aParentFrame, nsIFrame* aNextSibling)
10739 {
10740 NS_PRECONDITION(IsInlineFrame(aParentFrame),
10741 "Must have an inline parent here");
10742 do {
10743 NS_ASSERTION(IsFramePartOfIBSplit(aParentFrame),
10744 "How is this not part of an ib-split?");
10745 if (aNextSibling || aParentFrame->GetNextContinuation() ||
10746 GetIBSplitSibling(aParentFrame)) {
10747 return false;
10748 }
10750 aNextSibling = aParentFrame->GetNextSibling();
10751 aParentFrame = aParentFrame->GetParent();
10752 } while (IsInlineFrame(aParentFrame));
10754 return true;
10755 }
10757 bool
10758 nsCSSFrameConstructor::WipeContainingBlock(nsFrameConstructorState& aState,
10759 nsIFrame* aContainingBlock,
10760 nsIFrame* aFrame,
10761 FrameConstructionItemList& aItems,
10762 bool aIsAppend,
10763 nsIFrame* aPrevSibling)
10764 {
10765 if (aItems.IsEmpty()) {
10766 return false;
10767 }
10769 // Before we go and append the frames, we must check for several
10770 // special situations.
10772 // Situation #1 is a XUL frame that contains frames that are required
10773 // to be wrapped in blocks.
10774 if (aFrame->IsBoxFrame() &&
10775 !(aFrame->GetStateBits() & NS_STATE_BOX_WRAPS_KIDS_IN_BLOCK) &&
10776 aItems.AnyItemsNeedBlockParent()) {
10777 RecreateFramesForContent(aFrame->GetContent(), true);
10778 return true;
10779 }
10781 nsIFrame* nextSibling = ::GetInsertNextSibling(aFrame, aPrevSibling);
10783 // Situation #2 is a flex container frame into which we're inserting new
10784 // inline non-replaced children, adjacent to an existing anonymous flex item.
10785 if (aFrame->GetType() == nsGkAtoms::flexContainerFrame) {
10786 FCItemIterator iter(aItems);
10788 // Check if we're adding to-be-wrapped content right *after* an existing
10789 // anonymous flex item (which would need to absorb this content).
10790 if (aPrevSibling && IsAnonymousFlexItem(aPrevSibling) &&
10791 iter.item().NeedsAnonFlexItem(aState)) {
10792 RecreateFramesForContent(aFrame->GetContent(), true);
10793 return true;
10794 }
10796 // Check if we're adding to-be-wrapped content right *before* an existing
10797 // anonymous flex item (which would need to absorb this content).
10798 if (nextSibling && IsAnonymousFlexItem(nextSibling)) {
10799 // Jump to the last entry in the list
10800 iter.SetToEnd();
10801 iter.Prev();
10802 if (iter.item().NeedsAnonFlexItem(aState)) {
10803 RecreateFramesForContent(aFrame->GetContent(), true);
10804 return true;
10805 }
10806 }
10807 }
10809 // Situation #3 is an anonymous flex item that's getting new children who
10810 // don't want to be wrapped.
10811 if (IsAnonymousFlexItem(aFrame)) {
10812 nsIFrame* flexContainerFrame = aFrame->GetParent();
10813 NS_ABORT_IF_FALSE(flexContainerFrame &&
10814 flexContainerFrame->GetType() == nsGkAtoms::flexContainerFrame,
10815 "anonymous flex items should only exist as children "
10816 "of flex container frames");
10818 // We need to push a null float containing block to be sure that
10819 // "NeedsAnonFlexItem" will know we're not honoring floats for this
10820 // inserted content. (In particular, this is necessary in order for
10821 // NeedsAnonFlexItem's "GetGeometricParent" call to return the correct
10822 // result.) We're not honoring floats on this content because it has the
10823 // _flex container_ as its parent in the content tree.
10824 nsFrameConstructorSaveState floatSaveState;
10825 aState.PushFloatContainingBlock(nullptr, floatSaveState);
10827 FCItemIterator iter(aItems);
10828 // Skip over things that _do_ need an anonymous flex item, because
10829 // they're perfectly happy to go here -- they won't cause a reframe.
10830 if (!iter.SkipItemsThatNeedAnonFlexItem(aState)) {
10831 // We hit something that _doesn't_ need an anonymous flex item!
10832 // Rebuild the flex container to bust it out.
10833 RecreateFramesForContent(flexContainerFrame->GetContent(), true);
10834 return true;
10835 }
10837 // If we get here, then everything in |aItems| needs to be wrapped in
10838 // an anonymous flex item. That's where it's already going - good!
10839 }
10841 // Situation #4 is a case when table pseudo-frames don't work out right
10842 ParentType parentType = GetParentType(aFrame);
10843 // If all the kids want a parent of the type that aFrame is, then we're all
10844 // set to go. Indeed, there won't be any table pseudo-frames created between
10845 // aFrame and the kids, so those won't need to be merged with any table
10846 // pseudo-frames that might already be kids of aFrame. If aFrame itself is a
10847 // table pseudo-frame, then all the kids in this list would have wanted a
10848 // frame of that type wrapping them anyway, so putting them inside it is ok.
10849 if (!aItems.AllWantParentType(parentType)) {
10850 // Don't give up yet. If parentType is not eTypeBlock and the parent is
10851 // not a generated content frame, then try filtering whitespace out of the
10852 // list.
10853 if (parentType != eTypeBlock && !aFrame->IsGeneratedContentFrame()) {
10854 // For leading whitespace followed by a kid that wants our parent type,
10855 // there are four cases:
10856 // 1) We have a previous sibling which is not a table pseudo. That means
10857 // that previous sibling wanted a (non-block) parent of the type we're
10858 // looking at. Then the whitespace comes between two table-internal
10859 // elements, so should be collapsed out.
10860 // 2) We have a previous sibling which is a table pseudo. It might have
10861 // kids who want this whitespace, so we need to reframe.
10862 // 3) We have no previous sibling and our parent frame is not a table
10863 // pseudo. That means that we'll be at the beginning of our actual
10864 // non-block-type parent, and the whitespace is OK to collapse out.
10865 // If something is ever inserted before us, it'll find our own parent
10866 // as its parent and if it's something that would care about the
10867 // whitespace it'll want a block parent, so it'll trigger a reframe at
10868 // that point.
10869 // 4) We have no previous sibling and our parent frame is a table pseudo.
10870 // Need to reframe.
10871 // All that is predicated on finding the correct previous sibling. We
10872 // might have to walk backwards along continuations from aFrame to do so.
10873 //
10874 // It's always OK to drop whitespace between any two items that want a
10875 // parent of type parentType.
10876 //
10877 // For trailing whitespace preceded by a kid that wants our parent type,
10878 // there are four cases:
10879 // 1) We have a next sibling which is not a table pseudo. That means
10880 // that next sibling wanted a (non-block) parent of the type we're
10881 // looking at. Then the whitespace comes between two table-internal
10882 // elements, so should be collapsed out.
10883 // 2) We have a next sibling which is a table pseudo. It might have
10884 // kids who want this whitespace, so we need to reframe.
10885 // 3) We have no next sibling and our parent frame is not a table
10886 // pseudo. That means that we'll be at the end of our actual
10887 // non-block-type parent, and the whitespace is OK to collapse out.
10888 // If something is ever inserted after us, it'll find our own parent
10889 // as its parent and if it's something that would care about the
10890 // whitespace it'll want a block parent, so it'll trigger a reframe at
10891 // that point.
10892 // 4) We have no next sibling and our parent frame is a table pseudo.
10893 // Need to reframe.
10894 // All that is predicated on finding the correct next sibling. We might
10895 // have to walk forward along continuations from aFrame to do so. That
10896 // said, in the case when nextSibling is null at this point and aIsAppend
10897 // is true, we know we're in case 3. Furthermore, in that case we don't
10898 // even have to worry about the table pseudo situation; we know our
10899 // parent is not a table pseudo there.
10900 FCItemIterator iter(aItems);
10901 FCItemIterator start(iter);
10902 do {
10903 if (iter.SkipItemsWantingParentType(parentType)) {
10904 break;
10905 }
10907 // iter points to an item that wants a different parent. If it's not
10908 // whitespace, we're done; no more point scanning the list.
10909 if (!iter.item().IsWhitespace(aState)) {
10910 break;
10911 }
10913 if (iter == start) {
10914 // Leading whitespace. How to handle this depends on our
10915 // previous sibling and aFrame. See the long comment above.
10916 nsIFrame* prevSibling = aPrevSibling;
10917 if (!prevSibling) {
10918 // Try to find one after all
10919 nsIFrame* parentPrevCont = aFrame->GetPrevContinuation();
10920 while (parentPrevCont) {
10921 prevSibling = parentPrevCont->GetLastChild(kPrincipalList);
10922 if (prevSibling) {
10923 break;
10924 }
10925 parentPrevCont = parentPrevCont->GetPrevContinuation();
10926 }
10927 };
10928 if (prevSibling) {
10929 if (IsTablePseudo(prevSibling)) {
10930 // need to reframe
10931 break;
10932 }
10933 } else if (IsTablePseudo(aFrame)) {
10934 // need to reframe
10935 break;
10936 }
10937 }
10939 FCItemIterator spaceEndIter(iter);
10940 // Advance spaceEndIter past any whitespace
10941 bool trailingSpaces = spaceEndIter.SkipWhitespace(aState);
10943 bool okToDrop;
10944 if (trailingSpaces) {
10945 // Trailing whitespace. How to handle this depeds on aIsAppend, our
10946 // next sibling and aFrame. See the long comment above.
10947 okToDrop = aIsAppend && !nextSibling;
10948 if (!okToDrop) {
10949 if (!nextSibling) {
10950 // Try to find one after all
10951 nsIFrame* parentNextCont = aFrame->GetNextContinuation();
10952 while (parentNextCont) {
10953 nextSibling = parentNextCont->GetFirstPrincipalChild();
10954 if (nextSibling) {
10955 break;
10956 }
10957 parentNextCont = parentNextCont->GetNextContinuation();
10958 }
10959 }
10961 okToDrop = (nextSibling && !IsTablePseudo(nextSibling)) ||
10962 (!nextSibling && !IsTablePseudo(aFrame));
10963 }
10964 #ifdef DEBUG
10965 else {
10966 NS_ASSERTION(!IsTablePseudo(aFrame), "How did that happen?");
10967 }
10968 #endif
10969 } else {
10970 okToDrop = (spaceEndIter.item().DesiredParentType() == parentType);
10971 }
10973 if (okToDrop) {
10974 iter.DeleteItemsTo(spaceEndIter);
10975 } else {
10976 // We're done: we don't want to drop the whitespace, and it has the
10977 // wrong parent type.
10978 break;
10979 }
10981 // Now loop, since |iter| points to item right after the whitespace we
10982 // removed.
10983 } while (!iter.IsDone());
10984 }
10986 // We might be able to figure out some sort of optimizations here, but they
10987 // would have to depend on having a correct aPrevSibling and a correct next
10988 // sibling. For example, we can probably avoid reframing if none of
10989 // aFrame, aPrevSibling, and next sibling are table pseudo-frames. But it
10990 // doesn't seem worth it to worry about that for now, especially since we
10991 // in fact do not have a reliable aPrevSibling, nor any next sibling, in
10992 // this method.
10994 // aItems might have changed, so recheck the parent type thing. In fact,
10995 // it might be empty, so recheck that too.
10996 if (aItems.IsEmpty()) {
10997 return false;
10998 }
11000 if (!aItems.AllWantParentType(parentType)) {
11001 // Reframing aFrame->GetContent() is good enough, since the content of
11002 // table pseudo-frames is the ancestor content.
11003 RecreateFramesForContent(aFrame->GetContent(), true);
11004 return true;
11005 }
11006 }
11008 // Now we have several cases involving {ib} splits. Put them all in a
11009 // do/while with breaks to take us to the "go and reconstruct" code.
11010 do {
11011 if (IsInlineFrame(aFrame)) {
11012 if (aItems.AreAllItemsInline()) {
11013 // We can just put the kids in.
11014 return false;
11015 }
11017 if (!IsFramePartOfIBSplit(aFrame)) {
11018 // Need to go ahead and reconstruct.
11019 break;
11020 }
11022 // Now we're adding kids including some blocks to an inline part of an
11023 // {ib} split. If we plan to call AppendFrames, and don't have a next
11024 // sibling for the new frames, and our parent is the last continuation of
11025 // the last part of the {ib} split, and the same is true of all our
11026 // ancestor inlines (they have no following continuations and they're the
11027 // last part of their {ib} splits and we'd be adding to the end for all
11028 // of them), then AppendFrames will handle things for us. Bail out in
11029 // that case.
11030 if (aIsAppend && IsSafeToAppendToIBSplitInline(aFrame, nextSibling)) {
11031 return false;
11032 }
11034 // Need to reconstruct.
11035 break;
11036 }
11038 // Now we know we have a block parent. If it's not part of an
11039 // ib-split, we're all set.
11040 if (!IsFramePartOfIBSplit(aFrame)) {
11041 return false;
11042 }
11044 // We're adding some kids to a block part of an {ib} split. If all the
11045 // kids are blocks, we don't need to reconstruct.
11046 if (aItems.AreAllItemsBlock()) {
11047 return false;
11048 }
11050 // We might have some inline kids for this block. Just reconstruct.
11051 break;
11052 } while (0);
11054 // If we don't have a containing block, start with aFrame and look for one.
11055 if (!aContainingBlock) {
11056 aContainingBlock = aFrame;
11057 }
11059 // To find the right block to reframe, just walk up the tree until we find a
11060 // frame that is:
11061 // 1) Not part of an IB split
11062 // 2) Not a pseudo-frame
11063 // 3) Not an inline frame
11064 // We're guaranteed to find one, since nsStyleContext::ApplyStyleFixups
11065 // enforces that the root is display:none, display:table, or display:block.
11066 // Note that walking up "too far" is OK in terms of correctness, even if it
11067 // might be a little inefficient. This is why we walk out of all
11068 // pseudo-frames -- telling which ones are or are not OK to walk out of is
11069 // too hard (and I suspect that we do in fact need to walk out of all of
11070 // them).
11071 while (IsFramePartOfIBSplit(aContainingBlock) ||
11072 aContainingBlock->IsInlineOutside() ||
11073 aContainingBlock->StyleContext()->GetPseudo()) {
11074 aContainingBlock = aContainingBlock->GetParent();
11075 NS_ASSERTION(aContainingBlock,
11076 "Must have non-inline, non-ib-split, non-pseudo frame as "
11077 "root (or child of root, for a table root)!");
11078 }
11080 // Tell parent of the containing block to reformulate the
11081 // entire block. This is painful and definitely not optimal
11082 // but it will *always* get the right answer.
11084 nsIContent *blockContent = aContainingBlock->GetContent();
11085 #ifdef DEBUG
11086 if (gNoisyContentUpdates) {
11087 printf("nsCSSFrameConstructor::WipeContainingBlock: blockContent=%p\n",
11088 static_cast<void*>(blockContent));
11089 }
11090 #endif
11091 RecreateFramesForContent(blockContent, true);
11092 return true;
11093 }
11095 nsresult
11096 nsCSSFrameConstructor::ReframeContainingBlock(nsIFrame* aFrame)
11097 {
11099 #ifdef DEBUG
11100 // ReframeContainingBlock is a NASTY routine, it causes terrible performance problems
11101 // so I want to see when it is happening! Unfortunately, it is happening way to often because
11102 // so much content on the web causes block-in-inline frame situations and we handle them
11103 // very poorly
11104 if (gNoisyContentUpdates) {
11105 printf("nsCSSFrameConstructor::ReframeContainingBlock frame=%p\n",
11106 static_cast<void*>(aFrame));
11107 }
11108 #endif
11110 // XXXbz how exactly would we get here while isReflowing anyway? Should this
11111 // whole test be ifdef DEBUG?
11112 if (mPresShell->IsReflowLocked()) {
11113 // don't ReframeContainingBlock, this will result in a crash
11114 // if we remove a tree that's in reflow - see bug 121368 for testcase
11115 NS_ERROR("Atemptted to nsCSSFrameConstructor::ReframeContainingBlock during a Reflow!!!");
11116 return NS_OK;
11117 }
11119 // Get the first "normal" ancestor of the target frame.
11120 nsIFrame* containingBlock = GetIBContainingBlockFor(aFrame);
11121 if (containingBlock) {
11122 // From here we look for the containing block in case the target
11123 // frame is already a block (which can happen when an inline frame
11124 // wraps some of its content in an anonymous block; see
11125 // ConstructInline)
11127 // NOTE: We used to get the FloatContainingBlock here, but it was often wrong.
11128 // GetIBContainingBlock works much better and provides the correct container in all cases
11129 // so GetFloatContainingBlock(aFrame) has been removed
11131 // And get the containingBlock's content
11132 nsCOMPtr<nsIContent> blockContent = containingBlock->GetContent();
11133 if (blockContent) {
11134 #ifdef DEBUG
11135 if (gNoisyContentUpdates) {
11136 printf(" ==> blockContent=%p\n", static_cast<void*>(blockContent));
11137 }
11138 #endif
11139 return RecreateFramesForContent(blockContent, true);
11140 }
11141 }
11143 // If we get here, we're screwed!
11144 return RecreateFramesForContent(mPresShell->GetDocument()->GetRootElement(),
11145 true);
11146 }
11148 nsresult
11149 nsCSSFrameConstructor::GenerateChildFrames(nsIFrame* aFrame)
11150 {
11151 {
11152 nsAutoScriptBlocker scriptBlocker;
11153 BeginUpdate();
11155 nsFrameItems childItems;
11156 nsFrameConstructorState state(mPresShell, nullptr, nullptr, nullptr);
11157 // We don't have a parent frame with a pending binding constructor here,
11158 // so no need to worry about ordering of the kids' constructors with it.
11159 // Pass null for the PendingBinding.
11160 ProcessChildren(state, aFrame->GetContent(), aFrame->StyleContext(),
11161 aFrame, false, childItems, false,
11162 nullptr);
11164 aFrame->SetInitialChildList(kPrincipalList, childItems);
11166 EndUpdate();
11167 }
11169 #ifdef ACCESSIBILITY
11170 nsAccessibilityService* accService = nsIPresShell::AccService();
11171 if (accService) {
11172 nsIContent* container = aFrame->GetContent();
11173 nsIContent* child = container->GetFirstChild();
11174 if (child) {
11175 accService->ContentRangeInserted(mPresShell, container, child, nullptr);
11176 }
11177 }
11178 #endif
11180 // call XBL constructors after the frames are created
11181 mPresShell->GetDocument()->BindingManager()->ProcessAttachedQueue();
11183 return NS_OK;
11184 }
11186 //////////////////////////////////////////////////////////
11187 // nsCSSFrameConstructor::FrameConstructionItem methods //
11188 //////////////////////////////////////////////////////////
11189 bool
11190 nsCSSFrameConstructor::
11191 FrameConstructionItem::IsWhitespace(nsFrameConstructorState& aState) const
11192 {
11193 NS_PRECONDITION(aState.mCreatingExtraFrames ||
11194 !mContent->GetPrimaryFrame(), "How did that happen?");
11195 if (!mIsText) {
11196 return false;
11197 }
11198 mContent->SetFlags(NS_CREATE_FRAME_IF_NON_WHITESPACE |
11199 NS_REFRAME_IF_WHITESPACE);
11200 return mContent->TextIsOnlyWhitespace();
11201 }
11203 //////////////////////////////////////////////////////////////
11204 // nsCSSFrameConstructor::FrameConstructionItemList methods //
11205 //////////////////////////////////////////////////////////////
11206 void
11207 nsCSSFrameConstructor::FrameConstructionItemList::
11208 AdjustCountsForItem(FrameConstructionItem* aItem, int32_t aDelta)
11209 {
11210 NS_PRECONDITION(aDelta == 1 || aDelta == -1, "Unexpected delta");
11211 mItemCount += aDelta;
11212 if (aItem->mIsAllInline) {
11213 mInlineCount += aDelta;
11214 }
11215 if (aItem->mIsBlock) {
11216 mBlockCount += aDelta;
11217 }
11218 if (aItem->mIsLineParticipant) {
11219 mLineParticipantCount += aDelta;
11220 }
11221 mDesiredParentCounts[aItem->DesiredParentType()] += aDelta;
11222 }
11224 ////////////////////////////////////////////////////////////////////////
11225 // nsCSSFrameConstructor::FrameConstructionItemList::Iterator methods //
11226 ////////////////////////////////////////////////////////////////////////
11227 inline bool
11228 nsCSSFrameConstructor::FrameConstructionItemList::
11229 Iterator::SkipItemsWantingParentType(ParentType aParentType)
11230 {
11231 NS_PRECONDITION(!IsDone(), "Shouldn't be done yet");
11232 while (item().DesiredParentType() == aParentType) {
11233 Next();
11234 if (IsDone()) {
11235 return true;
11236 }
11237 }
11238 return false;
11239 }
11241 bool
11242 nsCSSFrameConstructor::FrameConstructionItem::
11243 NeedsAnonFlexItem(const nsFrameConstructorState& aState)
11244 {
11245 if (mFCData->mBits & FCDATA_IS_LINE_PARTICIPANT) {
11246 // This will be an inline non-replaced box.
11247 return true;
11248 }
11250 if (!(mFCData->mBits & FCDATA_DISALLOW_OUT_OF_FLOW) &&
11251 aState.GetGeometricParent(mStyleContext->StyleDisplay(), nullptr)) {
11252 // We're abspos or fixedpos, which means we'll spawn a placeholder which
11253 // we'll need to wrap in an anonymous flex item. So, we just treat
11254 // _this_ frame as if _it_ needs to be wrapped in an anonymous flex item,
11255 // and then when we spawn the placeholder, it'll end up in the right spot.
11256 return true;
11257 }
11259 return false;
11260 }
11262 inline bool
11263 nsCSSFrameConstructor::FrameConstructionItemList::
11264 Iterator::SkipItemsThatNeedAnonFlexItem(
11265 const nsFrameConstructorState& aState)
11266 {
11267 NS_PRECONDITION(!IsDone(), "Shouldn't be done yet");
11268 while (item().NeedsAnonFlexItem(aState)) {
11269 Next();
11270 if (IsDone()) {
11271 return true;
11272 }
11273 }
11274 return false;
11275 }
11277 inline bool
11278 nsCSSFrameConstructor::FrameConstructionItemList::
11279 Iterator::SkipItemsThatDontNeedAnonFlexItem(
11280 const nsFrameConstructorState& aState)
11281 {
11282 NS_PRECONDITION(!IsDone(), "Shouldn't be done yet");
11283 while (!(item().NeedsAnonFlexItem(aState))) {
11284 Next();
11285 if (IsDone()) {
11286 return true;
11287 }
11288 }
11289 return false;
11290 }
11292 inline bool
11293 nsCSSFrameConstructor::FrameConstructionItemList::
11294 Iterator::SkipWhitespace(nsFrameConstructorState& aState)
11295 {
11296 NS_PRECONDITION(!IsDone(), "Shouldn't be done yet");
11297 NS_PRECONDITION(item().IsWhitespace(aState), "Not pointing to whitespace?");
11298 do {
11299 Next();
11300 if (IsDone()) {
11301 return true;
11302 }
11303 } while (item().IsWhitespace(aState));
11305 return false;
11306 }
11308 void
11309 nsCSSFrameConstructor::FrameConstructionItemList::
11310 Iterator::AppendItemToList(FrameConstructionItemList& aTargetList)
11311 {
11312 NS_ASSERTION(&aTargetList != &mList, "Unexpected call");
11313 NS_PRECONDITION(!IsDone(), "should not be done");
11315 FrameConstructionItem* item = ToItem(mCurrent);
11316 Next();
11317 PR_REMOVE_LINK(item);
11318 PR_APPEND_LINK(item, &aTargetList.mItems);
11320 mList.AdjustCountsForItem(item, -1);
11321 aTargetList.AdjustCountsForItem(item, 1);
11322 }
11324 void
11325 nsCSSFrameConstructor::FrameConstructionItemList::
11326 Iterator::AppendItemsToList(const Iterator& aEnd,
11327 FrameConstructionItemList& aTargetList)
11328 {
11329 NS_ASSERTION(&aTargetList != &mList, "Unexpected call");
11330 NS_PRECONDITION(mEnd == aEnd.mEnd, "end iterator for some other list?");
11332 // We can't just move our guts to the other list if it already has
11333 // some information or if we're not moving our entire list.
11334 if (!AtStart() || !aEnd.IsDone() || !aTargetList.IsEmpty() ||
11335 !aTargetList.mUndisplayedItems.IsEmpty()) {
11336 do {
11337 AppendItemToList(aTargetList);
11338 } while (*this != aEnd);
11339 return;
11340 }
11342 // move over the list of items
11343 PR_INSERT_AFTER(&aTargetList.mItems, &mList.mItems);
11344 // Need to init when we remove to makd ~FrameConstructionItemList work right.
11345 PR_REMOVE_AND_INIT_LINK(&mList.mItems);
11347 // Copy over the various counters
11348 aTargetList.mInlineCount = mList.mInlineCount;
11349 aTargetList.mBlockCount = mList.mBlockCount;
11350 aTargetList.mLineParticipantCount = mList.mLineParticipantCount;
11351 aTargetList.mItemCount = mList.mItemCount;
11352 memcpy(aTargetList.mDesiredParentCounts, mList.mDesiredParentCounts,
11353 sizeof(aTargetList.mDesiredParentCounts));
11355 // Swap out undisplayed item arrays, before we nuke the array on our end
11356 aTargetList.mUndisplayedItems.SwapElements(mList.mUndisplayedItems);
11358 // reset mList
11359 mList.~FrameConstructionItemList();
11360 new (&mList) FrameConstructionItemList();
11362 // Point ourselves to aEnd, as advertised
11363 mCurrent = mEnd = &mList.mItems;
11364 NS_POSTCONDITION(*this == aEnd, "How did that happen?");
11365 }
11367 void
11368 nsCSSFrameConstructor::FrameConstructionItemList::
11369 Iterator::InsertItem(FrameConstructionItem* aItem)
11370 {
11371 // Just insert the item before us. There's no magic here.
11372 PR_INSERT_BEFORE(aItem, mCurrent);
11373 mList.AdjustCountsForItem(aItem, 1);
11375 NS_POSTCONDITION(PR_NEXT_LINK(aItem) == mCurrent, "How did that happen?");
11376 }
11378 void
11379 nsCSSFrameConstructor::FrameConstructionItemList::
11380 Iterator::DeleteItemsTo(const Iterator& aEnd)
11381 {
11382 NS_PRECONDITION(mEnd == aEnd.mEnd, "end iterator for some other list?");
11383 NS_PRECONDITION(*this != aEnd, "Shouldn't be at aEnd yet");
11385 do {
11386 NS_ASSERTION(!IsDone(), "Ran off end of list?");
11387 FrameConstructionItem* item = ToItem(mCurrent);
11388 Next();
11389 PR_REMOVE_LINK(item);
11390 mList.AdjustCountsForItem(item, -1);
11391 delete item;
11392 } while (*this != aEnd);
11393 }