Bug 1839338 [wpt PR 40636] - Clone encoded WebRTC audio frame when deserializing...
[gecko.git] / layout / base / nsCSSFrameConstructor.cpp
blob2508c8e141796db4cedf0b54b40fc3cd7f18336b
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
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
12 #include "nsCSSFrameConstructor.h"
14 #include "mozilla/AutoRestore.h"
15 #include "mozilla/ComputedStyleInlines.h"
16 #include "mozilla/DebugOnly.h"
17 #include "mozilla/ErrorResult.h"
18 #include "mozilla/ManualNAC.h"
19 #include "mozilla/dom/BindContext.h"
20 #include "mozilla/dom/BrowsingContext.h"
21 #include "mozilla/dom/GeneratedImageContent.h"
22 #include "mozilla/dom/HTMLSelectElement.h"
23 #include "mozilla/dom/HTMLSharedListElement.h"
24 #include "mozilla/dom/HTMLSummaryElement.h"
25 #include "mozilla/Likely.h"
26 #include "mozilla/LinkedList.h"
27 #include "mozilla/MemoryReporting.h"
28 #include "mozilla/PresShell.h"
29 #include "mozilla/PresShellInlines.h"
30 #include "mozilla/PrintedSheetFrame.h"
31 #include "mozilla/ProfilerLabels.h"
32 #include "mozilla/ProfilerMarkers.h"
33 #include "mozilla/ScopeExit.h"
34 #include "mozilla/ServoBindings.h"
35 #include "mozilla/ServoStyleSetInlines.h"
36 #include "mozilla/StaticPrefs_browser.h"
37 #include "mozilla/StaticPrefs_layout.h"
38 #include "mozilla/StaticPrefs_mathml.h"
39 #include "mozilla/Unused.h"
40 #include "RetainedDisplayListBuilder.h"
41 #include "nsAbsoluteContainingBlock.h"
42 #include "nsCSSPseudoElements.h"
43 #include "nsCheckboxRadioFrame.h"
44 #include "nsCRT.h"
45 #include "nsAtom.h"
46 #include "nsIFrameInlines.h"
47 #include "nsGkAtoms.h"
48 #include "nsPresContext.h"
49 #include "mozilla/dom/Document.h"
50 #include "mozilla/dom/DocumentInlines.h"
51 #include "nsTableFrame.h"
52 #include "nsTableColFrame.h"
53 #include "nsTableRowFrame.h"
54 #include "nsTableCellFrame.h"
55 #include "nsFileControlFrame.h"
56 #include "nsHTMLParts.h"
57 #include "nsUnicharUtils.h"
58 #include "nsViewManager.h"
59 #include "nsStyleConsts.h"
60 #include "nsXULElement.h"
61 #include "nsContainerFrame.h"
62 #include "nsNameSpaceManager.h"
63 #include "nsComboboxControlFrame.h"
64 #include "nsListControlFrame.h"
65 #include "nsPlaceholderFrame.h"
66 #include "nsTableRowGroupFrame.h"
67 #include "nsIFormControl.h"
68 #include "nsCSSAnonBoxes.h"
69 #include "nsTextFragment.h"
70 #include "nsIAnonymousContentCreator.h"
71 #include "nsContentUtils.h"
72 #include "nsIScriptError.h"
73 #ifdef XP_MACOSX
74 # include "nsIDocShell.h"
75 #endif
76 #include "ChildIterator.h"
77 #include "nsError.h"
78 #include "nsLayoutUtils.h"
79 #include "nsFlexContainerFrame.h"
80 #include "nsGridContainerFrame.h"
81 #include "RubyUtils.h"
82 #include "nsRubyFrame.h"
83 #include "nsRubyBaseFrame.h"
84 #include "nsRubyBaseContainerFrame.h"
85 #include "nsRubyTextFrame.h"
86 #include "nsRubyTextContainerFrame.h"
87 #include "nsImageFrame.h"
88 #include "nsIObjectLoadingContent.h"
89 #include "nsTArray.h"
90 #include "mozilla/dom/CharacterData.h"
91 #include "mozilla/dom/Element.h"
92 #include "mozilla/dom/ElementInlines.h"
93 #include "mozilla/dom/HTMLInputElement.h"
94 #include "nsAutoLayoutPhase.h"
95 #include "nsStyleStructInlines.h"
96 #include "nsPageContentFrame.h"
97 #include "mozilla/RestyleManager.h"
98 #include "StickyScrollContainer.h"
99 #include "nsFieldSetFrame.h"
100 #include "nsInlineFrame.h"
101 #include "nsBlockFrame.h"
102 #include "nsCanvasFrame.h"
103 #include "nsFirstLetterFrame.h"
104 #include "nsGfxScrollFrame.h"
105 #include "nsPageFrame.h"
106 #include "nsPageSequenceFrame.h"
107 #include "nsTableWrapperFrame.h"
108 #include "nsIScrollableFrame.h"
109 #include "nsBackdropFrame.h"
110 #include "nsTransitionManager.h"
112 #include "nsIPopupContainer.h"
113 #ifdef ACCESSIBILITY
114 # include "nsAccessibilityService.h"
115 #endif
117 #undef NOISY_FIRST_LETTER
119 #include "nsMathMLParts.h"
120 #include "mozilla/dom/SVGFilters.h"
121 #include "mozilla/dom/SVGTests.h"
122 #include "mozilla/SVGGradientFrame.h"
124 #include "nsRefreshDriver.h"
125 #include "nsTextNode.h"
126 #include "ActiveLayerTracker.h"
128 using namespace mozilla;
129 using namespace mozilla::dom;
131 nsIFrame* NS_NewHTMLCanvasFrame(PresShell* aPresShell, ComputedStyle* aStyle);
133 nsIFrame* NS_NewHTMLVideoFrame(PresShell* aPresShell, ComputedStyle* aStyle);
135 nsContainerFrame* NS_NewSVGOuterSVGFrame(PresShell* aPresShell,
136 ComputedStyle* aStyle);
137 nsContainerFrame* NS_NewSVGOuterSVGAnonChildFrame(PresShell* aPresShell,
138 ComputedStyle* aStyle);
139 nsIFrame* NS_NewSVGInnerSVGFrame(PresShell* aPresShell, ComputedStyle* aStyle);
140 nsIFrame* NS_NewSVGGeometryFrame(PresShell* aPresShell, ComputedStyle* aStyle);
141 nsIFrame* NS_NewSVGGFrame(PresShell* aPresShell, ComputedStyle* aStyle);
142 nsContainerFrame* NS_NewSVGForeignObjectFrame(PresShell* aPresShell,
143 ComputedStyle* aStyle);
144 nsIFrame* NS_NewSVGAFrame(PresShell* aPresShell, ComputedStyle* aStyle);
145 nsIFrame* NS_NewSVGSwitchFrame(PresShell* aPresShell, ComputedStyle* aStyle);
146 nsIFrame* NS_NewSVGSymbolFrame(PresShell* aPresShell, ComputedStyle* aStyle);
147 nsIFrame* NS_NewSVGTextFrame(PresShell* aPresShell, ComputedStyle* aStyle);
148 nsIFrame* NS_NewSVGContainerFrame(PresShell* aPresShell, ComputedStyle* aStyle);
149 nsIFrame* NS_NewSVGUseFrame(PresShell* aPresShell, ComputedStyle* aStyle);
150 nsIFrame* NS_NewSVGViewFrame(PresShell* aPresShell, ComputedStyle* aStyle);
151 extern nsIFrame* NS_NewSVGLinearGradientFrame(PresShell* aPresShell,
152 ComputedStyle* aStyle);
153 extern nsIFrame* NS_NewSVGRadialGradientFrame(PresShell* aPresShell,
154 ComputedStyle* aStyle);
155 extern nsIFrame* NS_NewSVGStopFrame(PresShell* aPresShell,
156 ComputedStyle* aStyle);
157 nsContainerFrame* NS_NewSVGMarkerFrame(PresShell* aPresShell,
158 ComputedStyle* aStyle);
159 nsContainerFrame* NS_NewSVGMarkerAnonChildFrame(PresShell* aPresShell,
160 ComputedStyle* aStyle);
161 extern nsIFrame* NS_NewSVGImageFrame(PresShell* aPresShell,
162 ComputedStyle* aStyle);
163 nsIFrame* NS_NewSVGClipPathFrame(PresShell* aPresShell, ComputedStyle* aStyle);
164 nsIFrame* NS_NewSVGFilterFrame(PresShell* aPresShell, ComputedStyle* aStyle);
165 nsIFrame* NS_NewSVGPatternFrame(PresShell* aPresShell, ComputedStyle* aStyle);
166 nsIFrame* NS_NewSVGMaskFrame(PresShell* aPresShell, ComputedStyle* aStyle);
167 nsIFrame* NS_NewSVGFEContainerFrame(PresShell* aPresShell,
168 ComputedStyle* aStyle);
169 nsIFrame* NS_NewSVGFELeafFrame(PresShell* aPresShell, ComputedStyle* aStyle);
170 nsIFrame* NS_NewSVGFEImageFrame(PresShell* aPresShell, ComputedStyle* aStyle);
171 nsIFrame* NS_NewSVGFEUnstyledLeafFrame(PresShell* aPresShell,
172 ComputedStyle* aStyle);
173 nsIFrame* NS_NewFileControlLabelFrame(PresShell*, ComputedStyle*);
174 nsIFrame* NS_NewMiddleCroppingLabelFrame(PresShell*, ComputedStyle*);
176 #include "mozilla/dom/NodeInfo.h"
177 #include "prenv.h"
178 #include "nsNodeInfoManager.h"
179 #include "nsContentCreatorFunctions.h"
181 #ifdef DEBUG
182 // Set the environment variable GECKO_FRAMECTOR_DEBUG_FLAGS to one or
183 // more of the following flags (comma separated) for handy debug
184 // output.
185 static bool gNoisyContentUpdates = false;
186 static bool gReallyNoisyContentUpdates = false;
187 static bool gNoisyInlineConstruction = false;
189 struct FrameCtorDebugFlags {
190 const char* name;
191 bool* on;
194 static FrameCtorDebugFlags gFlags[] = {
195 {"content-updates", &gNoisyContentUpdates},
196 {"really-noisy-content-updates", &gReallyNoisyContentUpdates},
197 {"noisy-inline", &gNoisyInlineConstruction}};
199 # define NUM_DEBUG_FLAGS (sizeof(gFlags) / sizeof(gFlags[0]))
200 #endif
202 //------------------------------------------------------------------
204 nsIFrame* NS_NewLeafBoxFrame(PresShell* aPresShell, ComputedStyle* aStyle);
206 nsIFrame* NS_NewRangeFrame(PresShell* aPresShell, ComputedStyle* aStyle);
208 nsIFrame* NS_NewTextBoxFrame(PresShell* aPresShell, ComputedStyle* aStyle);
210 nsIFrame* NS_NewSplitterFrame(PresShell* aPresShell, ComputedStyle* aStyle);
212 nsIFrame* NS_NewMenuPopupFrame(PresShell* aPresShell, ComputedStyle* aStyle);
214 nsIFrame* NS_NewTreeBodyFrame(PresShell* aPresShell, ComputedStyle* aStyle);
216 nsHTMLScrollFrame* NS_NewHTMLScrollFrame(PresShell* aPresShell,
217 ComputedStyle* aStyle, bool aIsRoot);
219 nsIFrame* NS_NewSliderFrame(PresShell* aPresShell, ComputedStyle* aStyle);
221 nsIFrame* NS_NewScrollbarFrame(PresShell* aPresShell, ComputedStyle* aStyle);
223 nsIFrame* NS_NewScrollbarButtonFrame(PresShell*, ComputedStyle*);
224 nsIFrame* NS_NewSimpleXULLeafFrame(PresShell*, ComputedStyle*);
226 nsIFrame* NS_NewXULImageFrame(PresShell*, ComputedStyle*);
227 nsIFrame* NS_NewImageFrameForContentProperty(PresShell*, ComputedStyle*);
228 nsIFrame* NS_NewImageFrameForGeneratedContentIndex(PresShell*, ComputedStyle*);
229 nsIFrame* NS_NewImageFrameForListStyleImage(PresShell*, ComputedStyle*);
231 // Returns true if aFrame is an anonymous flex/grid item.
232 static inline bool IsAnonymousItem(const nsIFrame* aFrame) {
233 return aFrame->Style()->GetPseudoType() == PseudoStyleType::anonymousItem;
236 // Returns true IFF the given nsIFrame is a nsFlexContainerFrame and represents
237 // a -webkit-{inline-}box container.
238 static inline bool IsFlexContainerForLegacyWebKitBox(const nsIFrame* aFrame) {
239 return aFrame->IsFlexContainerFrame() &&
240 aFrame->HasAnyStateBits(NS_STATE_FLEX_IS_EMULATING_LEGACY_WEBKIT_BOX);
243 #if DEBUG
244 static void AssertAnonymousFlexOrGridItemParent(const nsIFrame* aChild,
245 const nsIFrame* aParent) {
246 MOZ_ASSERT(IsAnonymousItem(aChild), "expected an anonymous item child frame");
247 MOZ_ASSERT(aParent, "expected a parent frame");
248 MOZ_ASSERT(aParent->IsFlexOrGridContainer(),
249 "anonymous items should only exist as children of flex/grid "
250 "container frames");
252 #else
253 # define AssertAnonymousFlexOrGridItemParent(x, y) PR_BEGIN_MACRO PR_END_MACRO
254 #endif
256 #define ToCreationFunc(_func) \
257 [](PresShell* aPs, ComputedStyle* aStyle) -> nsIFrame* { \
258 return _func(aPs, aStyle); \
262 * True if aFrame is an actual inline frame in the sense of non-replaced
263 * display:inline CSS boxes. In other words, it can be affected by {ib}
264 * splitting and can contain first-letter frames. Basically, this is either an
265 * inline frame (positioned or otherwise) or an line frame (this last because
266 * it can contain first-letter and because inserting blocks in the middle of it
267 * needs to terminate it).
269 static bool IsInlineFrame(const nsIFrame* aFrame) {
270 return aFrame->IsFrameOfType(nsIFrame::eLineParticipant);
274 * True for display: contents elements.
276 static inline bool IsDisplayContents(const Element* aElement) {
277 return aElement->IsDisplayContents();
280 static inline bool IsDisplayContents(const nsIContent* aContent) {
281 return aContent->IsElement() && IsDisplayContents(aContent->AsElement());
285 * True if aFrame is an instance of an SVG frame class or is an inline/block
286 * frame being used for SVG text.
288 static bool IsFrameForSVG(const nsIFrame* aFrame) {
289 return aFrame->IsFrameOfType(nsIFrame::eSVG) || aFrame->IsInSVGTextSubtree();
292 static bool IsLastContinuationForColumnContent(const nsIFrame* aFrame) {
293 MOZ_ASSERT(aFrame);
294 return aFrame->Style()->GetPseudoType() == PseudoStyleType::columnContent &&
295 !aFrame->GetNextContinuation();
299 * Returns true iff aFrame explicitly prevents its descendants from floating
300 * (at least, down to the level of descendants which themselves are
301 * float-containing blocks -- those will manage the floating status of any
302 * lower-level descendents inside them, of course).
304 static bool ShouldSuppressFloatingOfDescendants(nsIFrame* aFrame) {
305 return aFrame->IsFlexOrGridContainer() ||
306 aFrame->IsFrameOfType(nsIFrame::eMathML);
309 // Return true if column-span descendants should be suppressed under aFrame's
310 // subtree (until a multi-column container re-establishing a block formatting
311 // context). Basically, this is testing whether aFrame establishes a new block
312 // formatting context or not.
313 static bool ShouldSuppressColumnSpanDescendants(nsIFrame* aFrame) {
314 if (aFrame->Style()->GetPseudoType() == PseudoStyleType::columnContent) {
315 // Never suppress column-span under ::-moz-column-content frames.
316 return false;
319 if (aFrame->IsInlineFrame()) {
320 // Allow inline frames to have column-span block children.
321 return false;
324 if (!aFrame->IsBlockFrameOrSubclass() ||
325 aFrame->HasAnyStateBits(NS_BLOCK_FLOAT_MGR | NS_FRAME_OUT_OF_FLOW) ||
326 aFrame->IsFixedPosContainingBlock()) {
327 // Need to suppress column-span if we:
328 // - Are a different block formatting context,
329 // - Are an out-of-flow frame, OR
330 // - Establish a containing block for fixed-position descendants
332 // For example, the children of a column-span never need to be further
333 // processed even if there is a nested column-span child. Because a
334 // column-span always creates its own block formatting context, a nested
335 // column-span child won't be in the same block formatting context with the
336 // nearest multi-column ancestor. This is the same case as if the
337 // column-span is outside of a multi-column hierarchy.
338 return true;
341 return false;
344 // Reparent a frame into a wrapper frame that is a child of its old parent.
345 static void ReparentFrame(RestyleManager* aRestyleManager,
346 nsContainerFrame* aNewParentFrame, nsIFrame* aFrame,
347 bool aForceStyleReparent) {
348 aFrame->SetParent(aNewParentFrame);
349 // We reparent frames for two reasons: to put them inside ::first-line, and to
350 // put them inside some wrapper anonymous boxes.
351 if (aForceStyleReparent) {
352 aRestyleManager->ReparentComputedStyleForFirstLine(aFrame);
356 static void ReparentFrames(nsCSSFrameConstructor* aFrameConstructor,
357 nsContainerFrame* aNewParentFrame,
358 const nsFrameList& aFrameList,
359 bool aForceStyleReparent) {
360 RestyleManager* restyleManager = aFrameConstructor->RestyleManager();
361 for (nsIFrame* f : aFrameList) {
362 ReparentFrame(restyleManager, aNewParentFrame, f, aForceStyleReparent);
366 //----------------------------------------------------------------------
368 // When inline frames get weird and have block frames in them, we
369 // annotate them to help us respond to incremental content changes
370 // more easily.
372 static inline bool IsFramePartOfIBSplit(nsIFrame* aFrame) {
373 bool result = aFrame->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT);
374 MOZ_ASSERT(!result || static_cast<nsBlockFrame*>(do_QueryFrame(aFrame)) ||
375 static_cast<nsInlineFrame*>(do_QueryFrame(aFrame)),
376 "only block/inline frames can have NS_FRAME_PART_OF_IBSPLIT");
377 return result;
380 static nsContainerFrame* GetIBSplitSibling(nsIFrame* aFrame) {
381 MOZ_ASSERT(IsFramePartOfIBSplit(aFrame), "Shouldn't call this");
383 // We only store the "ib-split sibling" annotation with the first
384 // frame in the continuation chain. Walk back to find that frame now.
385 return aFrame->FirstContinuation()->GetProperty(nsIFrame::IBSplitSibling());
388 static nsContainerFrame* GetIBSplitPrevSibling(nsIFrame* aFrame) {
389 MOZ_ASSERT(IsFramePartOfIBSplit(aFrame), "Shouldn't call this");
391 // We only store the ib-split sibling annotation with the first
392 // frame in the continuation chain. Walk back to find that frame now.
393 return aFrame->FirstContinuation()->GetProperty(
394 nsIFrame::IBSplitPrevSibling());
397 static nsContainerFrame* GetLastIBSplitSibling(nsIFrame* aFrame) {
398 for (nsIFrame *frame = aFrame, *next;; frame = next) {
399 next = GetIBSplitSibling(frame);
400 if (!next) {
401 return static_cast<nsContainerFrame*>(frame);
404 MOZ_ASSERT_UNREACHABLE("unreachable code");
405 return nullptr;
408 static void SetFrameIsIBSplit(nsContainerFrame* aFrame,
409 nsContainerFrame* aIBSplitSibling) {
410 MOZ_ASSERT(aFrame, "bad args!");
412 // We should be the only continuation
413 NS_ASSERTION(!aFrame->GetPrevContinuation(),
414 "assigning ib-split sibling to other than first continuation!");
415 NS_ASSERTION(!aFrame->GetNextContinuation() ||
416 IsFramePartOfIBSplit(aFrame->GetNextContinuation()),
417 "should have no non-ib-split continuations here");
419 // Mark the frame as ib-split.
420 aFrame->AddStateBits(NS_FRAME_PART_OF_IBSPLIT);
422 if (aIBSplitSibling) {
423 NS_ASSERTION(!aIBSplitSibling->GetPrevContinuation(),
424 "assigning something other than the first continuation as the "
425 "ib-split sibling");
427 // Store the ib-split sibling (if we were given one) with the
428 // first frame in the flow.
429 aFrame->SetProperty(nsIFrame::IBSplitSibling(), aIBSplitSibling);
430 aIBSplitSibling->SetProperty(nsIFrame::IBSplitPrevSibling(), aFrame);
434 static nsIFrame* GetIBContainingBlockFor(nsIFrame* aFrame) {
435 MOZ_ASSERT(
436 IsFramePartOfIBSplit(aFrame),
437 "GetIBContainingBlockFor() should only be called on known IB frames");
439 // Get the first "normal" ancestor of the target frame.
440 nsIFrame* parentFrame;
441 do {
442 parentFrame = aFrame->GetParent();
444 if (!parentFrame) {
445 NS_ERROR("no unsplit block frame in IB hierarchy");
446 return aFrame;
449 // Note that we ignore non-ib-split frames which have a pseudo on their
450 // ComputedStyle -- they're not the frames we're looking for! In
451 // particular, they may be hiding a real parent that _is_ in an ib-split.
452 if (!IsFramePartOfIBSplit(parentFrame) &&
453 !parentFrame->Style()->IsPseudoOrAnonBox())
454 break;
456 aFrame = parentFrame;
457 } while (1);
459 // post-conditions
460 NS_ASSERTION(parentFrame,
461 "no normal ancestor found for ib-split frame "
462 "in GetIBContainingBlockFor");
463 NS_ASSERTION(parentFrame != aFrame,
464 "parentFrame is actually the child frame - bogus reslt");
466 return parentFrame;
469 // Find the multicol containing block suitable for reframing.
471 // Note: this function may not return a ColumnSetWrapperFrame. For example, if
472 // the multicol containing block has "overflow:scroll" style, HTMLScrollFrame is
473 // returned because ColumnSetWrapperFrame is the scrolled frame which has the
474 // -moz-scrolled-content pseudo style. We may walk up "too far", but in terms of
475 // correctness of reframing, it's OK.
476 static nsContainerFrame* GetMultiColumnContainingBlockFor(nsIFrame* aFrame) {
477 MOZ_ASSERT(aFrame->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR),
478 "Should only be called if the frame has a multi-column ancestor!");
480 nsContainerFrame* current = aFrame->GetParent();
481 while (current &&
482 (current->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR) ||
483 current->Style()->IsPseudoOrAnonBox())) {
484 current = current->GetParent();
487 MOZ_ASSERT(current,
488 "No multicol containing block in a valid column hierarchy?");
490 return current;
493 // This is a bit slow, but sometimes we need it.
494 static bool ParentIsWrapperAnonBox(nsIFrame* aParent) {
495 nsIFrame* maybeAnonBox = aParent;
496 if (maybeAnonBox->Style()->GetPseudoType() == PseudoStyleType::cellContent) {
497 // The thing that would maybe be a wrapper anon box is the cell.
498 maybeAnonBox = maybeAnonBox->GetParent();
500 return maybeAnonBox->Style()->IsWrapperAnonBox();
503 static bool InsertSeparatorBeforeAccessKey() {
504 static bool sInitialized = false;
505 static bool sValue = false;
506 if (!sInitialized) {
507 sInitialized = true;
509 const char* prefName = "intl.menuitems.insertseparatorbeforeaccesskeys";
510 nsAutoString val;
511 Preferences::GetLocalizedString(prefName, val);
512 sValue = val.EqualsLiteral("true");
514 return sValue;
517 static bool AlwaysAppendAccessKey() {
518 static bool sInitialized = false;
519 static bool sValue = false;
520 if (!sInitialized) {
521 sInitialized = true;
522 const char* prefName = "intl.menuitems.alwaysappendaccesskeys";
523 nsAutoString val;
524 Preferences::GetLocalizedString(prefName, val);
525 sValue = val.EqualsLiteral("true");
527 return sValue;
530 //----------------------------------------------------------------------
532 // Block/inline frame construction logic. We maintain a few invariants here:
534 // 1. Block frames contain block and inline frames.
536 // 2. Inline frames only contain inline frames. If an inline parent has a block
537 // child then the block child is migrated upward until it lands in a block
538 // parent (the inline frames containing block is where it will end up).
540 inline void SetInitialSingleChild(nsContainerFrame* aParent, nsIFrame* aFrame) {
541 MOZ_ASSERT(!aFrame->GetNextSibling(), "Should be using a frame list");
542 aParent->SetInitialChildList(FrameChildListID::Principal,
543 nsFrameList(aFrame, aFrame));
546 // -----------------------------------------------------------
548 // Structure used when constructing formatting object trees. Contains
549 // state information needed for absolutely positioned elements
550 namespace mozilla {
551 struct AbsoluteFrameList final : public nsFrameList {
552 // Containing block for absolutely positioned elements.
553 nsContainerFrame* mContainingBlock;
555 explicit AbsoluteFrameList(nsContainerFrame* aContainingBlock = nullptr)
556 : mContainingBlock(aContainingBlock) {}
558 // Transfer frames in aOther to this list. aOther becomes empty after this
559 // operation.
560 AbsoluteFrameList(AbsoluteFrameList&& aOther) = default;
561 AbsoluteFrameList& operator=(AbsoluteFrameList&& aOther) = default;
563 #ifdef DEBUG
564 // XXXbz Does this need a debug-only assignment operator that nulls out the
565 // childList in the AbsoluteFrameList we're copying? Introducing a difference
566 // between debug and non-debug behavior seems bad, so I guess not...
567 ~AbsoluteFrameList() {
568 NS_ASSERTION(!FirstChild(),
569 "Dangling child list. Someone forgot to insert it?");
571 #endif
573 } // namespace mozilla
575 // -----------------------------------------------------------
577 // Structure for saving the existing state when pushing/poping containing
578 // blocks. The destructor restores the state to its previous state
579 class MOZ_STACK_CLASS nsFrameConstructorSaveState {
580 public:
581 ~nsFrameConstructorSaveState();
583 private:
584 // Pointer to struct whose data we save/restore.
585 AbsoluteFrameList* mList = nullptr;
587 // Copy of original frame list. This can be the original absolute list or a
588 // float list. If we're saving the abs-pos state for a transformed element,
589 // i.e. when mSavedFixedPosIsAbsPos is true, this is the original fixed list.
590 AbsoluteFrameList mSavedList;
592 // The name of the child list in which our frames would belong.
593 mozilla::FrameChildListID mChildListID = FrameChildListID::Principal;
594 nsFrameConstructorState* mState = nullptr;
596 // Only used when saving an absolute list. See the description of
597 // nsFrameConstructorState::mFixedPosIsAbsPos for its meaning.
598 bool mSavedFixedPosIsAbsPos = false;
600 friend class nsFrameConstructorState;
603 // Structure used for maintaining state information during the
604 // frame construction process
605 class MOZ_STACK_CLASS nsFrameConstructorState {
606 public:
607 nsPresContext* mPresContext;
608 PresShell* mPresShell;
609 nsFrameManager* mFrameManager;
611 // Containing block information for out-of-flow frames.
612 AbsoluteFrameList mFixedList;
613 AbsoluteFrameList mAbsoluteList;
614 AbsoluteFrameList mFloatedList;
615 // The containing block of a frame in the top layer is defined by the
616 // spec: fixed-positioned frames are children of the viewport frame,
617 // and absolutely-positioned frames are children of the initial
618 // containing block. They would not be caught by any other containing
619 // block, e.g. frames with transform or filter.
620 AbsoluteFrameList mTopLayerFixedList;
621 AbsoluteFrameList mTopLayerAbsoluteList;
623 // What `page: auto` resolves to. This is the used page-name of the parent
624 // frame. Updated by AutoFrameConstructionPageName.
625 const nsAtom* mAutoPageNameValue;
627 nsCOMPtr<nsILayoutHistoryState> mFrameState;
628 // These bits will be added to the state bits of any frame we construct
629 // using this state.
630 nsFrameState mAdditionalStateBits;
632 // When working with transform / filter properties, we want to hook the
633 // abs-pos and fixed-pos lists together, since such elements are fixed-pos
634 // containing blocks.
636 // Similarly when restricting absolute positioning (for e.g. mathml).
638 // This flag determines whether or not we want to wire the fixed-pos and
639 // abs-pos lists together.
640 bool mFixedPosIsAbsPos;
642 // If false (which is the default) then call SetPrimaryFrame() as needed
643 // during frame construction. If true, don't make any SetPrimaryFrame()
644 // calls, except for generated content which doesn't have a primary frame
645 // yet. The mCreatingExtraFrames == true mode is meant to be used for
646 // construction of random "extra" frames for elements via normal frame
647 // construction APIs (e.g. replication of things across pages in paginated
648 // mode).
649 bool mCreatingExtraFrames;
651 // This keeps track of whether we have found a "rendered legend" for
652 // the current FieldSetFrame.
653 bool mHasRenderedLegend;
655 nsTArray<RefPtr<nsIContent>> mGeneratedContentWithInitializer;
657 #ifdef DEBUG
658 // Record the float containing block candidate passed into
659 // MaybePushFloatContainingBlock() to keep track that we've call the method to
660 // handle the float CB scope before processing the CB's children. It is reset
661 // in ConstructFramesFromItemList().
662 nsContainerFrame* mFloatCBCandidate = nullptr;
663 #endif
665 // Constructor
666 // Use the passed-in history state.
667 nsFrameConstructorState(
668 PresShell* aPresShell, nsContainerFrame* aFixedContainingBlock,
669 nsContainerFrame* aAbsoluteContainingBlock,
670 nsContainerFrame* aFloatContainingBlock,
671 already_AddRefed<nsILayoutHistoryState> aHistoryState);
672 // Get the history state from the pres context's pres shell.
673 nsFrameConstructorState(PresShell* aPresShell,
674 nsContainerFrame* aFixedContainingBlock,
675 nsContainerFrame* aAbsoluteContainingBlock,
676 nsContainerFrame* aFloatContainingBlock);
678 ~nsFrameConstructorState();
680 // Process the frame insertions for all the out-of-flow nsAbsoluteItems.
681 void ProcessFrameInsertionsForAllLists();
683 // Function to push the existing absolute containing block state and
684 // create a new scope. Code that uses this function should get matching
685 // logic in GetAbsoluteContainingBlock.
686 // Also makes aNewAbsoluteContainingBlock the containing block for
687 // fixed-pos elements if necessary.
688 // aPositionedFrame is the frame whose style actually makes
689 // aNewAbsoluteContainingBlock a containing block. E.g. for a scrollable
690 // element aPositionedFrame is the element's primary frame and
691 // aNewAbsoluteContainingBlock is the scrolled frame.
692 void PushAbsoluteContainingBlock(
693 nsContainerFrame* aNewAbsoluteContainingBlock, nsIFrame* aPositionedFrame,
694 nsFrameConstructorSaveState& aSaveState);
696 // Function to forbid floats descendants under aFloatCBCandidate, or open a
697 // new float containing block scope for aFloatCBCandidate. The current
698 // state is saved in aSaveState if a new scope is pushed.
699 void MaybePushFloatContainingBlock(nsContainerFrame* aFloatCBCandidate,
700 nsFrameConstructorSaveState& aSaveState);
702 // Helper function for MaybePushFloatContainingBlock().
703 void PushFloatContainingBlock(nsContainerFrame* aNewFloatContainingBlock,
704 nsFrameConstructorSaveState& aSaveState);
706 // Function to return the proper geometric parent for a frame with display
707 // struct given by aStyleDisplay and parent's frame given by
708 // aContentParentFrame.
709 nsContainerFrame* GetGeometricParent(
710 const nsStyleDisplay& aStyleDisplay,
711 nsContainerFrame* aContentParentFrame) const;
713 // Collect absolute frames in mAbsoluteList which are proper descendants
714 // of aNewParent, and reparent them to aNewParent.
716 // Note: This function does something unusual that moves absolute items
717 // after their frames are constructed under a column hierarchy which has
718 // column-span elements. Do not use this if you're not dealing with
719 // columns.
720 void ReparentAbsoluteItems(nsContainerFrame* aNewParent);
722 // Collect floats in mFloatedList which are proper descendants of aNewParent,
723 // and reparent them to aNewParent.
725 // Note: This function does something unusual that moves floats after their
726 // frames are constructed under a column hierarchy which has column-span
727 // elements. Do not use this if you're not dealing with columns.
728 void ReparentFloats(nsContainerFrame* aNewParent);
731 * Function to add a new frame to the right frame list. This MUST be called
732 * on frames before their children have been processed if the frames might
733 * conceivably be out-of-flow; otherwise cleanup in error cases won't work
734 * right. Also, this MUST be called on frames after they have been
735 * initialized.
736 * @param aNewFrame the frame to add
737 * @param aFrameList the list to add in-flow frames to
738 * @param aContent the content pointer for aNewFrame
739 * @param aParentFrame the parent frame for the content if it were in-flow
740 * @param aCanBePositioned pass false if the frame isn't allowed to be
741 * positioned
742 * @param aCanBeFloated pass false if the frame isn't allowed to be
743 * floated
745 void AddChild(nsIFrame* aNewFrame, nsFrameList& aFrameList,
746 nsIContent* aContent, nsContainerFrame* aParentFrame,
747 bool aCanBePositioned = true, bool aCanBeFloated = true,
748 bool aInsertAfter = false,
749 nsIFrame* aInsertAfterFrame = nullptr);
752 * Function to return the fixed-pos element list. Normally this will just
753 * hand back the fixed-pos element list, but in case we're dealing with a
754 * transformed element that's acting as an abs-pos and fixed-pos container,
755 * we'll hand back the abs-pos list. Callers should use this function if they
756 * want to get the list acting as the fixed-pos item parent.
758 AbsoluteFrameList& GetFixedList() {
759 return mFixedPosIsAbsPos ? mAbsoluteList : mFixedList;
761 const AbsoluteFrameList& GetFixedList() const {
762 return mFixedPosIsAbsPos ? mAbsoluteList : mFixedList;
765 protected:
766 friend class nsFrameConstructorSaveState;
769 * ProcessFrameInsertions takes the frames in aFrameList and adds them as
770 * kids to the aChildListID child list of |aFrameList.containingBlock|.
772 void ProcessFrameInsertions(AbsoluteFrameList& aFrameList,
773 mozilla::FrameChildListID aChildListID);
776 * GetOutOfFlowFrameList selects the out-of-flow frame list the new
777 * frame should be added to. If the frame shouldn't be added to any
778 * out-of-flow list, it returns nullptr. The corresponding type of
779 * placeholder is also returned via the aPlaceholderType parameter
780 * if this method doesn't return nullptr. The caller should check
781 * whether the returned list really has a containing block.
783 AbsoluteFrameList* GetOutOfFlowFrameList(nsIFrame* aNewFrame,
784 bool aCanBePositioned,
785 bool aCanBeFloated,
786 nsFrameState* aPlaceholderType);
788 void ConstructBackdropFrameFor(nsIContent* aContent, nsIFrame* aFrame);
791 nsFrameConstructorState::nsFrameConstructorState(
792 PresShell* aPresShell, nsContainerFrame* aFixedContainingBlock,
793 nsContainerFrame* aAbsoluteContainingBlock,
794 nsContainerFrame* aFloatContainingBlock,
795 already_AddRefed<nsILayoutHistoryState> aHistoryState)
796 : mPresContext(aPresShell->GetPresContext()),
797 mPresShell(aPresShell),
798 mFrameManager(aPresShell->FrameConstructor()),
799 mFixedList(aFixedContainingBlock),
800 mAbsoluteList(aAbsoluteContainingBlock),
801 mFloatedList(aFloatContainingBlock),
802 mTopLayerFixedList(
803 static_cast<nsContainerFrame*>(mFrameManager->GetRootFrame())),
804 mTopLayerAbsoluteList(
805 aPresShell->FrameConstructor()->GetDocElementContainingBlock()),
806 // Will be set by AutoFrameConstructionPageName
807 mAutoPageNameValue(nullptr),
808 // See PushAbsoluteContaningBlock below
809 mFrameState(aHistoryState),
810 mAdditionalStateBits(nsFrameState(0)),
811 // If the fixed-pos containing block is equal to the abs-pos containing
812 // block, use the abs-pos containing block's abs-pos list for fixed-pos
813 // frames.
814 mFixedPosIsAbsPos(aFixedContainingBlock == aAbsoluteContainingBlock),
815 mCreatingExtraFrames(false),
816 mHasRenderedLegend(false) {
817 MOZ_COUNT_CTOR(nsFrameConstructorState);
820 nsFrameConstructorState::nsFrameConstructorState(
821 PresShell* aPresShell, nsContainerFrame* aFixedContainingBlock,
822 nsContainerFrame* aAbsoluteContainingBlock,
823 nsContainerFrame* aFloatContainingBlock)
824 : nsFrameConstructorState(
825 aPresShell, aFixedContainingBlock, aAbsoluteContainingBlock,
826 aFloatContainingBlock,
827 aPresShell->GetDocument()->GetLayoutHistoryState()) {}
829 nsFrameConstructorState::~nsFrameConstructorState() {
830 MOZ_COUNT_DTOR(nsFrameConstructorState);
831 ProcessFrameInsertionsForAllLists();
832 for (auto& content : Reversed(mGeneratedContentWithInitializer)) {
833 content->RemoveProperty(nsGkAtoms::genConInitializerProperty);
837 void nsFrameConstructorState::ProcessFrameInsertionsForAllLists() {
838 ProcessFrameInsertions(mTopLayerFixedList, FrameChildListID::Fixed);
839 ProcessFrameInsertions(mTopLayerAbsoluteList, FrameChildListID::Absolute);
840 ProcessFrameInsertions(mFloatedList, FrameChildListID::Float);
841 ProcessFrameInsertions(mAbsoluteList, FrameChildListID::Absolute);
842 ProcessFrameInsertions(mFixedList, FrameChildListID::Fixed);
845 void nsFrameConstructorState::PushAbsoluteContainingBlock(
846 nsContainerFrame* aNewAbsoluteContainingBlock, nsIFrame* aPositionedFrame,
847 nsFrameConstructorSaveState& aSaveState) {
848 MOZ_ASSERT(!!aNewAbsoluteContainingBlock == !!aPositionedFrame,
849 "We should have both or none");
850 aSaveState.mList = &mAbsoluteList;
851 aSaveState.mChildListID = FrameChildListID::Absolute;
852 aSaveState.mState = this;
853 aSaveState.mSavedFixedPosIsAbsPos = mFixedPosIsAbsPos;
855 if (mFixedPosIsAbsPos) {
856 // Since we're going to replace mAbsoluteList, we need to save it into
857 // mFixedList now (and save the current value of mFixedList).
858 aSaveState.mSavedList = std::move(mFixedList);
859 mFixedList = std::move(mAbsoluteList);
860 } else {
861 // Otherwise, we just save mAbsoluteList.
862 aSaveState.mSavedList = std::move(mAbsoluteList);
865 mAbsoluteList = AbsoluteFrameList(aNewAbsoluteContainingBlock);
867 /* See if we're wiring the fixed-pos and abs-pos lists together. This happens
868 * if we're a transformed/filtered/etc element, or if we force a null abspos
869 * containing block (for mathml for example).
871 mFixedPosIsAbsPos =
872 !aPositionedFrame || aPositionedFrame->IsFixedPosContainingBlock();
874 if (aNewAbsoluteContainingBlock) {
875 aNewAbsoluteContainingBlock->MarkAsAbsoluteContainingBlock();
879 void nsFrameConstructorState::MaybePushFloatContainingBlock(
880 nsContainerFrame* aFloatCBCandidate,
881 nsFrameConstructorSaveState& aSaveState) {
882 // The logic here needs to match the logic in GetFloatContainingBlock().
883 if (ShouldSuppressFloatingOfDescendants(aFloatCBCandidate)) {
884 // Pushing a null float containing block forbids any frames from being
885 // floated until a new float containing block is pushed. See implementation
886 // of nsFrameConstructorState::AddChild().
888 // XXX we should get rid of null float containing blocks and teach the
889 // various frame classes to deal with floats instead.
890 PushFloatContainingBlock(nullptr, aSaveState);
891 } else if (aFloatCBCandidate->IsFloatContainingBlock()) {
892 PushFloatContainingBlock(aFloatCBCandidate, aSaveState);
895 #ifdef DEBUG
896 mFloatCBCandidate = aFloatCBCandidate;
897 #endif
900 void nsFrameConstructorState::PushFloatContainingBlock(
901 nsContainerFrame* aNewFloatContainingBlock,
902 nsFrameConstructorSaveState& aSaveState) {
903 MOZ_ASSERT(!aNewFloatContainingBlock ||
904 aNewFloatContainingBlock->IsFloatContainingBlock(),
905 "Please push a real float containing block!");
906 NS_ASSERTION(
907 !aNewFloatContainingBlock ||
908 !ShouldSuppressFloatingOfDescendants(aNewFloatContainingBlock),
909 "We should not push a frame that is supposed to _suppress_ "
910 "floats as a float containing block!");
911 aSaveState.mList = &mFloatedList;
912 aSaveState.mSavedList = std::move(mFloatedList);
913 aSaveState.mChildListID = FrameChildListID::Float;
914 aSaveState.mState = this;
915 mFloatedList = AbsoluteFrameList(aNewFloatContainingBlock);
918 nsContainerFrame* nsFrameConstructorState::GetGeometricParent(
919 const nsStyleDisplay& aStyleDisplay,
920 nsContainerFrame* aContentParentFrame) const {
921 // If there is no container for a fixed, absolute, or floating root
922 // frame, we will ignore the positioning. This hack is originally
923 // brought to you by the letter T: tables, since other roots don't
924 // even call into this code. See bug 178855.
926 // XXX Disabling positioning in this case is a hack. If one was so inclined,
927 // one could support this either by (1) inserting a dummy block between the
928 // table and the canvas or (2) teaching the canvas how to reflow positioned
929 // elements. (1) has the usual problems when multiple frames share the same
930 // content (notice all the special cases in this file dealing with inner
931 // tables and table wrappers which share the same content). (2) requires some
932 // work and possible factoring.
934 // XXXbz couldn't we just force position to "static" on roots and
935 // float to "none"? That's OK per CSS 2.1, as far as I can tell.
937 if (aContentParentFrame && aContentParentFrame->IsInSVGTextSubtree()) {
938 return aContentParentFrame;
941 if (aStyleDisplay.IsFloatingStyle() && mFloatedList.mContainingBlock) {
942 NS_ASSERTION(!aStyleDisplay.IsAbsolutelyPositionedStyle(),
943 "Absolutely positioned _and_ floating?");
944 return mFloatedList.mContainingBlock;
947 if (aStyleDisplay.mTopLayer != StyleTopLayer::None) {
948 MOZ_ASSERT(aStyleDisplay.mTopLayer == StyleTopLayer::Top,
949 "-moz-top-layer should be either none or top");
950 MOZ_ASSERT(aStyleDisplay.IsAbsolutelyPositionedStyle(),
951 "Top layer items should always be absolutely positioned");
952 if (aStyleDisplay.mPosition == StylePositionProperty::Fixed) {
953 MOZ_ASSERT(mTopLayerFixedList.mContainingBlock, "No root frame?");
954 return mTopLayerFixedList.mContainingBlock;
956 MOZ_ASSERT(aStyleDisplay.mPosition == StylePositionProperty::Absolute);
957 MOZ_ASSERT(mTopLayerAbsoluteList.mContainingBlock);
958 return mTopLayerAbsoluteList.mContainingBlock;
961 if (aStyleDisplay.mPosition == StylePositionProperty::Absolute &&
962 mAbsoluteList.mContainingBlock) {
963 return mAbsoluteList.mContainingBlock;
966 if (aStyleDisplay.mPosition == StylePositionProperty::Fixed &&
967 GetFixedList().mContainingBlock) {
968 return GetFixedList().mContainingBlock;
971 return aContentParentFrame;
974 void nsFrameConstructorState::ReparentAbsoluteItems(
975 nsContainerFrame* aNewParent) {
976 // Bug 1491727: This function might not conform to the spec. See
977 // https://github.com/w3c/csswg-drafts/issues/1894.
979 MOZ_ASSERT(aNewParent->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR),
980 "Restrict the usage under column hierarchy.");
982 AbsoluteFrameList newAbsoluteItems(aNewParent);
984 nsIFrame* current = mAbsoluteList.FirstChild();
985 while (current) {
986 nsIFrame* placeholder = current->GetPlaceholderFrame();
988 if (nsLayoutUtils::IsProperAncestorFrame(aNewParent, placeholder)) {
989 nsIFrame* next = current->GetNextSibling();
990 mAbsoluteList.RemoveFrame(current);
991 newAbsoluteItems.AppendFrame(aNewParent, current);
992 current = next;
993 } else {
994 current = current->GetNextSibling();
998 if (newAbsoluteItems.NotEmpty()) {
999 // ~nsFrameConstructorSaveState() will move newAbsoluteItems to
1000 // aNewParent's absolute child list.
1001 nsFrameConstructorSaveState absoluteSaveState;
1003 // It doesn't matter whether aNewParent has position style or not. Caller
1004 // won't call us if we can't have absolute children.
1005 PushAbsoluteContainingBlock(aNewParent, aNewParent, absoluteSaveState);
1006 mAbsoluteList = std::move(newAbsoluteItems);
1010 void nsFrameConstructorState::ReparentFloats(nsContainerFrame* aNewParent) {
1011 MOZ_ASSERT(aNewParent->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR),
1012 "Restrict the usage under column hierarchy.");
1013 MOZ_ASSERT(
1014 aNewParent->IsFloatContainingBlock(),
1015 "Why calling this method if aNewParent is not a float containing block?");
1017 // Gather floats that should reparent under aNewParent.
1018 AbsoluteFrameList floats(aNewParent);
1019 nsIFrame* current = mFloatedList.FirstChild();
1020 while (current) {
1021 nsIFrame* placeholder = current->GetPlaceholderFrame();
1022 nsIFrame* next = current->GetNextSibling();
1023 if (nsLayoutUtils::IsProperAncestorFrame(aNewParent, placeholder)) {
1024 mFloatedList.RemoveFrame(current);
1025 floats.AppendFrame(aNewParent, current);
1027 current = next;
1030 if (floats.NotEmpty()) {
1031 // Make floats move into aNewParent's float child list in
1032 // ~nsFrameConstructorSaveState() when destructing floatSaveState.
1033 nsFrameConstructorSaveState floatSaveState;
1034 PushFloatContainingBlock(aNewParent, floatSaveState);
1035 mFloatedList = std::move(floats);
1039 AbsoluteFrameList* nsFrameConstructorState::GetOutOfFlowFrameList(
1040 nsIFrame* aNewFrame, bool aCanBePositioned, bool aCanBeFloated,
1041 nsFrameState* aPlaceholderType) {
1042 const nsStyleDisplay* disp = aNewFrame->StyleDisplay();
1043 if (aCanBeFloated && disp->IsFloatingStyle()) {
1044 *aPlaceholderType = PLACEHOLDER_FOR_FLOAT;
1045 return &mFloatedList;
1048 if (aCanBePositioned) {
1049 if (disp->mTopLayer != StyleTopLayer::None) {
1050 *aPlaceholderType = PLACEHOLDER_FOR_TOPLAYER;
1051 if (disp->mPosition == StylePositionProperty::Fixed) {
1052 *aPlaceholderType |= PLACEHOLDER_FOR_FIXEDPOS;
1053 return &mTopLayerFixedList;
1055 *aPlaceholderType |= PLACEHOLDER_FOR_ABSPOS;
1056 return &mTopLayerAbsoluteList;
1058 if (disp->mPosition == StylePositionProperty::Absolute) {
1059 *aPlaceholderType = PLACEHOLDER_FOR_ABSPOS;
1060 return &mAbsoluteList;
1062 if (disp->mPosition == StylePositionProperty::Fixed) {
1063 *aPlaceholderType = PLACEHOLDER_FOR_FIXEDPOS;
1064 return &GetFixedList();
1067 return nullptr;
1070 void nsFrameConstructorState::ConstructBackdropFrameFor(nsIContent* aContent,
1071 nsIFrame* aFrame) {
1072 MOZ_ASSERT(aFrame->StyleDisplay()->mTopLayer == StyleTopLayer::Top);
1073 nsContainerFrame* frame = do_QueryFrame(aFrame);
1074 if (!frame) {
1075 NS_WARNING("Cannot create backdrop frame for non-container frame");
1076 return;
1079 RefPtr<ComputedStyle> style =
1080 mPresShell->StyleSet()->ResolvePseudoElementStyle(
1081 *aContent->AsElement(), PseudoStyleType::backdrop,
1082 /* aParentStyle */ nullptr);
1083 MOZ_ASSERT(style->StyleDisplay()->mTopLayer == StyleTopLayer::Top);
1084 nsContainerFrame* parentFrame =
1085 GetGeometricParent(*style->StyleDisplay(), nullptr);
1087 nsBackdropFrame* backdropFrame =
1088 new (mPresShell) nsBackdropFrame(style, mPresShell->GetPresContext());
1089 backdropFrame->Init(aContent, parentFrame, nullptr);
1091 nsFrameState placeholderType;
1092 AbsoluteFrameList* frameList =
1093 GetOutOfFlowFrameList(backdropFrame, true, true, &placeholderType);
1094 MOZ_ASSERT(placeholderType & PLACEHOLDER_FOR_TOPLAYER);
1096 nsIFrame* placeholder = nsCSSFrameConstructor::CreatePlaceholderFrameFor(
1097 mPresShell, aContent, backdropFrame, frame, nullptr, placeholderType);
1098 frame->SetInitialChildList(FrameChildListID::Backdrop,
1099 nsFrameList(placeholder, placeholder));
1101 frameList->AppendFrame(nullptr, backdropFrame);
1104 void nsFrameConstructorState::AddChild(
1105 nsIFrame* aNewFrame, nsFrameList& aFrameList, nsIContent* aContent,
1106 nsContainerFrame* aParentFrame, bool aCanBePositioned, bool aCanBeFloated,
1107 bool aInsertAfter, nsIFrame* aInsertAfterFrame) {
1108 MOZ_ASSERT(!aNewFrame->GetNextSibling(), "Shouldn't happen");
1110 nsFrameState placeholderType;
1111 AbsoluteFrameList* outOfFlowFrameList = GetOutOfFlowFrameList(
1112 aNewFrame, aCanBePositioned, aCanBeFloated, &placeholderType);
1114 // The comments in GetGeometricParent regarding root table frames
1115 // all apply here, unfortunately. Thus, we need to check whether
1116 // the returned frame items really has containing block.
1117 nsFrameList* frameList;
1118 if (outOfFlowFrameList && outOfFlowFrameList->mContainingBlock) {
1119 MOZ_ASSERT(aNewFrame->GetParent() == outOfFlowFrameList->mContainingBlock,
1120 "Parent of the frame is not the containing block?");
1121 frameList = outOfFlowFrameList;
1122 } else {
1123 frameList = &aFrameList;
1124 placeholderType = nsFrameState(0);
1127 if (placeholderType) {
1128 NS_ASSERTION(frameList != &aFrameList,
1129 "Putting frame in-flow _and_ want a placeholder?");
1130 nsIFrame* placeholderFrame =
1131 nsCSSFrameConstructor::CreatePlaceholderFrameFor(
1132 mPresShell, aContent, aNewFrame, aParentFrame, nullptr,
1133 placeholderType);
1135 placeholderFrame->AddStateBits(mAdditionalStateBits);
1136 // Add the placeholder frame to the flow
1137 aFrameList.AppendFrame(nullptr, placeholderFrame);
1139 if (placeholderType & PLACEHOLDER_FOR_TOPLAYER) {
1140 ConstructBackdropFrameFor(aContent, aNewFrame);
1143 #ifdef DEBUG
1144 else {
1145 NS_ASSERTION(aNewFrame->GetParent() == aParentFrame,
1146 "In-flow frame has wrong parent");
1148 #endif
1150 if (aInsertAfter) {
1151 frameList->InsertFrame(nullptr, aInsertAfterFrame, aNewFrame);
1152 } else {
1153 frameList->AppendFrame(nullptr, aNewFrame);
1157 // Some of this function's callers recurse 1000 levels deep in crashtests. On
1158 // platforms where stack limits are low, we can't afford to incorporate this
1159 // function's `AutoTArray`s into its callers' stack frames, so disable inlining.
1160 MOZ_NEVER_INLINE void nsFrameConstructorState::ProcessFrameInsertions(
1161 AbsoluteFrameList& aFrameList, FrameChildListID aChildListID) {
1162 #define NS_NONXUL_LIST_TEST \
1163 (&aFrameList == &mFloatedList && aChildListID == FrameChildListID::Float) || \
1164 ((&aFrameList == &mAbsoluteList || \
1165 &aFrameList == &mTopLayerAbsoluteList) && \
1166 aChildListID == FrameChildListID::Absolute) || \
1167 ((&aFrameList == &mFixedList || &aFrameList == &mTopLayerFixedList) && \
1168 aChildListID == FrameChildListID::Fixed)
1169 MOZ_ASSERT(NS_NONXUL_LIST_TEST,
1170 "Unexpected aFrameList/aChildListID combination");
1172 if (aFrameList.IsEmpty()) {
1173 return;
1176 nsContainerFrame* containingBlock = aFrameList.mContainingBlock;
1178 NS_ASSERTION(containingBlock, "Child list without containing block?");
1180 if (aChildListID == FrameChildListID::Fixed) {
1181 // Put this frame on the transformed-frame's abs-pos list instead, if
1182 // it has abs-pos children instead of fixed-pos children.
1183 aChildListID = containingBlock->GetAbsoluteListID();
1186 // Insert the frames hanging out in aItems. We can use SetInitialChildList()
1187 // if the containing block hasn't been reflowed yet (so NS_FRAME_FIRST_REFLOW
1188 // is set) and doesn't have any frames in the aChildListID child list yet.
1189 const nsFrameList& childList = containingBlock->GetChildList(aChildListID);
1190 if (childList.IsEmpty() &&
1191 containingBlock->HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
1192 // If we're injecting absolutely positioned frames, inject them on the
1193 // absolute containing block
1194 if (aChildListID == containingBlock->GetAbsoluteListID()) {
1195 containingBlock->GetAbsoluteContainingBlock()->SetInitialChildList(
1196 containingBlock, aChildListID, std::move(aFrameList));
1197 } else {
1198 containingBlock->SetInitialChildList(aChildListID, std::move(aFrameList));
1200 } else if (aChildListID == FrameChildListID::Fixed ||
1201 aChildListID == FrameChildListID::Absolute) {
1202 // The order is not important for abs-pos/fixed-pos frame list, just
1203 // append the frame items to the list directly.
1204 mFrameManager->AppendFrames(containingBlock, aChildListID,
1205 std::move(aFrameList));
1206 } else {
1207 // Note that whether the frame construction context is doing an append or
1208 // not is not helpful here, since it could be appending to some frame in
1209 // the middle of the document, which means we're not necessarily
1210 // appending to the children of the containing block.
1212 // We need to make sure the 'append to the end of document' case is fast.
1213 // So first test the last child of the containing block
1214 nsIFrame* lastChild = childList.LastChild();
1216 // CompareTreePosition uses placeholder hierarchy for out of flow frames,
1217 // so this will make out-of-flows respect the ordering of placeholders,
1218 // which is great because it takes care of anonymous content.
1219 nsIFrame* firstNewFrame = aFrameList.FirstChild();
1221 // Cache the ancestor chain so that we can reuse it if needed.
1222 AutoTArray<nsIFrame*, 20> firstNewFrameAncestors;
1223 nsIFrame* notCommonAncestor = nullptr;
1224 if (lastChild) {
1225 notCommonAncestor = nsLayoutUtils::FillAncestors(
1226 firstNewFrame, containingBlock, &firstNewFrameAncestors);
1229 if (!lastChild || nsLayoutUtils::CompareTreePosition(
1230 lastChild, firstNewFrame, firstNewFrameAncestors,
1231 notCommonAncestor ? containingBlock : nullptr) < 0) {
1232 // no lastChild, or lastChild comes before the new children, so just
1233 // append
1234 mFrameManager->AppendFrames(containingBlock, aChildListID,
1235 std::move(aFrameList));
1236 } else {
1237 // Try the other children. First collect them to an array so that a
1238 // reasonable fast binary search can be used to find the insertion point.
1239 AutoTArray<nsIFrame*, 128> children;
1240 for (nsIFrame* f = childList.FirstChild(); f != lastChild;
1241 f = f->GetNextSibling()) {
1242 children.AppendElement(f);
1245 nsIFrame* insertionPoint = nullptr;
1246 int32_t imin = 0;
1247 int32_t max = children.Length();
1248 while (max > imin) {
1249 int32_t imid = imin + ((max - imin) / 2);
1250 nsIFrame* f = children[imid];
1251 int32_t compare = nsLayoutUtils::CompareTreePosition(
1252 f, firstNewFrame, firstNewFrameAncestors,
1253 notCommonAncestor ? containingBlock : nullptr);
1254 if (compare > 0) {
1255 // f is after the new frame.
1256 max = imid;
1257 insertionPoint = imid > 0 ? children[imid - 1] : nullptr;
1258 } else if (compare < 0) {
1259 // f is before the new frame.
1260 imin = imid + 1;
1261 insertionPoint = f;
1262 } else {
1263 // This is for the old behavior. Should be removed once it is
1264 // guaranteed that CompareTreePosition can't return 0!
1265 // See bug 928645.
1266 NS_WARNING("Something odd happening???");
1267 insertionPoint = nullptr;
1268 for (uint32_t i = 0; i < children.Length(); ++i) {
1269 nsIFrame* f = children[i];
1270 if (nsLayoutUtils::CompareTreePosition(
1271 f, firstNewFrame, firstNewFrameAncestors,
1272 notCommonAncestor ? containingBlock : nullptr) > 0) {
1273 break;
1275 insertionPoint = f;
1277 break;
1280 mFrameManager->InsertFrames(containingBlock, aChildListID, insertionPoint,
1281 std::move(aFrameList));
1285 MOZ_ASSERT(aFrameList.IsEmpty(), "How did that happen?");
1288 nsFrameConstructorSaveState::~nsFrameConstructorSaveState() {
1289 // Restore the state
1290 if (mList) {
1291 MOZ_ASSERT(mState, "Can't have mList set without having a state!");
1292 mState->ProcessFrameInsertions(*mList, mChildListID);
1294 if (mList == &mState->mAbsoluteList) {
1295 mState->mFixedPosIsAbsPos = mSavedFixedPosIsAbsPos;
1296 // mAbsoluteList was moved to mFixedList, so move mFixedList back
1297 // and repair the old mFixedList now.
1298 if (mSavedFixedPosIsAbsPos) {
1299 mState->mAbsoluteList = std::move(mState->mFixedList);
1300 mState->mFixedList = std::move(mSavedList);
1301 } else {
1302 mState->mAbsoluteList = std::move(mSavedList);
1304 } else {
1305 mState->mFloatedList = std::move(mSavedList);
1308 MOZ_ASSERT(mSavedList.IsEmpty(),
1309 "Frames in mSavedList should've moved back into mState!");
1310 MOZ_ASSERT(!mList->LastChild() || !mList->LastChild()->GetNextSibling(),
1311 "Something corrupted our list!");
1316 * Moves aFrameList from aOldParent to aNewParent. This updates the parent
1317 * pointer of the frames in the list, and reparents their views as needed.
1318 * nsIFrame::SetParent sets the NS_FRAME_HAS_VIEW bit on aNewParent and its
1319 * ancestors as needed. Then it sets the list as the initial child list
1320 * on aNewParent, unless aNewParent either already has kids or has been
1321 * reflowed; in that case it appends the new frames. Note that this
1322 * method differs from ReparentFrames in that it doesn't change the kids'
1323 * style.
1325 // XXXbz Since this is only used for {ib} splits, could we just copy the view
1326 // bits from aOldParent to aNewParent and then use the
1327 // nsFrameList::ApplySetParent? That would still leave us doing two passes
1328 // over the list, of course; if we really wanted to we could factor out the
1329 // relevant part of ReparentFrameViewList, I suppose... Or just get rid of
1330 // views, which would make most of this function go away.
1331 static void MoveChildrenTo(nsIFrame* aOldParent, nsContainerFrame* aNewParent,
1332 nsFrameList& aFrameList) {
1333 #ifdef DEBUG
1334 bool sameGrandParent = aOldParent->GetParent() == aNewParent->GetParent();
1336 if (aNewParent->HasView() || aOldParent->HasView() || !sameGrandParent) {
1337 // Move the frames into the new view
1338 nsContainerFrame::ReparentFrameViewList(aFrameList, aOldParent, aNewParent);
1340 #endif
1342 aFrameList.ApplySetParent(aNewParent);
1344 if (aNewParent->PrincipalChildList().IsEmpty() &&
1345 aNewParent->HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
1346 aNewParent->SetInitialChildList(FrameChildListID::Principal,
1347 std::move(aFrameList));
1348 } else {
1349 aNewParent->AppendFrames(FrameChildListID::Principal,
1350 std::move(aFrameList));
1354 static bool MaybeApplyPageName(nsFrameConstructorState& aState,
1355 const StylePageName& aPageName) {
1356 if (aPageName.IsPageName()) {
1357 aState.mAutoPageNameValue = aPageName.AsPageName().AsAtom();
1358 return true;
1360 MOZ_ASSERT(aPageName.IsAuto(), "Impossible page name");
1361 return false;
1364 static void EnsureAutoPageName(nsFrameConstructorState& aState,
1365 const nsContainerFrame* const aFrame) {
1366 // Check if we need to figure out our used page name.
1367 // When building the entire document, this should only happen for the
1368 // root, which will mean the loop will immediately end. Either way, this will
1369 // only happen once for each time the frame constructor is run.
1370 if (aState.mAutoPageNameValue) {
1371 return;
1374 for (const nsContainerFrame* frame = aFrame; frame;
1375 frame = frame->GetParent()) {
1376 const StylePageName& pageName = frame->StylePage()->mPage;
1377 if (MaybeApplyPageName(aState, pageName)) {
1378 return;
1381 // Ensure that a root with `page: auto` gets an empty page name
1382 // https://drafts.csswg.org/css-page-3/#using-named-pages
1383 aState.mAutoPageNameValue = nsGkAtoms::_empty;
1386 nsCSSFrameConstructor::AutoFrameConstructionPageName::
1387 AutoFrameConstructionPageName(nsFrameConstructorState& aState,
1388 nsIFrame* const aFrame)
1389 : mState(aState), mNameToRestore(nullptr) {
1390 if (!aState.mPresContext->IsPaginated()) {
1391 MOZ_ASSERT(!aState.mAutoPageNameValue,
1392 "Page name should not have been set");
1393 return;
1395 #ifdef DEBUG
1396 MOZ_ASSERT(!aFrame->mWasVisitedByAutoFrameConstructionPageName,
1397 "Frame should only have been visited once");
1398 aFrame->mWasVisitedByAutoFrameConstructionPageName = true;
1399 #endif
1401 EnsureAutoPageName(aState, aFrame->GetParent());
1402 mNameToRestore = aState.mAutoPageNameValue;
1404 MOZ_ASSERT(mNameToRestore,
1405 "Page name should have been found by EnsureAutoPageName");
1406 MaybeApplyPageName(aState, aFrame->StylePage()->mPage);
1407 aFrame->SetAutoPageValue(aState.mAutoPageNameValue);
1410 nsCSSFrameConstructor::AutoFrameConstructionPageName::
1411 ~AutoFrameConstructionPageName() {
1412 // This isn't actually useful when not in paginated layout, but it's very
1413 // likely cheaper to unconditionally write this pointer than to test for
1414 // paginated layout and then branch on the result.
1415 mState.mAutoPageNameValue = mNameToRestore;
1418 //----------------------------------------------------------------------
1420 nsCSSFrameConstructor::nsCSSFrameConstructor(Document* aDocument,
1421 PresShell* aPresShell)
1422 : nsFrameManager(aPresShell),
1423 mDocument(aDocument),
1424 mRootElementFrame(nullptr),
1425 mRootElementStyleFrame(nullptr),
1426 mDocElementContainingBlock(nullptr),
1427 mPageSequenceFrame(nullptr),
1428 mFirstFreeFCItem(nullptr),
1429 mFCItemsInUse(0),
1430 mCurrentDepth(0),
1431 mQuotesDirty(false),
1432 mCountersDirty(false),
1433 mAlwaysCreateFramesForIgnorableWhitespace(false) {
1434 #ifdef DEBUG
1435 static bool gFirstTime = true;
1436 if (gFirstTime) {
1437 gFirstTime = false;
1438 char* flags = PR_GetEnv("GECKO_FRAMECTOR_DEBUG_FLAGS");
1439 if (flags) {
1440 bool error = false;
1441 for (;;) {
1442 char* comma = strchr(flags, ',');
1443 if (comma) *comma = '\0';
1445 bool found = false;
1446 FrameCtorDebugFlags* flag = gFlags;
1447 FrameCtorDebugFlags* limit = gFlags + NUM_DEBUG_FLAGS;
1448 while (flag < limit) {
1449 if (nsCRT::strcasecmp(flag->name, flags) == 0) {
1450 *(flag->on) = true;
1451 printf("nsCSSFrameConstructor: setting %s debug flag on\n",
1452 flag->name);
1453 found = true;
1454 break;
1456 ++flag;
1459 if (!found) error = true;
1461 if (!comma) break;
1463 *comma = ',';
1464 flags = comma + 1;
1467 if (error) {
1468 printf("Here are the available GECKO_FRAMECTOR_DEBUG_FLAGS:\n");
1469 FrameCtorDebugFlags* flag = gFlags;
1470 FrameCtorDebugFlags* limit = gFlags + NUM_DEBUG_FLAGS;
1471 while (flag < limit) {
1472 printf(" %s\n", flag->name);
1473 ++flag;
1475 printf(
1476 "Note: GECKO_FRAMECTOR_DEBUG_FLAGS is a comma separated list of "
1477 "flag\n");
1478 printf("names (no whitespace)\n");
1482 #endif
1485 void nsCSSFrameConstructor::NotifyDestroyingFrame(nsIFrame* aFrame) {
1486 if (aFrame->HasAnyStateBits(NS_FRAME_GENERATED_CONTENT) &&
1487 mContainStyleScopeManager.DestroyQuoteNodesFor(aFrame)) {
1488 QuotesDirty();
1491 if (aFrame->HasAnyStateBits(NS_FRAME_HAS_CSS_COUNTER_STYLE) &&
1492 mContainStyleScopeManager.DestroyCounterNodesFor(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();
1499 if (aFrame->StyleDisplay()->IsContainStyle()) {
1500 mContainStyleScopeManager.DestroyScopesFor(aFrame);
1503 RestyleManager()->NotifyDestroyingFrame(aFrame);
1506 struct nsGenConInitializer {
1507 UniquePtr<nsGenConNode> mNode;
1508 nsGenConList* mList;
1509 void (nsCSSFrameConstructor::*mDirtyAll)();
1511 nsGenConInitializer(UniquePtr<nsGenConNode> aNode, nsGenConList* aList,
1512 void (nsCSSFrameConstructor::*aDirtyAll)())
1513 : mNode(std::move(aNode)), mList(aList), mDirtyAll(aDirtyAll) {}
1516 already_AddRefed<nsIContent> nsCSSFrameConstructor::CreateGenConTextNode(
1517 nsFrameConstructorState& aState, const nsAString& aString,
1518 UniquePtr<nsGenConInitializer> aInitializer) {
1519 RefPtr<nsTextNode> content = new (mDocument->NodeInfoManager())
1520 nsTextNode(mDocument->NodeInfoManager());
1521 content->SetText(aString, false);
1522 if (aInitializer) {
1523 aInitializer->mNode->mText = content;
1524 content->SetProperty(nsGkAtoms::genConInitializerProperty,
1525 aInitializer.release(),
1526 nsINode::DeleteProperty<nsGenConInitializer>);
1527 aState.mGeneratedContentWithInitializer.AppendElement(content);
1529 return content.forget();
1532 void nsCSSFrameConstructor::CreateGeneratedContent(
1533 nsFrameConstructorState& aState, Element& aOriginatingElement,
1534 ComputedStyle& aPseudoStyle, uint32_t aContentIndex,
1535 const FunctionRef<void(nsIContent*)> aAddChild) {
1536 using Type = StyleContentItem::Tag;
1537 // Get the content value
1538 const auto& item = aPseudoStyle.StyleContent()->ContentAt(aContentIndex);
1539 const Type type = item.tag;
1541 switch (type) {
1542 case Type::Image: {
1543 RefPtr c = GeneratedImageContent::Create(*mDocument, aContentIndex);
1544 aAddChild(c);
1545 return;
1548 case Type::String: {
1549 RefPtr text = CreateGenConTextNode(
1550 aState, NS_ConvertUTF8toUTF16(item.AsString().AsString()), nullptr);
1551 aAddChild(text);
1552 return;
1555 case Type::Attr: {
1556 const auto& attr = item.AsAttr();
1557 RefPtr<nsAtom> attrName = attr.attribute.AsAtom();
1558 int32_t attrNameSpace = kNameSpaceID_None;
1559 RefPtr<nsAtom> ns = attr.namespace_url.AsAtom();
1560 if (!ns->IsEmpty()) {
1561 nsresult rv = nsNameSpaceManager::GetInstance()->RegisterNameSpace(
1562 ns.forget(), attrNameSpace);
1563 NS_ENSURE_SUCCESS_VOID(rv);
1566 if (mDocument->IsHTMLDocument() && aOriginatingElement.IsHTMLElement()) {
1567 ToLowerCaseASCII(attrName);
1570 nsCOMPtr<nsIContent> content;
1571 NS_NewAttributeContent(mDocument->NodeInfoManager(), attrNameSpace,
1572 attrName, getter_AddRefs(content));
1573 aAddChild(content);
1574 return;
1577 case Type::Counter:
1578 case Type::Counters: {
1579 RefPtr<nsAtom> name;
1580 CounterStylePtr ptr;
1581 nsString separator;
1582 if (type == Type::Counter) {
1583 auto& counter = item.AsCounter();
1584 name = counter._0.AsAtom();
1585 ptr = CounterStylePtr::FromStyle(counter._1);
1586 } else {
1587 auto& counters = item.AsCounters();
1588 name = counters._0.AsAtom();
1589 CopyUTF8toUTF16(counters._1.AsString(), separator);
1590 ptr = CounterStylePtr::FromStyle(counters._2);
1593 auto* counterList = mContainStyleScopeManager.GetOrCreateCounterList(
1594 aOriginatingElement, name);
1595 auto node = MakeUnique<nsCounterUseNode>(
1596 std::move(ptr), std::move(separator), aContentIndex,
1597 /* aAllCounters = */ type == Type::Counters);
1599 auto initializer = MakeUnique<nsGenConInitializer>(
1600 std::move(node), counterList, &nsCSSFrameConstructor::CountersDirty);
1601 RefPtr c = CreateGenConTextNode(aState, u""_ns, std::move(initializer));
1602 aAddChild(c);
1603 return;
1605 case Type::OpenQuote:
1606 case Type::CloseQuote:
1607 case Type::NoOpenQuote:
1608 case Type::NoCloseQuote: {
1609 auto node = MakeUnique<nsQuoteNode>(type, aContentIndex);
1610 auto* quoteList =
1611 mContainStyleScopeManager.QuoteListFor(aOriginatingElement);
1612 auto initializer = MakeUnique<nsGenConInitializer>(
1613 std::move(node), quoteList, &nsCSSFrameConstructor::QuotesDirty);
1614 RefPtr c = CreateGenConTextNode(aState, u""_ns, std::move(initializer));
1615 aAddChild(c);
1616 return;
1619 case Type::MozLabelContent: {
1620 nsAutoString accesskey;
1621 if (!aOriginatingElement.GetAttr(nsGkAtoms::accesskey, accesskey) ||
1622 accesskey.IsEmpty() || !LookAndFeel::GetMenuAccessKey()) {
1623 // Easy path: just return a regular value attribute content.
1624 nsCOMPtr<nsIContent> content;
1625 NS_NewAttributeContent(mDocument->NodeInfoManager(), kNameSpaceID_None,
1626 nsGkAtoms::value, getter_AddRefs(content));
1627 aAddChild(content);
1628 return;
1631 nsAutoString value;
1632 aOriginatingElement.GetAttr(nsGkAtoms::value, value);
1634 auto AppendAccessKeyLabel = [&] {
1635 // Always append accesskey text in uppercase, see bug 1806167.
1636 ToUpperCase(accesskey);
1637 nsAutoString accessKeyLabel = u"("_ns + accesskey + u")"_ns;
1638 if (!StringEndsWith(value, accessKeyLabel)) {
1639 if (InsertSeparatorBeforeAccessKey() && !value.IsEmpty() &&
1640 !NS_IS_SPACE(value.Last())) {
1641 value.Append(' ');
1643 value.Append(accessKeyLabel);
1646 if (AlwaysAppendAccessKey()) {
1647 AppendAccessKeyLabel();
1648 RefPtr c = CreateGenConTextNode(aState, value, nullptr);
1649 aAddChild(c);
1650 return;
1653 const auto accessKeyStart = [&]() -> Maybe<size_t> {
1654 nsAString::const_iterator start, end;
1655 value.BeginReading(start);
1656 value.EndReading(end);
1658 const auto originalStart = start;
1659 // not appending access key - do case-sensitive search
1660 // first
1661 bool found = true;
1662 if (!FindInReadable(accesskey, start, end)) {
1663 start = originalStart;
1664 // didn't find it - perform a case-insensitive search
1665 found = FindInReadable(accesskey, start, end,
1666 nsCaseInsensitiveStringComparator);
1668 if (!found) {
1669 return Nothing();
1671 return Some(Distance(originalStart, start));
1672 }();
1674 if (accessKeyStart.isNothing()) {
1675 AppendAccessKeyLabel();
1676 RefPtr c = CreateGenConTextNode(aState, value, nullptr);
1677 aAddChild(c);
1678 return;
1681 if (*accessKeyStart != 0) {
1682 RefPtr beginning = CreateGenConTextNode(
1683 aState, Substring(value, 0, *accessKeyStart), nullptr);
1684 aAddChild(beginning);
1688 RefPtr accessKeyText = CreateGenConTextNode(
1689 aState, Substring(value, *accessKeyStart, accesskey.Length()),
1690 nullptr);
1691 RefPtr<nsIContent> underline =
1692 mDocument->CreateHTMLElement(nsGkAtoms::u);
1693 underline->AppendChildTo(accessKeyText, /* aNotify = */ false,
1694 IgnoreErrors());
1695 aAddChild(underline);
1698 size_t accessKeyEnd = *accessKeyStart + accesskey.Length();
1699 if (accessKeyEnd != value.Length()) {
1700 RefPtr valueEnd = CreateGenConTextNode(
1701 aState, Substring(value, *accessKeyStart + accesskey.Length()),
1702 nullptr);
1703 aAddChild(valueEnd);
1705 break;
1707 case Type::MozAltContent: {
1708 // Use the "alt" attribute; if that fails and the node is an HTML
1709 // <input>, try the value attribute and then fall back to some default
1710 // localized text we have.
1711 // XXX what if the 'alt' attribute is added later, how will we
1712 // detect that and do the right thing here?
1713 if (aOriginatingElement.HasAttr(nsGkAtoms::alt)) {
1714 nsCOMPtr<nsIContent> content;
1715 NS_NewAttributeContent(mDocument->NodeInfoManager(), kNameSpaceID_None,
1716 nsGkAtoms::alt, getter_AddRefs(content));
1717 aAddChild(content);
1718 return;
1721 if (aOriginatingElement.IsHTMLElement(nsGkAtoms::input)) {
1722 if (aOriginatingElement.HasAttr(nsGkAtoms::value)) {
1723 nsCOMPtr<nsIContent> content;
1724 NS_NewAttributeContent(mDocument->NodeInfoManager(),
1725 kNameSpaceID_None, nsGkAtoms::value,
1726 getter_AddRefs(content));
1727 aAddChild(content);
1728 return;
1731 nsAutoString temp;
1732 nsContentUtils::GetMaybeLocalizedString(
1733 nsContentUtils::eFORMS_PROPERTIES, "Submit", mDocument, temp);
1734 RefPtr c = CreateGenConTextNode(aState, temp, nullptr);
1735 aAddChild(c);
1736 return;
1738 break;
1742 return;
1745 void nsCSSFrameConstructor::CreateGeneratedContentFromListStyle(
1746 nsFrameConstructorState& aState, Element& aOriginatingElement,
1747 const ComputedStyle& aPseudoStyle,
1748 const FunctionRef<void(nsIContent*)> aAddChild) {
1749 const nsStyleList* styleList = aPseudoStyle.StyleList();
1750 if (!styleList->mListStyleImage.IsNone()) {
1751 RefPtr<nsIContent> child =
1752 GeneratedImageContent::CreateForListStyleImage(*mDocument);
1753 aAddChild(child);
1754 child = CreateGenConTextNode(aState, u" "_ns, nullptr);
1755 aAddChild(child);
1756 return;
1758 CreateGeneratedContentFromListStyleType(aState, aOriginatingElement,
1759 aPseudoStyle, aAddChild);
1762 void nsCSSFrameConstructor::CreateGeneratedContentFromListStyleType(
1763 nsFrameConstructorState& aState, Element& aOriginatingElement,
1764 const ComputedStyle& aPseudoStyle,
1765 const FunctionRef<void(nsIContent*)> aAddChild) {
1766 const nsStyleList* styleList = aPseudoStyle.StyleList();
1767 CounterStyle* counterStyle =
1768 mPresShell->GetPresContext()->CounterStyleManager()->ResolveCounterStyle(
1769 styleList->mCounterStyle);
1770 bool needUseNode = false;
1771 switch (counterStyle->GetStyle()) {
1772 case ListStyle::None:
1773 return;
1774 case ListStyle::Disc:
1775 case ListStyle::Circle:
1776 case ListStyle::Square:
1777 case ListStyle::DisclosureClosed:
1778 case ListStyle::DisclosureOpen:
1779 break;
1780 default:
1781 const auto* anonStyle = counterStyle->AsAnonymous();
1782 if (!anonStyle || !anonStyle->IsSingleString()) {
1783 needUseNode = true;
1787 auto node = MakeUnique<nsCounterUseNode>(nsCounterUseNode::ForLegacyBullet,
1788 styleList->mCounterStyle);
1789 if (!needUseNode) {
1790 nsAutoString text;
1791 node->GetText(WritingMode(&aPseudoStyle), counterStyle, text);
1792 // Note that we're done with 'node' in this case. It's not inserted into
1793 // any list so it's deleted when we return.
1794 RefPtr<nsIContent> child = CreateGenConTextNode(aState, text, nullptr);
1795 aAddChild(child);
1796 return;
1799 auto* counterList = mContainStyleScopeManager.GetOrCreateCounterList(
1800 aOriginatingElement, nsGkAtoms::list_item);
1801 auto initializer = MakeUnique<nsGenConInitializer>(
1802 std::move(node), counterList, &nsCSSFrameConstructor::CountersDirty);
1803 RefPtr<nsIContent> child =
1804 CreateGenConTextNode(aState, EmptyString(), std::move(initializer));
1805 aAddChild(child);
1808 // Frames for these may not be leaves in the proper sense, but we still don't
1809 // want to expose generated content on them. For the purposes of the page they
1810 // should be leaves.
1811 static bool HasUAWidget(const Element& aOriginatingElement) {
1812 const ShadowRoot* sr = aOriginatingElement.GetShadowRoot();
1813 return sr && sr->IsUAWidget();
1817 * aParentFrame - the frame that should be the parent of the generated
1818 * content. This is the frame for the corresponding content node,
1819 * which must not be a leaf frame.
1821 * Any items created are added to aItems.
1823 * We create an XML element (tag _moz_generated_content_before/after/marker)
1824 * representing the pseudoelement. We create a DOM node for each 'content'
1825 * item and make those nodes the children of the XML element. Then we create
1826 * a frame subtree for the XML element as if it were a regular child of
1827 * aParentFrame/aParentContent, giving the XML element the ::before, ::after
1828 * or ::marker style.
1830 void nsCSSFrameConstructor::CreateGeneratedContentItem(
1831 nsFrameConstructorState& aState, nsContainerFrame* aParentFrame,
1832 Element& aOriginatingElement, ComputedStyle& aStyle,
1833 PseudoStyleType aPseudoElement, FrameConstructionItemList& aItems,
1834 ItemFlags aExtraFlags) {
1835 MOZ_ASSERT(aPseudoElement == PseudoStyleType::before ||
1836 aPseudoElement == PseudoStyleType::after ||
1837 aPseudoElement == PseudoStyleType::marker,
1838 "unexpected aPseudoElement");
1840 if (HasUAWidget(aOriginatingElement) &&
1841 !aOriginatingElement.IsHTMLElement(nsGkAtoms::details)) {
1842 return;
1845 ServoStyleSet* styleSet = mPresShell->StyleSet();
1847 // Probe for the existence of the pseudo-element.
1848 // |ProbePseudoElementStyle| checks the relevant properties for the pseudo.
1849 // It only returns a non-null value if the pseudo should exist.
1850 RefPtr<ComputedStyle> pseudoStyle = styleSet->ProbePseudoElementStyle(
1851 aOriginatingElement, aPseudoElement, &aStyle);
1852 if (!pseudoStyle) {
1853 return;
1856 nsAtom* elemName = nullptr;
1857 nsAtom* property = nullptr;
1858 switch (aPseudoElement) {
1859 case PseudoStyleType::before:
1860 elemName = nsGkAtoms::mozgeneratedcontentbefore;
1861 property = nsGkAtoms::beforePseudoProperty;
1862 break;
1863 case PseudoStyleType::after:
1864 elemName = nsGkAtoms::mozgeneratedcontentafter;
1865 property = nsGkAtoms::afterPseudoProperty;
1866 break;
1867 case PseudoStyleType::marker:
1868 // We want to get a marker style even if we match no rules, but we still
1869 // want to check the result of GeneratedContentPseudoExists.
1870 elemName = nsGkAtoms::mozgeneratedcontentmarker;
1871 property = nsGkAtoms::markerPseudoProperty;
1872 break;
1873 default:
1874 MOZ_ASSERT_UNREACHABLE("unexpected aPseudoElement");
1877 RefPtr<NodeInfo> nodeInfo = mDocument->NodeInfoManager()->GetNodeInfo(
1878 elemName, nullptr, kNameSpaceID_None, nsINode::ELEMENT_NODE);
1879 RefPtr<Element> container;
1880 nsresult rv = NS_NewXMLElement(getter_AddRefs(container), nodeInfo.forget());
1881 if (NS_FAILED(rv)) {
1882 return;
1885 // Cleared when the pseudo is unbound from the tree, so no need to store a
1886 // strong reference, nor a destructor.
1887 aOriginatingElement.SetProperty(property, container.get());
1889 container->SetIsNativeAnonymousRoot();
1890 container->SetPseudoElementType(aPseudoElement);
1892 BindContext context(aOriginatingElement, BindContext::ForNativeAnonymous);
1893 rv = container->BindToTree(context, aOriginatingElement);
1894 if (NS_FAILED(rv)) {
1895 container->UnbindFromTree();
1896 return;
1899 if (mDocument->DevToolsAnonymousAndShadowEventsEnabled()) {
1900 container->QueueDevtoolsAnonymousEvent(/* aIsRemove = */ false);
1903 // Servo has already eagerly computed the style for the container, so we can
1904 // just stick the style on the element and avoid an additional traversal.
1906 // We don't do this for pseudos that may trigger animations or transitions,
1907 // since those need to be kicked off by the traversal machinery.
1909 // Note that when a pseudo-element animates, we flag the originating element,
1910 // so we check that flag, but we could also a more expensive (but exhaustive)
1911 // check using EffectSet::GetEffectSet, for example.
1912 if (!Servo_ComputedValues_SpecifiesAnimationsOrTransitions(pseudoStyle) &&
1913 !aOriginatingElement.MayHaveAnimations()) {
1914 Servo_SetExplicitStyle(container, pseudoStyle);
1915 } else {
1916 // If animations are involved, we avoid the SetExplicitStyle optimization
1917 // above. We need to grab style with animations from the pseudo element and
1918 // replace old one.
1919 mPresShell->StyleSet()->StyleNewSubtree(container);
1920 pseudoStyle = ServoStyleSet::ResolveServoStyle(*container);
1923 auto AppendChild = [&container, this](nsIContent* aChild) {
1924 // We don't strictly have to set NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE
1925 // here; it would get set under AppendChildTo. But AppendChildTo might
1926 // think that we're going from not being anonymous to being anonymous and
1927 // do some extra work; setting the flag here avoids that.
1928 aChild->SetFlags(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE);
1929 container->AppendChildTo(aChild, false, IgnoreErrors());
1930 if (auto* childElement = Element::FromNode(aChild)) {
1931 // If we created any children elements, Servo needs to traverse them, but
1932 // the root is already set up.
1933 mPresShell->StyleSet()->StyleNewSubtree(childElement);
1936 const uint32_t contentCount = pseudoStyle->StyleContent()->ContentCount();
1937 for (uint32_t contentIndex = 0; contentIndex < contentCount; contentIndex++) {
1938 CreateGeneratedContent(aState, aOriginatingElement, *pseudoStyle,
1939 contentIndex, AppendChild);
1941 // If a ::marker has no 'content' then generate it from its 'list-style-*'.
1942 if (contentCount == 0 && aPseudoElement == PseudoStyleType::marker) {
1943 CreateGeneratedContentFromListStyle(aState, aOriginatingElement,
1944 *pseudoStyle, AppendChild);
1946 auto flags = ItemFlags{ItemFlag::IsGeneratedContent} + aExtraFlags;
1947 AddFrameConstructionItemsInternal(aState, container, aParentFrame, true,
1948 pseudoStyle, flags, aItems);
1951 /****************************************************
1952 ** BEGIN TABLE SECTION
1953 ****************************************************/
1955 // The term pseudo frame is being used instead of anonymous frame, since
1956 // anonymous frame has been used elsewhere to refer to frames that have
1957 // generated content
1959 // Return whether the given frame is a table pseudo-frame. Note that
1960 // cell-content and table-outer frames have pseudo-types, but are always
1961 // created, even for non-anonymous cells and tables respectively. So for those
1962 // we have to examine the cell or table frame to see whether it's a pseudo
1963 // frame. In particular, a lone table caption will have a table wrapper as its
1964 // parent, but will also trigger construction of an empty inner table, which
1965 // will be the one we can examine to see whether the wrapper was a pseudo-frame.
1966 static bool IsTablePseudo(nsIFrame* aFrame) {
1967 auto pseudoType = aFrame->Style()->GetPseudoType();
1968 return pseudoType != PseudoStyleType::NotPseudo &&
1969 (pseudoType == PseudoStyleType::table ||
1970 pseudoType == PseudoStyleType::inlineTable ||
1971 pseudoType == PseudoStyleType::tableColGroup ||
1972 pseudoType == PseudoStyleType::tableRowGroup ||
1973 pseudoType == PseudoStyleType::tableRow ||
1974 pseudoType == PseudoStyleType::tableCell ||
1975 (pseudoType == PseudoStyleType::cellContent &&
1976 aFrame->GetParent()->Style()->GetPseudoType() ==
1977 PseudoStyleType::tableCell) ||
1978 (pseudoType == PseudoStyleType::tableWrapper &&
1979 (aFrame->PrincipalChildList()
1980 .FirstChild()
1981 ->Style()
1982 ->GetPseudoType() == PseudoStyleType::table ||
1983 aFrame->PrincipalChildList()
1984 .FirstChild()
1985 ->Style()
1986 ->GetPseudoType() == PseudoStyleType::inlineTable)));
1989 static bool IsRubyPseudo(nsIFrame* aFrame) {
1990 return RubyUtils::IsRubyPseudo(aFrame->Style()->GetPseudoType());
1993 static bool IsTableOrRubyPseudo(nsIFrame* aFrame) {
1994 return IsTablePseudo(aFrame) || IsRubyPseudo(aFrame);
1997 /* static */
1998 nsCSSFrameConstructor::ParentType nsCSSFrameConstructor::GetParentType(
1999 LayoutFrameType aFrameType) {
2000 if (aFrameType == LayoutFrameType::Table) {
2001 return eTypeTable;
2003 if (aFrameType == LayoutFrameType::TableRowGroup) {
2004 return eTypeRowGroup;
2006 if (aFrameType == LayoutFrameType::TableRow) {
2007 return eTypeRow;
2009 if (aFrameType == LayoutFrameType::TableColGroup) {
2010 return eTypeColGroup;
2012 if (aFrameType == LayoutFrameType::RubyBaseContainer) {
2013 return eTypeRubyBaseContainer;
2015 if (aFrameType == LayoutFrameType::RubyTextContainer) {
2016 return eTypeRubyTextContainer;
2018 if (aFrameType == LayoutFrameType::Ruby) {
2019 return eTypeRuby;
2022 return eTypeBlock;
2025 // Pull all the captions present in aItems out into aCaptions.
2026 static void PullOutCaptionFrames(nsFrameList& aList, nsFrameList& aCaptions) {
2027 nsIFrame* child = aList.FirstChild();
2028 while (child) {
2029 nsIFrame* nextSibling = child->GetNextSibling();
2030 if (child->StyleDisplay()->mDisplay == StyleDisplay::TableCaption) {
2031 aList.RemoveFrame(child);
2032 aCaptions.AppendFrame(nullptr, child);
2034 child = nextSibling;
2038 // Construct the outer, inner table frames and the children frames for the
2039 // table.
2040 // XXX Page break frames for pseudo table frames are not constructed to avoid
2041 // the risk associated with revising the pseudo frame mechanism. The long term
2042 // solution of having frames handle page-break-before/after will solve the
2043 // problem.
2044 nsIFrame* nsCSSFrameConstructor::ConstructTable(nsFrameConstructorState& aState,
2045 FrameConstructionItem& aItem,
2046 nsContainerFrame* aParentFrame,
2047 const nsStyleDisplay* aDisplay,
2048 nsFrameList& aFrameList) {
2049 MOZ_ASSERT(aDisplay->mDisplay == StyleDisplay::Table ||
2050 aDisplay->mDisplay == StyleDisplay::InlineTable,
2051 "Unexpected call");
2053 nsIContent* const content = aItem.mContent;
2054 ComputedStyle* const computedStyle = aItem.mComputedStyle;
2055 const bool isMathMLContent = content->IsMathMLElement();
2057 // create the pseudo SC for the table wrapper as a child of the inner SC
2058 RefPtr<ComputedStyle> outerComputedStyle =
2059 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
2060 PseudoStyleType::tableWrapper, computedStyle);
2062 // Create the table wrapper frame which holds the caption and inner table
2063 // frame
2064 nsContainerFrame* newFrame;
2065 if (isMathMLContent)
2066 newFrame = NS_NewMathMLmtableOuterFrame(mPresShell, outerComputedStyle);
2067 else
2068 newFrame = NS_NewTableWrapperFrame(mPresShell, outerComputedStyle);
2070 nsContainerFrame* geometricParent = aState.GetGeometricParent(
2071 *outerComputedStyle->StyleDisplay(), aParentFrame);
2073 // Init the table wrapper frame
2074 InitAndRestoreFrame(aState, content, geometricParent, newFrame);
2076 // Create the inner table frame
2077 nsContainerFrame* innerFrame;
2078 if (isMathMLContent)
2079 innerFrame = NS_NewMathMLmtableFrame(mPresShell, computedStyle);
2080 else
2081 innerFrame = NS_NewTableFrame(mPresShell, computedStyle);
2083 InitAndRestoreFrame(aState, content, newFrame, innerFrame);
2084 innerFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
2086 // Put the newly created frames into the right child list
2087 SetInitialSingleChild(newFrame, innerFrame);
2089 aState.AddChild(newFrame, aFrameList, content, aParentFrame);
2091 if (!mRootElementFrame) {
2092 // The frame we're constructing will be the root element frame.
2093 SetRootElementFrameAndConstructCanvasAnonContent(newFrame, aState,
2094 aFrameList);
2097 nsFrameList childList;
2099 // Process children
2100 nsFrameConstructorSaveState absoluteSaveState;
2102 // Mark the table frame as an absolute container if needed
2103 newFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
2104 if (newFrame->IsAbsPosContainingBlock()) {
2105 aState.PushAbsoluteContainingBlock(newFrame, newFrame, absoluteSaveState);
2108 nsFrameConstructorSaveState floatSaveState;
2109 aState.MaybePushFloatContainingBlock(innerFrame, floatSaveState);
2111 if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) {
2112 ConstructFramesFromItemList(
2113 aState, aItem.mChildItems, innerFrame,
2114 aItem.mFCData->mBits & FCDATA_IS_WRAPPER_ANON_BOX, childList);
2115 } else {
2116 ProcessChildren(aState, content, computedStyle, innerFrame, true, childList,
2117 false);
2120 nsFrameList captionList;
2121 PullOutCaptionFrames(childList, captionList);
2123 // Set the inner table frame's initial primary list
2124 innerFrame->SetInitialChildList(FrameChildListID::Principal,
2125 std::move(childList));
2127 // Set the table wrapper frame's secondary childlist lists
2128 if (captionList.NotEmpty()) {
2129 captionList.ApplySetParent(newFrame);
2130 newFrame->SetInitialChildList(FrameChildListID::Caption,
2131 std::move(captionList));
2134 return newFrame;
2137 static void MakeTablePartAbsoluteContainingBlock(
2138 nsFrameConstructorState& aState, nsFrameConstructorSaveState& aAbsSaveState,
2139 nsContainerFrame* aFrame) {
2140 // If we're positioned, then we need to become an absolute containing block
2141 // for any absolutely positioned children.
2142 aFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
2143 if (aFrame->IsAbsPosContainingBlock()) {
2144 aState.PushAbsoluteContainingBlock(aFrame, aFrame, aAbsSaveState);
2148 nsIFrame* nsCSSFrameConstructor::ConstructTableRowOrRowGroup(
2149 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
2150 nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay,
2151 nsFrameList& aFrameList) {
2152 MOZ_ASSERT(aDisplay->mDisplay == StyleDisplay::TableRow ||
2153 aDisplay->mDisplay == StyleDisplay::TableRowGroup ||
2154 aDisplay->mDisplay == StyleDisplay::TableFooterGroup ||
2155 aDisplay->mDisplay == StyleDisplay::TableHeaderGroup,
2156 "Not a row or row group");
2157 MOZ_ASSERT(aItem.mComputedStyle->StyleDisplay() == aDisplay,
2158 "Display style doesn't match style");
2159 nsIContent* const content = aItem.mContent;
2160 ComputedStyle* const computedStyle = aItem.mComputedStyle;
2162 nsContainerFrame* newFrame;
2163 if (aDisplay->mDisplay == StyleDisplay::TableRow) {
2164 if (content->IsMathMLElement())
2165 newFrame = NS_NewMathMLmtrFrame(mPresShell, computedStyle);
2166 else
2167 newFrame = NS_NewTableRowFrame(mPresShell, computedStyle);
2168 } else {
2169 newFrame = NS_NewTableRowGroupFrame(mPresShell, computedStyle);
2172 InitAndRestoreFrame(aState, content, aParentFrame, newFrame);
2174 nsFrameConstructorSaveState absoluteSaveState;
2175 MakeTablePartAbsoluteContainingBlock(aState, absoluteSaveState, newFrame);
2177 nsFrameConstructorSaveState floatSaveState;
2178 aState.MaybePushFloatContainingBlock(newFrame, floatSaveState);
2180 nsFrameList childList;
2181 if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) {
2182 ConstructFramesFromItemList(
2183 aState, aItem.mChildItems, newFrame,
2184 aItem.mFCData->mBits & FCDATA_IS_WRAPPER_ANON_BOX, childList);
2185 } else {
2186 ProcessChildren(aState, content, computedStyle, newFrame, true, childList,
2187 false);
2190 newFrame->SetInitialChildList(FrameChildListID::Principal,
2191 std::move(childList));
2192 aFrameList.AppendFrame(nullptr, newFrame);
2193 return newFrame;
2196 nsIFrame* nsCSSFrameConstructor::ConstructTableCol(
2197 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
2198 nsContainerFrame* aParentFrame, const nsStyleDisplay* aStyleDisplay,
2199 nsFrameList& aFrameList) {
2200 nsIContent* const content = aItem.mContent;
2201 ComputedStyle* const computedStyle = aItem.mComputedStyle;
2203 nsTableColFrame* colFrame = NS_NewTableColFrame(mPresShell, computedStyle);
2204 InitAndRestoreFrame(aState, content, aParentFrame, colFrame);
2206 NS_ASSERTION(colFrame->Style() == computedStyle, "Unexpected style");
2208 aFrameList.AppendFrame(nullptr, colFrame);
2210 // construct additional col frames if the col frame has a span > 1
2211 int32_t span = colFrame->GetSpan();
2212 for (int32_t spanX = 1; spanX < span; spanX++) {
2213 nsTableColFrame* newCol = NS_NewTableColFrame(mPresShell, computedStyle);
2214 InitAndRestoreFrame(aState, content, aParentFrame, newCol, false);
2215 aFrameList.LastChild()->SetNextContinuation(newCol);
2216 newCol->SetPrevContinuation(aFrameList.LastChild());
2217 aFrameList.AppendFrame(nullptr, newCol);
2218 newCol->SetColType(eColAnonymousCol);
2221 return colFrame;
2224 nsIFrame* nsCSSFrameConstructor::ConstructTableCell(
2225 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
2226 nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay,
2227 nsFrameList& aFrameList) {
2228 MOZ_ASSERT(aDisplay->mDisplay == StyleDisplay::TableCell, "Unexpected call");
2230 nsIContent* const content = aItem.mContent;
2231 ComputedStyle* const computedStyle = aItem.mComputedStyle;
2232 const bool isMathMLContent = content->IsMathMLElement();
2234 nsTableFrame* tableFrame =
2235 static_cast<nsTableRowFrame*>(aParentFrame)->GetTableFrame();
2236 nsContainerFrame* newFrame;
2237 // <mtable> is border separate in mathml.css and the MathML code doesn't
2238 // implement border collapse. For those users who style <mtable> with border
2239 // collapse, give them the default non-MathML table frames that understand
2240 // border collapse. This won't break us because MathML table frames are all
2241 // subclasses of the default table code, and so we can freely mix <mtable>
2242 // with <mtr> or <tr>, <mtd> or <td>. What will happen is just that non-MathML
2243 // frames won't understand MathML attributes and will therefore miss the
2244 // special handling that the MathML code does.
2245 if (isMathMLContent && !tableFrame->IsBorderCollapse()) {
2246 newFrame = NS_NewMathMLmtdFrame(mPresShell, computedStyle, tableFrame);
2247 } else {
2248 // Warning: If you change this and add a wrapper frame around table cell
2249 // frames, make sure Bug 368554 doesn't regress!
2250 // See IsInAutoWidthTableCellForQuirk() in nsImageFrame.cpp.
2251 newFrame = NS_NewTableCellFrame(mPresShell, computedStyle, tableFrame);
2254 // Initialize the table cell frame
2255 InitAndRestoreFrame(aState, content, aParentFrame, newFrame);
2256 newFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
2258 // Resolve pseudo style and initialize the body cell frame
2259 RefPtr<ComputedStyle> innerPseudoStyle =
2260 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
2261 PseudoStyleType::cellContent, computedStyle);
2263 // Create a block frame that will format the cell's content
2264 nsContainerFrame* cellInnerFrame;
2265 if (isMathMLContent) {
2266 cellInnerFrame = NS_NewMathMLmtdInnerFrame(mPresShell, innerPseudoStyle);
2267 } else {
2268 cellInnerFrame = NS_NewBlockFormattingContext(mPresShell, innerPseudoStyle);
2271 InitAndRestoreFrame(aState, content, newFrame, cellInnerFrame);
2273 nsFrameConstructorSaveState absoluteSaveState;
2274 MakeTablePartAbsoluteContainingBlock(aState, absoluteSaveState, newFrame);
2276 nsFrameConstructorSaveState floatSaveState;
2277 aState.MaybePushFloatContainingBlock(cellInnerFrame, floatSaveState);
2279 nsFrameList childList;
2280 if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) {
2281 AutoFrameConstructionPageName pageNameTracker(aState, cellInnerFrame);
2282 ConstructFramesFromItemList(
2283 aState, aItem.mChildItems, cellInnerFrame,
2284 aItem.mFCData->mBits & FCDATA_IS_WRAPPER_ANON_BOX, childList);
2285 } else {
2286 // Process the child content
2287 ProcessChildren(aState, content, computedStyle, cellInnerFrame, true,
2288 childList, !isMathMLContent);
2291 cellInnerFrame->SetInitialChildList(FrameChildListID::Principal,
2292 std::move(childList));
2293 SetInitialSingleChild(newFrame, cellInnerFrame);
2294 aFrameList.AppendFrame(nullptr, newFrame);
2295 return newFrame;
2298 static inline bool NeedFrameFor(const nsFrameConstructorState& aState,
2299 nsContainerFrame* aParentFrame,
2300 nsIContent* aChildContent) {
2301 // XXX the GetContent() != aChildContent check is needed due to bug 135040.
2302 // Remove it once that's fixed.
2303 MOZ_ASSERT(
2304 !aChildContent->GetPrimaryFrame() || aState.mCreatingExtraFrames ||
2305 aChildContent->GetPrimaryFrame()->GetContent() != aChildContent,
2306 "Why did we get called?");
2308 // don't create a whitespace frame if aParentFrame doesn't want it.
2309 // always create frames for children in generated content. counter(),
2310 // quotes, and attr() content can easily change dynamically and we don't
2311 // want to be reconstructing frames. It's not even clear that these
2312 // should be considered ignorable just because they evaluate to
2313 // whitespace.
2315 // We could handle all this in CreateNeededPseudoContainers or some other
2316 // place after we build our frame construction items, but that would involve
2317 // creating frame construction items for whitespace kids that ignores
2318 // white-space, where we know we'll be dropping them all anyway, and involve
2319 // an extra walk down the frame construction item list.
2320 auto excludesIgnorableWhitespace = [](nsIFrame* aParentFrame) {
2321 return aParentFrame->IsFrameOfType(nsIFrame::eMathML);
2323 if (!aParentFrame || !excludesIgnorableWhitespace(aParentFrame) ||
2324 aParentFrame->IsGeneratedContentFrame() || !aChildContent->IsText()) {
2325 return true;
2328 aChildContent->SetFlags(NS_CREATE_FRAME_IF_NON_WHITESPACE |
2329 NS_REFRAME_IF_WHITESPACE);
2330 return !aChildContent->TextIsOnlyWhitespace();
2333 /***********************************************
2334 * END TABLE SECTION
2335 ***********************************************/
2337 void nsCSSFrameConstructor::SetRootElementFrameAndConstructCanvasAnonContent(
2338 nsContainerFrame* aRootElementFrame, nsFrameConstructorState& aState,
2339 nsFrameList& aFrameList) {
2340 MOZ_DIAGNOSTIC_ASSERT(!mRootElementFrame);
2341 mRootElementFrame = aRootElementFrame;
2342 if (mDocElementContainingBlock->IsCanvasFrame()) {
2343 // NOTE(emilio): This is in the reverse order compared to normal anonymous
2344 // children. We usually generate anonymous kids first, then non-anonymous,
2345 // but we generate the doc element frame the other way around. This is fine
2346 // either way, but generating anonymous children in a different order
2347 // requires changing nsCanvasFrame (and a whole lot of other potentially
2348 // unknown code) to look at the last child to find the root frame rather
2349 // than the first child.
2350 ConstructAnonymousContentForCanvas(aState, mDocElementContainingBlock,
2351 aRootElementFrame->GetContent(),
2352 aFrameList);
2356 nsIFrame* nsCSSFrameConstructor::ConstructDocElementFrame(
2357 Element* aDocElement) {
2358 MOZ_ASSERT(GetRootFrame(),
2359 "No viewport? Someone forgot to call ConstructRootFrame!");
2360 MOZ_ASSERT(!mDocElementContainingBlock,
2361 "Shouldn't have a doc element containing block here");
2363 // Resolve a new style for the viewport since it may be affected by a new root
2364 // element style (e.g. a propagated 'direction').
2366 // @see ComputedStyle::ApplyStyleFixups
2368 RefPtr<ComputedStyle> sc =
2369 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
2370 PseudoStyleType::viewport, nullptr);
2371 GetRootFrame()->SetComputedStyleWithoutNotification(sc);
2374 // Ensure the document element is styled at this point.
2375 if (!aDocElement->HasServoData()) {
2376 mPresShell->StyleSet()->StyleNewSubtree(aDocElement);
2378 aDocElement->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
2380 // Make sure to call UpdateViewportScrollStylesOverride before
2381 // SetUpDocElementContainingBlock, since it sets up our scrollbar state
2382 // properly.
2383 DebugOnly<nsIContent*> propagatedScrollFrom;
2384 if (nsPresContext* presContext = mPresShell->GetPresContext()) {
2385 propagatedScrollFrom = presContext->UpdateViewportScrollStylesOverride();
2388 SetUpDocElementContainingBlock(aDocElement);
2390 // This has the side-effect of getting `mFrameTreeState` from our docshell.
2392 // FIXME(emilio): There may be a more sensible time to do this.
2393 if (!mFrameTreeState) {
2394 mPresShell->CaptureHistoryState(getter_AddRefs(mFrameTreeState));
2397 NS_ASSERTION(mDocElementContainingBlock, "Should have parent by now");
2398 nsFrameConstructorState state(
2399 mPresShell,
2400 GetAbsoluteContainingBlock(mDocElementContainingBlock, FIXED_POS),
2401 nullptr, nullptr, do_AddRef(mFrameTreeState));
2403 RefPtr<ComputedStyle> computedStyle =
2404 ServoStyleSet::ResolveServoStyle(*aDocElement);
2406 const nsStyleDisplay* display = computedStyle->StyleDisplay();
2408 // --------- IF SCROLLABLE WRAP IN SCROLLFRAME --------
2410 NS_ASSERTION(!display->IsScrollableOverflow() ||
2411 state.mPresContext->IsPaginated() ||
2412 propagatedScrollFrom == aDocElement,
2413 "Scrollbars should have been propagated to the viewport");
2415 if (MOZ_UNLIKELY(display->mDisplay == StyleDisplay::None)) {
2416 return nullptr;
2419 if (mDocElementContainingBlock->IsCanvasFrame()) {
2420 // This implements "The Principal Writing Mode".
2421 // https://drafts.csswg.org/css-writing-modes-3/#principal-flow
2423 // If there's a <body> element in an HTML document, its writing-mode,
2424 // direction, and text-orientation override the root element's used value.
2426 // We need to copy <body>'s WritingMode to mDocElementContainingBlock before
2427 // construct mRootElementFrame so that anonymous internal frames such as
2428 // <html> with table style can copy their parent frame's mWritingMode in
2429 // nsIFrame::Init().
2430 MOZ_ASSERT(!mRootElementFrame,
2431 "We need to copy <body>'s principal writing-mode before "
2432 "constructing mRootElementFrame.");
2434 const WritingMode propagatedWM = [&] {
2435 const WritingMode rootWM(computedStyle);
2436 if (computedStyle->StyleDisplay()->IsContainAny()) {
2437 return rootWM;
2439 Element* body = mDocument->GetBodyElement();
2440 if (!body) {
2441 return rootWM;
2443 RefPtr<ComputedStyle> bodyStyle = ResolveComputedStyle(body);
2444 if (bodyStyle->StyleDisplay()->IsContainAny()) {
2445 return rootWM;
2447 const WritingMode bodyWM(bodyStyle);
2448 if (bodyWM != rootWM) {
2449 nsContentUtils::ReportToConsole(
2450 nsIScriptError::warningFlag, "Layout"_ns, mDocument,
2451 nsContentUtils::eLAYOUT_PROPERTIES,
2452 "PrincipalWritingModePropagationWarning");
2454 return bodyWM;
2455 }();
2457 mDocElementContainingBlock->PropagateWritingModeToSelfAndAncestors(
2458 propagatedWM);
2461 nsFrameConstructorSaveState docElementContainingBlockAbsoluteSaveState;
2462 // Push the absolute containing block now so we can absolutely position
2463 // the root element
2464 mDocElementContainingBlock->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
2465 state.PushAbsoluteContainingBlock(mDocElementContainingBlock,
2466 mDocElementContainingBlock,
2467 docElementContainingBlockAbsoluteSaveState);
2469 // The rules from CSS 2.1, section 9.2.4, have already been applied
2470 // by the style system, so we can assume that display->mDisplay is
2471 // either NONE, BLOCK, or TABLE.
2473 // contentFrame is the primary frame for the root element. frameList contains
2474 // the children of the initial containing block.
2476 // The first of those frames is usually `contentFrame`, but it can be
2477 // different, in particular if the root frame is positioned, in which case
2478 // contentFrame is the out-of-flow frame and frameList.FirstChild() is the
2479 // placeholder.
2481 // The rest of the frames in frameList are the anonymous content of the canvas
2482 // frame.
2483 nsContainerFrame* contentFrame;
2484 nsFrameList frameList;
2485 bool processChildren = false;
2487 nsFrameConstructorSaveState absoluteSaveState;
2489 if (aDocElement->IsSVGElement()) {
2490 if (!aDocElement->IsSVGElement(nsGkAtoms::svg)) {
2491 return nullptr;
2493 // We're going to call the right function ourselves, so no need to give a
2494 // function to this FrameConstructionData.
2496 // XXXbz on the other hand, if we converted this whole function to
2497 // FrameConstructionData/Item, then we'd need the right function
2498 // here... but would probably be able to get away with less code in this
2499 // function in general.
2500 static constexpr FrameConstructionData rootSVGData;
2501 AutoFrameConstructionItem item(this, &rootSVGData, aDocElement,
2502 do_AddRef(computedStyle), true);
2504 contentFrame = static_cast<nsContainerFrame*>(ConstructOuterSVG(
2505 state, item, mDocElementContainingBlock, display, frameList));
2506 } else if (display->mDisplay == StyleDisplay::Flex ||
2507 display->mDisplay == StyleDisplay::WebkitBox ||
2508 display->mDisplay == StyleDisplay::Grid) {
2509 auto func = [&] {
2510 if (display->mDisplay == StyleDisplay::Grid) {
2511 return NS_NewGridContainerFrame;
2513 return NS_NewFlexContainerFrame;
2514 }();
2515 contentFrame = func(mPresShell, computedStyle);
2516 InitAndRestoreFrame(
2517 state, aDocElement,
2518 state.GetGeometricParent(*display, mDocElementContainingBlock),
2519 contentFrame);
2520 state.AddChild(contentFrame, frameList, aDocElement,
2521 mDocElementContainingBlock);
2522 processChildren = true;
2524 contentFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
2525 if (contentFrame->IsAbsPosContainingBlock()) {
2526 state.PushAbsoluteContainingBlock(contentFrame, contentFrame,
2527 absoluteSaveState);
2529 } else if (display->mDisplay == StyleDisplay::Table) {
2530 // We're going to call the right function ourselves, so no need to give a
2531 // function to this FrameConstructionData.
2533 // XXXbz on the other hand, if we converted this whole function to
2534 // FrameConstructionData/Item, then we'd need the right function
2535 // here... but would probably be able to get away with less code in this
2536 // function in general.
2537 static constexpr FrameConstructionData rootTableData;
2538 AutoFrameConstructionItem item(this, &rootTableData, aDocElement,
2539 do_AddRef(computedStyle), true);
2541 // if the document is a table then just populate it.
2542 contentFrame = static_cast<nsContainerFrame*>(ConstructTable(
2543 state, item, mDocElementContainingBlock, display, frameList));
2544 } else if (display->DisplayInside() == StyleDisplayInside::Ruby) {
2545 static constexpr FrameConstructionData data(
2546 &nsCSSFrameConstructor::ConstructBlockRubyFrame);
2547 AutoFrameConstructionItem item(this, &data, aDocElement,
2548 do_AddRef(computedStyle), true);
2549 contentFrame = static_cast<nsContainerFrame*>(ConstructBlockRubyFrame(
2550 state, item,
2551 state.GetGeometricParent(*display, mDocElementContainingBlock), display,
2552 frameList));
2553 } else {
2554 MOZ_ASSERT(display->mDisplay == StyleDisplay::Block ||
2555 display->mDisplay == StyleDisplay::FlowRoot,
2556 "Unhandled display type for root element");
2557 contentFrame = NS_NewBlockFormattingContext(mPresShell, computedStyle);
2558 ConstructBlock(
2559 state, aDocElement,
2560 state.GetGeometricParent(*display, mDocElementContainingBlock),
2561 mDocElementContainingBlock, computedStyle, &contentFrame, frameList,
2562 contentFrame->IsAbsPosContainingBlock() ? contentFrame : nullptr);
2565 MOZ_ASSERT(frameList.FirstChild());
2566 MOZ_ASSERT(frameList.FirstChild()->GetContent() == aDocElement);
2567 MOZ_ASSERT(contentFrame);
2569 MOZ_ASSERT(
2570 processChildren ? !mRootElementFrame : mRootElementFrame == contentFrame,
2571 "unexpected mRootElementFrame");
2572 if (processChildren) {
2573 SetRootElementFrameAndConstructCanvasAnonContent(contentFrame, state,
2574 frameList);
2577 // Figure out which frame has the main style for the document element,
2578 // assigning it to mRootElementStyleFrame.
2579 // Backgrounds should be propagated from that frame to the viewport.
2580 contentFrame->GetParentComputedStyle(&mRootElementStyleFrame);
2581 bool isChild = mRootElementStyleFrame &&
2582 mRootElementStyleFrame->GetParent() == contentFrame;
2583 if (!isChild) {
2584 mRootElementStyleFrame = mRootElementFrame;
2587 if (processChildren) {
2588 // Still need to process the child content
2589 nsFrameList childList;
2591 NS_ASSERTION(!contentFrame->IsBlockFrameOrSubclass() &&
2592 !contentFrame->IsFrameOfType(nsIFrame::eSVG),
2593 "Only XUL frames should reach here");
2595 nsFrameConstructorSaveState floatSaveState;
2596 state.MaybePushFloatContainingBlock(contentFrame, floatSaveState);
2598 ProcessChildren(state, aDocElement, computedStyle, contentFrame, true,
2599 childList, false);
2601 // Set the initial child lists
2602 contentFrame->SetInitialChildList(FrameChildListID::Principal,
2603 std::move(childList));
2606 nsIFrame* newFrame = frameList.FirstChild();
2607 // set the primary frame
2608 aDocElement->SetPrimaryFrame(contentFrame);
2609 mDocElementContainingBlock->AppendFrames(FrameChildListID::Principal,
2610 std::move(frameList));
2612 return newFrame;
2615 nsIFrame* nsCSSFrameConstructor::ConstructRootFrame() {
2616 AUTO_PROFILER_LABEL("nsCSSFrameConstructor::ConstructRootFrame",
2617 LAYOUT_FrameConstruction);
2618 AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
2620 ServoStyleSet* styleSet = mPresShell->StyleSet();
2622 // --------- BUILD VIEWPORT -----------
2623 RefPtr<ComputedStyle> viewportPseudoStyle =
2624 styleSet->ResolveInheritingAnonymousBoxStyle(PseudoStyleType::viewport,
2625 nullptr);
2626 ViewportFrame* viewportFrame =
2627 NS_NewViewportFrame(mPresShell, viewportPseudoStyle);
2629 // XXXbz do we _have_ to pass a null content pointer to that frame?
2630 // Would it really kill us to pass in the root element or something?
2631 // What would that break?
2632 viewportFrame->Init(nullptr, nullptr, nullptr);
2634 viewportFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
2636 // Bind the viewport frame to the root view
2637 if (nsView* rootView = mPresShell->GetViewManager()->GetRootView()) {
2638 viewportFrame->SetView(rootView);
2639 viewportFrame->SyncFrameViewProperties(rootView);
2640 rootView->SetNeedsWindowPropertiesSync();
2643 // Make it an absolute container for fixed-pos elements
2644 viewportFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
2645 viewportFrame->MarkAsAbsoluteContainingBlock();
2647 return viewportFrame;
2650 void nsCSSFrameConstructor::SetUpDocElementContainingBlock(
2651 nsIContent* aDocElement) {
2652 MOZ_ASSERT(aDocElement, "No element?");
2653 MOZ_ASSERT(!aDocElement->GetParent(), "Not root content?");
2654 MOZ_ASSERT(aDocElement->GetUncomposedDoc(), "Not in a document?");
2655 MOZ_ASSERT(aDocElement->GetUncomposedDoc()->GetRootElement() == aDocElement,
2656 "Not the root of the document?");
2659 how the root frame hierarchy should look
2661 Galley presentation, with scrolling:
2663 ViewportFrame [fixed-cb]
2664 nsHTMLScrollFrame (if needed)
2665 nsCanvasFrame [abs-cb]
2666 root element frame (nsBlockFrame, SVGOuterSVGFrame,
2667 nsTableWrapperFrame, nsPlaceholderFrame)
2669 Print presentation, non-XUL
2671 ViewportFrame
2672 nsCanvasFrame
2673 nsPageSequenceFrame
2674 PrintedSheetFrame
2675 nsPageFrame
2676 nsPageContentFrame [fixed-cb]
2677 nsCanvasFrame [abs-cb]
2678 root element frame (nsBlockFrame, SVGOuterSVGFrame,
2679 nsTableWrapperFrame, nsPlaceholderFrame)
2681 Print-preview presentation, non-XUL
2683 ViewportFrame
2684 nsHTMLScrollFrame
2685 nsCanvasFrame
2686 nsPageSequenceFrame
2687 PrintedSheetFrame
2688 nsPageFrame
2689 nsPageContentFrame [fixed-cb]
2690 nsCanvasFrame [abs-cb]
2691 root element frame (nsBlockFrame, SVGOuterSVGFrame,
2692 nsTableWrapperFrame,
2693 nsPlaceholderFrame)
2695 Print/print preview of XUL is not supported.
2696 [fixed-cb]: the default containing block for fixed-pos content
2697 [abs-cb]: the default containing block for abs-pos content
2699 Meaning of nsCSSFrameConstructor fields:
2700 mRootElementFrame is "root element frame". This is the primary frame for
2701 the root element.
2702 mDocElementContainingBlock is the parent of mRootElementFrame
2703 (i.e. nsCanvasFrame)
2704 mPageSequenceFrame is the nsPageSequenceFrame, or null if there isn't
2708 // --------- CREATE ROOT FRAME -------
2710 // Create the root frame. The document element's frame is a child of the
2711 // root frame.
2713 // The root frame serves two purposes:
2714 // - reserves space for any margins needed for the document element's frame
2715 // - renders the document element's background. This ensures the background
2716 // covers the entire canvas as specified by the CSS2 spec
2718 nsPresContext* presContext = mPresShell->GetPresContext();
2719 const bool isPaginated = presContext->IsRootPaginatedDocument();
2721 const bool isHTML = aDocElement->IsHTMLElement();
2722 const bool isXUL = !isHTML && aDocElement->IsXULElement();
2724 const bool isScrollable = [&] {
2725 if (isPaginated) {
2726 return presContext->HasPaginatedScrolling();
2728 // Never create scrollbars for XUL documents or top level XHTML documents
2729 // that disable scrolling.
2730 if (isXUL) {
2731 return false;
2733 if (aDocElement->OwnerDoc()->ChromeRulesEnabled() &&
2734 aDocElement->AsElement()->AttrValueIs(
2735 kNameSpaceID_None, nsGkAtoms::scrolling, nsGkAtoms::_false,
2736 eCaseMatters)) {
2737 return false;
2739 return true;
2740 }();
2742 nsContainerFrame* viewportFrame =
2743 static_cast<nsContainerFrame*>(GetRootFrame());
2744 ComputedStyle* viewportPseudoStyle = viewportFrame->Style();
2746 nsContainerFrame* rootFrame =
2747 NS_NewCanvasFrame(mPresShell, viewportPseudoStyle);
2748 PseudoStyleType rootPseudo = PseudoStyleType::canvas;
2749 mDocElementContainingBlock = rootFrame;
2751 // --------- IF SCROLLABLE WRAP IN SCROLLFRAME --------
2753 // If the device supports scrolling (e.g., in galley mode on the screen and
2754 // for print-preview, but not when printing), then create a scroll frame that
2755 // will act as the scrolling mechanism for the viewport.
2756 // XXX Do we even need a viewport when printing to a printer?
2758 // We no longer need to do overflow propagation here. It's taken care of
2759 // when we construct frames for the element whose overflow might be
2760 // propagated
2761 NS_ASSERTION(!isScrollable || !isXUL,
2762 "XUL documents should never be scrollable - see above");
2764 nsContainerFrame* newFrame = rootFrame;
2765 RefPtr<ComputedStyle> rootPseudoStyle;
2766 // we must create a state because if the scrollbars are GFX it needs the
2767 // state to build the scrollbar frames.
2768 nsFrameConstructorState state(mPresShell, nullptr, nullptr, nullptr);
2770 // Start off with the viewport as parent; we'll adjust it as needed.
2771 nsContainerFrame* parentFrame = viewportFrame;
2773 ServoStyleSet* styleSet = mPresShell->StyleSet();
2774 // If paginated, make sure we don't put scrollbars in
2775 if (!isScrollable) {
2776 rootPseudoStyle = styleSet->ResolveInheritingAnonymousBoxStyle(
2777 rootPseudo, viewportPseudoStyle);
2778 } else {
2779 rootPseudo = PseudoStyleType::scrolledCanvas;
2781 // Build the frame. We give it the content we are wrapping which is the
2782 // document element, the root frame, the parent view port frame, and we
2783 // should get back the new frame and the scrollable view if one was
2784 // created.
2786 // resolve a context for the scrollframe
2787 RefPtr<ComputedStyle> computedStyle =
2788 styleSet->ResolveInheritingAnonymousBoxStyle(
2789 PseudoStyleType::viewportScroll, viewportPseudoStyle);
2791 // Note that the viewport scrollframe is always built with
2792 // overflow:auto style. This forces the scroll frame to create
2793 // anonymous content for both scrollbars. This is necessary even
2794 // if the HTML or BODY elements are overriding the viewport
2795 // scroll style to 'hidden' --- dynamic style changes might put
2796 // scrollbars back on the viewport and we don't want to have to
2797 // reframe the viewport to create the scrollbar content.
2798 newFrame = nullptr;
2799 rootPseudoStyle =
2800 BeginBuildingScrollFrame(state, aDocElement, computedStyle,
2801 viewportFrame, rootPseudo, true, newFrame);
2802 parentFrame = newFrame;
2805 rootFrame->SetComputedStyleWithoutNotification(rootPseudoStyle);
2806 rootFrame->Init(aDocElement, parentFrame, nullptr);
2808 if (isScrollable) {
2809 FinishBuildingScrollFrame(parentFrame, rootFrame);
2812 if (isPaginated) {
2813 // Create a page sequence frame
2815 RefPtr<ComputedStyle> pageSequenceStyle =
2816 styleSet->ResolveInheritingAnonymousBoxStyle(
2817 PseudoStyleType::pageSequence, viewportPseudoStyle);
2818 mPageSequenceFrame =
2819 NS_NewPageSequenceFrame(mPresShell, pageSequenceStyle);
2820 mPageSequenceFrame->Init(aDocElement, rootFrame, nullptr);
2821 SetInitialSingleChild(rootFrame, mPageSequenceFrame);
2824 // Create the first printed sheet frame, as the sole child (for now) of our
2825 // page sequence frame (mPageSequenceFrame).
2826 auto* printedSheetFrame =
2827 ConstructPrintedSheetFrame(mPresShell, mPageSequenceFrame, nullptr);
2828 SetInitialSingleChild(mPageSequenceFrame, printedSheetFrame);
2830 MOZ_ASSERT(!mNextPageContentFramePageName,
2831 "Next page name should not have been set.");
2833 // Create the first page, as the sole child (for now) of the printed sheet
2834 // frame that we just created.
2835 nsContainerFrame* canvasFrame;
2836 nsContainerFrame* pageFrame =
2837 ConstructPageFrame(mPresShell, printedSheetFrame, nullptr, canvasFrame);
2838 pageFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
2839 SetInitialSingleChild(printedSheetFrame, pageFrame);
2841 // The eventual parent of the document element frame.
2842 // XXX should this be set for every new page (in ConstructPageFrame)?
2843 mDocElementContainingBlock = canvasFrame;
2846 if (viewportFrame->HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
2847 SetInitialSingleChild(viewportFrame, newFrame);
2848 } else {
2849 viewportFrame->AppendFrames(FrameChildListID::Principal,
2850 nsFrameList(newFrame, newFrame));
2854 void nsCSSFrameConstructor::ConstructAnonymousContentForCanvas(
2855 nsFrameConstructorState& aState, nsContainerFrame* aFrame,
2856 nsIContent* aDocElement, nsFrameList& aFrameList) {
2857 NS_ASSERTION(aFrame->IsCanvasFrame(), "aFrame should be canvas frame!");
2858 MOZ_ASSERT(mRootElementFrame->GetContent() == aDocElement);
2860 AutoTArray<nsIAnonymousContentCreator::ContentInfo, 4> anonymousItems;
2861 GetAnonymousContent(aDocElement, aFrame, anonymousItems);
2862 if (anonymousItems.IsEmpty()) {
2863 return;
2866 AutoFrameConstructionItemList itemsToConstruct(this);
2867 AutoFrameConstructionPageName pageNameTracker(aState, aFrame);
2868 AddFCItemsForAnonymousContent(aState, aFrame, anonymousItems,
2869 itemsToConstruct, pageNameTracker);
2870 ConstructFramesFromItemList(aState, itemsToConstruct, aFrame,
2871 /* aParentIsWrapperAnonBox = */ false,
2872 aFrameList);
2875 PrintedSheetFrame* nsCSSFrameConstructor::ConstructPrintedSheetFrame(
2876 PresShell* aPresShell, nsContainerFrame* aParentFrame,
2877 nsIFrame* aPrevSheetFrame) {
2878 RefPtr<ComputedStyle> printedSheetPseudoStyle =
2879 aPresShell->StyleSet()->ResolveNonInheritingAnonymousBoxStyle(
2880 PseudoStyleType::printedSheet);
2882 auto* printedSheetFrame =
2883 NS_NewPrintedSheetFrame(aPresShell, printedSheetPseudoStyle);
2885 printedSheetFrame->Init(nullptr, aParentFrame, aPrevSheetFrame);
2887 return printedSheetFrame;
2890 nsContainerFrame* nsCSSFrameConstructor::ConstructPageFrame(
2891 PresShell* aPresShell, nsContainerFrame* aParentFrame,
2892 nsIFrame* aPrevPageFrame, nsContainerFrame*& aCanvasFrame) {
2893 ServoStyleSet* styleSet = aPresShell->StyleSet();
2895 RefPtr<ComputedStyle> pagePseudoStyle =
2896 styleSet->ResolveNonInheritingAnonymousBoxStyle(PseudoStyleType::page);
2898 nsContainerFrame* pageFrame = NS_NewPageFrame(aPresShell, pagePseudoStyle);
2900 // Initialize the page frame and force it to have a view. This makes printing
2901 // of the pages easier and faster.
2902 pageFrame->Init(nullptr, aParentFrame, aPrevPageFrame);
2904 RefPtr<const nsAtom> pageName;
2905 if (mNextPageContentFramePageName) {
2906 pageName = mNextPageContentFramePageName.forget();
2907 } else if (aPrevPageFrame) {
2908 pageName = aPrevPageFrame->ComputePageValue();
2909 MOZ_ASSERT(pageName,
2910 "Page name from prev-in-flow should not have been null");
2912 RefPtr<ComputedStyle> pageContentPseudoStyle =
2913 styleSet->ResolvePageContentStyle(pageName,
2914 StylePagePseudoClassFlags::NONE);
2916 nsContainerFrame* pageContentFrame = NS_NewPageContentFrame(
2917 aPresShell, pageContentPseudoStyle, pageName.forget());
2919 // Initialize the page content frame and force it to have a view. Also make it
2920 // the containing block for fixed elements which are repeated on every page.
2921 nsIFrame* prevPageContentFrame = nullptr;
2922 if (aPrevPageFrame) {
2923 prevPageContentFrame = aPrevPageFrame->PrincipalChildList().FirstChild();
2924 NS_ASSERTION(prevPageContentFrame, "missing page content frame");
2926 pageContentFrame->Init(nullptr, pageFrame, prevPageContentFrame);
2927 if (!prevPageContentFrame) {
2928 // The canvas is an inheriting anon box, so needs to be "owned" by the page
2929 // content.
2930 pageContentFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
2932 SetInitialSingleChild(pageFrame, pageContentFrame);
2933 // Make it an absolute container for fixed-pos elements
2934 pageContentFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
2935 pageContentFrame->MarkAsAbsoluteContainingBlock();
2937 RefPtr<ComputedStyle> canvasPseudoStyle =
2938 styleSet->ResolveInheritingAnonymousBoxStyle(PseudoStyleType::canvas,
2939 pageContentPseudoStyle);
2941 aCanvasFrame = NS_NewCanvasFrame(aPresShell, canvasPseudoStyle);
2943 nsIFrame* prevCanvasFrame = nullptr;
2944 if (prevPageContentFrame) {
2945 prevCanvasFrame = prevPageContentFrame->PrincipalChildList().FirstChild();
2946 NS_ASSERTION(prevCanvasFrame, "missing canvas frame");
2948 aCanvasFrame->Init(nullptr, pageContentFrame, prevCanvasFrame);
2949 SetInitialSingleChild(pageContentFrame, aCanvasFrame);
2950 return pageFrame;
2953 /* static */
2954 nsIFrame* nsCSSFrameConstructor::CreatePlaceholderFrameFor(
2955 PresShell* aPresShell, nsIContent* aContent, nsIFrame* aFrame,
2956 nsContainerFrame* aParentFrame, nsIFrame* aPrevInFlow,
2957 nsFrameState aTypeBit) {
2958 RefPtr<ComputedStyle> placeholderStyle =
2959 aPresShell->StyleSet()->ResolveStyleForPlaceholder();
2961 // The placeholder frame gets a pseudo style.
2962 nsPlaceholderFrame* placeholderFrame =
2963 NS_NewPlaceholderFrame(aPresShell, placeholderStyle, aTypeBit);
2965 placeholderFrame->Init(aContent, aParentFrame, aPrevInFlow);
2967 // Associate the placeholder/out-of-flow with each other.
2968 placeholderFrame->SetOutOfFlowFrame(aFrame);
2969 aFrame->SetProperty(nsIFrame::PlaceholderFrameProperty(), placeholderFrame);
2971 aFrame->AddStateBits(NS_FRAME_OUT_OF_FLOW);
2973 return placeholderFrame;
2976 // Clears any lazy bits set in the range [aStartContent, aEndContent). If
2977 // aEndContent is null, that means to clear bits in all siblings starting with
2978 // aStartContent. aStartContent must not be null unless aEndContent is also
2979 // null. We do this so that when new children are inserted under elements whose
2980 // frame is a leaf the new children don't cause us to try to construct frames
2981 // for the existing children again.
2982 static inline void ClearLazyBits(nsIContent* aStartContent,
2983 nsIContent* aEndContent) {
2984 MOZ_ASSERT(aStartContent || !aEndContent,
2985 "Must have start child if we have an end child");
2987 for (nsIContent* cur = aStartContent; cur != aEndContent;
2988 cur = cur->GetNextSibling()) {
2989 cur->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
2993 nsIFrame* nsCSSFrameConstructor::ConstructSelectFrame(
2994 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
2995 nsContainerFrame* aParentFrame, const nsStyleDisplay* aStyleDisplay,
2996 nsFrameList& aFrameList) {
2997 nsIContent* const content = aItem.mContent;
2998 ComputedStyle* const computedStyle = aItem.mComputedStyle;
3000 // Construct a frame-based listbox or combobox
3001 dom::HTMLSelectElement* sel = dom::HTMLSelectElement::FromNode(content);
3002 MOZ_ASSERT(sel);
3003 if (sel->IsCombobox()) {
3004 // Construct a frame-based combo box.
3005 // The frame-based combo box is built out of three parts. A display area, a
3006 // button and a dropdown list. The display area and button are created
3007 // through anonymous content. The drop-down list's frame is created
3008 // explicitly. The combobox frame shares its content with the drop-down
3009 // list.
3010 nsFrameState flags = NS_BLOCK_FLOAT_MGR;
3011 nsComboboxControlFrame* comboboxFrame =
3012 NS_NewComboboxControlFrame(mPresShell, computedStyle, flags);
3014 // Save the history state so we don't restore during construction
3015 // since the complete tree is required before we restore.
3016 nsILayoutHistoryState* historyState = aState.mFrameState;
3017 aState.mFrameState = nullptr;
3018 // Initialize the combobox frame
3019 InitAndRestoreFrame(aState, content,
3020 aState.GetGeometricParent(*aStyleDisplay, aParentFrame),
3021 comboboxFrame);
3023 comboboxFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
3025 aState.AddChild(comboboxFrame, aFrameList, content, aParentFrame);
3027 // Resolve pseudo element style for the dropdown list
3028 RefPtr<ComputedStyle> listStyle =
3029 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
3030 PseudoStyleType::dropDownList, computedStyle);
3032 // child frames of combobox frame
3033 nsFrameList childList;
3035 // Create display and button frames from the combobox's anonymous content.
3036 // The anonymous content is appended to existing anonymous content for this
3037 // element (the scrollbars).
3039 // nsComboboxControlFrame needs special frame creation behavior for its
3040 // first piece of anonymous content, which means that we can't take the
3041 // normal ProcessChildren path.
3042 AutoTArray<nsIAnonymousContentCreator::ContentInfo, 2> newAnonymousItems;
3043 DebugOnly<nsresult> rv =
3044 GetAnonymousContent(content, comboboxFrame, newAnonymousItems);
3045 MOZ_ASSERT(NS_SUCCEEDED(rv));
3046 MOZ_ASSERT(!newAnonymousItems.IsEmpty());
3048 // Manually create a frame for the special NAC.
3049 MOZ_ASSERT(newAnonymousItems[0].mContent ==
3050 comboboxFrame->GetDisplayNode());
3051 newAnonymousItems.RemoveElementAt(0);
3052 nsIFrame* customFrame = comboboxFrame->CreateFrameForDisplayNode();
3053 MOZ_ASSERT(customFrame);
3054 childList.AppendFrame(nullptr, customFrame);
3056 nsFrameConstructorSaveState floatSaveState;
3057 aState.MaybePushFloatContainingBlock(comboboxFrame, floatSaveState);
3059 // The other piece of NAC can take the normal path.
3060 AutoFrameConstructionItemList fcItems(this);
3061 AutoFrameConstructionPageName pageNameTracker(aState, comboboxFrame);
3062 AddFCItemsForAnonymousContent(aState, comboboxFrame, newAnonymousItems,
3063 fcItems, pageNameTracker);
3064 ConstructFramesFromItemList(aState, fcItems, comboboxFrame,
3065 /* aParentIsWrapperAnonBox = */ false,
3066 childList);
3068 comboboxFrame->SetInitialChildList(FrameChildListID::Principal,
3069 std::move(childList));
3071 aState.mFrameState = historyState;
3072 if (aState.mFrameState) {
3073 // Restore frame state for the entire subtree of |comboboxFrame|.
3074 RestoreFrameState(comboboxFrame, aState.mFrameState);
3076 return comboboxFrame;
3079 // Listbox, not combobox
3080 nsContainerFrame* listFrame =
3081 NS_NewListControlFrame(mPresShell, computedStyle);
3083 nsContainerFrame* scrolledFrame =
3084 NS_NewSelectsAreaFrame(mPresShell, computedStyle, NS_BLOCK_FLOAT_MGR);
3086 // ******* this code stolen from Initialze ScrollFrame ********
3087 // please adjust this code to use BuildScrollFrame.
3089 InitializeListboxSelect(aState, listFrame, scrolledFrame, content,
3090 aParentFrame, computedStyle, aFrameList);
3092 return listFrame;
3095 void nsCSSFrameConstructor::InitializeListboxSelect(
3096 nsFrameConstructorState& aState, nsContainerFrame* scrollFrame,
3097 nsContainerFrame* scrolledFrame, nsIContent* aContent,
3098 nsContainerFrame* aParentFrame, ComputedStyle* aComputedStyle,
3099 nsFrameList& aFrameList) {
3100 // Initialize it
3101 nsContainerFrame* geometricParent =
3102 aState.GetGeometricParent(*aComputedStyle->StyleDisplay(), aParentFrame);
3104 // We don't call InitAndRestoreFrame for scrollFrame because we can only
3105 // restore the frame state after its parts have been created (in particular,
3106 // the scrollable view). So we have to split Init and Restore.
3108 scrollFrame->Init(aContent, geometricParent, nullptr);
3109 aState.AddChild(scrollFrame, aFrameList, aContent, aParentFrame);
3110 BuildScrollFrame(aState, aContent, aComputedStyle, scrolledFrame,
3111 geometricParent, scrollFrame);
3112 if (aState.mFrameState) {
3113 // Restore frame state for the scroll frame
3114 RestoreFrameStateFor(scrollFrame, aState.mFrameState);
3117 nsFrameConstructorSaveState floatSaveState;
3118 aState.MaybePushFloatContainingBlock(scrolledFrame, floatSaveState);
3120 // Process children
3121 nsFrameList childList;
3123 ProcessChildren(aState, aContent, aComputedStyle, scrolledFrame, false,
3124 childList, false);
3126 // Set the scrolled frame's initial child lists
3127 scrolledFrame->SetInitialChildList(FrameChildListID::Principal,
3128 std::move(childList));
3131 nsIFrame* nsCSSFrameConstructor::ConstructFieldSetFrame(
3132 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
3133 nsContainerFrame* aParentFrame, const nsStyleDisplay* aStyleDisplay,
3134 nsFrameList& aFrameList) {
3135 AutoRestore<bool> savedHasRenderedLegend(aState.mHasRenderedLegend);
3136 aState.mHasRenderedLegend = false;
3137 nsIContent* const content = aItem.mContent;
3138 ComputedStyle* const computedStyle = aItem.mComputedStyle;
3140 nsContainerFrame* fieldsetFrame =
3141 NS_NewFieldSetFrame(mPresShell, computedStyle);
3143 // Initialize it
3144 InitAndRestoreFrame(aState, content,
3145 aState.GetGeometricParent(*aStyleDisplay, aParentFrame),
3146 fieldsetFrame);
3148 fieldsetFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
3150 // Resolve style and initialize the frame
3151 RefPtr<ComputedStyle> fieldsetContentStyle =
3152 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
3153 PseudoStyleType::fieldsetContent, computedStyle);
3155 const nsStyleDisplay* fieldsetContentDisplay =
3156 fieldsetContentStyle->StyleDisplay();
3157 bool isScrollable = fieldsetContentDisplay->IsScrollableOverflow();
3158 nsContainerFrame* scrollFrame = nullptr;
3159 if (isScrollable) {
3160 fieldsetContentStyle = BeginBuildingScrollFrame(
3161 aState, content, fieldsetContentStyle, fieldsetFrame,
3162 PseudoStyleType::scrolledContent, false, scrollFrame);
3165 // Create the inner ::-moz-fieldset-content frame.
3166 nsContainerFrame* contentFrameTop;
3167 nsContainerFrame* contentFrame;
3168 auto parent = scrollFrame ? scrollFrame : fieldsetFrame;
3169 MOZ_ASSERT(fieldsetContentDisplay->DisplayOutside() ==
3170 StyleDisplayOutside::Block);
3171 switch (fieldsetContentDisplay->DisplayInside()) {
3172 case StyleDisplayInside::Flex:
3173 contentFrame = NS_NewFlexContainerFrame(mPresShell, fieldsetContentStyle);
3174 InitAndRestoreFrame(aState, content, parent, contentFrame);
3175 contentFrameTop = contentFrame;
3176 break;
3177 case StyleDisplayInside::Grid:
3178 contentFrame = NS_NewGridContainerFrame(mPresShell, fieldsetContentStyle);
3179 InitAndRestoreFrame(aState, content, parent, contentFrame);
3180 contentFrameTop = contentFrame;
3181 break;
3182 default: {
3183 MOZ_ASSERT(fieldsetContentDisplay->mDisplay == StyleDisplay::Block,
3184 "bug in StyleAdjuster::adjust_for_fieldset_content?");
3186 contentFrame =
3187 NS_NewBlockFormattingContext(mPresShell, fieldsetContentStyle);
3188 if (fieldsetContentStyle->StyleColumn()->IsColumnContainerStyle()) {
3189 contentFrameTop = BeginBuildingColumns(
3190 aState, content, parent, contentFrame, fieldsetContentStyle);
3191 } else {
3192 // No need to create column container. Initialize content frame.
3193 InitAndRestoreFrame(aState, content, parent, contentFrame);
3194 contentFrameTop = contentFrame;
3197 break;
3201 aState.AddChild(fieldsetFrame, aFrameList, content, aParentFrame);
3203 // Process children
3204 nsFrameConstructorSaveState absoluteSaveState;
3205 nsFrameList childList;
3207 contentFrameTop->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
3208 if (fieldsetFrame->IsAbsPosContainingBlock()) {
3209 aState.PushAbsoluteContainingBlock(contentFrameTop, fieldsetFrame,
3210 absoluteSaveState);
3213 nsFrameConstructorSaveState floatSaveState;
3214 aState.MaybePushFloatContainingBlock(contentFrame, floatSaveState);
3216 ProcessChildren(aState, content, computedStyle, contentFrame, true, childList,
3217 true);
3218 nsFrameList fieldsetKids;
3219 fieldsetKids.AppendFrame(nullptr,
3220 scrollFrame ? scrollFrame : contentFrameTop);
3222 if (!MayNeedToCreateColumnSpanSiblings(contentFrame, childList)) {
3223 // Set the inner frame's initial child lists.
3224 contentFrame->SetInitialChildList(FrameChildListID::Principal,
3225 std::move(childList));
3226 } else {
3227 // Extract any initial non-column-span kids, and put them in inner frame's
3228 // child list.
3229 nsFrameList initialNonColumnSpanKids =
3230 childList.Split([](nsIFrame* f) { return f->IsColumnSpan(); });
3231 contentFrame->SetInitialChildList(FrameChildListID::Principal,
3232 std::move(initialNonColumnSpanKids));
3234 if (childList.NotEmpty()) {
3235 nsFrameList columnSpanSiblings = CreateColumnSpanSiblings(
3236 aState, contentFrame, childList,
3237 // Column content should never be a absolute/fixed positioned
3238 // containing block. Pass nullptr as aPositionedFrame.
3239 nullptr);
3240 FinishBuildingColumns(aState, contentFrameTop, contentFrame,
3241 columnSpanSiblings);
3245 if (isScrollable) {
3246 FinishBuildingScrollFrame(scrollFrame, contentFrameTop);
3249 // We use AppendFrames here because the rendered legend will already
3250 // be present in the principal child list if it exists.
3251 fieldsetFrame->AppendFrames(FrameChildListID::NoReflowPrincipal,
3252 std::move(fieldsetKids));
3254 return fieldsetFrame;
3257 nsIFrame* nsCSSFrameConstructor::ConstructDetails(
3258 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
3259 nsContainerFrame* aParentFrame, const nsStyleDisplay* aStyleDisplay,
3260 nsFrameList& aFrameList) {
3261 if (!aStyleDisplay->IsScrollableOverflow()) {
3262 return ConstructNonScrollableBlock(aState, aItem, aParentFrame,
3263 aStyleDisplay, aFrameList);
3266 // Build a scroll frame if necessary.
3267 return ConstructScrollableBlock(aState, aItem, aParentFrame, aStyleDisplay,
3268 aFrameList);
3271 nsIFrame* nsCSSFrameConstructor::ConstructBlockRubyFrame(
3272 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
3273 nsContainerFrame* aParentFrame, const nsStyleDisplay* aStyleDisplay,
3274 nsFrameList& aFrameList) {
3275 nsIContent* const content = aItem.mContent;
3276 ComputedStyle* const computedStyle = aItem.mComputedStyle;
3278 nsBlockFrame* blockFrame = NS_NewBlockFrame(mPresShell, computedStyle);
3279 nsContainerFrame* newFrame = blockFrame;
3280 nsContainerFrame* geometricParent =
3281 aState.GetGeometricParent(*aStyleDisplay, aParentFrame);
3282 AutoFrameConstructionPageName pageNameTracker(aState, blockFrame);
3283 if ((aItem.mFCData->mBits & FCDATA_MAY_NEED_SCROLLFRAME) &&
3284 aStyleDisplay->IsScrollableOverflow()) {
3285 nsContainerFrame* scrollframe = nullptr;
3286 BuildScrollFrame(aState, content, computedStyle, blockFrame,
3287 geometricParent, scrollframe);
3288 newFrame = scrollframe;
3289 } else {
3290 InitAndRestoreFrame(aState, content, geometricParent, blockFrame);
3293 RefPtr<ComputedStyle> rubyStyle =
3294 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
3295 PseudoStyleType::blockRubyContent, computedStyle);
3296 nsContainerFrame* rubyFrame = NS_NewRubyFrame(mPresShell, rubyStyle);
3297 InitAndRestoreFrame(aState, content, blockFrame, rubyFrame);
3298 SetInitialSingleChild(blockFrame, rubyFrame);
3299 blockFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
3301 aState.AddChild(newFrame, aFrameList, content, aParentFrame);
3303 if (!mRootElementFrame) {
3304 // The frame we're constructing will be the root element frame.
3305 SetRootElementFrameAndConstructCanvasAnonContent(newFrame, aState,
3306 aFrameList);
3309 nsFrameConstructorSaveState absoluteSaveState;
3310 blockFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
3311 if (newFrame->IsAbsPosContainingBlock()) {
3312 aState.PushAbsoluteContainingBlock(blockFrame, blockFrame,
3313 absoluteSaveState);
3315 nsFrameConstructorSaveState floatSaveState;
3316 aState.MaybePushFloatContainingBlock(blockFrame, floatSaveState);
3318 nsFrameList childList;
3319 ProcessChildren(aState, content, rubyStyle, rubyFrame, true, childList, false,
3320 nullptr);
3321 rubyFrame->SetInitialChildList(FrameChildListID::Principal,
3322 std::move(childList));
3324 return newFrame;
3327 static nsIFrame* FindAncestorWithGeneratedContentPseudo(nsIFrame* aFrame) {
3328 for (nsIFrame* f = aFrame->GetParent(); f; f = f->GetParent()) {
3329 NS_ASSERTION(f->IsGeneratedContentFrame(),
3330 "should not have exited generated content");
3331 auto pseudo = f->Style()->GetPseudoType();
3332 if (pseudo == PseudoStyleType::before || pseudo == PseudoStyleType::after ||
3333 pseudo == PseudoStyleType::marker)
3334 return f;
3336 return nullptr;
3339 /* static */
3340 const nsCSSFrameConstructor::FrameConstructionData*
3341 nsCSSFrameConstructor::FindTextData(const Text& aTextContent,
3342 nsIFrame* aParentFrame) {
3343 if (aParentFrame && IsFrameForSVG(aParentFrame)) {
3344 if (!aParentFrame->IsInSVGTextSubtree()) {
3345 return nullptr;
3348 // FIXME(bug 1588477) Don't render stuff in display: contents / Shadow DOM
3349 // subtrees, because TextCorrespondenceRecorder in the SVG text code doesn't
3350 // really know how to deal with it. This kinda sucks. :(
3351 if (aParentFrame->GetContent() != aTextContent.GetParent()) {
3352 return nullptr;
3355 static constexpr FrameConstructionData sSVGTextData(
3356 NS_NewTextFrame, FCDATA_IS_LINE_PARTICIPANT | FCDATA_IS_SVG_TEXT);
3357 return &sSVGTextData;
3360 static constexpr FrameConstructionData sTextData(NS_NewTextFrame,
3361 FCDATA_IS_LINE_PARTICIPANT);
3362 return &sTextData;
3365 void nsCSSFrameConstructor::ConstructTextFrame(
3366 const FrameConstructionData* aData, nsFrameConstructorState& aState,
3367 nsIContent* aContent, nsContainerFrame* aParentFrame,
3368 ComputedStyle* aComputedStyle, nsFrameList& aFrameList) {
3369 MOZ_ASSERT(aData, "Must have frame construction data");
3371 nsIFrame* newFrame =
3372 (*aData->mFunc.mCreationFunc)(mPresShell, aComputedStyle);
3374 InitAndRestoreFrame(aState, aContent, aParentFrame, newFrame);
3376 // We never need to create a view for a text frame.
3378 if (newFrame->IsGeneratedContentFrame()) {
3379 UniquePtr<nsGenConInitializer> initializer(
3380 static_cast<nsGenConInitializer*>(
3381 aContent->TakeProperty(nsGkAtoms::genConInitializerProperty)));
3382 if (initializer) {
3383 if (initializer->mNode.release()->InitTextFrame(
3384 initializer->mList,
3385 FindAncestorWithGeneratedContentPseudo(newFrame), newFrame)) {
3386 (this->*(initializer->mDirtyAll))();
3391 // Add the newly constructed frame to the flow
3392 aFrameList.AppendFrame(nullptr, newFrame);
3394 if (!aState.mCreatingExtraFrames || (aContent->IsInNativeAnonymousSubtree() &&
3395 !aContent->GetPrimaryFrame())) {
3396 aContent->SetPrimaryFrame(newFrame);
3400 /* static */
3401 const nsCSSFrameConstructor::FrameConstructionData*
3402 nsCSSFrameConstructor::FindDataByInt(int32_t aInt, const Element& aElement,
3403 ComputedStyle& aComputedStyle,
3404 const FrameConstructionDataByInt* aDataPtr,
3405 uint32_t aDataLength) {
3406 for (const FrameConstructionDataByInt *curData = aDataPtr,
3407 *endData = aDataPtr + aDataLength;
3408 curData != endData; ++curData) {
3409 if (curData->mInt == aInt) {
3410 const FrameConstructionData* data = &curData->mData;
3411 if (data->mBits & FCDATA_FUNC_IS_DATA_GETTER) {
3412 return data->mFunc.mDataGetter(aElement, aComputedStyle);
3415 return data;
3419 return nullptr;
3422 /* static */
3423 const nsCSSFrameConstructor::FrameConstructionData*
3424 nsCSSFrameConstructor::FindDataByTag(const Element& aElement,
3425 ComputedStyle& aStyle,
3426 const FrameConstructionDataByTag* aDataPtr,
3427 uint32_t aDataLength) {
3428 const nsAtom* tag = aElement.NodeInfo()->NameAtom();
3429 for (const FrameConstructionDataByTag *curData = aDataPtr,
3430 *endData = aDataPtr + aDataLength;
3431 curData != endData; ++curData) {
3432 if (curData->mTag == tag) {
3433 const FrameConstructionData* data = &curData->mData;
3434 if (data->mBits & FCDATA_FUNC_IS_DATA_GETTER) {
3435 return data->mFunc.mDataGetter(aElement, aStyle);
3438 return data;
3442 return nullptr;
3445 #define SUPPRESS_FCDATA() FrameConstructionData(nullptr, FCDATA_SUPPRESS_FRAME)
3446 #define SIMPLE_INT_CREATE(_int, _func) \
3447 { int32_t(_int), FrameConstructionData(_func) }
3448 #define SIMPLE_INT_CHAIN(_int, _func) \
3449 { int32_t(_int), FrameConstructionData(_func) }
3450 #define COMPLEX_INT_CREATE(_int, _func) \
3451 { int32_t(_int), FrameConstructionData(_func) }
3453 #define SIMPLE_TAG_CREATE(_tag, _func) \
3454 { nsGkAtoms::_tag, FrameConstructionData(_func) }
3455 #define SIMPLE_TAG_CHAIN(_tag, _func) \
3456 { nsGkAtoms::_tag, FrameConstructionData(_func) }
3457 #define COMPLEX_TAG_CREATE(_tag, _func) \
3458 { nsGkAtoms::_tag, FrameConstructionData(_func) }
3460 static nsFieldSetFrame* GetFieldSetFrameFor(nsIFrame* aFrame) {
3461 auto pseudo = aFrame->Style()->GetPseudoType();
3462 if (pseudo == PseudoStyleType::fieldsetContent ||
3463 pseudo == PseudoStyleType::scrolledContent ||
3464 pseudo == PseudoStyleType::columnSet ||
3465 pseudo == PseudoStyleType::columnContent) {
3466 return GetFieldSetFrameFor(aFrame->GetParent());
3468 return do_QueryFrame(aFrame);
3471 /* static */
3472 const nsCSSFrameConstructor::FrameConstructionData*
3473 nsCSSFrameConstructor::FindHTMLData(const Element& aElement,
3474 nsIFrame* aParentFrame,
3475 ComputedStyle& aStyle) {
3476 MOZ_ASSERT(aElement.IsHTMLElement());
3477 NS_ASSERTION(!aParentFrame ||
3478 aParentFrame->Style()->GetPseudoType() !=
3479 PseudoStyleType::fieldsetContent ||
3480 aParentFrame->GetParent()->IsFieldSetFrame(),
3481 "Unexpected parent for fieldset content anon box");
3483 if (aElement.IsInNativeAnonymousSubtree() &&
3484 aElement.NodeInfo()->NameAtom() == nsGkAtoms::label &&
3485 static_cast<nsFileControlFrame*>(do_QueryFrame(aParentFrame))) {
3486 static constexpr FrameConstructionData sFileLabelData(
3487 NS_NewFileControlLabelFrame);
3488 return &sFileLabelData;
3491 static constexpr FrameConstructionDataByTag sHTMLData[] = {
3492 SIMPLE_TAG_CHAIN(img, nsCSSFrameConstructor::FindImgData),
3493 SIMPLE_TAG_CHAIN(mozgeneratedcontentimage,
3494 nsCSSFrameConstructor::FindGeneratedImageData),
3495 {nsGkAtoms::br,
3496 {NS_NewBRFrame, FCDATA_IS_LINE_PARTICIPANT | FCDATA_IS_LINE_BREAK}},
3497 SIMPLE_TAG_CREATE(wbr, NS_NewWBRFrame),
3498 SIMPLE_TAG_CHAIN(input, nsCSSFrameConstructor::FindInputData),
3499 SIMPLE_TAG_CREATE(textarea, NS_NewTextControlFrame),
3500 COMPLEX_TAG_CREATE(select, &nsCSSFrameConstructor::ConstructSelectFrame),
3501 SIMPLE_TAG_CHAIN(object, nsCSSFrameConstructor::FindObjectData),
3502 SIMPLE_TAG_CHAIN(embed, nsCSSFrameConstructor::FindObjectData),
3503 COMPLEX_TAG_CREATE(fieldset,
3504 &nsCSSFrameConstructor::ConstructFieldSetFrame),
3505 SIMPLE_TAG_CREATE(frameset, NS_NewHTMLFramesetFrame),
3506 SIMPLE_TAG_CREATE(iframe, NS_NewSubDocumentFrame),
3507 {nsGkAtoms::button,
3508 {ToCreationFunc(NS_NewHTMLButtonControlFrame),
3509 FCDATA_ALLOW_BLOCK_STYLES | FCDATA_ALLOW_GRID_FLEX_COLUMN,
3510 PseudoStyleType::buttonContent}},
3511 SIMPLE_TAG_CHAIN(canvas, nsCSSFrameConstructor::FindCanvasData),
3512 SIMPLE_TAG_CREATE(video, NS_NewHTMLVideoFrame),
3513 SIMPLE_TAG_CREATE(audio, NS_NewHTMLVideoFrame),
3514 SIMPLE_TAG_CREATE(progress, NS_NewProgressFrame),
3515 SIMPLE_TAG_CREATE(meter, NS_NewMeterFrame),
3516 COMPLEX_TAG_CREATE(details, &nsCSSFrameConstructor::ConstructDetails)};
3518 return FindDataByTag(aElement, aStyle, sHTMLData, ArrayLength(sHTMLData));
3521 /* static */
3522 const nsCSSFrameConstructor::FrameConstructionData*
3523 nsCSSFrameConstructor::FindGeneratedImageData(const Element& aElement,
3524 ComputedStyle&) {
3525 if (!aElement.IsInNativeAnonymousSubtree()) {
3526 return nullptr;
3529 auto& generatedContent = static_cast<const GeneratedImageContent&>(aElement);
3530 if (generatedContent.IsForListStyleImageMarker()) {
3531 static constexpr FrameConstructionData sImgData(
3532 NS_NewImageFrameForListStyleImage);
3533 return &sImgData;
3536 static constexpr FrameConstructionData sImgData(
3537 NS_NewImageFrameForGeneratedContentIndex);
3538 return &sImgData;
3541 /* static */
3542 const nsCSSFrameConstructor::FrameConstructionData*
3543 nsCSSFrameConstructor::FindImgData(const Element& aElement,
3544 ComputedStyle& aStyle) {
3545 if (nsImageFrame::ImageFrameTypeFor(aElement, aStyle) !=
3546 nsImageFrame::ImageFrameType::ForElementRequest) {
3547 // content: url gets handled by the generic code-path.
3548 return nullptr;
3551 static constexpr FrameConstructionData sImgData(NS_NewImageFrame);
3552 return &sImgData;
3555 /* static */
3556 const nsCSSFrameConstructor::FrameConstructionData*
3557 nsCSSFrameConstructor::FindImgControlData(const Element& aElement,
3558 ComputedStyle& aStyle) {
3559 if (nsImageFrame::ImageFrameTypeFor(aElement, aStyle) !=
3560 nsImageFrame::ImageFrameType::ForElementRequest) {
3561 return nullptr;
3564 static constexpr FrameConstructionData sImgControlData(
3565 NS_NewImageControlFrame);
3566 return &sImgControlData;
3569 /* static */
3570 const nsCSSFrameConstructor::FrameConstructionData*
3571 nsCSSFrameConstructor::FindSearchControlData(const Element& aElement,
3572 ComputedStyle& aStyle) {
3573 if (StaticPrefs::layout_forms_input_type_search_enabled()) {
3574 static constexpr FrameConstructionData sSearchControlData(
3575 NS_NewSearchControlFrame);
3576 return &sSearchControlData;
3579 static constexpr FrameConstructionData sTextControlData(
3580 NS_NewTextControlFrame);
3581 return &sTextControlData;
3584 /* static */
3585 const nsCSSFrameConstructor::FrameConstructionData*
3586 nsCSSFrameConstructor::FindInputData(const Element& aElement,
3587 ComputedStyle& aStyle) {
3588 static constexpr FrameConstructionDataByInt sInputData[] = {
3589 SIMPLE_INT_CREATE(FormControlType::InputCheckbox,
3590 ToCreationFunc(NS_NewCheckboxRadioFrame)),
3591 SIMPLE_INT_CREATE(FormControlType::InputRadio,
3592 ToCreationFunc(NS_NewCheckboxRadioFrame)),
3593 SIMPLE_INT_CREATE(FormControlType::InputFile, NS_NewFileControlFrame),
3594 SIMPLE_INT_CHAIN(FormControlType::InputImage,
3595 nsCSSFrameConstructor::FindImgControlData),
3596 SIMPLE_INT_CREATE(FormControlType::InputEmail, NS_NewTextControlFrame),
3597 SIMPLE_INT_CREATE(FormControlType::InputText, NS_NewTextControlFrame),
3598 SIMPLE_INT_CREATE(FormControlType::InputTel, NS_NewTextControlFrame),
3599 SIMPLE_INT_CREATE(FormControlType::InputUrl, NS_NewTextControlFrame),
3600 SIMPLE_INT_CREATE(FormControlType::InputRange, NS_NewRangeFrame),
3601 SIMPLE_INT_CREATE(FormControlType::InputPassword, NS_NewTextControlFrame),
3602 {int32_t(FormControlType::InputColor),
3603 {NS_NewColorControlFrame, 0, PseudoStyleType::buttonContent}},
3605 SIMPLE_INT_CHAIN(FormControlType::InputSearch,
3606 nsCSSFrameConstructor::FindSearchControlData),
3607 SIMPLE_INT_CREATE(FormControlType::InputNumber, NS_NewNumberControlFrame),
3608 SIMPLE_INT_CREATE(FormControlType::InputTime, NS_NewDateTimeControlFrame),
3609 SIMPLE_INT_CREATE(FormControlType::InputDate, NS_NewDateTimeControlFrame),
3610 SIMPLE_INT_CREATE(FormControlType::InputDatetimeLocal,
3611 NS_NewDateTimeControlFrame),
3612 // TODO: this is temporary until a frame is written: bug 888320
3613 SIMPLE_INT_CREATE(FormControlType::InputMonth, NS_NewTextControlFrame),
3614 // TODO: this is temporary until a frame is written: bug 888320
3615 SIMPLE_INT_CREATE(FormControlType::InputWeek, NS_NewTextControlFrame),
3616 {int32_t(FormControlType::InputSubmit),
3617 {ToCreationFunc(NS_NewGfxButtonControlFrame), 0,
3618 PseudoStyleType::buttonContent}},
3619 {int32_t(FormControlType::InputReset),
3620 {ToCreationFunc(NS_NewGfxButtonControlFrame), 0,
3621 PseudoStyleType::buttonContent}},
3622 {int32_t(FormControlType::InputButton),
3623 {ToCreationFunc(NS_NewGfxButtonControlFrame), 0,
3624 PseudoStyleType::buttonContent}}
3625 // Keeping hidden inputs out of here on purpose for so they get frames by
3626 // display (in practice, none).
3629 auto controlType = HTMLInputElement::FromNode(aElement)->ControlType();
3631 // radio and checkbox inputs with appearance:none should be constructed
3632 // by display type. (Note that we're not checking that appearance is
3633 // not (respectively) StyleAppearance::Radio and StyleAppearance::Checkbox.)
3634 if ((controlType == FormControlType::InputCheckbox ||
3635 controlType == FormControlType::InputRadio) &&
3636 !aStyle.StyleDisplay()->HasAppearance()) {
3637 return nullptr;
3640 return FindDataByInt(int32_t(controlType), aElement, aStyle, sInputData,
3641 ArrayLength(sInputData));
3644 static nsIFrame* NS_NewSubDocumentOrImageFrame(mozilla::PresShell* aPresShell,
3645 mozilla::ComputedStyle* aStyle) {
3646 return StaticPrefs::
3647 browser_opaqueResponseBlocking_syntheticBrowsingContext_AtStartup()
3648 ? NS_NewSubDocumentFrame(aPresShell, aStyle)
3649 : NS_NewImageFrame(aPresShell, aStyle);
3652 /* static */
3653 const nsCSSFrameConstructor::FrameConstructionData*
3654 nsCSSFrameConstructor::FindObjectData(const Element& aElement,
3655 ComputedStyle& aStyle) {
3656 // GetDisplayedType isn't necessarily nsIObjectLoadingContent::TYPE_NULL for
3657 // cases when the object is broken/suppressed/etc (e.g. a broken image), but
3658 // we want to treat those cases as TYPE_NULL
3659 uint32_t type;
3660 if (aElement.State().HasState(ElementState::BROKEN)) {
3661 type = nsIObjectLoadingContent::TYPE_NULL;
3662 } else {
3663 nsCOMPtr<nsIObjectLoadingContent> objContent =
3664 do_QueryInterface(const_cast<Element*>(&aElement));
3665 NS_ASSERTION(objContent,
3666 "embed and object must implement "
3667 "nsIObjectLoadingContent!");
3669 objContent->GetDisplayedType(&type);
3672 if (type == nsIObjectLoadingContent::TYPE_FALLBACK &&
3673 !StaticPrefs::layout_use_plugin_fallback()) {
3674 type = nsIObjectLoadingContent::TYPE_NULL;
3677 static constexpr FrameConstructionDataByInt sObjectData[] = {
3678 SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_LOADING,
3679 NS_NewEmptyFrame),
3680 SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_FALLBACK,
3681 ToCreationFunc(NS_NewBlockFrame)),
3682 SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_IMAGE,
3683 NS_NewSubDocumentOrImageFrame),
3684 SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_DOCUMENT,
3685 NS_NewSubDocumentFrame),
3686 // Fake plugin handlers load as documents
3687 // XXXmats is TYPE_FAKE_PLUGIN something we need?
3688 SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_FAKE_PLUGIN,
3689 NS_NewSubDocumentFrame)
3690 // Nothing for TYPE_NULL so we'll construct frames by display there
3693 return FindDataByInt((int32_t)type, aElement, aStyle, sObjectData,
3694 ArrayLength(sObjectData));
3697 /* static */
3698 const nsCSSFrameConstructor::FrameConstructionData*
3699 nsCSSFrameConstructor::FindCanvasData(const Element& aElement,
3700 ComputedStyle& aStyle) {
3701 // We want to check whether script is enabled on the document that
3702 // could be painting to the canvas. That's the owner document of
3703 // the canvas, except when the owner document is a static document,
3704 // in which case it's the original document it was cloned from.
3705 Document* doc = aElement.OwnerDoc();
3706 if (doc->IsStaticDocument()) {
3707 doc = doc->GetOriginalDocument();
3709 if (!doc->IsScriptEnabled()) {
3710 return nullptr;
3713 static constexpr FrameConstructionData sCanvasData(
3714 NS_NewHTMLCanvasFrame, 0, PseudoStyleType::htmlCanvasContent);
3715 return &sCanvasData;
3718 void nsCSSFrameConstructor::ConstructFrameFromItemInternal(
3719 FrameConstructionItem& aItem, nsFrameConstructorState& aState,
3720 nsContainerFrame* aParentFrame, nsFrameList& aFrameList) {
3721 const FrameConstructionData* data = aItem.mFCData;
3722 NS_ASSERTION(data, "Must have frame construction data");
3724 uint32_t bits = data->mBits;
3726 NS_ASSERTION(!(bits & FCDATA_FUNC_IS_DATA_GETTER),
3727 "Should have dealt with this inside the data finder");
3729 // Some sets of bits are not compatible with each other
3730 #define CHECK_ONLY_ONE_BIT(_bit1, _bit2) \
3731 NS_ASSERTION(!(bits & _bit1) || !(bits & _bit2), \
3732 "Only one of these bits should be set")
3733 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR,
3734 FCDATA_FORCE_NULL_ABSPOS_CONTAINER);
3735 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_WRAP_KIDS_IN_BLOCKS);
3736 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_IS_POPUP);
3737 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_SKIP_ABSPOS_PUSH);
3738 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR,
3739 FCDATA_DISALLOW_GENERATED_CONTENT);
3740 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_ALLOW_BLOCK_STYLES);
3741 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR,
3742 FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS);
3743 CHECK_ONLY_ONE_BIT(FCDATA_WRAP_KIDS_IN_BLOCKS,
3744 FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS);
3745 #undef CHECK_ONLY_ONE_BIT
3746 NS_ASSERTION(!(bits & FCDATA_FORCED_NON_SCROLLABLE_BLOCK) ||
3747 ((bits & FCDATA_FUNC_IS_FULL_CTOR) &&
3748 data->mFunc.mFullConstructor ==
3749 &nsCSSFrameConstructor::ConstructNonScrollableBlock),
3750 "Unexpected FCDATA_FORCED_NON_SCROLLABLE_BLOCK flag");
3751 MOZ_ASSERT(
3752 !(bits & FCDATA_IS_WRAPPER_ANON_BOX) || (bits & FCDATA_USE_CHILD_ITEMS),
3753 "Wrapper anon boxes should always have FCDATA_USE_CHILD_ITEMS");
3754 MOZ_ASSERT(!(bits & FCDATA_ALLOW_GRID_FLEX_COLUMN) ||
3755 (bits & FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS),
3756 "Need the block wrapper bit to create grid/flex/column.");
3758 // Don't create a subdocument frame for iframes if we're creating extra frames
3759 if (aState.mCreatingExtraFrames &&
3760 aItem.mContent->IsHTMLElement(nsGkAtoms::iframe)) {
3761 return;
3764 nsIContent* const content = aItem.mContent;
3765 nsIFrame* newFrame;
3766 nsIFrame* primaryFrame;
3767 ComputedStyle* const computedStyle = aItem.mComputedStyle;
3768 const nsStyleDisplay* display = computedStyle->StyleDisplay();
3769 if (bits & FCDATA_FUNC_IS_FULL_CTOR) {
3770 newFrame = (this->*(data->mFunc.mFullConstructor))(
3771 aState, aItem, aParentFrame, display, aFrameList);
3772 MOZ_ASSERT(newFrame, "Full constructor failed");
3773 primaryFrame = newFrame;
3774 } else {
3775 newFrame = (*data->mFunc.mCreationFunc)(mPresShell, computedStyle);
3777 const bool allowOutOfFlow = !(bits & FCDATA_DISALLOW_OUT_OF_FLOW);
3778 const bool isPopup = aItem.mIsPopup;
3780 nsContainerFrame* geometricParent =
3781 (isPopup || allowOutOfFlow)
3782 ? aState.GetGeometricParent(*display, aParentFrame)
3783 : aParentFrame;
3785 // In the non-scrollframe case, primaryFrame and newFrame are equal; in the
3786 // scrollframe case, newFrame is the scrolled frame while primaryFrame is
3787 // the scrollframe.
3788 if ((bits & FCDATA_MAY_NEED_SCROLLFRAME) &&
3789 display->IsScrollableOverflow()) {
3790 nsContainerFrame* scrollframe = nullptr;
3791 BuildScrollFrame(aState, content, computedStyle, newFrame,
3792 geometricParent, scrollframe);
3793 primaryFrame = scrollframe;
3794 } else {
3795 InitAndRestoreFrame(aState, content, geometricParent, newFrame);
3796 primaryFrame = newFrame;
3799 // If we need to create a block formatting context to wrap our
3800 // kids, do it now.
3801 nsIFrame* maybeAbsoluteContainingBlockStyleFrame = primaryFrame;
3802 nsIFrame* maybeAbsoluteContainingBlock = newFrame;
3803 nsIFrame* possiblyLeafFrame = newFrame;
3804 nsContainerFrame* outerFrame = nullptr;
3805 if (bits & FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS) {
3806 RefPtr<ComputedStyle> outerStyle =
3807 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
3808 data->mAnonBoxPseudo, computedStyle);
3809 #ifdef DEBUG
3810 nsContainerFrame* containerFrame = do_QueryFrame(newFrame);
3811 MOZ_ASSERT(containerFrame);
3812 #endif
3813 nsContainerFrame* container = static_cast<nsContainerFrame*>(newFrame);
3814 nsContainerFrame* innerFrame;
3815 if (bits & FCDATA_ALLOW_GRID_FLEX_COLUMN) {
3816 switch (display->DisplayInside()) {
3817 case StyleDisplayInside::Flex:
3818 outerFrame = NS_NewFlexContainerFrame(mPresShell, outerStyle);
3819 InitAndRestoreFrame(aState, content, container, outerFrame);
3820 innerFrame = outerFrame;
3821 break;
3822 case StyleDisplayInside::Grid:
3823 outerFrame = NS_NewGridContainerFrame(mPresShell, outerStyle);
3824 InitAndRestoreFrame(aState, content, container, outerFrame);
3825 innerFrame = outerFrame;
3826 break;
3827 default: {
3828 innerFrame = NS_NewBlockFormattingContext(mPresShell, outerStyle);
3829 if (outerStyle->StyleColumn()->IsColumnContainerStyle()) {
3830 outerFrame = BeginBuildingColumns(aState, content, container,
3831 innerFrame, outerStyle);
3832 } else {
3833 // No need to create column container. Initialize innerFrame.
3834 InitAndRestoreFrame(aState, content, container, innerFrame);
3835 outerFrame = innerFrame;
3837 break;
3840 } else {
3841 innerFrame = NS_NewBlockFormattingContext(mPresShell, outerStyle);
3842 InitAndRestoreFrame(aState, content, container, innerFrame);
3843 outerFrame = innerFrame;
3846 SetInitialSingleChild(container, outerFrame);
3848 container->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
3850 // Now figure out whether newFrame or outerFrame should be the
3851 // absolute container.
3852 if (outerFrame->IsAbsPosContainingBlock()) {
3853 maybeAbsoluteContainingBlock = outerFrame;
3854 maybeAbsoluteContainingBlockStyleFrame = outerFrame;
3855 innerFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
3858 // Our kids should go into the innerFrame.
3859 newFrame = innerFrame;
3862 aState.AddChild(primaryFrame, aFrameList, content, aParentFrame,
3863 allowOutOfFlow, allowOutOfFlow);
3865 nsContainerFrame* newFrameAsContainer = do_QueryFrame(newFrame);
3866 if (newFrameAsContainer) {
3867 // Process the child content if requested
3868 nsFrameList childList;
3869 nsFrameConstructorSaveState absoluteSaveState;
3871 if (bits & FCDATA_FORCE_NULL_ABSPOS_CONTAINER) {
3872 aState.PushAbsoluteContainingBlock(nullptr, nullptr, absoluteSaveState);
3873 } else if (!(bits & FCDATA_SKIP_ABSPOS_PUSH)) {
3874 maybeAbsoluteContainingBlock->AddStateBits(
3875 NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
3876 if (maybeAbsoluteContainingBlockStyleFrame->IsAbsPosContainingBlock()) {
3877 auto* cf =
3878 static_cast<nsContainerFrame*>(maybeAbsoluteContainingBlock);
3879 aState.PushAbsoluteContainingBlock(
3880 cf, maybeAbsoluteContainingBlockStyleFrame, absoluteSaveState);
3884 nsFrameConstructorSaveState floatSaveState;
3885 aState.MaybePushFloatContainingBlock(newFrameAsContainer, floatSaveState);
3887 if (bits & FCDATA_USE_CHILD_ITEMS) {
3888 // At this point, we have not set up the auto value for this frame, and
3889 // no caller will have set it so it is not redundant and therefor will
3890 // not assert.
3891 AutoFrameConstructionPageName pageNameTracker(aState,
3892 newFrameAsContainer);
3893 ConstructFramesFromItemList(
3894 aState, aItem.mChildItems, newFrameAsContainer,
3895 bits & FCDATA_IS_WRAPPER_ANON_BOX, childList);
3896 } else {
3897 // Process the child frames.
3898 ProcessChildren(aState, content, computedStyle, newFrameAsContainer,
3899 !(bits & FCDATA_DISALLOW_GENERATED_CONTENT), childList,
3900 (bits & FCDATA_ALLOW_BLOCK_STYLES) != 0,
3901 possiblyLeafFrame);
3904 if (bits & FCDATA_WRAP_KIDS_IN_BLOCKS) {
3905 nsFrameList newList;
3906 nsFrameList currentBlockList;
3907 nsIFrame* f;
3908 while ((f = childList.FirstChild()) != nullptr) {
3909 bool wrapFrame = IsInlineFrame(f) || IsFramePartOfIBSplit(f);
3910 if (!wrapFrame) {
3911 FlushAccumulatedBlock(aState, content, newFrameAsContainer,
3912 currentBlockList, newList);
3915 childList.RemoveFrame(f);
3916 if (wrapFrame) {
3917 currentBlockList.AppendFrame(nullptr, f);
3918 } else {
3919 newList.AppendFrame(nullptr, f);
3922 FlushAccumulatedBlock(aState, content, newFrameAsContainer,
3923 currentBlockList, newList);
3925 if (childList.NotEmpty()) {
3926 // an error must have occurred, delete unprocessed frames
3927 childList.DestroyFrames();
3930 childList = std::move(newList);
3933 if (!(bits & FCDATA_ALLOW_GRID_FLEX_COLUMN) ||
3934 !MayNeedToCreateColumnSpanSiblings(newFrameAsContainer, childList)) {
3935 // Set the frame's initial child list. Note that MathML depends on this
3936 // being called even if childList is empty!
3937 newFrameAsContainer->SetInitialChildList(FrameChildListID::Principal,
3938 std::move(childList));
3939 } else {
3940 // Extract any initial non-column-span kids, and put them in inner
3941 // frame's child list.
3942 nsFrameList initialNonColumnSpanKids =
3943 childList.Split([](nsIFrame* f) { return f->IsColumnSpan(); });
3944 newFrameAsContainer->SetInitialChildList(
3945 FrameChildListID::Principal, std::move(initialNonColumnSpanKids));
3947 if (childList.NotEmpty()) {
3948 nsFrameList columnSpanSiblings = CreateColumnSpanSiblings(
3949 aState, newFrameAsContainer, childList,
3950 // Column content should never be a absolute/fixed positioned
3951 // containing block. Pass nullptr as aPositionedFrame.
3952 nullptr);
3954 MOZ_ASSERT(outerFrame,
3955 "outerFrame should be non-null if multi-column container "
3956 "is created.");
3957 FinishBuildingColumns(aState, outerFrame, newFrameAsContainer,
3958 columnSpanSiblings);
3964 NS_ASSERTION(newFrame->IsFrameOfType(nsIFrame::eLineParticipant) ==
3965 ((bits & FCDATA_IS_LINE_PARTICIPANT) != 0),
3966 "Incorrectly set FCDATA_IS_LINE_PARTICIPANT bits");
3968 // Even if mCreatingExtraFrames is set, we may need to SetPrimaryFrame for
3969 // generated content that doesn't have one yet. Note that we have to examine
3970 // the frame bit, because by this point mIsGeneratedContent has been cleared
3971 // on aItem.
3972 if ((!aState.mCreatingExtraFrames ||
3973 (aItem.mContent->IsRootOfNativeAnonymousSubtree() &&
3974 !aItem.mContent->GetPrimaryFrame())) &&
3975 !(bits & FCDATA_SKIP_FRAMESET)) {
3976 aItem.mContent->SetPrimaryFrame(primaryFrame);
3977 ActiveLayerTracker::TransferActivityToFrame(aItem.mContent, primaryFrame);
3981 static void GatherSubtreeElements(Element* aElement,
3982 nsTArray<Element*>& aElements) {
3983 aElements.AppendElement(aElement);
3984 StyleChildrenIterator iter(aElement);
3985 for (nsIContent* c = iter.GetNextChild(); c; c = iter.GetNextChild()) {
3986 if (!c->IsElement()) {
3987 continue;
3989 GatherSubtreeElements(c->AsElement(), aElements);
3993 nsresult nsCSSFrameConstructor::GetAnonymousContent(
3994 nsIContent* aParent, nsIFrame* aParentFrame,
3995 nsTArray<nsIAnonymousContentCreator::ContentInfo>& aContent) {
3996 nsIAnonymousContentCreator* creator = do_QueryFrame(aParentFrame);
3997 if (!creator) {
3998 return NS_OK;
4001 nsresult rv = creator->CreateAnonymousContent(aContent);
4002 if (NS_FAILED(rv)) {
4003 // CreateAnonymousContent failed, e.g. because the page has a <use> loop.
4004 return rv;
4007 if (aContent.IsEmpty()) {
4008 return NS_OK;
4011 const bool devtoolsEventsEnabled =
4012 mDocument->DevToolsAnonymousAndShadowEventsEnabled();
4014 MOZ_ASSERT(aParent->IsElement());
4015 for (const auto& info : aContent) {
4016 // get our child's content and set its parent to our content
4017 nsIContent* content = info.mContent;
4018 content->SetIsNativeAnonymousRoot();
4020 BindContext context(*aParent->AsElement(), BindContext::ForNativeAnonymous);
4021 rv = content->BindToTree(context, *aParent);
4023 if (NS_FAILED(rv)) {
4024 content->UnbindFromTree();
4025 return rv;
4028 if (devtoolsEventsEnabled) {
4029 content->QueueDevtoolsAnonymousEvent(/* aIsRemove = */ false);
4033 // Some situations where we don't cache anonymous content styles:
4035 // * when visibility or pointer-events is anything other than the initial
4036 // value; we rely on visibility and pointer-events inheriting into anonymous
4037 // content, but don't bother adding this state to the AnonymousContentKey,
4038 // since it's not so common. Note that with overlay scrollbars, scrollbars
4039 // always start off with pointer-events: none so we don't need to check for
4040 // that in that case.
4042 // * when the medium is anything other than screen; some UA style sheet rules
4043 // apply in e.g. print medium, and will give different results from the
4044 // cached styles
4045 Maybe<bool> computedAllowStyleCaching;
4046 auto ComputeAllowStyleCaching = [&] {
4047 if (!StaticPrefs::layout_css_cached_scrollbar_styles_enabled()) {
4048 return false;
4050 if (aParentFrame->StyleVisibility()->mVisible != StyleVisibility::Visible) {
4051 return false;
4053 nsPresContext* pc = mPresShell->GetPresContext();
4054 if (!pc->UseOverlayScrollbars() &&
4055 aParentFrame->StyleUI()->ComputedPointerEvents() !=
4056 StylePointerEvents::Auto) {
4057 return false;
4059 if (pc->Medium() != nsGkAtoms::screen) {
4060 return false;
4062 return true;
4065 auto AllowStyleCaching = [&] {
4066 if (computedAllowStyleCaching.isNothing()) {
4067 computedAllowStyleCaching.emplace(ComputeAllowStyleCaching());
4069 return computedAllowStyleCaching.value();
4072 // Compute styles for the anonymous content tree.
4073 ServoStyleSet* styleSet = mPresShell->StyleSet();
4074 for (auto& info : aContent) {
4075 Element* e = Element::FromNode(info.mContent);
4076 if (!e) {
4077 continue;
4080 if (info.mKey == AnonymousContentKey::None || !AllowStyleCaching()) {
4081 // Most NAC subtrees do not use caching of computed styles. Just go
4082 // ahead and eagerly style the subtree.
4083 styleSet->StyleNewSubtree(e);
4084 continue;
4087 // We have a NAC subtree for which we can use cached styles.
4088 AutoTArray<RefPtr<ComputedStyle>, 2> cachedStyles;
4089 AutoTArray<Element*, 2> elements;
4091 GatherSubtreeElements(e, elements);
4092 styleSet->GetCachedAnonymousContentStyles(info.mKey, cachedStyles);
4094 if (cachedStyles.IsEmpty()) {
4095 // We haven't stored cached styles for this kind of NAC subtree yet.
4096 // Eagerly compute those styles, then cache them for later.
4097 styleSet->StyleNewSubtree(e);
4098 for (Element* e : elements) {
4099 if (e->HasServoData()) {
4100 cachedStyles.AppendElement(ServoStyleSet::ResolveServoStyle(*e));
4101 } else {
4102 cachedStyles.AppendElement(nullptr);
4105 styleSet->PutCachedAnonymousContentStyles(info.mKey,
4106 std::move(cachedStyles));
4107 continue;
4110 // We previously stored cached styles for this kind of NAC subtree.
4111 // Iterate over them and set them on the subtree's elements.
4112 MOZ_ASSERT(cachedStyles.Length() == elements.Length(),
4113 "should always produce the same size NAC subtree");
4114 for (size_t i = 0, len = cachedStyles.Length(); i != len; ++i) {
4115 if (cachedStyles[i]) {
4116 #ifdef DEBUG
4117 // Assert that our cached style is the same as one we could compute.
4118 RefPtr<ComputedStyle> cs = styleSet->ResolveStyleLazily(*elements[i]);
4119 MOZ_ASSERT(
4120 cachedStyles[i]->EqualForCachedAnonymousContentStyle(*cs),
4121 "cached anonymous content styles should be identical to those we "
4122 "would compute normally");
4123 // All overlay scrollbars start off as inactive, so we can rely on their
4124 // pointer-events value being always none.
4125 MOZ_ASSERT(!mPresShell->GetPresContext()->UseOverlayScrollbars() ||
4126 cs->StyleUI()->ComputedPointerEvents() ==
4127 StylePointerEvents::None);
4128 #endif
4129 Servo_SetExplicitStyle(elements[i], cachedStyles[i]);
4134 return NS_OK;
4137 // XUL frames are not allowed to be out of flow.
4138 #define SIMPLE_XUL_FCDATA(_func) \
4139 FrameConstructionData(_func, \
4140 FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_SKIP_ABSPOS_PUSH)
4141 #define SCROLLABLE_XUL_FCDATA(_func) \
4142 FrameConstructionData(_func, FCDATA_DISALLOW_OUT_OF_FLOW | \
4143 FCDATA_SKIP_ABSPOS_PUSH | \
4144 FCDATA_MAY_NEED_SCROLLFRAME)
4145 // .. but we allow some XUL frames to be _containers_ for out-of-flow content
4146 // (This is the same as SCROLLABLE_XUL_FCDATA, but w/o FCDATA_SKIP_ABSPOS_PUSH)
4147 #define SCROLLABLE_ABSPOS_CONTAINER_XUL_FCDATA(_func) \
4148 FrameConstructionData( \
4149 _func, FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_MAY_NEED_SCROLLFRAME)
4151 #define SIMPLE_XUL_CREATE(_tag, _func) \
4152 { nsGkAtoms::_tag, SIMPLE_XUL_FCDATA(_func) }
4153 #define SCROLLABLE_XUL_CREATE(_tag, _func) \
4154 { nsGkAtoms::_tag, SCROLLABLE_XUL_FCDATA(_func) }
4156 /* static */
4157 const nsCSSFrameConstructor::FrameConstructionData*
4158 nsCSSFrameConstructor::FindXULTagData(const Element& aElement,
4159 ComputedStyle& aStyle) {
4160 MOZ_ASSERT(aElement.IsXULElement());
4161 static constexpr FrameConstructionData kPopupData(
4162 NS_NewMenuPopupFrame, FCDATA_IS_POPUP | FCDATA_SKIP_ABSPOS_PUSH);
4164 static constexpr FrameConstructionDataByTag sXULTagData[] = {
4165 SIMPLE_XUL_CREATE(image, NS_NewXULImageFrame),
4166 SIMPLE_XUL_CREATE(treechildren, NS_NewTreeBodyFrame),
4167 SIMPLE_TAG_CHAIN(label,
4168 nsCSSFrameConstructor::FindXULLabelOrDescriptionData),
4169 SIMPLE_TAG_CHAIN(description,
4170 nsCSSFrameConstructor::FindXULLabelOrDescriptionData),
4171 #ifdef XP_MACOSX
4172 SIMPLE_TAG_CHAIN(menubar, nsCSSFrameConstructor::FindXULMenubarData),
4173 #endif /* XP_MACOSX */
4174 SIMPLE_XUL_CREATE(iframe, NS_NewSubDocumentFrame),
4175 SIMPLE_XUL_CREATE(editor, NS_NewSubDocumentFrame),
4176 SIMPLE_XUL_CREATE(browser, NS_NewSubDocumentFrame),
4177 SIMPLE_XUL_CREATE(splitter, NS_NewSplitterFrame),
4178 SIMPLE_XUL_CREATE(scrollbar, NS_NewScrollbarFrame),
4179 SIMPLE_XUL_CREATE(slider, NS_NewSliderFrame),
4180 SIMPLE_XUL_CREATE(thumb, NS_NewSimpleXULLeafFrame),
4181 SIMPLE_XUL_CREATE(scrollcorner, NS_NewSimpleXULLeafFrame),
4182 SIMPLE_XUL_CREATE(resizer, NS_NewSimpleXULLeafFrame),
4183 SIMPLE_XUL_CREATE(scrollbarbutton, NS_NewScrollbarButtonFrame),
4184 {nsGkAtoms::panel, kPopupData},
4185 {nsGkAtoms::menupopup, kPopupData},
4186 {nsGkAtoms::tooltip, kPopupData},
4189 return FindDataByTag(aElement, aStyle, sXULTagData, ArrayLength(sXULTagData));
4192 /* static */
4193 const nsCSSFrameConstructor::FrameConstructionData*
4194 nsCSSFrameConstructor::FindXULLabelOrDescriptionData(const Element& aElement,
4195 ComputedStyle&) {
4196 // Follow CSS display value if no value attribute
4197 if (!aElement.HasAttr(nsGkAtoms::value)) {
4198 return nullptr;
4201 // Follow CSS display if there's no crop="center".
4202 if (!aElement.AttrValueIs(kNameSpaceID_None, nsGkAtoms::crop,
4203 nsGkAtoms::center, eCaseMatters)) {
4204 return nullptr;
4207 static constexpr FrameConstructionData sMiddleCroppingData =
4208 SIMPLE_XUL_FCDATA(NS_NewMiddleCroppingLabelFrame);
4209 return &sMiddleCroppingData;
4212 #ifdef XP_MACOSX
4213 /* static */
4214 const nsCSSFrameConstructor::FrameConstructionData*
4215 nsCSSFrameConstructor::FindXULMenubarData(const Element& aElement,
4216 ComputedStyle&) {
4217 if (aElement.OwnerDoc()->IsInChromeDocShell()) {
4218 BrowsingContext* bc = aElement.OwnerDoc()->GetBrowsingContext();
4219 bool isRoot = bc && !bc->GetParent();
4220 if (isRoot) {
4221 // This is the root. Suppress the menubar, since on Mac
4222 // window menus are not attached to the window.
4223 static constexpr FrameConstructionData sSuppressData = SUPPRESS_FCDATA();
4224 return &sSuppressData;
4228 return nullptr;
4230 #endif /* XP_MACOSX */
4232 already_AddRefed<ComputedStyle> nsCSSFrameConstructor::BeginBuildingScrollFrame(
4233 nsFrameConstructorState& aState, nsIContent* aContent,
4234 ComputedStyle* aContentStyle, nsContainerFrame* aParentFrame,
4235 PseudoStyleType aScrolledPseudo, bool aIsRoot,
4236 nsContainerFrame*& aNewFrame) {
4237 nsContainerFrame* gfxScrollFrame = aNewFrame;
4239 if (!gfxScrollFrame) {
4240 gfxScrollFrame = NS_NewHTMLScrollFrame(mPresShell, aContentStyle, aIsRoot);
4241 InitAndRestoreFrame(aState, aContent, aParentFrame, gfxScrollFrame);
4244 MOZ_ASSERT(gfxScrollFrame);
4246 // if there are any anonymous children for the scroll frame, create
4247 // frames for them.
4249 // We can't take the normal ProcessChildren path, because the NAC needs to
4250 // be parented to the scrollframe, and everything else needs to be parented
4251 // to the scrolledframe.
4252 AutoTArray<nsIAnonymousContentCreator::ContentInfo, 4> scrollNAC;
4253 DebugOnly<nsresult> rv =
4254 GetAnonymousContent(aContent, gfxScrollFrame, scrollNAC);
4255 MOZ_ASSERT(NS_SUCCEEDED(rv));
4256 nsFrameList anonymousList;
4257 if (!scrollNAC.IsEmpty()) {
4258 nsFrameConstructorSaveState floatSaveState;
4259 aState.MaybePushFloatContainingBlock(gfxScrollFrame, floatSaveState);
4261 AutoFrameConstructionItemList items(this);
4262 AutoFrameConstructionPageName pageNameTracker(aState, gfxScrollFrame);
4263 AddFCItemsForAnonymousContent(aState, gfxScrollFrame, scrollNAC, items,
4264 pageNameTracker);
4265 ConstructFramesFromItemList(aState, items, gfxScrollFrame,
4266 /* aParentIsWrapperAnonBox = */ false,
4267 anonymousList);
4270 aNewFrame = gfxScrollFrame;
4271 gfxScrollFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
4273 // we used the style that was passed in. So resolve another one.
4274 ServoStyleSet* styleSet = mPresShell->StyleSet();
4275 RefPtr<ComputedStyle> scrolledChildStyle =
4276 styleSet->ResolveInheritingAnonymousBoxStyle(aScrolledPseudo,
4277 aContentStyle);
4279 gfxScrollFrame->SetInitialChildList(FrameChildListID::Principal,
4280 std::move(anonymousList));
4282 return scrolledChildStyle.forget();
4285 void nsCSSFrameConstructor::FinishBuildingScrollFrame(
4286 nsContainerFrame* aScrollFrame, nsIFrame* aScrolledFrame) {
4287 aScrollFrame->AppendFrames(FrameChildListID::Principal,
4288 nsFrameList(aScrolledFrame, aScrolledFrame));
4292 * Called to wrap a gfx scrollframe around a frame. The hierarchy will look like
4293 * this
4295 * ------- for gfx scrollbars ------
4298 * ScrollFrame
4301 * Frame (scrolled frame you passed in)
4304 * -----------------------------------
4305 * LEGEND:
4307 * ScrollFrame: This is a frame that manages gfx cross platform frame based
4308 * scrollbars.
4310 * @param aContent the content node of the child to wrap.
4312 * @param aScrolledFrame The frame of the content to wrap. This should not be
4313 * Initialized. This method will initialize it with a scrolled pseudo and no
4314 * nsIContent. The content will be attached to the scrollframe returned.
4316 * @param aContentStyle the style that has already been resolved for the content
4317 * being passed in.
4319 * @param aParentFrame The parent to attach the scroll frame to
4321 * @param aNewFrame The new scrollframe or gfx scrollframe that we create. It
4322 * will contain the scrolled frame you passed in. (returned) If this is not
4323 * null, we'll just use it
4325 * @param aScrolledContentStyle the style that was resolved for the scrolled
4326 * frame. (returned)
4328 void nsCSSFrameConstructor::BuildScrollFrame(nsFrameConstructorState& aState,
4329 nsIContent* aContent,
4330 ComputedStyle* aContentStyle,
4331 nsIFrame* aScrolledFrame,
4332 nsContainerFrame* aParentFrame,
4333 nsContainerFrame*& aNewFrame) {
4334 RefPtr<ComputedStyle> scrolledContentStyle = BeginBuildingScrollFrame(
4335 aState, aContent, aContentStyle, aParentFrame,
4336 PseudoStyleType::scrolledContent, false, aNewFrame);
4338 aScrolledFrame->SetComputedStyleWithoutNotification(scrolledContentStyle);
4339 InitAndRestoreFrame(aState, aContent, aNewFrame, aScrolledFrame);
4341 FinishBuildingScrollFrame(aNewFrame, aScrolledFrame);
4344 const nsCSSFrameConstructor::FrameConstructionData*
4345 nsCSSFrameConstructor::FindDisplayData(const nsStyleDisplay& aDisplay,
4346 const Element& aElement) {
4347 static_assert(eParentTypeCount < (1 << (32 - FCDATA_PARENT_TYPE_OFFSET)),
4348 "Check eParentTypeCount should not overflow");
4350 // The style system ensures that floated and positioned frames are
4351 // block-level.
4352 NS_ASSERTION(
4353 !(aDisplay.IsFloatingStyle() || aDisplay.IsAbsolutelyPositionedStyle()) ||
4354 aDisplay.IsBlockOutsideStyle(),
4355 "Style system did not apply CSS2.1 section 9.7 fixups");
4357 // If this is "body", try propagating its scroll style to the viewport
4358 // Note that we need to do this even if the body is NOT scrollable;
4359 // it might have dynamically changed from scrollable to not scrollable,
4360 // and that might need to be propagated.
4361 // XXXbz is this the right place to do this? If this code moves,
4362 // make this function static.
4363 bool propagatedScrollToViewport = false;
4364 if (aElement.IsHTMLElement(nsGkAtoms::body)) {
4365 if (nsPresContext* presContext = mPresShell->GetPresContext()) {
4366 propagatedScrollToViewport =
4367 presContext->UpdateViewportScrollStylesOverride() == &aElement;
4368 MOZ_ASSERT(!propagatedScrollToViewport ||
4369 !mPresShell->GetPresContext()->IsPaginated(),
4370 "Shouldn't propagate scroll in paginated contexts");
4374 switch (aDisplay.DisplayInside()) {
4375 case StyleDisplayInside::Flow:
4376 case StyleDisplayInside::FlowRoot: {
4377 if (aDisplay.IsInlineFlow()) {
4378 static constexpr FrameConstructionData data(
4379 &nsCSSFrameConstructor::ConstructInline,
4380 FCDATA_IS_INLINE | FCDATA_IS_LINE_PARTICIPANT);
4381 return &data;
4384 // If the frame is a block-level frame and is scrollable, then wrap it in
4385 // a scroll frame. Except we don't want to do that for paginated contexts
4386 // for frames that are block-outside and aren't frames for native
4387 // anonymous stuff.
4388 // XXX Ignore tables for the time being (except caption)
4389 const uint32_t kCaptionCtorFlags =
4390 FCDATA_IS_TABLE_PART | FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable);
4391 bool caption = aDisplay.mDisplay == StyleDisplay::TableCaption;
4392 bool suppressScrollFrame = false;
4393 bool needScrollFrame =
4394 aDisplay.IsScrollableOverflow() && !propagatedScrollToViewport;
4395 if (needScrollFrame) {
4396 suppressScrollFrame = mPresShell->GetPresContext()->IsPaginated() &&
4397 aDisplay.IsBlockOutsideStyle() &&
4398 !aElement.IsInNativeAnonymousSubtree();
4399 if (!suppressScrollFrame) {
4400 static constexpr FrameConstructionData sScrollableBlockData[2] = {
4401 {&nsCSSFrameConstructor::ConstructScrollableBlock},
4402 {&nsCSSFrameConstructor::ConstructScrollableBlock,
4403 kCaptionCtorFlags}};
4404 return &sScrollableBlockData[caption];
4407 // If the scrollable frame would have propagated its scrolling to the
4408 // viewport, we still want to construct a regular block rather than a
4409 // scrollframe so that it paginates correctly, but we don't want to set
4410 // the bit on the block that tells it to clip at paint time.
4411 if (mPresShell->GetPresContext()->ElementWouldPropagateScrollStyles(
4412 aElement)) {
4413 suppressScrollFrame = false;
4417 // Handle various non-scrollable blocks.
4418 static constexpr FrameConstructionData sNonScrollableBlockData[2][2] = {
4419 {{&nsCSSFrameConstructor::ConstructNonScrollableBlock},
4420 {&nsCSSFrameConstructor::ConstructNonScrollableBlock,
4421 kCaptionCtorFlags}},
4422 {{&nsCSSFrameConstructor::ConstructNonScrollableBlock,
4423 FCDATA_FORCED_NON_SCROLLABLE_BLOCK},
4424 {&nsCSSFrameConstructor::ConstructNonScrollableBlock,
4425 FCDATA_FORCED_NON_SCROLLABLE_BLOCK | kCaptionCtorFlags}}};
4426 return &sNonScrollableBlockData[suppressScrollFrame][caption];
4428 case StyleDisplayInside::Table: {
4429 static constexpr FrameConstructionData data(
4430 &nsCSSFrameConstructor::ConstructTable);
4431 return &data;
4433 // NOTE: In the unlikely event that we add another table-part here that
4434 // has a desired-parent-type (& hence triggers table fixup), we'll need to
4435 // also update the flexbox chunk in ComputedStyle::ApplyStyleFixups().
4436 case StyleDisplayInside::TableRowGroup: {
4437 static constexpr FrameConstructionData data(
4438 &nsCSSFrameConstructor::ConstructTableRowOrRowGroup,
4439 FCDATA_IS_TABLE_PART |
4440 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable));
4441 return &data;
4443 case StyleDisplayInside::TableColumn: {
4444 static constexpr FrameConstructionData data(
4445 &nsCSSFrameConstructor::ConstructTableCol,
4446 FCDATA_IS_TABLE_PART |
4447 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeColGroup));
4448 return &data;
4450 case StyleDisplayInside::TableColumnGroup: {
4451 static constexpr FrameConstructionData data(
4452 ToCreationFunc(NS_NewTableColGroupFrame),
4453 FCDATA_IS_TABLE_PART | FCDATA_DISALLOW_OUT_OF_FLOW |
4454 FCDATA_SKIP_ABSPOS_PUSH |
4455 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable));
4456 return &data;
4458 case StyleDisplayInside::TableHeaderGroup: {
4459 static constexpr FrameConstructionData data(
4460 &nsCSSFrameConstructor::ConstructTableRowOrRowGroup,
4461 FCDATA_IS_TABLE_PART |
4462 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable));
4463 return &data;
4465 case StyleDisplayInside::TableFooterGroup: {
4466 static constexpr FrameConstructionData data(
4467 &nsCSSFrameConstructor::ConstructTableRowOrRowGroup,
4468 FCDATA_IS_TABLE_PART |
4469 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable));
4470 return &data;
4472 case StyleDisplayInside::TableRow: {
4473 static constexpr FrameConstructionData data(
4474 &nsCSSFrameConstructor::ConstructTableRowOrRowGroup,
4475 FCDATA_IS_TABLE_PART |
4476 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRowGroup));
4477 return &data;
4479 case StyleDisplayInside::TableCell: {
4480 static constexpr FrameConstructionData data(
4481 &nsCSSFrameConstructor::ConstructTableCell,
4482 FCDATA_IS_TABLE_PART | FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRow));
4483 return &data;
4485 case StyleDisplayInside::Flex:
4486 case StyleDisplayInside::WebkitBox: {
4487 static constexpr FrameConstructionData nonScrollableData(
4488 ToCreationFunc(NS_NewFlexContainerFrame));
4489 static constexpr FrameConstructionData data(
4490 ToCreationFunc(NS_NewFlexContainerFrame),
4491 FCDATA_MAY_NEED_SCROLLFRAME);
4492 return MOZ_UNLIKELY(propagatedScrollToViewport) ? &nonScrollableData
4493 : &data;
4495 case StyleDisplayInside::Grid: {
4496 static constexpr FrameConstructionData nonScrollableData(
4497 ToCreationFunc(NS_NewGridContainerFrame));
4498 static constexpr FrameConstructionData data(
4499 ToCreationFunc(NS_NewGridContainerFrame),
4500 FCDATA_MAY_NEED_SCROLLFRAME);
4501 return MOZ_UNLIKELY(propagatedScrollToViewport) ? &nonScrollableData
4502 : &data;
4504 case StyleDisplayInside::Ruby: {
4505 static constexpr FrameConstructionData data[] = {
4506 {&nsCSSFrameConstructor::ConstructBlockRubyFrame,
4507 FCDATA_MAY_NEED_SCROLLFRAME},
4508 {ToCreationFunc(NS_NewRubyFrame), FCDATA_IS_LINE_PARTICIPANT}};
4509 bool isInline = aDisplay.DisplayOutside() == StyleDisplayOutside::Inline;
4510 return &data[isInline];
4512 case StyleDisplayInside::RubyBase: {
4513 static constexpr FrameConstructionData data(
4514 ToCreationFunc(NS_NewRubyBaseFrame),
4515 FCDATA_IS_LINE_PARTICIPANT |
4516 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRubyBaseContainer));
4517 return &data;
4519 case StyleDisplayInside::RubyBaseContainer: {
4520 static constexpr FrameConstructionData data(
4521 ToCreationFunc(NS_NewRubyBaseContainerFrame),
4522 FCDATA_IS_LINE_PARTICIPANT |
4523 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRuby));
4524 return &data;
4526 case StyleDisplayInside::RubyText: {
4527 static constexpr FrameConstructionData data(
4528 ToCreationFunc(NS_NewRubyTextFrame),
4529 FCDATA_IS_LINE_PARTICIPANT |
4530 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRubyTextContainer));
4531 return &data;
4533 case StyleDisplayInside::RubyTextContainer: {
4534 static constexpr FrameConstructionData data(
4535 ToCreationFunc(NS_NewRubyTextContainerFrame),
4536 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRuby));
4537 return &data;
4539 default:
4540 MOZ_ASSERT_UNREACHABLE("unknown 'display' value");
4541 return nullptr;
4545 nsIFrame* nsCSSFrameConstructor::ConstructScrollableBlock(
4546 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
4547 nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay,
4548 nsFrameList& aFrameList) {
4549 nsIContent* const content = aItem.mContent;
4550 ComputedStyle* const computedStyle = aItem.mComputedStyle;
4552 nsContainerFrame* newFrame = nullptr;
4553 RefPtr<ComputedStyle> scrolledContentStyle = BeginBuildingScrollFrame(
4554 aState, content, computedStyle,
4555 aState.GetGeometricParent(*aDisplay, aParentFrame),
4556 PseudoStyleType::scrolledContent, false, newFrame);
4558 // Create our block frame
4559 // pass a temporary stylecontext, the correct one will be set later
4560 nsContainerFrame* scrolledFrame =
4561 NS_NewBlockFormattingContext(mPresShell, computedStyle);
4563 // Make sure to AddChild before we call ConstructBlock so that we
4564 // end up before our descendants in fixed-pos lists as needed.
4565 aState.AddChild(newFrame, aFrameList, content, aParentFrame);
4567 nsFrameList blockList;
4568 ConstructBlock(aState, content, newFrame, newFrame, scrolledContentStyle,
4569 &scrolledFrame, blockList,
4570 newFrame->IsAbsPosContainingBlock() ? newFrame : nullptr);
4572 MOZ_ASSERT(blockList.OnlyChild() == scrolledFrame,
4573 "Scrollframe's frameList should be exactly the scrolled frame!");
4574 FinishBuildingScrollFrame(newFrame, scrolledFrame);
4576 return newFrame;
4579 nsIFrame* nsCSSFrameConstructor::ConstructNonScrollableBlock(
4580 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
4581 nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay,
4582 nsFrameList& aFrameList) {
4583 ComputedStyle* const computedStyle = aItem.mComputedStyle;
4585 // We want a block formatting context root in paginated contexts for
4586 // every block that would be scrollable in a non-paginated context.
4587 // We mark our blocks with a bit here if this condition is true, so
4588 // we can check it later in nsIFrame::ApplyPaginatedOverflowClipping.
4589 bool clipPaginatedOverflow =
4590 (aItem.mFCData->mBits & FCDATA_FORCED_NON_SCROLLABLE_BLOCK) != 0;
4591 nsFrameState flags = nsFrameState(0);
4592 if ((aDisplay->IsAbsolutelyPositionedStyle() || aDisplay->IsFloatingStyle() ||
4593 aDisplay->DisplayInside() == StyleDisplayInside::FlowRoot ||
4594 clipPaginatedOverflow) &&
4595 !aParentFrame->IsInSVGTextSubtree()) {
4596 flags = NS_BLOCK_FORMATTING_CONTEXT_STATE_BITS;
4597 if (clipPaginatedOverflow) {
4598 flags |= NS_BLOCK_CLIP_PAGINATED_OVERFLOW;
4602 nsContainerFrame* newFrame = NS_NewBlockFrame(mPresShell, computedStyle);
4603 newFrame->AddStateBits(flags);
4604 ConstructBlock(aState, aItem.mContent,
4605 aState.GetGeometricParent(*aDisplay, aParentFrame),
4606 aParentFrame, computedStyle, &newFrame, aFrameList,
4607 newFrame->IsAbsPosContainingBlock() ? newFrame : nullptr);
4608 return newFrame;
4611 void nsCSSFrameConstructor::InitAndRestoreFrame(
4612 const nsFrameConstructorState& aState, nsIContent* aContent,
4613 nsContainerFrame* aParentFrame, nsIFrame* aNewFrame, bool aAllowCounters) {
4614 MOZ_ASSERT(aNewFrame, "Null frame cannot be initialized");
4616 // Initialize the frame
4617 aNewFrame->Init(aContent, aParentFrame, nullptr);
4618 aNewFrame->AddStateBits(aState.mAdditionalStateBits);
4620 if (aState.mFrameState) {
4621 // Restore frame state for just the newly created frame.
4622 RestoreFrameStateFor(aNewFrame, aState.mFrameState);
4625 if (aAllowCounters &&
4626 mContainStyleScopeManager.AddCounterChanges(aNewFrame)) {
4627 CountersDirty();
4631 already_AddRefed<ComputedStyle> nsCSSFrameConstructor::ResolveComputedStyle(
4632 nsIContent* aContent) {
4633 if (auto* element = Element::FromNode(aContent)) {
4634 return ServoStyleSet::ResolveServoStyle(*element);
4637 MOZ_ASSERT(aContent->IsText(),
4638 "shouldn't waste time creating ComputedStyles for "
4639 "comments and processing instructions");
4641 Element* parent = aContent->GetFlattenedTreeParentElement();
4642 MOZ_ASSERT(parent, "Text out of the flattened tree?");
4644 // FIXME(emilio): The const_cast is unfortunate, but it's not worse than what
4645 // we did before.
4647 // We could use ResolveServoStyle, but that would involve extra unnecessary
4648 // refcount traffic...
4649 auto* parentStyle =
4650 const_cast<ComputedStyle*>(Servo_Element_GetMaybeOutOfDateStyle(parent));
4651 MOZ_ASSERT(parentStyle,
4652 "How are we inserting text frames in an unstyled element?");
4653 return mPresShell->StyleSet()->ResolveStyleForText(aContent, parentStyle);
4656 // MathML Mod - RBS
4657 void nsCSSFrameConstructor::FlushAccumulatedBlock(
4658 nsFrameConstructorState& aState, nsIContent* aContent,
4659 nsContainerFrame* aParentFrame, nsFrameList& aBlockList,
4660 nsFrameList& aNewList) {
4661 if (aBlockList.IsEmpty()) {
4662 // Nothing to do
4663 return;
4666 auto anonPseudo = PseudoStyleType::mozMathMLAnonymousBlock;
4668 ComputedStyle* parentContext =
4669 nsIFrame::CorrectStyleParentFrame(aParentFrame, anonPseudo)->Style();
4670 ServoStyleSet* styleSet = mPresShell->StyleSet();
4671 RefPtr<ComputedStyle> blockContext =
4672 styleSet->ResolveInheritingAnonymousBoxStyle(anonPseudo, parentContext);
4674 // then, create a block frame that will wrap the child frames. Make it a
4675 // MathML frame so that Get(Absolute/Float)ContainingBlockFor know that this
4676 // is not a suitable block.
4677 nsContainerFrame* blockFrame =
4678 NS_NewMathMLmathBlockFrame(mPresShell, blockContext);
4680 InitAndRestoreFrame(aState, aContent, aParentFrame, blockFrame);
4681 ReparentFrames(this, blockFrame, aBlockList, false);
4682 // We have to walk over aBlockList before we hand it over to blockFrame.
4683 for (nsIFrame* f : aBlockList) {
4684 f->SetParentIsWrapperAnonBox();
4686 // abs-pos and floats are disabled in MathML children so we don't have to
4687 // worry about messing up those.
4688 blockFrame->SetInitialChildList(FrameChildListID::Principal,
4689 std::move(aBlockList));
4690 aNewList.AppendFrame(nullptr, blockFrame);
4693 // Only <math> elements can be floated or positioned. All other MathML
4694 // should be in-flow.
4695 #define SIMPLE_MATHML_CREATE(_tag, _func) \
4697 nsGkAtoms::_tag, { \
4698 _func, FCDATA_DISALLOW_OUT_OF_FLOW | \
4699 FCDATA_FORCE_NULL_ABSPOS_CONTAINER | \
4700 FCDATA_WRAP_KIDS_IN_BLOCKS \
4704 /* static */
4705 const nsCSSFrameConstructor::FrameConstructionData*
4706 nsCSSFrameConstructor::FindMathMLData(const Element& aElement,
4707 ComputedStyle& aStyle) {
4708 MOZ_ASSERT(aElement.IsMathMLElement());
4710 nsAtom* tag = aElement.NodeInfo()->NameAtom();
4712 // Handle <math> specially, because it sometimes produces inlines
4713 if (tag == nsGkAtoms::math) {
4714 // The IsBlockOutsideStyle() check must match what
4715 // specified::Display::equivalent_block_display is checking for
4716 // already-block-outside things. Though the behavior here for the
4717 // display:table case is pretty weird...
4718 if (aStyle.StyleDisplay()->IsBlockOutsideStyle()) {
4719 static constexpr FrameConstructionData sBlockMathData(
4720 ToCreationFunc(NS_NewMathMLmathBlockFrame),
4721 FCDATA_FORCE_NULL_ABSPOS_CONTAINER | FCDATA_WRAP_KIDS_IN_BLOCKS);
4722 return &sBlockMathData;
4725 static constexpr FrameConstructionData sInlineMathData(
4726 ToCreationFunc(NS_NewMathMLmathInlineFrame),
4727 FCDATA_FORCE_NULL_ABSPOS_CONTAINER | FCDATA_IS_LINE_PARTICIPANT |
4728 FCDATA_WRAP_KIDS_IN_BLOCKS);
4729 return &sInlineMathData;
4732 if (!StaticPrefs::
4733 mathml_legacy_maction_and_semantics_implementations_disabled()) {
4734 static constexpr FrameConstructionDataByTag sMactionAndSemanticsData[] = {
4735 SIMPLE_MATHML_CREATE(maction_, NS_NewMathMLmactionFrame),
4736 SIMPLE_MATHML_CREATE(semantics_, NS_NewMathMLsemanticsFrame)};
4737 const FrameConstructionData* data =
4738 FindDataByTag(aElement, aStyle, sMactionAndSemanticsData,
4739 ArrayLength(sMactionAndSemanticsData));
4740 if (data) {
4741 return data;
4745 static constexpr FrameConstructionDataByTag sMathMLData[] = {
4746 SIMPLE_MATHML_CREATE(annotation_, NS_NewMathMLTokenFrame),
4747 SIMPLE_MATHML_CREATE(annotation_xml_, NS_NewMathMLmrowFrame),
4748 SIMPLE_MATHML_CREATE(mi_, NS_NewMathMLTokenFrame),
4749 SIMPLE_MATHML_CREATE(mn_, NS_NewMathMLTokenFrame),
4750 SIMPLE_MATHML_CREATE(ms_, NS_NewMathMLTokenFrame),
4751 SIMPLE_MATHML_CREATE(mtext_, NS_NewMathMLTokenFrame),
4752 SIMPLE_MATHML_CREATE(mo_, NS_NewMathMLmoFrame),
4753 SIMPLE_MATHML_CREATE(mfrac_, NS_NewMathMLmfracFrame),
4754 SIMPLE_MATHML_CREATE(msup_, NS_NewMathMLmmultiscriptsFrame),
4755 SIMPLE_MATHML_CREATE(msub_, NS_NewMathMLmmultiscriptsFrame),
4756 SIMPLE_MATHML_CREATE(msubsup_, NS_NewMathMLmmultiscriptsFrame),
4757 SIMPLE_MATHML_CREATE(munder_, NS_NewMathMLmunderoverFrame),
4758 SIMPLE_MATHML_CREATE(mover_, NS_NewMathMLmunderoverFrame),
4759 SIMPLE_MATHML_CREATE(munderover_, NS_NewMathMLmunderoverFrame),
4760 SIMPLE_MATHML_CREATE(mphantom_, NS_NewMathMLmrowFrame),
4761 SIMPLE_MATHML_CREATE(mpadded_, NS_NewMathMLmpaddedFrame),
4762 SIMPLE_MATHML_CREATE(mspace_, NS_NewMathMLmspaceFrame),
4763 SIMPLE_MATHML_CREATE(none, NS_NewMathMLmrowFrame),
4764 SIMPLE_MATHML_CREATE(mprescripts_, NS_NewMathMLmrowFrame),
4765 SIMPLE_MATHML_CREATE(mfenced_, NS_NewMathMLmrowFrame),
4766 SIMPLE_MATHML_CREATE(mmultiscripts_, NS_NewMathMLmmultiscriptsFrame),
4767 SIMPLE_MATHML_CREATE(mstyle_, NS_NewMathMLmrowFrame),
4768 SIMPLE_MATHML_CREATE(msqrt_, NS_NewMathMLmsqrtFrame),
4769 SIMPLE_MATHML_CREATE(mroot_, NS_NewMathMLmrootFrame),
4770 SIMPLE_MATHML_CREATE(maction_, NS_NewMathMLmrowFrame),
4771 SIMPLE_MATHML_CREATE(mrow_, NS_NewMathMLmrowFrame),
4772 SIMPLE_MATHML_CREATE(merror_, NS_NewMathMLmrowFrame),
4773 SIMPLE_MATHML_CREATE(menclose_, NS_NewMathMLmencloseFrame),
4774 SIMPLE_MATHML_CREATE(semantics_, NS_NewMathMLmrowFrame)};
4776 return FindDataByTag(aElement, aStyle, sMathMLData, ArrayLength(sMathMLData));
4779 nsContainerFrame* nsCSSFrameConstructor::ConstructFrameWithAnonymousChild(
4780 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
4781 nsContainerFrame* aParentFrame, nsFrameList& aFrameList,
4782 ContainerFrameCreationFunc aConstructor,
4783 ContainerFrameCreationFunc aInnerConstructor, PseudoStyleType aInnerPseudo,
4784 bool aCandidateRootFrame) {
4785 nsIContent* const content = aItem.mContent;
4786 ComputedStyle* const computedStyle = aItem.mComputedStyle;
4788 // Create the outer frame:
4789 nsContainerFrame* newFrame = aConstructor(mPresShell, computedStyle);
4791 InitAndRestoreFrame(aState, content,
4792 aCandidateRootFrame
4793 ? aState.GetGeometricParent(
4794 *computedStyle->StyleDisplay(), aParentFrame)
4795 : aParentFrame,
4796 newFrame);
4797 newFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
4799 // Create the pseudo SC for the anonymous wrapper child as a child of the SC:
4800 RefPtr<ComputedStyle> scForAnon =
4801 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(aInnerPseudo,
4802 computedStyle);
4804 // Create the anonymous inner wrapper frame
4805 nsContainerFrame* innerFrame = aInnerConstructor(mPresShell, scForAnon);
4807 InitAndRestoreFrame(aState, content, newFrame, innerFrame);
4809 // Put the newly created frames into the right child list
4810 SetInitialSingleChild(newFrame, innerFrame);
4812 aState.AddChild(newFrame, aFrameList, content, aParentFrame,
4813 aCandidateRootFrame, aCandidateRootFrame);
4815 if (!mRootElementFrame && aCandidateRootFrame) {
4816 // The frame we're constructing will be the root element frame.
4817 SetRootElementFrameAndConstructCanvasAnonContent(newFrame, aState,
4818 aFrameList);
4821 nsFrameConstructorSaveState floatSaveState;
4822 aState.MaybePushFloatContainingBlock(innerFrame, floatSaveState);
4824 nsFrameList childList;
4826 // Process children
4827 if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) {
4828 ConstructFramesFromItemList(
4829 aState, aItem.mChildItems, innerFrame,
4830 aItem.mFCData->mBits & FCDATA_IS_WRAPPER_ANON_BOX, childList);
4831 } else {
4832 ProcessChildren(aState, content, computedStyle, innerFrame, true, childList,
4833 false);
4836 // Set the inner wrapper frame's initial primary list
4837 innerFrame->SetInitialChildList(FrameChildListID::Principal,
4838 std::move(childList));
4840 return newFrame;
4843 nsIFrame* nsCSSFrameConstructor::ConstructOuterSVG(
4844 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
4845 nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay,
4846 nsFrameList& aFrameList) {
4847 return ConstructFrameWithAnonymousChild(
4848 aState, aItem, aParentFrame, aFrameList, NS_NewSVGOuterSVGFrame,
4849 NS_NewSVGOuterSVGAnonChildFrame, PseudoStyleType::mozSVGOuterSVGAnonChild,
4850 true);
4853 nsIFrame* nsCSSFrameConstructor::ConstructMarker(
4854 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
4855 nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay,
4856 nsFrameList& aFrameList) {
4857 return ConstructFrameWithAnonymousChild(
4858 aState, aItem, aParentFrame, aFrameList, NS_NewSVGMarkerFrame,
4859 NS_NewSVGMarkerAnonChildFrame, PseudoStyleType::mozSVGMarkerAnonChild,
4860 false);
4863 // Only outer <svg> elements can be floated or positioned. All other SVG
4864 // should be in-flow.
4865 #define SIMPLE_SVG_FCDATA(_func) \
4866 FrameConstructionData(ToCreationFunc(_func), \
4867 FCDATA_DISALLOW_OUT_OF_FLOW | \
4868 FCDATA_SKIP_ABSPOS_PUSH | \
4869 FCDATA_DISALLOW_GENERATED_CONTENT)
4870 #define SIMPLE_SVG_CREATE(_tag, _func) \
4871 { nsGkAtoms::_tag, SIMPLE_SVG_FCDATA(_func) }
4873 static bool IsFilterPrimitiveChildTag(const nsAtom* aTag) {
4874 return aTag == nsGkAtoms::feDistantLight || aTag == nsGkAtoms::fePointLight ||
4875 aTag == nsGkAtoms::feSpotLight || aTag == nsGkAtoms::feFuncR ||
4876 aTag == nsGkAtoms::feFuncG || aTag == nsGkAtoms::feFuncB ||
4877 aTag == nsGkAtoms::feFuncA || aTag == nsGkAtoms::feMergeNode;
4880 /* static */
4881 const nsCSSFrameConstructor::FrameConstructionData*
4882 nsCSSFrameConstructor::FindSVGData(const Element& aElement,
4883 nsIFrame* aParentFrame,
4884 bool aIsWithinSVGText,
4885 bool aAllowsTextPathChild,
4886 ComputedStyle& aStyle) {
4887 MOZ_ASSERT(aElement.IsSVGElement());
4889 static constexpr FrameConstructionData sSuppressData = SUPPRESS_FCDATA();
4890 static constexpr FrameConstructionData sContainerData =
4891 SIMPLE_SVG_FCDATA(NS_NewSVGContainerFrame);
4893 bool parentIsSVG = aIsWithinSVGText;
4894 nsIContent* parentContent =
4895 aParentFrame ? aParentFrame->GetContent() : nullptr;
4897 nsAtom* tag = aElement.NodeInfo()->NameAtom();
4899 // XXXbz should this really be based on the tag of the parent frame's content?
4900 // Should it not be based on the type of the parent frame (e.g. whether it's
4901 // an SVG frame)?
4902 if (parentContent) {
4903 // It's not clear whether the SVG spec intends to allow any SVG
4904 // content within svg:foreignObject at all (SVG 1.1, section
4905 // 23.2), but if it does, it better be svg:svg. So given that
4906 // we're allowing it, treat it as a non-SVG parent.
4907 parentIsSVG =
4908 parentContent->IsSVGElement() &&
4909 parentContent->NodeInfo()->NameAtom() != nsGkAtoms::foreignObject;
4912 if ((tag != nsGkAtoms::svg && !parentIsSVG) ||
4913 (tag == nsGkAtoms::desc || tag == nsGkAtoms::title ||
4914 tag == nsGkAtoms::metadata)) {
4915 // Sections 5.1 and G.4 of SVG 1.1 say that SVG elements other than
4916 // svg:svg not contained within svg:svg are incorrect, although they
4917 // don't seem to specify error handling. Ignore them, since many of
4918 // our frame classes can't deal. It *may* be that the document
4919 // should at that point be considered in error according to F.2, but
4920 // it's hard to tell.
4922 // Style mutation can't change this situation, so don't bother
4923 // adding to the undisplayed content map.
4925 // We don't currently handle any UI for desc/title/metadata
4926 return &sSuppressData;
4929 // We don't need frames for animation elements
4930 if (aElement.IsSVGAnimationElement()) {
4931 return &sSuppressData;
4934 if (tag == nsGkAtoms::svg && !parentIsSVG) {
4935 // We need outer <svg> elements to have an SVGOuterSVGFrame regardless
4936 // of whether they fail conditional processing attributes, since various
4937 // SVG frames assume that one exists. We handle the non-rendering
4938 // of failing outer <svg> element contents like <switch> statements,
4939 // and do the PassesConditionalProcessingTests call in
4940 // SVGOuterSVGFrame::Init.
4941 static constexpr FrameConstructionData sOuterSVGData(
4942 &nsCSSFrameConstructor::ConstructOuterSVG);
4943 return &sOuterSVGData;
4946 if (tag == nsGkAtoms::marker) {
4947 static constexpr FrameConstructionData sMarkerSVGData(
4948 &nsCSSFrameConstructor::ConstructMarker);
4949 return &sMarkerSVGData;
4952 nsCOMPtr<SVGTests> tests = do_QueryInterface(const_cast<Element*>(&aElement));
4953 if (tests && !tests->PassesConditionalProcessingTests()) {
4954 // Elements with failing conditional processing attributes never get
4955 // rendered. Note that this is not where we select which frame in a
4956 // <switch> to render! That happens in SVGSwitchFrame::PaintSVG.
4957 if (aIsWithinSVGText) {
4958 // SVGTextFrame doesn't handle conditional processing attributes,
4959 // so don't create frames for descendants of <text> with failing
4960 // attributes. We need frames not to be created so that text layout
4961 // is correct.
4962 return &sSuppressData;
4964 // If we're not inside <text>, create an SVGContainerFrame (which is a
4965 // frame that doesn't render) so that paint servers can still be referenced,
4966 // even if they live inside an element with failing conditional processing
4967 // attributes.
4968 return &sContainerData;
4971 // Ensure that a stop frame is a child of a gradient and that gradients
4972 // can only have stop children.
4973 bool parentIsGradient = aParentFrame && static_cast<SVGGradientFrame*>(
4974 do_QueryFrame(aParentFrame));
4975 bool stop = (tag == nsGkAtoms::stop);
4976 if ((parentIsGradient && !stop) || (!parentIsGradient && stop)) {
4977 return &sSuppressData;
4980 // Prevent bad frame types being children of filters or parents of filter
4981 // primitives. If aParentFrame is null, we know that the frame that will
4982 // be created will be an nsInlineFrame, so it can never be a filter.
4983 bool parentIsFilter = aParentFrame && aParentFrame->IsSVGFilterFrame();
4984 nsCOMPtr<SVGFE> filterPrimitive =
4985 do_QueryInterface(const_cast<Element*>(&aElement));
4986 if ((parentIsFilter && !filterPrimitive) ||
4987 (!parentIsFilter && filterPrimitive)) {
4988 return &sSuppressData;
4991 // Prevent bad frame types being children of filter primitives or parents of
4992 // filter primitive children. If aParentFrame is null, we know that the frame
4993 // that will be created will be an nsInlineFrame, so it can never be a filter
4994 // primitive.
4995 bool parentIsFEContainerFrame =
4996 aParentFrame && aParentFrame->IsSVGFEContainerFrame();
4997 if ((parentIsFEContainerFrame && !IsFilterPrimitiveChildTag(tag)) ||
4998 (!parentIsFEContainerFrame && IsFilterPrimitiveChildTag(tag))) {
4999 return &sSuppressData;
5002 // Special cases for text/tspan/textPath, because the kind of frame
5003 // they get depends on the parent frame. We ignore 'a' elements when
5004 // determining the parent, however.
5005 if (aIsWithinSVGText) {
5006 // If aIsWithinSVGText is true, then we know that the "SVG text uses
5007 // CSS frames" pref was true when this SVG fragment was first constructed.
5009 // FIXME(bug 1588477) Don't render stuff in display: contents / Shadow DOM
5010 // subtrees, because TextCorrespondenceRecorder in the SVG text code doesn't
5011 // really know how to deal with it. This kinda sucks. :(
5012 if (aParentFrame && aParentFrame->GetContent() != aElement.GetParent()) {
5013 return &sSuppressData;
5016 // We don't use ConstructInline because we want different behavior
5017 // for generated content.
5018 static constexpr FrameConstructionData sTSpanData(
5019 ToCreationFunc(NS_NewInlineFrame),
5020 FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_SKIP_ABSPOS_PUSH |
5021 FCDATA_DISALLOW_GENERATED_CONTENT | FCDATA_IS_LINE_PARTICIPANT |
5022 FCDATA_IS_INLINE | FCDATA_USE_CHILD_ITEMS);
5023 if (tag == nsGkAtoms::textPath) {
5024 if (aAllowsTextPathChild) {
5025 return &sTSpanData;
5027 } else if (tag == nsGkAtoms::tspan || tag == nsGkAtoms::a) {
5028 return &sTSpanData;
5030 return &sSuppressData;
5031 } else if (tag == nsGkAtoms::tspan || tag == nsGkAtoms::textPath) {
5032 return &sSuppressData;
5035 static constexpr FrameConstructionDataByTag sSVGData[] = {
5036 SIMPLE_SVG_CREATE(svg, NS_NewSVGInnerSVGFrame),
5037 SIMPLE_SVG_CREATE(g, NS_NewSVGGFrame),
5038 SIMPLE_SVG_CREATE(svgSwitch, NS_NewSVGSwitchFrame),
5039 SIMPLE_SVG_CREATE(symbol, NS_NewSVGSymbolFrame),
5040 SIMPLE_SVG_CREATE(polygon, NS_NewSVGGeometryFrame),
5041 SIMPLE_SVG_CREATE(polyline, NS_NewSVGGeometryFrame),
5042 SIMPLE_SVG_CREATE(circle, NS_NewSVGGeometryFrame),
5043 SIMPLE_SVG_CREATE(ellipse, NS_NewSVGGeometryFrame),
5044 SIMPLE_SVG_CREATE(line, NS_NewSVGGeometryFrame),
5045 SIMPLE_SVG_CREATE(rect, NS_NewSVGGeometryFrame),
5046 SIMPLE_SVG_CREATE(path, NS_NewSVGGeometryFrame),
5047 SIMPLE_SVG_CREATE(defs, NS_NewSVGContainerFrame),
5048 {nsGkAtoms::text,
5049 {NS_NewSVGTextFrame,
5050 FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_ALLOW_BLOCK_STYLES,
5051 PseudoStyleType::mozSVGText}},
5052 {nsGkAtoms::foreignObject,
5053 {ToCreationFunc(NS_NewSVGForeignObjectFrame),
5054 FCDATA_DISALLOW_OUT_OF_FLOW, PseudoStyleType::mozSVGForeignContent}},
5055 SIMPLE_SVG_CREATE(a, NS_NewSVGAFrame),
5056 SIMPLE_SVG_CREATE(linearGradient, NS_NewSVGLinearGradientFrame),
5057 SIMPLE_SVG_CREATE(radialGradient, NS_NewSVGRadialGradientFrame),
5058 SIMPLE_SVG_CREATE(stop, NS_NewSVGStopFrame),
5059 SIMPLE_SVG_CREATE(use, NS_NewSVGUseFrame),
5060 SIMPLE_SVG_CREATE(view, NS_NewSVGViewFrame),
5061 SIMPLE_SVG_CREATE(image, NS_NewSVGImageFrame),
5062 SIMPLE_SVG_CREATE(clipPath, NS_NewSVGClipPathFrame),
5063 SIMPLE_SVG_CREATE(filter, NS_NewSVGFilterFrame),
5064 SIMPLE_SVG_CREATE(pattern, NS_NewSVGPatternFrame),
5065 SIMPLE_SVG_CREATE(mask, NS_NewSVGMaskFrame),
5066 SIMPLE_SVG_CREATE(feDistantLight, NS_NewSVGFEUnstyledLeafFrame),
5067 SIMPLE_SVG_CREATE(fePointLight, NS_NewSVGFEUnstyledLeafFrame),
5068 SIMPLE_SVG_CREATE(feSpotLight, NS_NewSVGFEUnstyledLeafFrame),
5069 SIMPLE_SVG_CREATE(feBlend, NS_NewSVGFELeafFrame),
5070 SIMPLE_SVG_CREATE(feColorMatrix, NS_NewSVGFELeafFrame),
5071 SIMPLE_SVG_CREATE(feFuncR, NS_NewSVGFEUnstyledLeafFrame),
5072 SIMPLE_SVG_CREATE(feFuncG, NS_NewSVGFEUnstyledLeafFrame),
5073 SIMPLE_SVG_CREATE(feFuncB, NS_NewSVGFEUnstyledLeafFrame),
5074 SIMPLE_SVG_CREATE(feFuncA, NS_NewSVGFEUnstyledLeafFrame),
5075 SIMPLE_SVG_CREATE(feComposite, NS_NewSVGFELeafFrame),
5076 SIMPLE_SVG_CREATE(feComponentTransfer, NS_NewSVGFEContainerFrame),
5077 SIMPLE_SVG_CREATE(feConvolveMatrix, NS_NewSVGFELeafFrame),
5078 SIMPLE_SVG_CREATE(feDiffuseLighting, NS_NewSVGFEContainerFrame),
5079 SIMPLE_SVG_CREATE(feDisplacementMap, NS_NewSVGFELeafFrame),
5080 SIMPLE_SVG_CREATE(feDropShadow, NS_NewSVGFELeafFrame),
5081 SIMPLE_SVG_CREATE(feFlood, NS_NewSVGFELeafFrame),
5082 SIMPLE_SVG_CREATE(feGaussianBlur, NS_NewSVGFELeafFrame),
5083 SIMPLE_SVG_CREATE(feImage, NS_NewSVGFEImageFrame),
5084 SIMPLE_SVG_CREATE(feMerge, NS_NewSVGFEContainerFrame),
5085 SIMPLE_SVG_CREATE(feMergeNode, NS_NewSVGFEUnstyledLeafFrame),
5086 SIMPLE_SVG_CREATE(feMorphology, NS_NewSVGFELeafFrame),
5087 SIMPLE_SVG_CREATE(feOffset, NS_NewSVGFELeafFrame),
5088 SIMPLE_SVG_CREATE(feSpecularLighting, NS_NewSVGFEContainerFrame),
5089 SIMPLE_SVG_CREATE(feTile, NS_NewSVGFELeafFrame),
5090 SIMPLE_SVG_CREATE(feTurbulence, NS_NewSVGFELeafFrame)};
5092 const FrameConstructionData* data =
5093 FindDataByTag(aElement, aStyle, sSVGData, ArrayLength(sSVGData));
5095 if (!data) {
5096 data = &sContainerData;
5099 return data;
5102 void nsCSSFrameConstructor::InsertPageBreakItem(
5103 nsIContent* aContent, FrameConstructionItemList& aItems,
5104 InsertPageBreakLocation location) {
5105 RefPtr<ComputedStyle> pseudoStyle =
5106 mPresShell->StyleSet()->ResolveNonInheritingAnonymousBoxStyle(
5107 PseudoStyleType::pageBreak);
5109 MOZ_ASSERT(pseudoStyle->StyleDisplay()->mDisplay == StyleDisplay::Block,
5110 "Unexpected display");
5112 static constexpr FrameConstructionData sPageBreakData(NS_NewPageBreakFrame,
5113 FCDATA_SKIP_FRAMESET);
5114 if (location == InsertPageBreakLocation::eBefore) {
5115 aItems.PrependItem(this, &sPageBreakData, aContent, pseudoStyle.forget(),
5116 true);
5117 } else {
5118 aItems.AppendItem(this, &sPageBreakData, aContent, pseudoStyle.forget(),
5119 true);
5123 bool nsCSSFrameConstructor::ShouldCreateItemsForChild(
5124 nsFrameConstructorState& aState, nsIContent* aContent,
5125 nsContainerFrame* aParentFrame) {
5126 aContent->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
5127 // XXX the GetContent() != aContent check is needed due to bug 135040.
5128 // Remove it once that's fixed.
5129 if (aContent->GetPrimaryFrame() &&
5130 aContent->GetPrimaryFrame()->GetContent() == aContent &&
5131 !aState.mCreatingExtraFrames) {
5132 MOZ_ASSERT(false,
5133 "asked to create frame construction item for a node that "
5134 "already has a frame");
5135 return false;
5138 // don't create a whitespace frame if aParent doesn't want it
5139 if (!NeedFrameFor(aState, aParentFrame, aContent)) {
5140 return false;
5143 // never create frames for comments or PIs
5144 if (aContent->IsComment() || aContent->IsProcessingInstruction()) {
5145 return false;
5148 return true;
5151 void nsCSSFrameConstructor::AddFrameConstructionItems(
5152 nsFrameConstructorState& aState, nsIContent* aContent,
5153 bool aSuppressWhiteSpaceOptimizations, const ComputedStyle& aParentStyle,
5154 const InsertionPoint& aInsertion, FrameConstructionItemList& aItems,
5155 ItemFlags aFlags) {
5156 nsContainerFrame* parentFrame = aInsertion.mParentFrame;
5157 if (!ShouldCreateItemsForChild(aState, aContent, parentFrame)) {
5158 return;
5160 if (MOZ_UNLIKELY(aParentStyle.StyleContent()->mContent.IsNone()) &&
5161 StaticPrefs::layout_css_element_content_none_enabled()) {
5162 return;
5165 RefPtr<ComputedStyle> computedStyle = ResolveComputedStyle(aContent);
5166 auto flags = aFlags + ItemFlag::AllowPageBreak;
5167 if (parentFrame) {
5168 if (parentFrame->IsInSVGTextSubtree()) {
5169 flags += ItemFlag::IsWithinSVGText;
5171 if (parentFrame->IsBlockFrame() && parentFrame->GetParent() &&
5172 parentFrame->GetParent()->IsSVGTextFrame()) {
5173 flags += ItemFlag::AllowTextPathChild;
5176 AddFrameConstructionItemsInternal(aState, aContent, parentFrame,
5177 aSuppressWhiteSpaceOptimizations,
5178 computedStyle, flags, aItems);
5181 // Whether we should suppress frames for a child under a <select> frame.
5183 // Never create frames for non-option/optgroup kids of <select> and non-option
5184 // kids of <optgroup> inside a <select>.
5185 static bool ShouldSuppressFrameInSelect(const nsIContent* aParent,
5186 const nsIContent& aChild) {
5187 if (!aParent ||
5188 !aParent->IsAnyOfHTMLElements(nsGkAtoms::select, nsGkAtoms::optgroup,
5189 nsGkAtoms::option)) {
5190 return false;
5193 // Options with labels have their label text added in ::before by forms.css.
5194 // Suppress frames for their child text.
5195 if (aParent->IsHTMLElement(nsGkAtoms::option) &&
5196 !aChild.IsRootOfNativeAnonymousSubtree()) {
5197 return aParent->AsElement()->HasNonEmptyAttr(nsGkAtoms::label);
5200 // If we're in any display: contents subtree, just suppress the frame.
5202 // We can't be regular NAC, since display: contents has no frame to generate
5203 // them off.
5204 if (aChild.GetParent() != aParent) {
5205 return true;
5208 // Option is always fine.
5209 if (aChild.IsHTMLElement(nsGkAtoms::option)) {
5210 return false;
5213 // <optgroup> is OK in <select> but not in <optgroup>.
5214 if (aChild.IsHTMLElement(nsGkAtoms::optgroup) &&
5215 aParent->IsHTMLElement(nsGkAtoms::select)) {
5216 return false;
5219 // Allow native anonymous content no matter what.
5220 if (aChild.IsRootOfNativeAnonymousSubtree()) {
5221 return false;
5224 return true;
5227 const nsCSSFrameConstructor::FrameConstructionData*
5228 nsCSSFrameConstructor::FindDataForContent(nsIContent& aContent,
5229 ComputedStyle& aStyle,
5230 nsIFrame* aParentFrame,
5231 ItemFlags aFlags) {
5232 MOZ_ASSERT(aStyle.StyleDisplay()->mDisplay != StyleDisplay::None &&
5233 aStyle.StyleDisplay()->mDisplay != StyleDisplay::Contents,
5234 "These two special display values should be handled earlier");
5236 if (auto* text = Text::FromNode(aContent)) {
5237 return FindTextData(*text, aParentFrame);
5240 return FindElementData(*aContent.AsElement(), aStyle, aParentFrame, aFlags);
5243 const nsCSSFrameConstructor::FrameConstructionData*
5244 nsCSSFrameConstructor::FindElementData(const Element& aElement,
5245 ComputedStyle& aStyle,
5246 nsIFrame* aParentFrame,
5247 ItemFlags aFlags) {
5248 // Don't create frames for non-SVG element children of SVG elements.
5249 if (!aElement.IsSVGElement()) {
5250 if (aParentFrame && IsFrameForSVG(aParentFrame) &&
5251 !aParentFrame->IsSVGForeignObjectFrame()) {
5252 return nullptr;
5254 if (aFlags.contains(ItemFlag::IsWithinSVGText)) {
5255 return nullptr;
5259 if (auto* data = FindElementTagData(aElement, aStyle, aParentFrame, aFlags)) {
5260 return data;
5263 // Check for 'content: <image-url>' on the element (which makes us ignore
5264 // 'display' values other than 'none' or 'contents').
5265 if (nsImageFrame::ShouldCreateImageFrameForContentProperty(aElement,
5266 aStyle)) {
5267 static constexpr FrameConstructionData sImgData(
5268 NS_NewImageFrameForContentProperty);
5269 return &sImgData;
5272 const bool shouldBlockify = aFlags.contains(ItemFlag::IsForRenderedLegend) ||
5273 aFlags.contains(ItemFlag::IsForOutsideMarker);
5274 if (shouldBlockify && !aStyle.StyleDisplay()->IsBlockOutsideStyle()) {
5275 // Make a temp copy of StyleDisplay and blockify its mDisplay value.
5276 auto display = *aStyle.StyleDisplay();
5277 bool isRootElement = false;
5278 uint16_t rawDisplayValue =
5279 Servo_ComputedValues_BlockifiedDisplay(&aStyle, isRootElement);
5280 display.mDisplay = StyleDisplay(rawDisplayValue);
5281 return FindDisplayData(display, aElement);
5284 const auto& display = *aStyle.StyleDisplay();
5285 return FindDisplayData(display, aElement);
5288 const nsCSSFrameConstructor::FrameConstructionData*
5289 nsCSSFrameConstructor::FindElementTagData(const Element& aElement,
5290 ComputedStyle& aStyle,
5291 nsIFrame* aParentFrame,
5292 ItemFlags aFlags) {
5293 switch (aElement.GetNameSpaceID()) {
5294 case kNameSpaceID_XHTML:
5295 return FindHTMLData(aElement, aParentFrame, aStyle);
5296 case kNameSpaceID_MathML:
5297 return FindMathMLData(aElement, aStyle);
5298 case kNameSpaceID_SVG:
5299 return FindSVGData(aElement, aParentFrame,
5300 aFlags.contains(ItemFlag::IsWithinSVGText),
5301 aFlags.contains(ItemFlag::AllowTextPathChild), aStyle);
5302 case kNameSpaceID_XUL:
5303 return FindXULTagData(aElement, aStyle);
5304 default:
5305 return nullptr;
5309 void nsCSSFrameConstructor::AddFrameConstructionItemsInternal(
5310 nsFrameConstructorState& aState, nsIContent* aContent,
5311 nsContainerFrame* aParentFrame, bool aSuppressWhiteSpaceOptimizations,
5312 ComputedStyle* aComputedStyle, ItemFlags aFlags,
5313 FrameConstructionItemList& aItems) {
5314 MOZ_ASSERT(aContent->IsText() || aContent->IsElement(),
5315 "Shouldn't get anything else here!");
5316 MOZ_ASSERT(aContent->IsInComposedDoc());
5317 MOZ_ASSERT(!aContent->GetPrimaryFrame() || aState.mCreatingExtraFrames ||
5318 aContent->NodeInfo()->NameAtom() == nsGkAtoms::area);
5320 const bool withinSVGText = aFlags.contains(ItemFlag::IsWithinSVGText);
5321 const bool isGeneratedContent = aFlags.contains(ItemFlag::IsGeneratedContent);
5322 MOZ_ASSERT(!isGeneratedContent || aComputedStyle->IsPseudoElement(),
5323 "Generated content should be a pseudo-element");
5325 FrameConstructionItem* item = nullptr;
5326 auto cleanupGeneratedContent = mozilla::MakeScopeExit([&]() {
5327 if (isGeneratedContent && !item) {
5328 MOZ_ASSERT(!IsDisplayContents(aContent),
5329 "This would need to change if we support display: contents "
5330 "in generated content");
5331 aContent->UnbindFromTree();
5335 // 'display:none' elements never creates any frames at all.
5336 const nsStyleDisplay& display = *aComputedStyle->StyleDisplay();
5337 if (display.mDisplay == StyleDisplay::None) {
5338 return;
5341 if (display.mDisplay == StyleDisplay::Contents) {
5342 // See the mDisplay fixup code in StyleAdjuster::adjust.
5343 MOZ_ASSERT(!aContent->AsElement()->IsRootOfNativeAnonymousSubtree(),
5344 "display:contents on anonymous content is unsupported");
5346 // FIXME(bug 1588477): <svg:text>'s TextNodeCorrespondenceRecorder has
5347 // trouble with everything that looks like display: contents.
5348 if (withinSVGText) {
5349 return;
5352 CreateGeneratedContentItem(aState, aParentFrame, *aContent->AsElement(),
5353 *aComputedStyle, PseudoStyleType::before,
5354 aItems);
5356 FlattenedChildIterator iter(aContent);
5357 InsertionPoint insertion(aParentFrame, aContent);
5358 for (nsIContent* child = iter.GetNextChild(); child;
5359 child = iter.GetNextChild()) {
5360 AddFrameConstructionItems(aState, child, aSuppressWhiteSpaceOptimizations,
5361 *aComputedStyle, insertion, aItems, aFlags);
5363 aItems.SetParentHasNoShadowDOM(!iter.ShadowDOMInvolved());
5365 CreateGeneratedContentItem(aState, aParentFrame, *aContent->AsElement(),
5366 *aComputedStyle, PseudoStyleType::after, aItems);
5367 return;
5370 nsIContent* parent = aParentFrame ? aParentFrame->GetContent() : nullptr;
5371 if (ShouldSuppressFrameInSelect(parent, *aContent)) {
5372 return;
5375 if (aContent->IsHTMLElement(nsGkAtoms::legend) && aParentFrame) {
5376 const nsFieldSetFrame* const fs = GetFieldSetFrameFor(aParentFrame);
5377 if (fs && !fs->GetLegend() && !aState.mHasRenderedLegend &&
5378 !aComputedStyle->StyleDisplay()->IsFloatingStyle() &&
5379 !aComputedStyle->StyleDisplay()->IsAbsolutelyPositionedStyle()) {
5380 aState.mHasRenderedLegend = true;
5381 aFlags += ItemFlag::IsForRenderedLegend;
5385 const FrameConstructionData* const data =
5386 FindDataForContent(*aContent, *aComputedStyle, aParentFrame, aFlags);
5387 if (!data || data->mBits & FCDATA_SUPPRESS_FRAME) {
5388 return;
5391 const bool isPopup = data->mBits & FCDATA_IS_POPUP;
5393 const uint32_t bits = data->mBits;
5395 // Inside colgroups, suppress everything except columns.
5396 if (aParentFrame && aParentFrame->IsTableColGroupFrame() &&
5397 (!(bits & FCDATA_IS_TABLE_PART) ||
5398 display.mDisplay != StyleDisplay::TableColumn)) {
5399 return;
5402 const bool canHavePageBreak =
5403 aFlags.contains(ItemFlag::AllowPageBreak) &&
5404 aState.mPresContext->IsPaginated() &&
5405 !display.IsAbsolutelyPositionedStyle() &&
5406 !(aParentFrame && aParentFrame->IsGridContainerFrame()) &&
5407 !(bits & FCDATA_IS_TABLE_PART) && !(bits & FCDATA_IS_SVG_TEXT);
5408 if (canHavePageBreak && display.BreakBefore()) {
5409 AppendPageBreakItem(aContent, aItems);
5412 if (!item) {
5413 item = aItems.AppendItem(this, data, aContent, do_AddRef(aComputedStyle),
5414 aSuppressWhiteSpaceOptimizations);
5415 if (aFlags.contains(ItemFlag::IsForRenderedLegend)) {
5416 item->mIsRenderedLegend = true;
5419 item->mIsText = !aContent->IsElement();
5420 item->mIsGeneratedContent = isGeneratedContent;
5421 if (isGeneratedContent) {
5422 // We need to keep this alive until the frame takes ownership.
5423 // This corresponds to the Release in ConstructFramesFromItem.
5424 item->mContent->AddRef();
5426 item->mIsPopup = isPopup;
5428 if (canHavePageBreak && display.BreakAfter()) {
5429 AppendPageBreakItem(aContent, aItems);
5432 if (bits & FCDATA_IS_INLINE) {
5433 // To correctly set item->mIsAllInline we need to build up our child items
5434 // right now.
5435 BuildInlineChildItems(aState, *item,
5436 aFlags.contains(ItemFlag::IsWithinSVGText),
5437 aFlags.contains(ItemFlag::AllowTextPathChild));
5438 item->mIsBlock = false;
5439 } else {
5440 // Compute a boolean isInline which is guaranteed to be false for blocks
5441 // (but may also be false for some inlines).
5442 const bool isInline =
5443 // Table-internal things are inline-outside if and only if they're kids
5444 // of inlines, since they'll trigger construction of inline-table
5445 // pseudos.
5446 ((bits & FCDATA_IS_TABLE_PART) &&
5447 (!aParentFrame || // No aParentFrame means inline
5448 aParentFrame->StyleDisplay()->IsInlineFlow())) ||
5449 // Things that are inline-outside but aren't inline frames are inline
5450 display.IsInlineOutsideStyle() ||
5451 // Popups that are certainly out of flow.
5452 isPopup;
5454 // Set mIsAllInline conservatively. It just might be that even an inline
5455 // that has mIsAllInline false doesn't need an {ib} split. So this is just
5456 // an optimization to keep from doing too much work in cases when we can
5457 // show that mIsAllInline is true..
5458 item->mIsAllInline =
5459 isInline ||
5460 // Figure out whether we're guaranteed this item will be out of flow.
5461 // This is not a precise test, since one of our ancestor inlines might
5462 // add an absolute containing block (if it's relatively positioned) when
5463 // there wasn't such a containing block before. But it's conservative
5464 // in the sense that anything that will really end up as an in-flow
5465 // non-inline will test false here. In other words, if this test is
5466 // true we're guaranteed to be inline; if it's false we don't know what
5467 // we'll end up as.
5469 // If we make this test precise, we can remove some of the code dealing
5470 // with the imprecision in ConstructInline and adjust the comments on
5471 // mIsAllInline and mIsBlock in the header.
5472 (!(bits & FCDATA_DISALLOW_OUT_OF_FLOW) &&
5473 aState.GetGeometricParent(display, nullptr));
5475 // Set mIsBlock conservatively. It's OK to set it false for some real
5476 // blocks, but not OK to set it true for things that aren't blocks. Since
5477 // isOutOfFlow might be false even in cases when the frame will end up
5478 // out-of-flow, we can't use it here. But we _can_ say that the frame will
5479 // for sure end up in-flow if it's not floated or absolutely positioned.
5480 item->mIsBlock = !isInline && !display.IsAbsolutelyPositionedStyle() &&
5481 !display.IsFloatingStyle() && !(bits & FCDATA_IS_SVG_TEXT);
5484 if (item->mIsAllInline) {
5485 aItems.InlineItemAdded();
5486 } else if (item->mIsBlock) {
5487 aItems.BlockItemAdded();
5492 * Return true if the frame construction item pointed to by aIter will
5493 * create a frame adjacent to a line boundary in the frame tree, and that
5494 * line boundary is induced by a content node adjacent to the frame's
5495 * content node in the content tree. The latter condition is necessary so
5496 * that ContentAppended/ContentInserted/ContentRemoved can easily find any
5497 * text nodes that were suppressed here.
5499 bool nsCSSFrameConstructor::AtLineBoundary(FCItemIterator& aIter) {
5500 if (aIter.item().mSuppressWhiteSpaceOptimizations) {
5501 return false;
5504 if (aIter.AtStart()) {
5505 if (aIter.List()->HasLineBoundaryAtStart() &&
5506 !aIter.item().mContent->GetPreviousSibling())
5507 return true;
5508 } else {
5509 FCItemIterator prev = aIter;
5510 prev.Prev();
5511 if (prev.item().IsLineBoundary() &&
5512 !prev.item().mSuppressWhiteSpaceOptimizations &&
5513 aIter.item().mContent->GetPreviousSibling() == prev.item().mContent)
5514 return true;
5517 FCItemIterator next = aIter;
5518 next.Next();
5519 if (next.IsDone()) {
5520 if (aIter.List()->HasLineBoundaryAtEnd() &&
5521 !aIter.item().mContent->GetNextSibling())
5522 return true;
5523 } else {
5524 if (next.item().IsLineBoundary() &&
5525 !next.item().mSuppressWhiteSpaceOptimizations &&
5526 aIter.item().mContent->GetNextSibling() == next.item().mContent)
5527 return true;
5530 return false;
5533 void nsCSSFrameConstructor::ConstructFramesFromItem(
5534 nsFrameConstructorState& aState, FCItemIterator& aIter,
5535 nsContainerFrame* aParentFrame, nsFrameList& aFrameList) {
5536 FrameConstructionItem& item = aIter.item();
5537 ComputedStyle* computedStyle = item.mComputedStyle;
5538 if (item.mIsText) {
5539 // If this is collapsible whitespace next to a line boundary,
5540 // don't create a frame. item.IsWhitespace() also sets the
5541 // NS_CREATE_FRAME_IF_NON_WHITESPACE flag in the text node. (If we
5542 // end up creating a frame, nsTextFrame::Init will clear the flag.)
5543 // We don't do this for generated content, because some generated
5544 // text content is empty text nodes that are about to be initialized.
5545 // (We check mAdditionalStateBits because only the generated content
5546 // container's frame construction item is marked with
5547 // mIsGeneratedContent, and we might not have an aParentFrame.)
5548 // We don't do it for content that may have Shadow DOM siblings / insertion
5549 // points, because they make it difficult to correctly create the frame due
5550 // to dynamic changes.
5551 // We don't do it for SVG text, since we might need to position and
5552 // measure the white space glyphs due to x/y/dx/dy attributes.
5553 if (AtLineBoundary(aIter) &&
5554 !computedStyle->StyleText()->WhiteSpaceOrNewlineIsSignificant() &&
5555 aIter.List()->ParentHasNoShadowDOM() &&
5556 !(aState.mAdditionalStateBits & NS_FRAME_GENERATED_CONTENT) &&
5557 (item.mFCData->mBits & FCDATA_IS_LINE_PARTICIPANT) &&
5558 !(item.mFCData->mBits & FCDATA_IS_SVG_TEXT) &&
5559 !mAlwaysCreateFramesForIgnorableWhitespace && item.IsWhitespace(aState))
5560 return;
5562 ConstructTextFrame(item.mFCData, aState, item.mContent, aParentFrame,
5563 computedStyle, aFrameList);
5564 return;
5567 AutoRestore<nsFrameState> savedStateBits(aState.mAdditionalStateBits);
5568 if (item.mIsGeneratedContent) {
5569 // Ensure that frames created here are all tagged with
5570 // NS_FRAME_GENERATED_CONTENT.
5571 aState.mAdditionalStateBits |= NS_FRAME_GENERATED_CONTENT;
5574 // XXXbz maybe just inline ConstructFrameFromItemInternal here or something?
5575 ConstructFrameFromItemInternal(item, aState, aParentFrame, aFrameList);
5577 if (item.mIsGeneratedContent) {
5578 // This corresponds to the AddRef in AddFrameConstructionItemsInternal.
5579 // The frame owns the generated content now.
5580 item.mContent->Release();
5582 // Now that we've passed ownership of item.mContent to the frame, unset
5583 // our generated content flag so we don't release or unbind it ourselves.
5584 item.mIsGeneratedContent = false;
5588 nsContainerFrame* nsCSSFrameConstructor::GetAbsoluteContainingBlock(
5589 nsIFrame* aFrame, ContainingBlockType aType) {
5590 // Starting with aFrame, look for a frame that is absolutely positioned or
5591 // relatively positioned (and transformed, if aType is FIXED)
5592 for (nsIFrame* frame = aFrame; frame; frame = frame->GetParent()) {
5593 if (frame->IsFrameOfType(nsIFrame::eMathML)) {
5594 // If it's mathml, bail out -- no absolute positioning out from inside
5595 // mathml frames. Note that we don't make this part of the loop
5596 // condition because of the stuff at the end of this method...
5597 return nullptr;
5600 // Look for the ICB.
5601 if (aType == FIXED_POS) {
5602 LayoutFrameType t = frame->Type();
5603 if (t == LayoutFrameType::Viewport || t == LayoutFrameType::PageContent) {
5604 return static_cast<nsContainerFrame*>(frame);
5608 // If the frame is positioned, we will probably return it as the containing
5609 // block (see the exceptions below). Otherwise, we'll start looking at the
5610 // parent frame, unless we're dealing with a scrollframe.
5611 // Scrollframes are special since they're not positioned, but their
5612 // scrolledframe might be. So, we need to check this special case to return
5613 // the correct containing block (the scrolledframe) in that case.
5614 // If we're looking for a fixed-pos containing block and the frame is
5615 // not transformed, skip it.
5616 if (!frame->IsAbsPosContainingBlock() ||
5617 (aType == FIXED_POS && !frame->IsFixedPosContainingBlock())) {
5618 continue;
5620 nsIFrame* absPosCBCandidate = frame;
5621 LayoutFrameType type = absPosCBCandidate->Type();
5622 if (type == LayoutFrameType::FieldSet) {
5623 absPosCBCandidate =
5624 static_cast<nsFieldSetFrame*>(absPosCBCandidate)->GetInner();
5625 if (!absPosCBCandidate) {
5626 continue;
5628 type = absPosCBCandidate->Type();
5630 if (type == LayoutFrameType::Scroll) {
5631 nsIScrollableFrame* scrollFrame = do_QueryFrame(absPosCBCandidate);
5632 absPosCBCandidate = scrollFrame->GetScrolledFrame();
5633 if (!absPosCBCandidate) {
5634 continue;
5636 type = absPosCBCandidate->Type();
5638 // Only first continuations can be containing blocks.
5639 absPosCBCandidate = absPosCBCandidate->FirstContinuation();
5640 // Is the frame really an absolute container?
5641 if (!absPosCBCandidate->IsAbsoluteContainer()) {
5642 continue;
5645 // For tables, skip the inner frame and consider the table wrapper frame.
5646 if (type == LayoutFrameType::Table) {
5647 continue;
5649 // For table wrapper frames, we can just return absPosCBCandidate.
5650 MOZ_ASSERT((nsContainerFrame*)do_QueryFrame(absPosCBCandidate),
5651 "abs.pos. containing block must be nsContainerFrame sub-class");
5652 return static_cast<nsContainerFrame*>(absPosCBCandidate);
5655 MOZ_ASSERT(aType != FIXED_POS, "no ICB in this frame tree?");
5657 // It is possible for the search for the containing block to fail, because
5658 // no absolute container can be found in the parent chain. In those cases,
5659 // we fall back to the document element's containing block.
5660 return mDocElementContainingBlock;
5663 nsContainerFrame* nsCSSFrameConstructor::GetFloatContainingBlock(
5664 nsIFrame* aFrame) {
5665 // Starting with aFrame, look for a frame that is a float containing block.
5666 // If we hit a frame which prevents its descendants from floating, bail out.
5667 // The logic here needs to match the logic in MaybePushFloatContainingBlock().
5668 for (nsIFrame* containingBlock = aFrame;
5669 containingBlock && !ShouldSuppressFloatingOfDescendants(containingBlock);
5670 containingBlock = containingBlock->GetParent()) {
5671 if (containingBlock->IsFloatContainingBlock()) {
5672 MOZ_ASSERT((nsContainerFrame*)do_QueryFrame(containingBlock),
5673 "float containing block must be nsContainerFrame sub-class");
5674 return static_cast<nsContainerFrame*>(containingBlock);
5678 // If we didn't find a containing block, then there just isn't
5679 // one.... return null
5680 return nullptr;
5684 * This function will get the previous sibling to use for an append operation.
5686 * It takes a parent frame (must not be null) and the next insertion sibling, if
5687 * the parent content is display: contents or has ::after content (may be null).
5689 static nsIFrame* FindAppendPrevSibling(nsIFrame* aParentFrame,
5690 nsIFrame* aNextSibling) {
5691 aParentFrame->DrainSelfOverflowList();
5693 if (aNextSibling) {
5694 MOZ_ASSERT(
5695 aNextSibling->GetParent()->GetContentInsertionFrame() == aParentFrame,
5696 "Wrong parent");
5697 return aNextSibling->GetPrevSibling();
5700 return aParentFrame->PrincipalChildList().LastChild();
5704 * Finds the right parent frame to append content to aParentFrame.
5706 * Cannot return or receive null.
5708 static nsContainerFrame* ContinuationToAppendTo(
5709 nsContainerFrame* aParentFrame) {
5710 MOZ_ASSERT(aParentFrame);
5712 if (IsFramePartOfIBSplit(aParentFrame)) {
5713 // If the frame we are manipulating is a ib-split frame (that is, one that's
5714 // been created as a result of a block-in-inline situation) then we need to
5715 // append to the last ib-split sibling, not to the frame itself.
5717 // Always make sure to look at the last continuation of the frame for the
5718 // {ib} case, even if that continuation is empty.
5720 // We don't do this for the non-ib-split-frame case, since in the other
5721 // cases appending to the last nonempty continuation is fine and in fact not
5722 // doing that can confuse code that doesn't know to pull kids from
5723 // continuations other than its next one.
5724 return static_cast<nsContainerFrame*>(
5725 GetLastIBSplitSibling(aParentFrame)->LastContinuation());
5728 return nsLayoutUtils::LastContinuationWithChild(aParentFrame);
5732 * This function will get the next sibling for a frame insert operation given
5733 * the parent and previous sibling. aPrevSibling may be null.
5735 static nsIFrame* GetInsertNextSibling(nsIFrame* aParentFrame,
5736 nsIFrame* aPrevSibling) {
5737 if (aPrevSibling) {
5738 return aPrevSibling->GetNextSibling();
5741 return aParentFrame->PrincipalChildList().FirstChild();
5744 void nsCSSFrameConstructor::AppendFramesToParent(
5745 nsFrameConstructorState& aState, nsContainerFrame* aParentFrame,
5746 nsFrameList& aFrameList, nsIFrame* aPrevSibling, bool aIsRecursiveCall) {
5747 MOZ_ASSERT(
5748 !IsFramePartOfIBSplit(aParentFrame) || !GetIBSplitSibling(aParentFrame) ||
5749 !GetIBSplitSibling(aParentFrame)->PrincipalChildList().FirstChild(),
5750 "aParentFrame has a ib-split sibling with kids?");
5751 MOZ_ASSERT(!aPrevSibling || aPrevSibling->GetParent() == aParentFrame,
5752 "Parent and prevsibling don't match");
5753 MOZ_ASSERT(
5754 !aParentFrame->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR) ||
5755 !IsFramePartOfIBSplit(aParentFrame),
5756 "We should have wiped aParentFrame in WipeContainingBlock() "
5757 "if it's part of an IB split!");
5759 nsIFrame* nextSibling = ::GetInsertNextSibling(aParentFrame, aPrevSibling);
5761 NS_ASSERTION(nextSibling || !aParentFrame->GetNextContinuation() ||
5762 !aParentFrame->GetNextContinuation()
5763 ->PrincipalChildList()
5764 .FirstChild() ||
5765 aIsRecursiveCall,
5766 "aParentFrame has later continuations with kids?");
5767 NS_ASSERTION(
5768 nextSibling || !IsFramePartOfIBSplit(aParentFrame) ||
5769 (IsInlineFrame(aParentFrame) && !GetIBSplitSibling(aParentFrame) &&
5770 !aParentFrame->GetNextContinuation()) ||
5771 aIsRecursiveCall,
5772 "aParentFrame is not last?");
5774 // If we're inserting a list of frames at the end of the trailing inline
5775 // of an {ib} split, we may need to create additional {ib} siblings to parent
5776 // them.
5777 if (!nextSibling && IsFramePartOfIBSplit(aParentFrame)) {
5778 // When we get here, our frame list might start with a block. If it does
5779 // so, and aParentFrame is an inline, and it and all its previous
5780 // continuations have no siblings, then put the initial blocks from the
5781 // frame list into the previous block of the {ib} split. Note that we
5782 // didn't want to stop at the block part of the split when figuring out
5783 // initial parent, because that could screw up float parenting; it's easier
5784 // to do this little fixup here instead.
5785 if (aFrameList.NotEmpty() && aFrameList.FirstChild()->IsBlockOutside()) {
5786 // See whether our trailing inline is empty
5787 nsIFrame* firstContinuation = aParentFrame->FirstContinuation();
5788 if (firstContinuation->PrincipalChildList().IsEmpty()) {
5789 // Our trailing inline is empty. Collect our starting blocks from
5790 // aFrameList, get the right parent frame for them, and put them in.
5791 nsFrameList blockKids =
5792 aFrameList.Split([](nsIFrame* f) { return !f->IsBlockOutside(); });
5793 NS_ASSERTION(blockKids.NotEmpty(), "No blocks?");
5795 nsContainerFrame* prevBlock = GetIBSplitPrevSibling(firstContinuation);
5796 prevBlock =
5797 static_cast<nsContainerFrame*>(prevBlock->LastContinuation());
5798 NS_ASSERTION(prevBlock, "Should have previous block here");
5800 MoveChildrenTo(aParentFrame, prevBlock, blockKids);
5804 // We want to put some of the frames into this inline frame.
5805 nsFrameList inlineKids =
5806 aFrameList.Split([](nsIFrame* f) { return f->IsBlockOutside(); });
5808 if (!inlineKids.IsEmpty()) {
5809 AppendFrames(aParentFrame, FrameChildListID::Principal,
5810 std::move(inlineKids));
5813 if (!aFrameList.IsEmpty()) {
5814 nsFrameList ibSiblings;
5815 CreateIBSiblings(aState, aParentFrame,
5816 aParentFrame->IsAbsPosContainingBlock(), aFrameList,
5817 ibSiblings);
5819 // Make sure to trigger reflow of the inline that used to be our
5820 // last one and now isn't anymore, since its GetSkipSides() has
5821 // changed.
5822 mPresShell->FrameNeedsReflow(aParentFrame,
5823 IntrinsicDirty::FrameAndAncestors,
5824 NS_FRAME_HAS_DIRTY_CHILDREN);
5826 // Recurse so we create new ib siblings as needed for aParentFrame's
5827 // parent
5828 return AppendFramesToParent(aState, aParentFrame->GetParent(), ibSiblings,
5829 aParentFrame, true);
5831 return;
5834 // If we're appending a list of frames to the last continuations of a
5835 // ::-moz-column-content, we may need to create column-span siblings for them.
5836 if (!nextSibling && IsLastContinuationForColumnContent(aParentFrame)) {
5837 // Extract any initial non-column-span kids, and append them to
5838 // ::-moz-column-content's child list.
5839 nsFrameList initialNonColumnSpanKids =
5840 aFrameList.Split([](nsIFrame* f) { return f->IsColumnSpan(); });
5841 AppendFrames(aParentFrame, FrameChildListID::Principal,
5842 std::move(initialNonColumnSpanKids));
5844 if (aFrameList.IsEmpty()) {
5845 // No more kids to process (there weren't any column-span kids).
5846 return;
5849 nsFrameList columnSpanSiblings = CreateColumnSpanSiblings(
5850 aState, aParentFrame, aFrameList,
5851 // Column content should never be a absolute/fixed positioned containing
5852 // block. Pass nullptr as aPositionedFrame.
5853 nullptr);
5855 nsContainerFrame* columnSetWrapper = aParentFrame->GetParent();
5856 while (!columnSetWrapper->IsColumnSetWrapperFrame()) {
5857 columnSetWrapper = columnSetWrapper->GetParent();
5859 MOZ_ASSERT(columnSetWrapper,
5860 "No ColumnSetWrapperFrame ancestor for -moz-column-content?");
5862 FinishBuildingColumns(aState, columnSetWrapper, aParentFrame,
5863 columnSpanSiblings);
5865 MOZ_ASSERT(columnSpanSiblings.IsEmpty(),
5866 "The column-span siblings should be moved to the proper place!");
5867 return;
5870 // Insert the frames after our aPrevSibling
5871 InsertFrames(aParentFrame, FrameChildListID::Principal, aPrevSibling,
5872 std::move(aFrameList));
5875 // This gets called to see if the frames corresponding to aSibling and aContent
5876 // should be siblings in the frame tree. Although (1) rows and cols, (2) row
5877 // groups and col groups, (3) row groups and captions, (4) legends and content
5878 // inside fieldsets, (5) popups and other kids of the menu are siblings from a
5879 // content perspective, they are not considered siblings in the frame tree.
5880 bool nsCSSFrameConstructor::IsValidSibling(nsIFrame* aSibling,
5881 nsIContent* aContent,
5882 Maybe<StyleDisplay>& aDisplay) {
5883 StyleDisplay siblingDisplay = aSibling->GetDisplay();
5884 if (StyleDisplay::TableColumnGroup == siblingDisplay ||
5885 StyleDisplay::TableColumn == siblingDisplay ||
5886 StyleDisplay::TableCaption == siblingDisplay ||
5887 StyleDisplay::TableHeaderGroup == siblingDisplay ||
5888 StyleDisplay::TableRowGroup == siblingDisplay ||
5889 StyleDisplay::TableFooterGroup == siblingDisplay) {
5890 // if we haven't already, resolve a style to find the display type of
5891 // aContent.
5892 if (aDisplay.isNothing()) {
5893 if (aContent->IsComment() || aContent->IsProcessingInstruction()) {
5894 // Comments and processing instructions never have frames, so we should
5895 // not try to generate styles for them.
5896 return false;
5898 // FIXME(emilio): This is buggy some times, see bug 1424656.
5899 RefPtr<ComputedStyle> computedStyle = ResolveComputedStyle(aContent);
5900 const nsStyleDisplay* display = computedStyle->StyleDisplay();
5901 aDisplay.emplace(display->mDisplay);
5904 StyleDisplay display = aDisplay.value();
5905 // To have decent performance we want to return false in cases in which
5906 // reordering the two siblings has no effect on display. To ensure
5907 // correctness, we MUST return false in cases where the two siblings have
5908 // the same desired parent type and live on different display lists.
5909 // Specificaly, columns and column groups should only consider columns and
5910 // column groups as valid siblings. Captions should only consider other
5911 // captions. All other things should consider each other as valid
5912 // siblings. The restriction in the |if| above on siblingDisplay is ok,
5913 // because for correctness the only part that really needs to happen is to
5914 // not consider captions, column groups, and row/header/footer groups
5915 // siblings of each other. Treating a column or colgroup as a valid
5916 // sibling of a non-table-related frame will just mean we end up reframing.
5917 if ((siblingDisplay == StyleDisplay::TableCaption) !=
5918 (display == StyleDisplay::TableCaption)) {
5919 // One's a caption and the other is not. Not valid siblings.
5920 return false;
5923 if ((siblingDisplay == StyleDisplay::TableColumnGroup ||
5924 siblingDisplay == StyleDisplay::TableColumn) !=
5925 (display == StyleDisplay::TableColumnGroup ||
5926 display == StyleDisplay::TableColumn)) {
5927 // One's a column or column group and the other is not. Not valid
5928 // siblings.
5929 return false;
5931 // Fall through; it's possible that the display type was overridden and
5932 // a different sort of frame was constructed, so we may need to return false
5933 // below.
5936 return true;
5939 // FIXME(emilio): If we ever kill IsValidSibling() we can simplify this quite a
5940 // bit (no need to pass aTargetContent or aTargetContentDisplay, and the
5941 // adjust() calls can be responsibility of the caller).
5942 template <nsCSSFrameConstructor::SiblingDirection aDirection>
5943 nsIFrame* nsCSSFrameConstructor::FindSiblingInternal(
5944 FlattenedChildIterator& aIter, nsIContent* aTargetContent,
5945 Maybe<StyleDisplay>& aTargetContentDisplay) {
5946 auto adjust = [&](nsIFrame* aPotentialSiblingFrame) -> nsIFrame* {
5947 return AdjustSiblingFrame(aPotentialSiblingFrame, aTargetContent,
5948 aTargetContentDisplay, aDirection);
5951 auto nextDomSibling = [](FlattenedChildIterator& aIter) -> nsIContent* {
5952 return aDirection == SiblingDirection::Forward ? aIter.GetNextChild()
5953 : aIter.GetPreviousChild();
5956 auto getInsideMarkerFrame = [](const nsIContent* aContent) -> nsIFrame* {
5957 auto* marker = nsLayoutUtils::GetMarkerFrame(aContent);
5958 const bool isInsideMarker =
5959 marker && marker->GetInFlowParent()->StyleList()->mListStylePosition ==
5960 StyleListStylePosition::Inside;
5961 return isInsideMarker ? marker : nullptr;
5964 auto getNearPseudo = [&](const nsIContent* aContent) -> nsIFrame* {
5965 if (aDirection == SiblingDirection::Forward) {
5966 if (auto* marker = getInsideMarkerFrame(aContent)) {
5967 return marker;
5969 return nsLayoutUtils::GetBeforeFrame(aContent);
5971 return nsLayoutUtils::GetAfterFrame(aContent);
5974 auto getFarPseudo = [&](const nsIContent* aContent) -> nsIFrame* {
5975 if (aDirection == SiblingDirection::Forward) {
5976 return nsLayoutUtils::GetAfterFrame(aContent);
5978 if (auto* before = nsLayoutUtils::GetBeforeFrame(aContent)) {
5979 return before;
5981 return getInsideMarkerFrame(aContent);
5984 while (nsIContent* sibling = nextDomSibling(aIter)) {
5985 // NOTE(emilio): It's important to check GetPrimaryFrame() before
5986 // IsDisplayContents to get the correct insertion point when multiple
5987 // siblings go from display: non-none to display: contents.
5988 if (nsIFrame* primaryFrame = sibling->GetPrimaryFrame()) {
5989 // XXX the GetContent() == sibling check is needed due to bug 135040.
5990 // Remove it once that's fixed.
5991 if (primaryFrame->GetContent() == sibling) {
5992 if (nsIFrame* frame = adjust(primaryFrame)) {
5993 return frame;
5998 if (IsDisplayContents(sibling)) {
5999 if (nsIFrame* frame = adjust(getNearPseudo(sibling))) {
6000 return frame;
6003 const bool startFromBeginning = aDirection == SiblingDirection::Forward;
6004 FlattenedChildIterator iter(sibling, startFromBeginning);
6005 nsIFrame* sibling = FindSiblingInternal<aDirection>(
6006 iter, aTargetContent, aTargetContentDisplay);
6007 if (sibling) {
6008 return sibling;
6013 return adjust(getFarPseudo(aIter.Parent()));
6016 nsIFrame* nsCSSFrameConstructor::AdjustSiblingFrame(
6017 nsIFrame* aSibling, nsIContent* aTargetContent,
6018 Maybe<StyleDisplay>& aTargetContentDisplay, SiblingDirection aDirection) {
6019 if (!aSibling) {
6020 return nullptr;
6023 if (aSibling->IsRenderedLegend()) {
6024 return nullptr;
6027 if (aSibling->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
6028 aSibling = aSibling->GetPlaceholderFrame();
6029 MOZ_ASSERT(aSibling);
6032 MOZ_ASSERT(!aSibling->GetPrevContinuation(), "How?");
6033 if (aDirection == SiblingDirection::Backward) {
6034 // The frame may be a ib-split frame (a split inline frame that contains a
6035 // block). Get the last part of that split.
6036 if (IsFramePartOfIBSplit(aSibling)) {
6037 aSibling = GetLastIBSplitSibling(aSibling);
6040 // The frame may have a continuation. If so, we want the last
6041 // non-overflow-container continuation as our previous sibling.
6042 aSibling = aSibling->GetTailContinuation();
6045 if (!IsValidSibling(aSibling, aTargetContent, aTargetContentDisplay)) {
6046 return nullptr;
6049 return aSibling;
6052 nsIFrame* nsCSSFrameConstructor::FindPreviousSibling(
6053 const FlattenedChildIterator& aIter,
6054 Maybe<StyleDisplay>& aTargetContentDisplay) {
6055 return FindSibling<SiblingDirection::Backward>(aIter, aTargetContentDisplay);
6058 nsIFrame* nsCSSFrameConstructor::FindNextSibling(
6059 const FlattenedChildIterator& aIter,
6060 Maybe<StyleDisplay>& aTargetContentDisplay) {
6061 return FindSibling<SiblingDirection::Forward>(aIter, aTargetContentDisplay);
6064 template <nsCSSFrameConstructor::SiblingDirection aDirection>
6065 nsIFrame* nsCSSFrameConstructor::FindSibling(
6066 const FlattenedChildIterator& aIter,
6067 Maybe<StyleDisplay>& aTargetContentDisplay) {
6068 nsIContent* targetContent = aIter.Get();
6069 FlattenedChildIterator siblingIter = aIter;
6070 nsIFrame* sibling = FindSiblingInternal<aDirection>(
6071 siblingIter, targetContent, aTargetContentDisplay);
6072 if (sibling) {
6073 return sibling;
6076 // Our siblings (if any) do not have a frame to guide us. The frame for the
6077 // target content should be inserted whereever a frame for the container would
6078 // be inserted. This is needed when inserting into display: contents nodes.
6079 const nsIContent* current = aIter.Parent();
6080 while (IsDisplayContents(current)) {
6081 const nsIContent* parent = current->GetFlattenedTreeParent();
6082 MOZ_ASSERT(parent, "No display: contents on the root");
6084 FlattenedChildIterator iter(parent);
6085 iter.Seek(current);
6086 sibling = FindSiblingInternal<aDirection>(iter, targetContent,
6087 aTargetContentDisplay);
6088 if (sibling) {
6089 return sibling;
6092 current = parent;
6095 return nullptr;
6098 // For fieldsets, returns the area frame, if the child is not a legend.
6099 static nsContainerFrame* GetAdjustedParentFrame(nsContainerFrame* aParentFrame,
6100 nsIContent* aChildContent) {
6101 MOZ_ASSERT(!aParentFrame->IsTableWrapperFrame(), "Shouldn't be happening!");
6103 nsContainerFrame* newParent = nullptr;
6104 if (aParentFrame->IsFieldSetFrame()) {
6105 // If the parent is a fieldSet, use the fieldSet's area frame as the
6106 // parent unless the new content is a legend.
6107 if (!aChildContent->IsHTMLElement(nsGkAtoms::legend)) {
6108 newParent = static_cast<nsFieldSetFrame*>(aParentFrame)->GetInner();
6109 if (newParent) {
6110 newParent = newParent->GetContentInsertionFrame();
6114 return newParent ? newParent : aParentFrame;
6117 nsIFrame* nsCSSFrameConstructor::GetInsertionPrevSibling(
6118 InsertionPoint* aInsertion, nsIContent* aChild, bool* aIsAppend,
6119 bool* aIsRangeInsertSafe, nsIContent* aStartSkipChild,
6120 nsIContent* aEndSkipChild) {
6121 MOZ_ASSERT(aInsertion->mParentFrame, "Must have parent frame to start with");
6123 *aIsAppend = false;
6125 // Find the frame that precedes the insertion point.
6126 FlattenedChildIterator iter(aInsertion->mContainer);
6127 if (iter.ShadowDOMInvolved() || !aChild->IsRootOfNativeAnonymousSubtree()) {
6128 // The check for IsRootOfNativeAnonymousSubtree() is because editor is
6129 // severely broken and calls us directly for native anonymous
6130 // nodes that it creates.
6131 if (aStartSkipChild) {
6132 iter.Seek(aStartSkipChild);
6133 } else {
6134 iter.Seek(aChild);
6136 } else {
6137 // Prime the iterator for the call to FindPreviousSibling.
6138 iter.GetNextChild();
6139 MOZ_ASSERT(aChild->GetProperty(nsGkAtoms::restylableAnonymousNode),
6140 "Someone passed native anonymous content directly into frame "
6141 "construction. Stop doing that!");
6144 // Note that FindPreviousSibling is passed the iterator by value, so that
6145 // the later usage of the iterator starts from the same place.
6146 Maybe<StyleDisplay> childDisplay;
6147 nsIFrame* prevSibling = FindPreviousSibling(iter, childDisplay);
6149 // Now, find the geometric parent so that we can handle
6150 // continuations properly. Use the prev sibling if we have it;
6151 // otherwise use the next sibling.
6152 if (prevSibling) {
6153 aInsertion->mParentFrame =
6154 prevSibling->GetParent()->GetContentInsertionFrame();
6155 } else {
6156 // If there is no previous sibling, then find the frame that follows
6158 // FIXME(emilio): This is really complex and probably shouldn't be.
6159 if (aEndSkipChild) {
6160 iter.Seek(aEndSkipChild);
6161 iter.GetPreviousChild();
6163 if (nsIFrame* nextSibling = FindNextSibling(iter, childDisplay)) {
6164 aInsertion->mParentFrame =
6165 nextSibling->GetParent()->GetContentInsertionFrame();
6166 } else {
6167 // No previous or next sibling, so treat this like an appended frame.
6168 *aIsAppend = true;
6170 // Deal with fieldsets.
6171 aInsertion->mParentFrame =
6172 ::GetAdjustedParentFrame(aInsertion->mParentFrame, aChild);
6174 aInsertion->mParentFrame =
6175 ::ContinuationToAppendTo(aInsertion->mParentFrame);
6177 prevSibling = ::FindAppendPrevSibling(aInsertion->mParentFrame, nullptr);
6181 *aIsRangeInsertSafe = childDisplay.isNothing();
6182 return prevSibling;
6185 nsContainerFrame* nsCSSFrameConstructor::GetContentInsertionFrameFor(
6186 nsIContent* aContent) {
6187 nsIFrame* frame;
6188 while (!(frame = aContent->GetPrimaryFrame())) {
6189 if (!IsDisplayContents(aContent)) {
6190 return nullptr;
6193 aContent = aContent->GetFlattenedTreeParent();
6194 if (!aContent) {
6195 return nullptr;
6199 // If the content of the frame is not the desired content then this is not
6200 // really a frame for the desired content.
6201 // XXX This check is needed due to bug 135040. Remove it once that's fixed.
6202 if (frame->GetContent() != aContent) {
6203 return nullptr;
6206 nsContainerFrame* insertionFrame = frame->GetContentInsertionFrame();
6208 NS_ASSERTION(!insertionFrame || insertionFrame == frame || !frame->IsLeaf(),
6209 "The insertion frame is the primary frame or the primary frame "
6210 "isn't a leaf");
6212 return insertionFrame;
6215 static bool IsSpecialFramesetChild(nsIContent* aContent) {
6216 // IMPORTANT: This must match the conditions in nsHTMLFramesetFrame::Init.
6217 return aContent->IsAnyOfHTMLElements(nsGkAtoms::frameset, nsGkAtoms::frame);
6220 static void InvalidateCanvasIfNeeded(PresShell* aPresShell, nsIContent* aNode);
6222 void nsCSSFrameConstructor::AddTextItemIfNeeded(
6223 nsFrameConstructorState& aState, const ComputedStyle& aParentStyle,
6224 const InsertionPoint& aInsertion, nsIContent* aPossibleTextContent,
6225 FrameConstructionItemList& aItems) {
6226 MOZ_ASSERT(aPossibleTextContent, "Must have node");
6227 if (!aPossibleTextContent->IsText() ||
6228 !aPossibleTextContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE) ||
6229 aPossibleTextContent->HasFlag(NODE_NEEDS_FRAME)) {
6230 // Not text, or not suppressed due to being all-whitespace (if it were being
6231 // suppressed, it would have the NS_CREATE_FRAME_IF_NON_WHITESPACE flag), or
6232 // going to be reframed anyway.
6233 return;
6235 MOZ_ASSERT(!aPossibleTextContent->GetPrimaryFrame(),
6236 "Text node has a frame and NS_CREATE_FRAME_IF_NON_WHITESPACE");
6237 AddFrameConstructionItems(aState, aPossibleTextContent, false, aParentStyle,
6238 aInsertion, aItems);
6241 void nsCSSFrameConstructor::ReframeTextIfNeeded(nsIContent* aContent) {
6242 if (!aContent->IsText() ||
6243 !aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE) ||
6244 aContent->HasFlag(NODE_NEEDS_FRAME)) {
6245 // Not text, or not suppressed due to being all-whitespace (if it were being
6246 // suppressed, it would have the NS_CREATE_FRAME_IF_NON_WHITESPACE flag), or
6247 // going to be reframed anyway.
6248 return;
6250 MOZ_ASSERT(!aContent->GetPrimaryFrame(),
6251 "Text node has a frame and NS_CREATE_FRAME_IF_NON_WHITESPACE");
6252 ContentInserted(aContent, InsertionKind::Async);
6255 #ifdef DEBUG
6256 void nsCSSFrameConstructor::CheckBitsForLazyFrameConstruction(
6257 nsIContent* aParent) {
6258 // If we hit a node with no primary frame, or the NODE_NEEDS_FRAME bit set
6259 // we want to assert, but leaf frames that process their own children and may
6260 // ignore anonymous children (eg framesets) make this complicated. So we set
6261 // these two booleans if we encounter these situations and unset them if we
6262 // hit a node with a leaf frame.
6264 // It's fine if one of node without primary frame is in a display:none
6265 // subtree.
6267 // Also, it's fine if one of the nodes without primary frame is a display:
6268 // contents node.
6269 bool noPrimaryFrame = false;
6270 bool needsFrameBitSet = false;
6271 nsIContent* content = aParent;
6272 while (content && !content->HasFlag(NODE_DESCENDANTS_NEED_FRAMES)) {
6273 if (content->GetPrimaryFrame() && content->GetPrimaryFrame()->IsLeaf()) {
6274 noPrimaryFrame = needsFrameBitSet = false;
6276 if (!noPrimaryFrame && !content->GetPrimaryFrame()) {
6277 noPrimaryFrame = !IsDisplayContents(content);
6279 if (!needsFrameBitSet && content->HasFlag(NODE_NEEDS_FRAME)) {
6280 needsFrameBitSet = true;
6283 content = content->GetFlattenedTreeParent();
6285 if (content && content->GetPrimaryFrame() &&
6286 content->GetPrimaryFrame()->IsLeaf()) {
6287 noPrimaryFrame = needsFrameBitSet = false;
6289 MOZ_ASSERT(!noPrimaryFrame,
6290 "Ancestors of nodes with frames to be "
6291 "constructed lazily should have frames");
6292 MOZ_ASSERT(!needsFrameBitSet,
6293 "Ancestors of nodes with frames to be "
6294 "constructed lazily should not have NEEDS_FRAME bit set");
6296 #endif
6298 // Returns true if this operation can be lazy, false if not.
6300 // FIXME(emilio, bug 1410020): This function assumes that the flattened tree
6301 // parent of all the appended children is the same, which, afaict, is not
6302 // necessarily true.
6303 void nsCSSFrameConstructor::ConstructLazily(Operation aOperation,
6304 nsIContent* aChild) {
6305 MOZ_ASSERT(aChild->GetParent());
6307 // We can construct lazily; just need to set suitable bits in the content
6308 // tree.
6309 Element* parent = aChild->GetFlattenedTreeParentElement();
6310 if (!parent) {
6311 // Not part of the flat tree, nothing to do.
6312 return;
6315 if (Servo_Element_IsDisplayNone(parent)) {
6316 // Nothing to do either.
6318 // FIXME(emilio): This should be an assert, except for weird <frameset>
6319 // stuff that does its own frame construction. Such an assert would fire in
6320 // layout/style/crashtests/1411478.html, for example.
6321 return;
6324 // Set NODE_NEEDS_FRAME on the new nodes.
6325 if (aOperation == CONTENTINSERT) {
6326 NS_ASSERTION(!aChild->GetPrimaryFrame() ||
6327 aChild->GetPrimaryFrame()->GetContent() != aChild,
6328 // XXX the aChild->GetPrimaryFrame()->GetContent() != aChild
6329 // check is needed due to bug 135040. Remove it once that's
6330 // fixed.
6331 "setting NEEDS_FRAME on a node that already has a frame?");
6332 aChild->SetFlags(NODE_NEEDS_FRAME);
6333 } else { // CONTENTAPPEND
6334 for (nsIContent* child = aChild; child; child = child->GetNextSibling()) {
6335 NS_ASSERTION(!child->GetPrimaryFrame() ||
6336 child->GetPrimaryFrame()->GetContent() != child,
6337 // XXX the child->GetPrimaryFrame()->GetContent() != child
6338 // check is needed due to bug 135040. Remove it once that's
6339 // fixed.
6340 "setting NEEDS_FRAME on a node that already has a frame?");
6341 child->SetFlags(NODE_NEEDS_FRAME);
6345 CheckBitsForLazyFrameConstruction(parent);
6346 parent->NoteDescendantsNeedFramesForServo();
6349 void nsCSSFrameConstructor::IssueSingleInsertNofications(
6350 nsIContent* aStartChild, nsIContent* aEndChild,
6351 InsertionKind aInsertionKind) {
6352 for (nsIContent* child = aStartChild; child != aEndChild;
6353 child = child->GetNextSibling()) {
6354 // XXX the GetContent() != child check is needed due to bug 135040.
6355 // Remove it once that's fixed.
6356 MOZ_ASSERT(!child->GetPrimaryFrame() ||
6357 child->GetPrimaryFrame()->GetContent() != child);
6359 // Call ContentRangeInserted with this node.
6360 ContentRangeInserted(child, child->GetNextSibling(), aInsertionKind);
6364 bool nsCSSFrameConstructor::InsertionPoint::IsMultiple() const {
6365 // Fieldset frames have multiple normal flow child frame lists so handle it
6366 // the same as if it had multiple content insertion points.
6367 return mParentFrame && mParentFrame->IsFieldSetFrame();
6370 nsCSSFrameConstructor::InsertionPoint
6371 nsCSSFrameConstructor::GetRangeInsertionPoint(nsIContent* aStartChild,
6372 nsIContent* aEndChild,
6373 InsertionKind aInsertionKind) {
6374 MOZ_ASSERT(aStartChild);
6376 nsIContent* parent = aStartChild->GetParent();
6377 if (!parent) {
6378 IssueSingleInsertNofications(aStartChild, aEndChild, aInsertionKind);
6379 return {};
6382 // If the children of the container may be distributed to different insertion
6383 // points, insert them separately and bail out, letting ContentInserted handle
6384 // the mess.
6385 if (parent->GetShadowRoot()) {
6386 IssueSingleInsertNofications(aStartChild, aEndChild, aInsertionKind);
6387 return {};
6390 #ifdef DEBUG
6392 nsIContent* expectedParent = aStartChild->GetFlattenedTreeParent();
6393 for (nsIContent* child = aStartChild->GetNextSibling(); child;
6394 child = child->GetNextSibling()) {
6395 MOZ_ASSERT(child->GetFlattenedTreeParent() == expectedParent);
6398 #endif
6400 // Now the flattened tree parent of all the siblings is the same, just use the
6401 // same insertion point and take the fast path, unless it's a multiple
6402 // insertion point.
6403 InsertionPoint ip = GetInsertionPoint(aStartChild);
6404 if (ip.IsMultiple()) {
6405 IssueSingleInsertNofications(aStartChild, aEndChild, aInsertionKind);
6406 return {};
6409 return ip;
6412 bool nsCSSFrameConstructor::MaybeRecreateForFrameset(nsIFrame* aParentFrame,
6413 nsIContent* aStartChild,
6414 nsIContent* aEndChild) {
6415 if (aParentFrame->IsFrameSetFrame()) {
6416 // Check whether we have any kids we care about.
6417 for (nsIContent* cur = aStartChild; cur != aEndChild;
6418 cur = cur->GetNextSibling()) {
6419 if (IsSpecialFramesetChild(cur)) {
6420 // Just reframe the parent, since framesets are weird like that.
6421 RecreateFramesForContent(aParentFrame->GetContent(),
6422 InsertionKind::Async);
6423 return true;
6427 return false;
6430 void nsCSSFrameConstructor::LazilyStyleNewChildRange(nsIContent* aStartChild,
6431 nsIContent* aEndChild) {
6432 for (nsIContent* child = aStartChild; child != aEndChild;
6433 child = child->GetNextSibling()) {
6434 if (child->IsElement()) {
6435 child->AsElement()->NoteDirtyForServo();
6440 #ifdef DEBUG
6441 static bool IsFlattenedTreeChild(nsIContent* aParent, nsIContent* aChild) {
6442 FlattenedChildIterator iter(aParent);
6443 for (nsIContent* node = iter.GetNextChild(); node;
6444 node = iter.GetNextChild()) {
6445 if (node == aChild) {
6446 return true;
6449 return false;
6451 #endif
6453 void nsCSSFrameConstructor::StyleNewChildRange(nsIContent* aStartChild,
6454 nsIContent* aEndChild) {
6455 ServoStyleSet* styleSet = mPresShell->StyleSet();
6457 for (nsIContent* child = aStartChild; child != aEndChild;
6458 child = child->GetNextSibling()) {
6459 if (!child->IsElement()) {
6460 continue;
6463 Element* childElement = child->AsElement();
6465 // We only come in here from non-lazy frame construction, so the children
6466 // should be unstyled.
6467 MOZ_ASSERT(!childElement->HasServoData());
6469 #ifdef DEBUG
6471 // Furthermore, all of them should have the same flattened tree parent
6472 // (GetRangeInsertionPoint ensures it). And that parent should be styled,
6473 // otherwise we would've never found an insertion point at all.
6474 Element* parent = childElement->GetFlattenedTreeParentElement();
6475 MOZ_ASSERT(parent);
6476 MOZ_ASSERT(parent->HasServoData());
6477 MOZ_ASSERT(
6478 IsFlattenedTreeChild(parent, child),
6479 "GetFlattenedTreeParent and ChildIterator don't agree, fix this!");
6481 #endif
6483 styleSet->StyleNewSubtree(childElement);
6487 nsIFrame* nsCSSFrameConstructor::FindNextSiblingForAppend(
6488 const InsertionPoint& aInsertion) {
6489 auto SlowPath = [&]() -> nsIFrame* {
6490 FlattenedChildIterator iter(aInsertion.mContainer,
6491 /* aStartAtBeginning = */ false);
6492 iter.GetPreviousChild(); // Prime the iterator.
6493 Maybe<StyleDisplay> unused;
6494 return FindNextSibling(iter, unused);
6497 if (!IsDisplayContents(aInsertion.mContainer) &&
6498 !nsLayoutUtils::GetAfterFrame(aInsertion.mContainer)) {
6499 MOZ_ASSERT(!SlowPath());
6500 return nullptr;
6503 return SlowPath();
6506 void nsCSSFrameConstructor::ContentAppended(nsIContent* aFirstNewContent,
6507 InsertionKind aInsertionKind) {
6508 MOZ_ASSERT(aInsertionKind == InsertionKind::Sync ||
6509 !RestyleManager()->IsInStyleRefresh());
6511 AUTO_PROFILER_LABEL("nsCSSFrameConstructor::ContentAppended",
6512 LAYOUT_FrameConstruction);
6513 AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
6515 #ifdef DEBUG
6516 if (gNoisyContentUpdates) {
6517 printf(
6518 "nsCSSFrameConstructor::ContentAppended container=%p "
6519 "first-child=%p lazy=%d\n",
6520 aFirstNewContent->GetParent(), aFirstNewContent,
6521 aInsertionKind == InsertionKind::Async);
6522 if (gReallyNoisyContentUpdates && aFirstNewContent->GetParent()) {
6523 aFirstNewContent->GetParent()->List(stdout, 0);
6527 for (nsIContent* child = aFirstNewContent; child;
6528 child = child->GetNextSibling()) {
6529 // XXX the GetContent() != child check is needed due to bug 135040.
6530 // Remove it once that's fixed.
6531 MOZ_ASSERT(
6532 !child->GetPrimaryFrame() ||
6533 child->GetPrimaryFrame()->GetContent() != child,
6534 "asked to construct a frame for a node that already has a frame");
6536 #endif
6538 LAYOUT_PHASE_TEMP_EXIT();
6539 InsertionPoint insertion =
6540 GetRangeInsertionPoint(aFirstNewContent, nullptr, aInsertionKind);
6541 nsContainerFrame*& parentFrame = insertion.mParentFrame;
6542 LAYOUT_PHASE_TEMP_REENTER();
6543 if (!parentFrame) {
6544 // We're punting on frame construction because there's no container frame.
6545 // The Servo-backed style system handles this case like the lazy frame
6546 // construction case, except when we're already constructing frames, in
6547 // which case we shouldn't need to do anything else.
6548 if (aInsertionKind == InsertionKind::Async) {
6549 LazilyStyleNewChildRange(aFirstNewContent, nullptr);
6551 return;
6554 if (aInsertionKind == InsertionKind::Async) {
6555 ConstructLazily(CONTENTAPPEND, aFirstNewContent);
6556 LazilyStyleNewChildRange(aFirstNewContent, nullptr);
6557 return;
6560 LAYOUT_PHASE_TEMP_EXIT();
6561 if (MaybeRecreateForFrameset(parentFrame, aFirstNewContent, nullptr)) {
6562 LAYOUT_PHASE_TEMP_REENTER();
6563 return;
6565 LAYOUT_PHASE_TEMP_REENTER();
6567 if (parentFrame->IsLeaf()) {
6568 // Nothing to do here; we shouldn't be constructing kids of leaves
6569 // Clear lazy bits so we don't try to construct again.
6570 ClearLazyBits(aFirstNewContent, nullptr);
6571 return;
6574 LAYOUT_PHASE_TEMP_EXIT();
6575 if (WipeInsertionParent(parentFrame)) {
6576 LAYOUT_PHASE_TEMP_REENTER();
6577 return;
6579 LAYOUT_PHASE_TEMP_REENTER();
6581 #ifdef DEBUG
6582 if (gNoisyContentUpdates && IsFramePartOfIBSplit(parentFrame)) {
6583 printf("nsCSSFrameConstructor::ContentAppended: parentFrame=");
6584 parentFrame->ListTag(stdout);
6585 printf(" is ib-split\n");
6587 #endif
6589 // We should never get here with fieldsets, since they have
6590 // multiple insertion points.
6591 MOZ_ASSERT(!parentFrame->IsFieldSetFrame(),
6592 "Parent frame should not be fieldset!");
6594 nsIFrame* nextSibling = FindNextSiblingForAppend(insertion);
6595 if (nextSibling) {
6596 parentFrame = nextSibling->GetParent()->GetContentInsertionFrame();
6597 } else {
6598 parentFrame = ::ContinuationToAppendTo(parentFrame);
6601 nsContainerFrame* containingBlock = GetFloatContainingBlock(parentFrame);
6603 // See if the containing block has :first-letter style applied.
6604 const bool haveFirstLetterStyle =
6605 containingBlock && HasFirstLetterStyle(containingBlock);
6607 const bool haveFirstLineStyle =
6608 containingBlock && ShouldHaveFirstLineStyle(containingBlock->GetContent(),
6609 containingBlock->Style());
6611 if (haveFirstLetterStyle) {
6612 AutoWeakFrame wf(nextSibling);
6614 // Before we get going, remove the current letter frames
6615 RemoveLetterFrames(mPresShell, containingBlock);
6617 // Reget nextSibling, since we may have killed it.
6619 // FIXME(emilio): This kinda sucks! :(
6620 if (nextSibling && !wf) {
6621 nextSibling = FindNextSiblingForAppend(insertion);
6622 if (nextSibling) {
6623 parentFrame = nextSibling->GetParent()->GetContentInsertionFrame();
6624 containingBlock = GetFloatContainingBlock(parentFrame);
6629 // Create some new frames
6630 nsFrameConstructorState state(
6631 mPresShell, GetAbsoluteContainingBlock(parentFrame, FIXED_POS),
6632 GetAbsoluteContainingBlock(parentFrame, ABS_POS), containingBlock);
6634 if (mPresShell->GetPresContext()->IsPaginated()) {
6635 // Because this function can be called outside frame construction, we need
6636 // to set state.mAutoPageNameValue based on what the parent frame's auto
6637 // value is.
6638 // Calling this from outside the frame constructor can violate many of the
6639 // expectations in AutoFrameConstructionPageName, and unlike during frame
6640 // construction we already have an auto value from parentFrame, so we do
6641 // not use AutoFrameConstructionPageName here.
6642 state.mAutoPageNameValue = parentFrame->GetAutoPageValue();
6643 #ifdef DEBUG
6644 parentFrame->mWasVisitedByAutoFrameConstructionPageName = true;
6645 #endif
6648 LayoutFrameType frameType = parentFrame->Type();
6650 RefPtr<ComputedStyle> parentStyle =
6651 ResolveComputedStyle(insertion.mContainer);
6652 FlattenedChildIterator iter(insertion.mContainer);
6653 const bool haveNoShadowDOM =
6654 !iter.ShadowDOMInvolved() || !iter.GetNextChild();
6656 AutoFrameConstructionItemList items(this);
6657 if (aFirstNewContent->GetPreviousSibling() &&
6658 GetParentType(frameType) == eTypeBlock && haveNoShadowDOM) {
6659 // If there's a text node in the normal content list just before the new
6660 // items, and it has no frame, make a frame construction item for it. If it
6661 // doesn't need a frame, ConstructFramesFromItemList below won't give it
6662 // one. No need to do all this if our parent type is not block, though,
6663 // since WipeContainingBlock already handles that situation.
6665 // Because we're appending, we don't need to worry about any text
6666 // after the appended content; there can only be generated content
6667 // (and bare text nodes are not generated). Native anonymous content
6668 // generated by frames never participates in inline layout.
6669 AddTextItemIfNeeded(state, *parentStyle, insertion,
6670 aFirstNewContent->GetPreviousSibling(), items);
6672 for (nsIContent* child = aFirstNewContent; child;
6673 child = child->GetNextSibling()) {
6674 AddFrameConstructionItems(state, child, false, *parentStyle, insertion,
6675 items);
6678 nsIFrame* prevSibling = ::FindAppendPrevSibling(parentFrame, nextSibling);
6680 // Perform special check for diddling around with the frames in
6681 // a ib-split inline frame.
6682 // If we're appending before :after content, then we're not really
6683 // appending, so let WipeContainingBlock know that.
6684 LAYOUT_PHASE_TEMP_EXIT();
6685 if (WipeContainingBlock(state, containingBlock, parentFrame, items, true,
6686 prevSibling)) {
6687 LAYOUT_PHASE_TEMP_REENTER();
6688 return;
6690 LAYOUT_PHASE_TEMP_REENTER();
6692 // If the parent is a block frame, and we're not in a special case
6693 // where frames can be moved around, determine if the list is for the
6694 // start or end of the block.
6695 if (parentFrame->IsBlockFrameOrSubclass() && !haveFirstLetterStyle &&
6696 !haveFirstLineStyle && !IsFramePartOfIBSplit(parentFrame)) {
6697 items.SetLineBoundaryAtStart(!prevSibling ||
6698 !prevSibling->IsInlineOutside() ||
6699 prevSibling->IsBrFrame());
6700 // :after content can't be <br> so no need to check it
6702 // FIXME(emilio): A display: contents sibling could! Write a test-case and
6703 // fix.
6704 items.SetLineBoundaryAtEnd(!nextSibling || !nextSibling->IsInlineOutside());
6706 // To suppress whitespace-only text frames, we have to verify that
6707 // our container's DOM child list matches its flattened tree child list.
6708 items.SetParentHasNoShadowDOM(haveNoShadowDOM);
6710 nsFrameConstructorSaveState floatSaveState;
6711 state.MaybePushFloatContainingBlock(parentFrame, floatSaveState);
6713 nsFrameList frameList;
6714 ConstructFramesFromItemList(state, items, parentFrame,
6715 ParentIsWrapperAnonBox(parentFrame), frameList);
6717 for (nsIContent* child = aFirstNewContent; child;
6718 child = child->GetNextSibling()) {
6719 // Invalidate now instead of before the WipeContainingBlock call, just in
6720 // case we do wipe; in that case we don't need to do this walk at all.
6721 // XXXbz does that matter? Would it make more sense to save some virtual
6722 // GetChildAt_Deprecated calls instead and do this during construction of
6723 // our FrameConstructionItemList?
6724 InvalidateCanvasIfNeeded(mPresShell, child);
6727 // If the container is a table and a caption was appended, it needs to be put
6728 // in the table wrapper frame's additional child list.
6729 nsFrameList captionList;
6730 if (LayoutFrameType::Table == frameType) {
6731 // Pull out the captions. Note that we don't want to do that as we go,
6732 // because processing a single caption can add a whole bunch of things to
6733 // the frame items due to pseudoframe processing. So we'd have to pull
6734 // captions from a list anyway; might as well do that here.
6735 // XXXbz this is no longer true; we could pull captions directly out of the
6736 // FrameConstructionItemList now.
6737 PullOutCaptionFrames(frameList, captionList);
6740 if (haveFirstLineStyle && parentFrame == containingBlock) {
6741 // It's possible that some of the new frames go into a
6742 // first-line frame. Look at them and see...
6743 AppendFirstLineFrames(state, containingBlock->GetContent(), containingBlock,
6744 frameList);
6745 // That moved things into line frames as needed, reparenting their
6746 // styles. Nothing else needs to be done.
6747 } else if (parentFrame->Style()->HasPseudoElementData()) {
6748 // parentFrame might be inside a ::first-line frame. Check whether it is,
6749 // and if so fix up our styles.
6750 CheckForFirstLineInsertion(parentFrame, frameList);
6751 CheckForFirstLineInsertion(parentFrame, captionList);
6754 // Notify the parent frame passing it the list of new frames
6755 // Append the flowed frames to the principal child list; captions
6756 // need special treatment
6757 if (captionList.NotEmpty()) { // append the caption to the table wrapper
6758 NS_ASSERTION(LayoutFrameType::Table == frameType, "how did that happen?");
6759 nsContainerFrame* outerTable = parentFrame->GetParent();
6760 captionList.ApplySetParent(outerTable);
6761 AppendFrames(outerTable, FrameChildListID::Caption, std::move(captionList));
6764 LAYOUT_PHASE_TEMP_EXIT();
6765 if (MaybeRecreateForColumnSpan(state, parentFrame, frameList, prevSibling)) {
6766 LAYOUT_PHASE_TEMP_REENTER();
6767 return;
6769 LAYOUT_PHASE_TEMP_REENTER();
6771 if (frameList.NotEmpty()) { // append the in-flow kids
6772 AppendFramesToParent(state, parentFrame, frameList, prevSibling);
6775 // Recover first-letter frames
6776 if (haveFirstLetterStyle) {
6777 RecoverLetterFrames(containingBlock);
6780 #ifdef DEBUG
6781 if (gReallyNoisyContentUpdates) {
6782 printf("nsCSSFrameConstructor::ContentAppended: resulting frame model:\n");
6783 parentFrame->List(stdout);
6785 #endif
6787 #ifdef ACCESSIBILITY
6788 if (nsAccessibilityService* accService = GetAccService()) {
6789 accService->ContentRangeInserted(mPresShell, aFirstNewContent, nullptr);
6791 #endif
6794 void nsCSSFrameConstructor::ContentInserted(nsIContent* aChild,
6795 InsertionKind aInsertionKind) {
6796 ContentRangeInserted(aChild, aChild->GetNextSibling(), aInsertionKind);
6799 // ContentRangeInserted handles creating frames for a range of nodes that
6800 // aren't at the end of their childlist. ContentRangeInserted isn't a real
6801 // content notification, but rather it handles regular ContentInserted calls
6802 // for a single node as well as the lazy construction of frames for a range of
6803 // nodes when called from CreateNeededFrames. For a range of nodes to be
6804 // suitable to have its frames constructed all at once they must meet the same
6805 // conditions that ContentAppended imposes (GetRangeInsertionPoint checks
6806 // these), plus more. Namely when finding the insertion prevsibling we must not
6807 // need to consult something specific to any one node in the range, so that the
6808 // insertion prevsibling would be the same for each node in the range. So we
6809 // pass the first node in the range to GetInsertionPrevSibling, and if
6810 // IsValidSibling (the only place GetInsertionPrevSibling might look at the
6811 // passed in node itself) needs to resolve style on the node we record this and
6812 // return that this range needs to be split up and inserted separately. Table
6813 // captions need extra attention as we need to determine where to insert them
6814 // in the caption list, while skipping any nodes in the range being inserted
6815 // (because when we treat the caption frames the other nodes have had their
6816 // frames constructed but not yet inserted into the frame tree).
6817 void nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aStartChild,
6818 nsIContent* aEndChild,
6819 InsertionKind aInsertionKind) {
6820 MOZ_ASSERT(aInsertionKind == InsertionKind::Sync ||
6821 !RestyleManager()->IsInStyleRefresh());
6823 AUTO_PROFILER_LABEL("nsCSSFrameConstructor::ContentRangeInserted",
6824 LAYOUT_FrameConstruction);
6825 AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
6827 MOZ_ASSERT(aStartChild, "must always pass a child");
6829 #ifdef DEBUG
6830 if (gNoisyContentUpdates) {
6831 printf(
6832 "nsCSSFrameConstructor::ContentRangeInserted container=%p "
6833 "start-child=%p end-child=%p lazy=%d\n",
6834 aStartChild->GetParent(), aStartChild, aEndChild,
6835 aInsertionKind == InsertionKind::Async);
6836 if (gReallyNoisyContentUpdates) {
6837 if (aStartChild->GetParent()) {
6838 aStartChild->GetParent()->List(stdout, 0);
6839 } else {
6840 aStartChild->List(stdout, 0);
6845 for (nsIContent* child = aStartChild; child != aEndChild;
6846 child = child->GetNextSibling()) {
6847 // XXX the GetContent() != child check is needed due to bug 135040.
6848 // Remove it once that's fixed.
6849 NS_ASSERTION(
6850 !child->GetPrimaryFrame() ||
6851 child->GetPrimaryFrame()->GetContent() != child,
6852 "asked to construct a frame for a node that already has a frame");
6854 #endif
6856 bool isSingleInsert = (aStartChild->GetNextSibling() == aEndChild);
6857 NS_ASSERTION(isSingleInsert || aInsertionKind == InsertionKind::Sync,
6858 "range insert shouldn't be lazy");
6859 NS_ASSERTION(isSingleInsert || aEndChild,
6860 "range should not include all nodes after aStartChild");
6862 // If we have a null parent, then this must be the document element being
6863 // inserted, or some other child of the document in the DOM (might be a PI,
6864 // say).
6865 if (!aStartChild->GetParent()) {
6866 MOZ_ASSERT(isSingleInsert,
6867 "root node insertion should be a single insertion");
6868 Element* docElement = mDocument->GetRootElement();
6869 if (aStartChild != docElement) {
6870 // Not the root element; just bail out
6871 return;
6874 MOZ_ASSERT(!mRootElementFrame, "root element frame already created");
6875 if (aInsertionKind == InsertionKind::Async) {
6876 docElement->SetFlags(NODE_NEEDS_FRAME);
6877 LazilyStyleNewChildRange(docElement, nullptr);
6878 return;
6881 // Create frames for the document element and its child elements
6882 if (ConstructDocElementFrame(docElement)) {
6883 InvalidateCanvasIfNeeded(mPresShell, aStartChild);
6884 #ifdef DEBUG
6885 if (gReallyNoisyContentUpdates) {
6886 printf(
6887 "nsCSSFrameConstructor::ContentRangeInserted: resulting frame "
6888 "model:\n");
6889 mRootElementFrame->List(stdout);
6891 #endif
6894 #ifdef ACCESSIBILITY
6895 if (nsAccessibilityService* accService = GetAccService()) {
6896 accService->ContentRangeInserted(mPresShell, aStartChild, aEndChild);
6898 #endif
6900 return;
6903 InsertionPoint insertion;
6904 if (isSingleInsert) {
6905 // See if we have a Shadow DOM insertion point. If so, then that's our real
6906 // parent frame; if not, then the frame hasn't been built yet and we just
6907 // bail.
6908 insertion = GetInsertionPoint(aStartChild);
6909 } else {
6910 // Get our insertion point. If we need to issue single ContentInserteds
6911 // GetRangeInsertionPoint will take care of that for us.
6912 LAYOUT_PHASE_TEMP_EXIT();
6913 insertion = GetRangeInsertionPoint(aStartChild, aEndChild, aInsertionKind);
6914 LAYOUT_PHASE_TEMP_REENTER();
6917 if (!insertion.mParentFrame) {
6918 // We're punting on frame construction because there's no container frame.
6919 // The Servo-backed style system handles this case like the lazy frame
6920 // construction case, except when we're already constructing frames, in
6921 // which case we shouldn't need to do anything else.
6922 if (aInsertionKind == InsertionKind::Async) {
6923 LazilyStyleNewChildRange(aStartChild, aEndChild);
6925 return;
6928 if (aInsertionKind == InsertionKind::Async) {
6929 ConstructLazily(CONTENTINSERT, aStartChild);
6930 LazilyStyleNewChildRange(aStartChild, aEndChild);
6931 return;
6934 bool isAppend, isRangeInsertSafe;
6935 nsIFrame* prevSibling = GetInsertionPrevSibling(
6936 &insertion, aStartChild, &isAppend, &isRangeInsertSafe);
6938 // check if range insert is safe
6939 if (!isSingleInsert && !isRangeInsertSafe) {
6940 // must fall back to a single ContertInserted for each child in the range
6941 LAYOUT_PHASE_TEMP_EXIT();
6942 IssueSingleInsertNofications(aStartChild, aEndChild, InsertionKind::Sync);
6943 LAYOUT_PHASE_TEMP_REENTER();
6944 return;
6947 LayoutFrameType frameType = insertion.mParentFrame->Type();
6948 LAYOUT_PHASE_TEMP_EXIT();
6949 if (MaybeRecreateForFrameset(insertion.mParentFrame, aStartChild,
6950 aEndChild)) {
6951 LAYOUT_PHASE_TEMP_REENTER();
6952 return;
6954 LAYOUT_PHASE_TEMP_REENTER();
6956 // We should only get here with fieldsets when doing a single insert, because
6957 // fieldsets have multiple insertion points.
6958 NS_ASSERTION(isSingleInsert || frameType != LayoutFrameType::FieldSet,
6959 "Unexpected parent");
6960 // Note that this check is insufficient if aStartChild is not a legend with
6961 // display::contents that contains a legend. We'll catch that case in
6962 // WipeContainingBlock. (That code would also catch this case, but handling
6963 // this early is slightly faster.)
6964 // XXXmats we should be able to optimize this when the fieldset doesn't
6965 // currently have a rendered legend. ContentRangeInserted needs to be fixed
6966 // to use the inner frame as the content insertion frame in that case.
6967 if (GetFieldSetFrameFor(insertion.mParentFrame) &&
6968 aStartChild->NodeInfo()->NameAtom() == nsGkAtoms::legend) {
6969 // Just reframe the parent, since figuring out whether this
6970 // should be the new legend and then handling it is too complex.
6971 // We could do a little better here --- check if the fieldset already
6972 // has a legend which occurs earlier in its child list than this node,
6973 // and if so, proceed. But we'd have to extend nsFieldSetFrame
6974 // to locate this legend in the inserted frames and extract it.
6975 LAYOUT_PHASE_TEMP_EXIT();
6976 RecreateFramesForContent(insertion.mParentFrame->GetContent(),
6977 InsertionKind::Async);
6978 LAYOUT_PHASE_TEMP_REENTER();
6979 return;
6982 // Don't construct kids of leaves
6983 if (insertion.mParentFrame->IsLeaf()) {
6984 // Clear lazy bits so we don't try to construct again.
6985 ClearLazyBits(aStartChild, aEndChild);
6986 return;
6989 LAYOUT_PHASE_TEMP_EXIT();
6990 if (WipeInsertionParent(insertion.mParentFrame)) {
6991 LAYOUT_PHASE_TEMP_REENTER();
6992 return;
6994 LAYOUT_PHASE_TEMP_REENTER();
6996 nsFrameConstructorState state(
6997 mPresShell, GetAbsoluteContainingBlock(insertion.mParentFrame, FIXED_POS),
6998 GetAbsoluteContainingBlock(insertion.mParentFrame, ABS_POS),
6999 GetFloatContainingBlock(insertion.mParentFrame),
7000 do_AddRef(mFrameTreeState));
7002 // Recover state for the containing block - we need to know if
7003 // it has :first-letter or :first-line style applied to it. The
7004 // reason we care is that the internal structure in these cases
7005 // is not the normal structure and requires custom updating
7006 // logic.
7007 nsContainerFrame* containingBlock = state.mFloatedList.mContainingBlock;
7008 bool haveFirstLetterStyle = false;
7009 bool haveFirstLineStyle = false;
7011 // In order to shave off some cycles, we only dig up the
7012 // containing block haveFirst* flags if the parent frame where
7013 // the insertion/append is occurring is an inline or block
7014 // container. For other types of containers this isn't relevant.
7015 StyleDisplayInside parentDisplayInside =
7016 insertion.mParentFrame->StyleDisplay()->DisplayInside();
7018 // Examine the insertion.mParentFrame where the insertion is taking
7019 // place. If it's a certain kind of container then some special
7020 // processing is done.
7021 if (StyleDisplayInside::Flow == parentDisplayInside) {
7022 // Recover the special style flags for the containing block
7023 if (containingBlock) {
7024 haveFirstLetterStyle = HasFirstLetterStyle(containingBlock);
7025 haveFirstLineStyle = ShouldHaveFirstLineStyle(
7026 containingBlock->GetContent(), containingBlock->Style());
7029 if (haveFirstLetterStyle) {
7030 // If our current insertion.mParentFrame is a Letter frame, use its parent
7031 // as our new parent hint
7032 if (insertion.mParentFrame->IsLetterFrame()) {
7033 // If insertion.mParentFrame is out of flow, then we actually want the
7034 // parent of the placeholder frame.
7035 if (insertion.mParentFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
7036 nsPlaceholderFrame* placeholderFrame =
7037 insertion.mParentFrame->GetPlaceholderFrame();
7038 NS_ASSERTION(placeholderFrame, "No placeholder for out-of-flow?");
7039 insertion.mParentFrame = placeholderFrame->GetParent();
7040 } else {
7041 insertion.mParentFrame = insertion.mParentFrame->GetParent();
7045 // Remove the old letter frames before doing the insertion
7046 RemoveLetterFrames(mPresShell, state.mFloatedList.mContainingBlock);
7048 // Removing the letterframes messes around with the frame tree, removing
7049 // and creating frames. We need to reget our prevsibling, parent frame,
7050 // etc.
7051 prevSibling = GetInsertionPrevSibling(&insertion, aStartChild, &isAppend,
7052 &isRangeInsertSafe);
7054 // Need check whether a range insert is still safe.
7055 if (!isSingleInsert && !isRangeInsertSafe) {
7056 // Need to recover the letter frames first.
7057 RecoverLetterFrames(state.mFloatedList.mContainingBlock);
7059 // must fall back to a single ContertInserted for each child in the
7060 // range
7061 LAYOUT_PHASE_TEMP_EXIT();
7062 IssueSingleInsertNofications(aStartChild, aEndChild,
7063 InsertionKind::Sync);
7064 LAYOUT_PHASE_TEMP_REENTER();
7065 return;
7068 frameType = insertion.mParentFrame->Type();
7072 // This handles fallback to 'list-style-type' when a 'list-style-image' fails
7073 // to load.
7074 if (aStartChild->IsInNativeAnonymousSubtree() &&
7075 aStartChild->IsHTMLElement(nsGkAtoms::mozgeneratedcontentimage)) {
7076 MOZ_ASSERT(isSingleInsert);
7077 MOZ_ASSERT(insertion.mParentFrame->Style()->GetPseudoType() ==
7078 PseudoStyleType::marker,
7079 "we can only handle ::marker fallback for now");
7080 nsIContent* const nextSibling = aStartChild->GetNextSibling();
7081 MOZ_ASSERT(nextSibling && nextSibling->IsText(),
7082 "expected a text node after the list-style-image image");
7083 RemoveFrame(FrameChildListID::Principal, nextSibling->GetPrimaryFrame());
7084 auto* const container = aStartChild->GetParent()->AsElement();
7085 nsIContent* firstNewChild = nullptr;
7086 auto InsertChild = [this, container, nextSibling,
7087 &firstNewChild](RefPtr<nsIContent>&& aChild) {
7088 // We don't strictly have to set NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE
7089 // here; it would get set under AppendChildTo. But AppendChildTo might
7090 // think that we're going from not being anonymous to being anonymous and
7091 // do some extra work; setting the flag here avoids that.
7092 aChild->SetFlags(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE);
7093 container->InsertChildBefore(aChild, nextSibling, false, IgnoreErrors());
7094 if (auto* childElement = Element::FromNode(aChild)) {
7095 // If we created any children elements, Servo needs to traverse them,
7096 // but the root is already set up.
7097 mPresShell->StyleSet()->StyleNewSubtree(childElement);
7099 if (!firstNewChild) {
7100 firstNewChild = aChild;
7103 CreateGeneratedContentFromListStyleType(
7104 state, *insertion.mContainer->AsElement(),
7105 *insertion.mParentFrame->Style(), InsertChild);
7106 if (!firstNewChild) {
7107 // No fallback content - we're done.
7108 return;
7110 aStartChild = firstNewChild;
7111 MOZ_ASSERT(firstNewChild->GetNextSibling() == nextSibling,
7112 "list-style-type should only create one child");
7115 AutoFrameConstructionItemList items(this);
7116 RefPtr<ComputedStyle> parentStyle =
7117 ResolveComputedStyle(insertion.mContainer);
7118 ParentType parentType = GetParentType(frameType);
7119 FlattenedChildIterator iter(insertion.mContainer);
7120 const bool haveNoShadowDOM =
7121 !iter.ShadowDOMInvolved() || !iter.GetNextChild();
7122 if (aStartChild->GetPreviousSibling() && parentType == eTypeBlock &&
7123 haveNoShadowDOM) {
7124 // If there's a text node in the normal content list just before the
7125 // new nodes, and it has no frame, make a frame construction item for
7126 // it, because it might need a frame now. No need to do this if our
7127 // parent type is not block, though, since WipeContainingBlock
7128 // already handles that situation.
7129 AddTextItemIfNeeded(state, *parentStyle, insertion,
7130 aStartChild->GetPreviousSibling(), items);
7133 if (isSingleInsert) {
7134 AddFrameConstructionItems(state, aStartChild,
7135 aStartChild->IsRootOfNativeAnonymousSubtree(),
7136 *parentStyle, insertion, items);
7137 } else {
7138 for (nsIContent* child = aStartChild; child != aEndChild;
7139 child = child->GetNextSibling()) {
7140 AddFrameConstructionItems(state, child, false, *parentStyle, insertion,
7141 items);
7145 if (aEndChild && parentType == eTypeBlock && haveNoShadowDOM) {
7146 // If there's a text node in the normal content list just after the
7147 // new nodes, and it has no frame, make a frame construction item for
7148 // it, because it might need a frame now. No need to do this if our
7149 // parent type is not block, though, since WipeContainingBlock
7150 // already handles that situation.
7151 AddTextItemIfNeeded(state, *parentStyle, insertion, aEndChild, items);
7154 // Perform special check for diddling around with the frames in
7155 // a special inline frame.
7156 // If we're appending before :after content, then we're not really
7157 // appending, so let WipeContainingBlock know that.
7158 LAYOUT_PHASE_TEMP_EXIT();
7159 if (WipeContainingBlock(state, containingBlock, insertion.mParentFrame, items,
7160 isAppend, prevSibling)) {
7161 LAYOUT_PHASE_TEMP_REENTER();
7162 return;
7164 LAYOUT_PHASE_TEMP_REENTER();
7166 nsFrameConstructorSaveState floatSaveState;
7167 state.MaybePushFloatContainingBlock(insertion.mParentFrame, floatSaveState);
7169 if (state.mPresContext->IsPaginated()) {
7170 // Because this function can be called outside frame construction, we need
7171 // to set state.mAutoPageNameValue based on what the parent frame's auto
7172 // value is.
7173 // Calling this from outside the frame constructor can violate many of the
7174 // expectations in AutoFrameConstructionPageName, and unlike during frame
7175 // construction we already have an auto value from parentFrame, so we do
7176 // not use AutoFrameConstructionPageName here.
7177 state.mAutoPageNameValue = insertion.mParentFrame->GetAutoPageValue();
7178 #ifdef DEBUG
7179 insertion.mParentFrame->mWasVisitedByAutoFrameConstructionPageName = true;
7180 #endif
7183 // If the container is a table and a caption will be appended, it needs to be
7184 // put in the table wrapper frame's additional child list.
7185 // We make no attempt here to set flags to indicate whether the list
7186 // will be at the start or end of a block. It doesn't seem worthwhile.
7187 nsFrameList frameList, captionList;
7188 ConstructFramesFromItemList(state, items, insertion.mParentFrame,
7189 ParentIsWrapperAnonBox(insertion.mParentFrame),
7190 frameList);
7192 if (frameList.NotEmpty()) {
7193 for (nsIContent* child = aStartChild; child != aEndChild;
7194 child = child->GetNextSibling()) {
7195 InvalidateCanvasIfNeeded(mPresShell, child);
7198 if (LayoutFrameType::Table == frameType ||
7199 LayoutFrameType::TableWrapper == frameType) {
7200 PullOutCaptionFrames(frameList, captionList);
7204 if (haveFirstLineStyle && insertion.mParentFrame == containingBlock &&
7205 isAppend) {
7206 // It's possible that the new frame goes into a first-line
7207 // frame. Look at it and see...
7208 AppendFirstLineFrames(state, containingBlock->GetContent(), containingBlock,
7209 frameList);
7210 } else if (insertion.mParentFrame->Style()->HasPseudoElementData()) {
7211 CheckForFirstLineInsertion(insertion.mParentFrame, frameList);
7212 CheckForFirstLineInsertion(insertion.mParentFrame, captionList);
7215 // We might have captions; put them into the caption list of the
7216 // table wrapper frame.
7217 if (captionList.NotEmpty()) {
7218 NS_ASSERTION(LayoutFrameType::Table == frameType ||
7219 LayoutFrameType::TableWrapper == frameType,
7220 "parent for caption is not table?");
7221 // We need to determine where to put the caption items; start with the
7222 // the parent frame that has already been determined and get the insertion
7223 // prevsibling of the first caption item.
7224 bool captionIsAppend;
7225 nsIFrame* captionPrevSibling = nullptr;
7227 // aIsRangeInsertSafe is ignored on purpose because it is irrelevant here.
7228 bool ignored;
7229 InsertionPoint captionInsertion = insertion;
7230 if (isSingleInsert) {
7231 captionPrevSibling = GetInsertionPrevSibling(
7232 &captionInsertion, aStartChild, &captionIsAppend, &ignored);
7233 } else {
7234 nsIContent* firstCaption = captionList.FirstChild()->GetContent();
7235 // It is very important here that we skip the children in
7236 // [aStartChild,aEndChild) when looking for a
7237 // prevsibling.
7238 captionPrevSibling = GetInsertionPrevSibling(
7239 &captionInsertion, firstCaption, &captionIsAppend, &ignored,
7240 aStartChild, aEndChild);
7243 nsContainerFrame* outerTable =
7244 captionInsertion.mParentFrame->IsTableFrame()
7245 ? captionInsertion.mParentFrame->GetParent()
7246 : captionInsertion.mParentFrame;
7248 // If the parent is not a table wrapper frame we will try to add frames
7249 // to a named child list that the parent does not honor and the frames
7250 // will get lost.
7251 MOZ_ASSERT(outerTable->IsTableWrapperFrame(),
7252 "Pseudo frame construction failure; "
7253 "a caption can be only a child of a table wrapper frame");
7255 // If the parent of our current prevSibling is different from the frame
7256 // we'll actually use as the parent, then the calculated insertion
7257 // point is now invalid (bug 341382).
7258 if (captionPrevSibling && captionPrevSibling->GetParent() != outerTable) {
7259 captionPrevSibling = nullptr;
7262 captionList.ApplySetParent(outerTable);
7263 if (captionIsAppend) {
7264 AppendFrames(outerTable, FrameChildListID::Caption,
7265 std::move(captionList));
7266 } else {
7267 InsertFrames(outerTable, FrameChildListID::Caption, captionPrevSibling,
7268 std::move(captionList));
7272 LAYOUT_PHASE_TEMP_EXIT();
7273 if (MaybeRecreateForColumnSpan(state, insertion.mParentFrame, frameList,
7274 prevSibling)) {
7275 LAYOUT_PHASE_TEMP_REENTER();
7276 return;
7278 LAYOUT_PHASE_TEMP_REENTER();
7280 if (frameList.NotEmpty()) {
7281 // Notify the parent frame
7282 if (isAppend) {
7283 AppendFramesToParent(state, insertion.mParentFrame, frameList,
7284 prevSibling);
7285 } else {
7286 InsertFrames(insertion.mParentFrame, FrameChildListID::Principal,
7287 prevSibling, std::move(frameList));
7291 if (haveFirstLetterStyle) {
7292 // Recover the letter frames for the containing block when
7293 // it has first-letter style.
7294 RecoverLetterFrames(state.mFloatedList.mContainingBlock);
7297 #ifdef DEBUG
7298 if (gReallyNoisyContentUpdates && insertion.mParentFrame) {
7299 printf(
7300 "nsCSSFrameConstructor::ContentRangeInserted: resulting frame "
7301 "model:\n");
7302 insertion.mParentFrame->List(stdout);
7304 #endif
7306 #ifdef ACCESSIBILITY
7307 if (nsAccessibilityService* accService = GetAccService()) {
7308 accService->ContentRangeInserted(mPresShell, aStartChild, aEndChild);
7310 #endif
7313 bool nsCSSFrameConstructor::ContentRemoved(nsIContent* aChild,
7314 nsIContent* aOldNextSibling,
7315 RemoveFlags aFlags) {
7316 MOZ_ASSERT(aChild);
7317 MOZ_ASSERT(!aChild->IsRootOfNativeAnonymousSubtree() || !aOldNextSibling,
7318 "Anonymous roots don't have siblings");
7319 AUTO_PROFILER_LABEL("nsCSSFrameConstructor::ContentRemoved",
7320 LAYOUT_FrameConstruction);
7321 AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
7322 nsPresContext* presContext = mPresShell->GetPresContext();
7323 MOZ_ASSERT(presContext, "Our presShell should have a valid presContext");
7325 // We want to detect when the viewport override element stored in the
7326 // prescontext is in the subtree being removed. Except in fullscreen cases
7327 // (which are handled in Element::UnbindFromTree and do not get stored on the
7328 // prescontext), the override element is always either the root element or a
7329 // <body> child of the root element. So we can only be removing the stored
7330 // override element if the thing being removed is either the override element
7331 // itself or the root element (which can be a parent of the override element).
7332 if (aChild == presContext->GetViewportScrollStylesOverrideElement() ||
7333 (aChild->IsElement() && !aChild->GetParent())) {
7334 // We might be removing the element that we propagated viewport scrollbar
7335 // styles from. Recompute those. (This clause covers two of the three
7336 // possible scrollbar-propagation sources: the <body> [as aChild or a
7337 // descendant] and the root node. The other possible scrollbar-propagation
7338 // source is a fullscreen element, and we have code elsewhere to update
7339 // scrollbars after fullscreen elements are removed -- specifically, it's
7340 // part of the fullscreen cleanup code called by Element::UnbindFromTree.
7341 // We don't handle the fullscreen case here, because it doesn't change the
7342 // scrollbar styles override element stored on the prescontext.)
7343 Element* newOverrideElement =
7344 presContext->UpdateViewportScrollStylesOverride();
7346 // If aChild is the root, then we don't need to do any reframing of
7347 // newOverrideElement, because we're about to tear down the whole frame tree
7348 // anyway. And we need to make sure we don't do any such reframing, because
7349 // reframing the <body> can trigger a reframe of the <html> and then reenter
7350 // here.
7352 // But if aChild is not the root, and if newOverrideElement is not
7353 // the root and isn't aChild (which it could be if all we're doing
7354 // here is reframing the current override element), it needs
7355 // reframing. In particular, it used to have a scrollframe
7356 // (because its overflow was not "visible"), but now it will
7357 // propagate its overflow to the viewport, so it should not need a
7358 // scrollframe anymore.
7359 if (aChild->GetParent() && newOverrideElement &&
7360 newOverrideElement->GetParent() && newOverrideElement != aChild) {
7361 LAYOUT_PHASE_TEMP_EXIT();
7362 RecreateFramesForContent(newOverrideElement, InsertionKind::Async);
7363 LAYOUT_PHASE_TEMP_REENTER();
7367 #ifdef DEBUG
7368 if (gNoisyContentUpdates) {
7369 printf(
7370 "nsCSSFrameConstructor::ContentRemoved container=%p child=%p "
7371 "old-next-sibling=%p\n",
7372 aChild->GetParent(), aChild, aOldNextSibling);
7373 if (gReallyNoisyContentUpdates) {
7374 aChild->GetParent()->List(stdout, 0);
7377 #endif
7379 nsIFrame* childFrame = aChild->GetPrimaryFrame();
7380 if (!childFrame || childFrame->GetContent() != aChild) {
7381 // XXXbz the GetContent() != aChild check is needed due to bug 135040.
7382 // Remove it once that's fixed.
7383 childFrame = nullptr;
7386 // If we're removing the root, then make sure to remove things starting at
7387 // the viewport's child instead of the primary frame (which might even be
7388 // null if the root was display:none, even though the frames above it got
7389 // created). Detecting removal of a root is a little exciting; in particular,
7390 // having no parent is necessary but NOT sufficient.
7392 // Due to how we process reframes, the content node might not even be in our
7393 // document by now. So explicitly check whether the viewport's first kid's
7394 // content node is aChild.
7396 // FIXME(emilio): I think the "might not be in our document" bit is impossible
7397 // now.
7398 bool isRoot = false;
7399 if (!aChild->GetParent()) {
7400 if (nsIFrame* viewport = GetRootFrame()) {
7401 nsIFrame* firstChild = viewport->PrincipalChildList().FirstChild();
7402 if (firstChild && firstChild->GetContent() == aChild) {
7403 isRoot = true;
7404 childFrame = firstChild;
7405 NS_ASSERTION(!childFrame->GetNextSibling(), "How did that happen?");
7410 // We need to be conservative about when to determine whether something has
7411 // display: contents or not because at this point our actual display may be
7412 // different.
7414 // Consider the case of:
7416 // <div id="A" style="display: contents"><div id="B"></div></div>
7418 // If we reconstruct A because its display changed to "none", we still need to
7419 // cleanup the frame on B, but A's display is now "none", so we can't poke at
7420 // the style of it.
7422 // FIXME(emilio, bug 1450366): We can make this faster without adding much
7423 // complexity for the display: none -> other case, which right now
7424 // unnecessarily walks the content tree down.
7425 auto CouldHaveBeenDisplayContents = [aFlags](nsIContent* aContent) -> bool {
7426 return aFlags == REMOVE_FOR_RECONSTRUCTION || IsDisplayContents(aContent);
7429 if (!childFrame && CouldHaveBeenDisplayContents(aChild)) {
7430 // NOTE(emilio): We may iterate through ::before and ::after here and they
7431 // may be gone after the respective ContentRemoved call. Right now
7432 // StyleChildrenIterator handles that properly, so it's not an issue.
7433 StyleChildrenIterator iter(aChild);
7434 for (nsIContent* c = iter.GetNextChild(); c; c = iter.GetNextChild()) {
7435 if (c->GetPrimaryFrame() || CouldHaveBeenDisplayContents(c)) {
7436 LAYOUT_PHASE_TEMP_EXIT();
7437 bool didReconstruct = ContentRemoved(c, nullptr, aFlags);
7438 LAYOUT_PHASE_TEMP_REENTER();
7439 if (didReconstruct) {
7440 return true;
7444 return false;
7447 if (childFrame) {
7448 if (aFlags == REMOVE_FOR_RECONSTRUCTION) {
7449 // Before removing the frames associated with the content object,
7450 // ask them to save their state onto our state object.
7451 CaptureStateForFramesOf(aChild, mFrameTreeState);
7454 InvalidateCanvasIfNeeded(mPresShell, aChild);
7456 // See whether we need to remove more than just childFrame
7457 LAYOUT_PHASE_TEMP_EXIT();
7458 if (MaybeRecreateContainerForFrameRemoval(childFrame)) {
7459 LAYOUT_PHASE_TEMP_REENTER();
7460 return true;
7462 LAYOUT_PHASE_TEMP_REENTER();
7464 // Get the childFrame's parent frame
7465 nsIFrame* parentFrame = childFrame->GetParent();
7466 LayoutFrameType parentType = parentFrame->Type();
7468 if (parentType == LayoutFrameType::FrameSet &&
7469 IsSpecialFramesetChild(aChild)) {
7470 // Just reframe the parent, since framesets are weird like that.
7471 LAYOUT_PHASE_TEMP_EXIT();
7472 RecreateFramesForContent(parentFrame->GetContent(), InsertionKind::Async);
7473 LAYOUT_PHASE_TEMP_REENTER();
7474 return true;
7477 // If we're a child of MathML, then we should reframe the MathML content.
7478 // If we're non-MathML, then we would be wrapped in a block so we need to
7479 // check our grandparent in that case.
7480 nsIFrame* possibleMathMLAncestor = parentType == LayoutFrameType::Block
7481 ? parentFrame->GetParent()
7482 : parentFrame;
7483 if (possibleMathMLAncestor->IsFrameOfType(nsIFrame::eMathML)) {
7484 LAYOUT_PHASE_TEMP_EXIT();
7485 RecreateFramesForContent(parentFrame->GetContent(), InsertionKind::Async);
7486 LAYOUT_PHASE_TEMP_REENTER();
7487 return true;
7490 #ifdef ACCESSIBILITY
7491 if (aFlags != REMOVE_FOR_RECONSTRUCTION) {
7492 if (nsAccessibilityService* accService = GetAccService()) {
7493 accService->ContentRemoved(mPresShell, aChild);
7496 #endif
7498 // Examine the containing-block for the removed content and see if
7499 // :first-letter style applies.
7500 nsIFrame* inflowChild = childFrame;
7501 if (childFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
7502 inflowChild = childFrame->GetPlaceholderFrame();
7503 NS_ASSERTION(inflowChild, "No placeholder for out-of-flow?");
7505 nsContainerFrame* containingBlock =
7506 GetFloatContainingBlock(inflowChild->GetParent());
7507 bool haveFLS = containingBlock && HasFirstLetterStyle(containingBlock);
7508 if (haveFLS) {
7509 // Trap out to special routine that handles adjusting a blocks
7510 // frame tree when first-letter style is present.
7511 #ifdef NOISY_FIRST_LETTER
7512 printf("ContentRemoved: containingBlock=");
7513 containingBlock->ListTag(stdout);
7514 printf(" parentFrame=");
7515 parentFrame->ListTag(stdout);
7516 printf(" childFrame=");
7517 childFrame->ListTag(stdout);
7518 printf("\n");
7519 #endif
7521 // First update the containing blocks structure by removing the
7522 // existing letter frames. This makes the subsequent logic
7523 // simpler.
7524 RemoveLetterFrames(mPresShell, containingBlock);
7526 // Recover childFrame and parentFrame
7527 childFrame = aChild->GetPrimaryFrame();
7528 if (!childFrame || childFrame->GetContent() != aChild) {
7529 // XXXbz the GetContent() != aChild check is needed due to bug 135040.
7530 // Remove it once that's fixed.
7531 return false;
7533 parentFrame = childFrame->GetParent();
7534 parentType = parentFrame->Type();
7536 #ifdef NOISY_FIRST_LETTER
7537 printf(" ==> revised parentFrame=");
7538 parentFrame->ListTag(stdout);
7539 printf(" childFrame=");
7540 childFrame->ListTag(stdout);
7541 printf("\n");
7542 #endif
7545 #ifdef DEBUG
7546 if (gReallyNoisyContentUpdates) {
7547 printf("nsCSSFrameConstructor::ContentRemoved: childFrame=");
7548 childFrame->ListTag(stdout);
7549 putchar('\n');
7550 parentFrame->List(stdout);
7552 #endif
7554 // Notify the parent frame that it should delete the frame
7555 if (childFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
7556 childFrame = childFrame->GetPlaceholderFrame();
7557 NS_ASSERTION(childFrame, "Missing placeholder frame for out of flow.");
7558 parentFrame = childFrame->GetParent();
7561 RemoveFrame(nsLayoutUtils::GetChildListNameFor(childFrame), childFrame);
7563 // NOTE(emilio): aChild could be dead here already if it is a ::before or
7564 // ::after pseudo-element (since in that case it was owned by childFrame,
7565 // which we just destroyed).
7567 if (isRoot) {
7568 mRootElementFrame = nullptr;
7569 mRootElementStyleFrame = nullptr;
7570 mDocElementContainingBlock = nullptr;
7571 mPageSequenceFrame = nullptr;
7574 if (haveFLS && mRootElementFrame) {
7575 RecoverLetterFrames(containingBlock);
7578 // If we're just reconstructing frames for the element, then the
7579 // following ContentInserted notification on the element will
7580 // take care of fixing up any adjacent text nodes. We don't need
7581 // to do this if the table parent type of our parent type is not
7582 // eTypeBlock, though, because in that case the whitespace isn't
7583 // being suppressed due to us anyway.
7584 if (aOldNextSibling && aFlags == REMOVE_CONTENT &&
7585 GetParentType(parentType) == eTypeBlock) {
7586 MOZ_ASSERT(aChild->GetParentNode(),
7587 "How did we have a sibling without a parent?");
7588 // Adjacent whitespace-only text nodes might have been suppressed if
7589 // this node does not have inline ends. Create frames for them now
7590 // if necessary.
7591 // Reframe any text node just before the node being removed, if there is
7592 // one, and if it's not the last child or the first child. If a whitespace
7593 // textframe was being suppressed and it's now the last child or first
7594 // child then it can stay suppressed since the parent must be a block
7595 // and hence it's adjacent to a block end.
7596 // If aOldNextSibling is null, then the text node before the node being
7597 // removed is the last node, and we don't need to worry about it.
7599 // FIXME(emilio): This should probably use the lazy frame construction
7600 // bits if possible instead of reframing it in place.
7601 nsIContent* prevSibling = aOldNextSibling->GetPreviousSibling();
7602 if (prevSibling && prevSibling->GetPreviousSibling()) {
7603 LAYOUT_PHASE_TEMP_EXIT();
7604 ReframeTextIfNeeded(prevSibling);
7605 LAYOUT_PHASE_TEMP_REENTER();
7607 // Reframe any text node just after the node being removed, if there is
7608 // one, and if it's not the last child or the first child.
7609 if (aOldNextSibling->GetNextSibling() &&
7610 aOldNextSibling->GetPreviousSibling()) {
7611 LAYOUT_PHASE_TEMP_EXIT();
7612 ReframeTextIfNeeded(aOldNextSibling);
7613 LAYOUT_PHASE_TEMP_REENTER();
7617 #ifdef DEBUG
7618 if (gReallyNoisyContentUpdates && parentFrame) {
7619 printf("nsCSSFrameConstructor::ContentRemoved: resulting frame model:\n");
7620 parentFrame->List(stdout);
7622 #endif
7625 return false;
7629 * This method invalidates the canvas when frames are removed or added for a
7630 * node that might have its background propagated to the canvas, i.e., a
7631 * document root node or an HTML BODY which is a child of the root node.
7633 * @param aFrame a frame for a content node about to be removed or a frame that
7634 * was just created for a content node that was inserted.
7636 static void InvalidateCanvasIfNeeded(PresShell* aPresShell, nsIContent* aNode) {
7637 MOZ_ASSERT(aPresShell->GetRootFrame(), "What happened here?");
7638 MOZ_ASSERT(aPresShell->GetPresContext(), "Say what?");
7640 // Note that both in ContentRemoved and ContentInserted the content node
7641 // will still have the right parent pointer, so looking at that is ok.
7643 nsIContent* parent = aNode->GetParent();
7644 if (parent) {
7645 // Has a parent; might not be what we want
7646 nsIContent* grandParent = parent->GetParent();
7647 if (grandParent) {
7648 // Has a grandparent, so not what we want
7649 return;
7652 // Check whether it's an HTML body
7653 if (!aNode->IsHTMLElement(nsGkAtoms::body)) {
7654 return;
7658 // At this point the node has no parent or it's an HTML <body> child of the
7659 // root. We might not need to invalidate in this case (eg we might be in
7660 // XHTML or something), but chances are we want to. Play it safe.
7661 // Invalidate the viewport.
7663 nsIFrame* rootFrame = aPresShell->GetRootFrame();
7664 rootFrame->InvalidateFrameSubtree();
7667 bool nsCSSFrameConstructor::EnsureFrameForTextNodeIsCreatedAfterFlush(
7668 CharacterData* aContent) {
7669 if (!aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE)) {
7670 return false;
7673 if (mAlwaysCreateFramesForIgnorableWhitespace) {
7674 return false;
7677 // Text frame may have been suppressed. Disable suppression and signal that a
7678 // flush should be performed. We do this on a document-wide basis so that
7679 // pages that repeatedly query metrics for collapsed-whitespace text nodes
7680 // don't trigger pathological behavior.
7681 mAlwaysCreateFramesForIgnorableWhitespace = true;
7682 Element* root = mDocument->GetRootElement();
7683 if (!root) {
7684 return false;
7687 RestyleManager()->PostRestyleEvent(root, RestyleHint{0},
7688 nsChangeHint_ReconstructFrame);
7689 return true;
7692 void nsCSSFrameConstructor::CharacterDataChanged(
7693 nsIContent* aContent, const CharacterDataChangeInfo& aInfo) {
7694 AUTO_PROFILER_LABEL("nsCSSFrameConstructor::CharacterDataChanged",
7695 LAYOUT_FrameConstruction);
7696 AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
7698 if ((aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE) &&
7699 !aContent->TextIsOnlyWhitespace()) ||
7700 (aContent->HasFlag(NS_REFRAME_IF_WHITESPACE) &&
7701 aContent->TextIsOnlyWhitespace())) {
7702 #ifdef DEBUG
7703 nsIFrame* frame = aContent->GetPrimaryFrame();
7704 NS_ASSERTION(!frame || !frame->IsGeneratedContentFrame(),
7705 "Bit should never be set on generated content");
7706 #endif
7707 LAYOUT_PHASE_TEMP_EXIT();
7708 RecreateFramesForContent(aContent, InsertionKind::Async);
7709 LAYOUT_PHASE_TEMP_REENTER();
7710 return;
7713 // It's possible the frame whose content changed isn't inserted into the
7714 // frame hierarchy yet, or that there is no frame that maps the content
7715 if (nsIFrame* frame = aContent->GetPrimaryFrame()) {
7716 #if 0
7717 NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
7718 ("nsCSSFrameConstructor::CharacterDataChanged: content=%p[%s] subcontent=%p frame=%p",
7719 aContent, ContentTag(aContent, 0),
7720 aSubContent, frame));
7721 #endif
7723 // Special check for text content that is a child of a letter frame. If
7724 // this happens, we should remove the letter frame, do whatever we're
7725 // planning to do with this notification, then put the letter frame back.
7726 // Note that this is basically what RecreateFramesForContent ends up doing;
7727 // the reason we dont' want to call that here is that our text content
7728 // could be native anonymous, in which case RecreateFramesForContent would
7729 // completely barf on it. And recreating the non-anonymous ancestor would
7730 // just lead us to come back into this notification (e.g. if quotes or
7731 // counters are involved), leading to a loop.
7732 nsContainerFrame* block = GetFloatContainingBlock(frame);
7733 bool haveFirstLetterStyle = false;
7734 if (block) {
7735 // See if the block has first-letter style applied to it.
7736 haveFirstLetterStyle = HasFirstLetterStyle(block);
7737 if (haveFirstLetterStyle) {
7738 RemoveLetterFrames(mPresShell, block);
7739 // Reget |frame|, since we might have killed it.
7740 // Do we really need to call CharacterDataChanged in this case, though?
7741 frame = aContent->GetPrimaryFrame();
7742 NS_ASSERTION(frame, "Should have frame here!");
7746 // Notify the first frame that maps the content. It will generate a reflow
7747 // command
7748 frame->CharacterDataChanged(aInfo);
7750 if (haveFirstLetterStyle) {
7751 RecoverLetterFrames(block);
7756 void nsCSSFrameConstructor::RecalcQuotesAndCounters() {
7757 nsAutoScriptBlocker scriptBlocker;
7759 if (mQuotesDirty) {
7760 mQuotesDirty = false;
7761 mContainStyleScopeManager.RecalcAllQuotes();
7764 if (mCountersDirty) {
7765 mCountersDirty = false;
7766 mContainStyleScopeManager.RecalcAllCounters();
7769 NS_ASSERTION(!mQuotesDirty, "Quotes updates will be lost");
7770 NS_ASSERTION(!mCountersDirty, "Counter updates will be lost");
7773 void nsCSSFrameConstructor::NotifyCounterStylesAreDirty() {
7774 mContainStyleScopeManager.SetAllCountersDirty();
7775 CountersDirty();
7778 void nsCSSFrameConstructor::WillDestroyFrameTree() {
7779 #if defined(DEBUG_dbaron_off)
7780 mContainStyleScopeManager.DumpCounters();
7781 #endif
7783 // Prevent frame tree destruction from being O(N^2)
7784 mContainStyleScopeManager.Clear();
7785 nsFrameManager::Destroy();
7788 // STATIC
7790 // XXXbz I'd really like this method to go away. Once we have inline-block and
7791 // I can just use that for sized broken images, that can happen, maybe.
7793 // NOTE(emilio): This needs to match MozAltContent handling.
7794 void nsCSSFrameConstructor::GetAlternateTextFor(const Element& aElement,
7795 nsAString& aAltText) {
7796 // The "alt" attribute specifies alternate text that is rendered
7797 // when the image can not be displayed.
7798 if (aElement.GetAttr(nsGkAtoms::alt, aAltText)) {
7799 return;
7802 if (aElement.IsHTMLElement(nsGkAtoms::input)) {
7803 // If there's no "alt" attribute, and aElement is an input element, then use
7804 // the value of the "value" attribute.
7805 if (aElement.GetAttr(nsGkAtoms::value, aAltText)) {
7806 return;
7809 // If there's no "value" attribute either, then use the localized string for
7810 // "Submit" as the alternate text.
7811 nsContentUtils::GetMaybeLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
7812 "Submit", aElement.OwnerDoc(),
7813 aAltText);
7817 nsIFrame* nsCSSFrameConstructor::CreateContinuingOuterTableFrame(
7818 nsIFrame* aFrame, nsContainerFrame* aParentFrame, nsIContent* aContent,
7819 ComputedStyle* aComputedStyle) {
7820 nsTableWrapperFrame* newFrame =
7821 NS_NewTableWrapperFrame(mPresShell, aComputedStyle);
7823 newFrame->Init(aContent, aParentFrame, aFrame);
7825 // Create a continuing inner table frame, and if there's a caption then
7826 // replicate the caption
7827 nsFrameList newChildFrames;
7829 nsIFrame* childFrame = aFrame->PrincipalChildList().FirstChild();
7830 if (childFrame) {
7831 nsIFrame* continuingTableFrame =
7832 CreateContinuingFrame(childFrame, newFrame);
7833 newChildFrames.AppendFrame(nullptr, continuingTableFrame);
7835 NS_ASSERTION(!childFrame->GetNextSibling(),
7836 "there can be only one inner table frame");
7839 // Set the table wrapper's initial child list
7840 newFrame->SetInitialChildList(FrameChildListID::Principal,
7841 std::move(newChildFrames));
7843 return newFrame;
7846 nsIFrame* nsCSSFrameConstructor::CreateContinuingTableFrame(
7847 nsIFrame* aFrame, nsContainerFrame* aParentFrame, nsIContent* aContent,
7848 ComputedStyle* aComputedStyle) {
7849 nsTableFrame* newFrame = NS_NewTableFrame(mPresShell, aComputedStyle);
7851 newFrame->Init(aContent, aParentFrame, aFrame);
7853 // Replicate any header/footer frames
7854 nsFrameList childFrames;
7855 for (nsIFrame* childFrame : aFrame->PrincipalChildList()) {
7856 // See if it's a header/footer, possibly wrapped in a scroll frame.
7857 nsTableRowGroupFrame* rowGroupFrame =
7858 static_cast<nsTableRowGroupFrame*>(childFrame);
7859 // If the row group was continued, then don't replicate it.
7860 nsIFrame* rgNextInFlow = rowGroupFrame->GetNextInFlow();
7861 if (rgNextInFlow) {
7862 rowGroupFrame->SetRepeatable(false);
7863 } else if (rowGroupFrame->IsRepeatable()) {
7864 // Replicate the header/footer frame.
7865 nsTableRowGroupFrame* headerFooterFrame;
7866 nsFrameList childList;
7868 nsFrameConstructorState state(
7869 mPresShell, GetAbsoluteContainingBlock(newFrame, FIXED_POS),
7870 GetAbsoluteContainingBlock(newFrame, ABS_POS), nullptr);
7871 state.mCreatingExtraFrames = true;
7873 ComputedStyle* const headerFooterComputedStyle = rowGroupFrame->Style();
7874 headerFooterFrame = static_cast<nsTableRowGroupFrame*>(
7875 NS_NewTableRowGroupFrame(mPresShell, headerFooterComputedStyle));
7877 nsIContent* headerFooter = rowGroupFrame->GetContent();
7878 headerFooterFrame->Init(headerFooter, newFrame, nullptr);
7880 nsFrameConstructorSaveState absoluteSaveState;
7881 MakeTablePartAbsoluteContainingBlock(state, absoluteSaveState,
7882 headerFooterFrame);
7884 nsFrameConstructorSaveState floatSaveState;
7885 state.MaybePushFloatContainingBlock(headerFooterFrame, floatSaveState);
7887 ProcessChildren(state, headerFooter, rowGroupFrame->Style(),
7888 headerFooterFrame, true, childList, false, nullptr);
7889 NS_ASSERTION(state.mFloatedList.IsEmpty(), "unexpected floated element");
7890 headerFooterFrame->SetInitialChildList(FrameChildListID::Principal,
7891 std::move(childList));
7892 headerFooterFrame->SetRepeatable(true);
7894 // Table specific initialization
7895 headerFooterFrame->InitRepeatedFrame(rowGroupFrame);
7897 // XXX Deal with absolute and fixed frames...
7898 childFrames.AppendFrame(nullptr, headerFooterFrame);
7902 // Set the table frame's initial child list
7903 newFrame->SetInitialChildList(FrameChildListID::Principal,
7904 std::move(childFrames));
7906 return newFrame;
7909 nsIFrame* nsCSSFrameConstructor::CreateContinuingFrame(
7910 nsIFrame* aFrame, nsContainerFrame* aParentFrame, bool aIsFluid) {
7911 ComputedStyle* computedStyle = aFrame->Style();
7912 nsIFrame* newFrame = nullptr;
7913 nsIFrame* nextContinuation = aFrame->GetNextContinuation();
7914 nsIFrame* nextInFlow = aFrame->GetNextInFlow();
7916 // Use the frame type to determine what type of frame to create
7917 LayoutFrameType frameType = aFrame->Type();
7918 nsIContent* content = aFrame->GetContent();
7920 if (LayoutFrameType::Text == frameType) {
7921 newFrame = NS_NewContinuingTextFrame(mPresShell, computedStyle);
7922 newFrame->Init(content, aParentFrame, aFrame);
7923 } else if (LayoutFrameType::Inline == frameType) {
7924 newFrame = NS_NewInlineFrame(mPresShell, computedStyle);
7925 newFrame->Init(content, aParentFrame, aFrame);
7926 } else if (LayoutFrameType::Block == frameType) {
7927 MOZ_ASSERT(!aFrame->IsTableCaption(),
7928 "no support for fragmenting table captions yet");
7929 newFrame = NS_NewBlockFrame(mPresShell, computedStyle);
7930 newFrame->Init(content, aParentFrame, aFrame);
7931 } else if (LayoutFrameType::ColumnSetWrapper == frameType) {
7932 newFrame =
7933 NS_NewColumnSetWrapperFrame(mPresShell, computedStyle, nsFrameState(0));
7934 newFrame->Init(content, aParentFrame, aFrame);
7935 } else if (LayoutFrameType::ColumnSet == frameType) {
7936 MOZ_ASSERT(!aFrame->IsTableCaption(),
7937 "no support for fragmenting table captions yet");
7938 newFrame = NS_NewColumnSetFrame(mPresShell, computedStyle, nsFrameState(0));
7939 newFrame->Init(content, aParentFrame, aFrame);
7940 } else if (LayoutFrameType::PrintedSheet == frameType) {
7941 newFrame = ConstructPrintedSheetFrame(mPresShell, aParentFrame, aFrame);
7942 } else if (LayoutFrameType::Page == frameType) {
7943 nsContainerFrame* canvasFrame; // (unused outparam for ConstructPageFrame)
7944 newFrame =
7945 ConstructPageFrame(mPresShell, aParentFrame, aFrame, canvasFrame);
7946 } else if (LayoutFrameType::TableWrapper == frameType) {
7947 newFrame = CreateContinuingOuterTableFrame(aFrame, aParentFrame, content,
7948 computedStyle);
7949 } else if (LayoutFrameType::Table == frameType) {
7950 newFrame = CreateContinuingTableFrame(aFrame, aParentFrame, content,
7951 computedStyle);
7952 } else if (LayoutFrameType::TableRowGroup == frameType) {
7953 newFrame = NS_NewTableRowGroupFrame(mPresShell, computedStyle);
7954 newFrame->Init(content, aParentFrame, aFrame);
7955 } else if (LayoutFrameType::TableRow == frameType) {
7956 nsTableRowFrame* rowFrame = NS_NewTableRowFrame(mPresShell, computedStyle);
7958 rowFrame->Init(content, aParentFrame, aFrame);
7960 // Create a continuing frame for each table cell frame
7961 nsFrameList newChildList;
7962 nsIFrame* cellFrame = aFrame->PrincipalChildList().FirstChild();
7963 while (cellFrame) {
7964 // See if it's a table cell frame
7965 if (cellFrame->IsTableCellFrame()) {
7966 nsIFrame* continuingCellFrame =
7967 CreateContinuingFrame(cellFrame, rowFrame);
7968 newChildList.AppendFrame(nullptr, continuingCellFrame);
7970 cellFrame = cellFrame->GetNextSibling();
7973 rowFrame->SetInitialChildList(FrameChildListID::Principal,
7974 std::move(newChildList));
7975 newFrame = rowFrame;
7977 } else if (LayoutFrameType::TableCell == frameType) {
7978 // Warning: If you change this and add a wrapper frame around table cell
7979 // frames, make sure Bug 368554 doesn't regress!
7980 // See IsInAutoWidthTableCellForQuirk() in nsImageFrame.cpp.
7981 nsTableFrame* tableFrame =
7982 static_cast<nsTableRowFrame*>(aParentFrame)->GetTableFrame();
7983 nsTableCellFrame* cellFrame =
7984 NS_NewTableCellFrame(mPresShell, computedStyle, tableFrame);
7986 cellFrame->Init(content, aParentFrame, aFrame);
7988 // Create a continuing area frame
7989 nsIFrame* blockFrame = aFrame->PrincipalChildList().FirstChild();
7990 nsIFrame* continuingBlockFrame =
7991 CreateContinuingFrame(blockFrame, cellFrame);
7993 SetInitialSingleChild(cellFrame, continuingBlockFrame);
7994 newFrame = cellFrame;
7995 } else if (LayoutFrameType::Line == frameType) {
7996 newFrame = NS_NewFirstLineFrame(mPresShell, computedStyle);
7997 newFrame->Init(content, aParentFrame, aFrame);
7998 } else if (LayoutFrameType::Letter == frameType) {
7999 newFrame = NS_NewFirstLetterFrame(mPresShell, computedStyle);
8000 newFrame->Init(content, aParentFrame, aFrame);
8001 } else if (LayoutFrameType::Image == frameType) {
8002 auto* imageFrame = static_cast<nsImageFrame*>(aFrame);
8003 newFrame = imageFrame->CreateContinuingFrame(mPresShell, computedStyle);
8004 newFrame->Init(content, aParentFrame, aFrame);
8005 } else if (LayoutFrameType::ImageControl == frameType) {
8006 newFrame = NS_NewImageControlFrame(mPresShell, computedStyle);
8007 newFrame->Init(content, aParentFrame, aFrame);
8008 } else if (LayoutFrameType::FieldSet == frameType) {
8009 newFrame = NS_NewFieldSetFrame(mPresShell, computedStyle);
8010 newFrame->Init(content, aParentFrame, aFrame);
8011 } else if (LayoutFrameType::FlexContainer == frameType) {
8012 newFrame = NS_NewFlexContainerFrame(mPresShell, computedStyle);
8013 newFrame->Init(content, aParentFrame, aFrame);
8014 } else if (LayoutFrameType::GridContainer == frameType) {
8015 newFrame = NS_NewGridContainerFrame(mPresShell, computedStyle);
8016 newFrame->Init(content, aParentFrame, aFrame);
8017 } else if (LayoutFrameType::Ruby == frameType) {
8018 newFrame = NS_NewRubyFrame(mPresShell, computedStyle);
8019 newFrame->Init(content, aParentFrame, aFrame);
8020 } else if (LayoutFrameType::RubyBaseContainer == frameType) {
8021 newFrame = NS_NewRubyBaseContainerFrame(mPresShell, computedStyle);
8022 newFrame->Init(content, aParentFrame, aFrame);
8023 } else if (LayoutFrameType::RubyTextContainer == frameType) {
8024 newFrame = NS_NewRubyTextContainerFrame(mPresShell, computedStyle);
8025 newFrame->Init(content, aParentFrame, aFrame);
8026 } else {
8027 MOZ_CRASH("unexpected frame type");
8030 // Init() set newFrame to be a fluid continuation of aFrame.
8031 // If we want a non-fluid continuation, we need to call SetPrevContinuation()
8032 // to reset NS_FRAME_IS_FLUID_CONTINUATION.
8033 if (!aIsFluid) {
8034 newFrame->SetPrevContinuation(aFrame);
8037 // If a continuing frame needs to carry frame state bits from its previous
8038 // continuation or parent, set them in nsIFrame::Init(), or in any derived
8039 // frame class's Init() if the bits are belong to specific group.
8041 if (nextInFlow) {
8042 nextInFlow->SetPrevInFlow(newFrame);
8043 newFrame->SetNextInFlow(nextInFlow);
8044 } else if (nextContinuation) {
8045 nextContinuation->SetPrevContinuation(newFrame);
8046 newFrame->SetNextContinuation(nextContinuation);
8049 // aFrame cannot be a dynamic reflow root because it has a continuation now.
8050 aFrame->RemoveStateBits(NS_FRAME_DYNAMIC_REFLOW_ROOT);
8052 // XXXalaskanemily: This avoids linear-time FirstContinuation lookups during
8053 // paginated reflow, but there are a lot of smarter ways to manage this. We
8054 // might also want to share the struct (refcount or some guarantee the struct
8055 // will remain valid during reflow).
8056 if (nsIFrame::PageValues* pageValues =
8057 aFrame->GetProperty(nsIFrame::PageValuesProperty())) {
8058 // It is possible that both values of a PageValues struct can be
8059 // overwritten with null. If that's the case, then as a minor optimization
8060 // we don't need to create a copy of the struct since this property being
8061 // missing is equivalent to having null start/end values.
8062 if (pageValues->mStartPageValue || pageValues->mEndPageValue) {
8063 nsIFrame::PageValues* const newPageValues =
8064 new nsIFrame::PageValues(*pageValues);
8065 newFrame->SetProperty(nsIFrame::PageValuesProperty(), newPageValues);
8069 MOZ_ASSERT(!newFrame->GetNextSibling(), "unexpected sibling");
8070 return newFrame;
8073 nsresult nsCSSFrameConstructor::ReplicateFixedFrames(
8074 nsPageContentFrame* aParentFrame) {
8075 // Now deal with fixed-pos things.... They should appear on all pages,
8076 // so we want to move over the placeholders when processing the child
8077 // of the pageContentFrame.
8079 nsIFrame* prevPageContentFrame = aParentFrame->GetPrevInFlow();
8080 if (!prevPageContentFrame) {
8081 return NS_OK;
8083 nsContainerFrame* canvasFrame =
8084 do_QueryFrame(aParentFrame->PrincipalChildList().FirstChild());
8085 nsIFrame* prevCanvasFrame =
8086 prevPageContentFrame->PrincipalChildList().FirstChild();
8087 if (!canvasFrame || !prevCanvasFrame) {
8088 // document's root element frame missing
8089 return NS_ERROR_UNEXPECTED;
8092 nsFrameList fixedPlaceholders;
8093 nsIFrame* firstFixed =
8094 prevPageContentFrame->GetChildList(FrameChildListID::Fixed).FirstChild();
8095 if (!firstFixed) {
8096 return NS_OK;
8099 // Don't allow abs-pos descendants of the fixed content to escape the content.
8100 // This should not normally be possible (because fixed-pos elements should
8101 // be absolute containers) but fixed-pos tables currently aren't abs-pos
8102 // containers.
8103 nsFrameConstructorState state(mPresShell, aParentFrame, nullptr,
8104 mRootElementFrame);
8105 state.mCreatingExtraFrames = true;
8107 // We can't use an ancestor filter here, because we're not going to
8108 // be usefully recurring down the tree. This means that other
8109 // places in frame construction can't assume a filter is
8110 // initialized!
8112 // Iterate across fixed frames and replicate each whose placeholder is a
8113 // descendant of aFrame. (We don't want to explicitly copy placeholders that
8114 // are within fixed frames, because that would cause duplicates on the new
8115 // page - bug 389619)
8116 for (nsIFrame* fixed = firstFixed; fixed; fixed = fixed->GetNextSibling()) {
8117 nsIFrame* prevPlaceholder = fixed->GetPlaceholderFrame();
8118 if (prevPlaceholder && nsLayoutUtils::IsProperAncestorFrame(
8119 prevCanvasFrame, prevPlaceholder)) {
8120 // We want to use the same style as the primary style frame for
8121 // our content
8122 nsIContent* content = fixed->GetContent();
8123 ComputedStyle* computedStyle =
8124 nsLayoutUtils::GetStyleFrame(content)->Style();
8125 AutoFrameConstructionItemList items(this);
8126 AddFrameConstructionItemsInternal(state, content, canvasFrame, true,
8127 computedStyle,
8128 {ItemFlag::AllowPageBreak}, items);
8129 ConstructFramesFromItemList(state, items, canvasFrame,
8130 /* aParentIsWrapperAnonBox = */ false,
8131 fixedPlaceholders);
8135 // Add the placeholders to our primary child list.
8136 // XXXbz this is a little screwed up, since the fixed frames will have
8137 // broken auto-positioning. Oh, well.
8138 NS_ASSERTION(!canvasFrame->PrincipalChildList().FirstChild(),
8139 "leaking frames; doc root continuation must be empty");
8140 canvasFrame->SetInitialChildList(FrameChildListID::Principal,
8141 std::move(fixedPlaceholders));
8142 return NS_OK;
8145 nsCSSFrameConstructor::InsertionPoint nsCSSFrameConstructor::GetInsertionPoint(
8146 nsIContent* aChild) {
8147 MOZ_ASSERT(aChild);
8148 nsIContent* insertionElement = aChild->GetFlattenedTreeParent();
8149 if (!insertionElement) {
8150 // The element doesn't belong in the flattened tree, and thus we don't want
8151 // to render it.
8152 return {};
8155 return {GetContentInsertionFrameFor(insertionElement), insertionElement};
8158 // Capture state for the frame tree rooted at the frame associated with the
8159 // content object, aContent
8160 void nsCSSFrameConstructor::CaptureStateForFramesOf(
8161 nsIContent* aContent, nsILayoutHistoryState* aHistoryState) {
8162 if (!aHistoryState) {
8163 return;
8165 nsIFrame* frame = aContent->GetPrimaryFrame();
8166 if (frame == mRootElementFrame) {
8167 frame = mRootElementFrame
8168 ? GetAbsoluteContainingBlock(mRootElementFrame, FIXED_POS)
8169 : GetRootFrame();
8171 for (; frame;
8172 frame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(frame)) {
8173 CaptureFrameState(frame, aHistoryState);
8177 static bool IsWhitespaceFrame(nsIFrame* aFrame) {
8178 MOZ_ASSERT(aFrame, "invalid argument");
8179 return aFrame->IsTextFrame() && aFrame->GetContent()->TextIsOnlyWhitespace();
8182 static nsIFrame* FindFirstNonWhitespaceChild(nsIFrame* aParentFrame) {
8183 nsIFrame* f = aParentFrame->PrincipalChildList().FirstChild();
8184 while (f && IsWhitespaceFrame(f)) {
8185 f = f->GetNextSibling();
8187 return f;
8190 static nsIFrame* FindNextNonWhitespaceSibling(nsIFrame* aFrame) {
8191 nsIFrame* f = aFrame;
8192 do {
8193 f = f->GetNextSibling();
8194 } while (f && IsWhitespaceFrame(f));
8195 return f;
8198 static nsIFrame* FindPreviousNonWhitespaceSibling(nsIFrame* aFrame) {
8199 nsIFrame* f = aFrame;
8200 do {
8201 f = f->GetPrevSibling();
8202 } while (f && IsWhitespaceFrame(f));
8203 return f;
8206 bool nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(
8207 nsIFrame* aFrame) {
8208 #define TRACE(reason) \
8209 PROFILER_MARKER("MaybeRecreateContainerForFrameRemoval: " reason, LAYOUT, \
8210 {}, Tracing, "Layout")
8211 MOZ_ASSERT(aFrame, "Must have a frame");
8212 MOZ_ASSERT(aFrame->GetParent(), "Frame shouldn't be root");
8213 MOZ_ASSERT(aFrame == aFrame->FirstContinuation(),
8214 "aFrame not the result of GetPrimaryFrame()?");
8216 nsIFrame* inFlowFrame = aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)
8217 ? aFrame->GetPlaceholderFrame()
8218 : aFrame;
8219 MOZ_ASSERT(inFlowFrame, "How did that happen?");
8220 MOZ_ASSERT(inFlowFrame == inFlowFrame->FirstContinuation(),
8221 "placeholder for primary frame has previous continuations?");
8222 nsIFrame* parent = inFlowFrame->GetParent();
8224 if (inFlowFrame->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR)) {
8225 nsIFrame* grandparent = parent->GetParent();
8226 MOZ_ASSERT(grandparent);
8228 bool needsReframe =
8229 // 1. Removing a column-span may lead to an empty
8230 // ::-moz-column-span-wrapper.
8231 inFlowFrame->IsColumnSpan() ||
8232 // 2. Removing a frame which has any column-span siblings may also
8233 // lead to an empty ::-moz-column-span-wrapper subtree. The
8234 // column-span siblings were the frame's children, but later become
8235 // the frame's siblings after CreateColumnSpanSiblings().
8236 inFlowFrame->HasColumnSpanSiblings() ||
8237 // 3. Removing the only child of a ::-moz-column-content, whose
8238 // ColumnSet grandparent has a previous column-span sibling, requires
8239 // reframing since we might connect the ColumnSet's next column-span
8240 // sibling (if there's one). Note that this isn't actually needed if
8241 // the ColumnSet is at the end of ColumnSetWrapper since we create
8242 // empty ones at the end anyway, but we're not worried about
8243 // optimizing that case.
8244 (parent->Style()->GetPseudoType() == PseudoStyleType::columnContent &&
8245 // The only child in ::-moz-column-content (might be tall enough to
8246 // split across columns)
8247 !inFlowFrame->GetPrevSibling() && !inFlowFrame->GetNextSibling() &&
8248 // That ::-moz-column-content is the first column.
8249 !parent->GetPrevInFlow() &&
8250 // The ColumnSet grandparent has a previous sibling that is a
8251 // column-span.
8252 grandparent->GetPrevSibling());
8254 if (needsReframe) {
8255 nsContainerFrame* containingBlock =
8256 GetMultiColumnContainingBlockFor(inFlowFrame);
8258 #ifdef DEBUG
8259 if (IsFramePartOfIBSplit(inFlowFrame)) {
8260 nsIFrame* ibContainingBlock = GetIBContainingBlockFor(inFlowFrame);
8261 MOZ_ASSERT(containingBlock == ibContainingBlock ||
8262 nsLayoutUtils::IsProperAncestorFrame(containingBlock,
8263 ibContainingBlock),
8264 "Multi-column containing block should be equal to or be the "
8265 "ancestor of the IB containing block!");
8267 #endif
8269 TRACE("Multi-column");
8270 RecreateFramesForContent(containingBlock->GetContent(),
8271 InsertionKind::Async);
8272 return true;
8276 if (IsFramePartOfIBSplit(aFrame)) {
8277 // The removal functions can't handle removal of an {ib} split directly; we
8278 // need to rebuild the containing block.
8279 TRACE("IB split removal");
8280 ReframeContainingBlock(aFrame);
8281 return true;
8284 if (inFlowFrame->IsRenderedLegend()) {
8285 TRACE("Fieldset / Legend");
8286 RecreateFramesForContent(parent->GetContent(), InsertionKind::Async);
8287 return true;
8290 // Now check for possibly needing to reconstruct due to a pseudo parent
8291 // For the case of ruby pseudo parent, effectively, only pseudo rb/rt frame
8292 // need to be checked here, since all other types of parent will be catched
8293 // by "Check ruby containers" section below.
8294 if (IsTableOrRubyPseudo(parent)) {
8295 if (FindFirstNonWhitespaceChild(parent) == inFlowFrame ||
8296 !FindNextNonWhitespaceSibling(inFlowFrame->LastContinuation()) ||
8297 // If it is a whitespace, and is the only child of the parent, the
8298 // pseudo parent was created for the space, and should now be removed.
8299 (IsWhitespaceFrame(aFrame) &&
8300 parent->PrincipalChildList().OnlyChild()) ||
8301 // If we're a table-column-group, then the OnlyChild check above is
8302 // not going to catch cases when we're the first child.
8303 (inFlowFrame->IsTableColGroupFrame() &&
8304 parent->GetChildList(FrameChildListID::ColGroup).FirstChild() ==
8305 inFlowFrame) ||
8306 // Similar if we're a table-caption.
8307 (inFlowFrame->IsTableCaption() &&
8308 parent->GetChildList(FrameChildListID::Caption).FirstChild() ==
8309 inFlowFrame)) {
8310 TRACE("Table or ruby pseudo parent");
8311 RecreateFramesForContent(parent->GetContent(), InsertionKind::Async);
8312 return true;
8316 // Might need to reconstruct things if this frame's nextSibling is a table
8317 // or ruby pseudo, since removal of this frame might mean that this pseudo
8318 // needs to get merged with the frame's prevSibling if that's also a table
8319 // or ruby pseudo.
8320 nsIFrame* nextSibling =
8321 FindNextNonWhitespaceSibling(inFlowFrame->LastContinuation());
8322 NS_ASSERTION(!IsTableOrRubyPseudo(inFlowFrame), "Shouldn't happen here");
8323 // Effectively, for the ruby pseudo sibling case, only pseudo <ruby> frame
8324 // need to be checked here, since all other types of such frames will have
8325 // a ruby container parent, and be catched by "Check ruby containers" below.
8326 if (nextSibling && IsTableOrRubyPseudo(nextSibling)) {
8327 nsIFrame* prevSibling = FindPreviousNonWhitespaceSibling(inFlowFrame);
8328 if (prevSibling && IsTableOrRubyPseudo(prevSibling)) {
8329 TRACE("Table or ruby pseudo sibling");
8330 // Good enough to recreate frames for aFrame's parent's content; even if
8331 // aFrame's parent is a pseudo, that'll be the right content node.
8332 RecreateFramesForContent(parent->GetContent(), InsertionKind::Async);
8333 return true;
8337 // Check ruby containers
8338 LayoutFrameType parentType = parent->Type();
8339 if (parentType == LayoutFrameType::Ruby ||
8340 RubyUtils::IsRubyContainerBox(parentType)) {
8341 // In ruby containers, pseudo frames may be created from
8342 // whitespaces or even nothing. There are two cases we actually
8343 // need to handle here, but hard to check exactly:
8344 // 1. Status of spaces beside the frame may vary, and related
8345 // frames may be constructed or destroyed accordingly.
8346 // 2. The type of the first child of a ruby frame determines
8347 // whether a pseudo ruby base container should exist.
8348 TRACE("Ruby container");
8349 RecreateFramesForContent(parent->GetContent(), InsertionKind::Async);
8350 return true;
8353 // Might need to reconstruct things if the removed frame's nextSibling is an
8354 // anonymous flex item. The removed frame might've been what divided two
8355 // runs of inline content into two anonymous flex items, which would now
8356 // need to be merged.
8357 // NOTE: It's fine that we've advanced nextSibling past whitespace (up above);
8358 // we're only interested in anonymous flex items here, and those can never
8359 // be adjacent to whitespace, since they absorb contiguous runs of inline
8360 // non-replaced content (including whitespace).
8361 if (nextSibling && IsAnonymousItem(nextSibling)) {
8362 AssertAnonymousFlexOrGridItemParent(nextSibling, parent);
8363 TRACE("Anon flex or grid item next sibling");
8364 // Recreate frames for the flex container (the removed frame's parent)
8365 RecreateFramesForContent(parent->GetContent(), InsertionKind::Async);
8366 return true;
8369 // Might need to reconstruct things if the removed frame's nextSibling is
8370 // null and its parent is an anonymous flex item. (This might be the last
8371 // remaining child of that anonymous flex item, which can then go away.)
8372 if (!nextSibling && IsAnonymousItem(parent)) {
8373 AssertAnonymousFlexOrGridItemParent(parent, parent->GetParent());
8374 TRACE("Anon flex or grid item parent");
8375 // Recreate frames for the flex container (the removed frame's grandparent)
8376 RecreateFramesForContent(parent->GetParent()->GetContent(),
8377 InsertionKind::Async);
8378 return true;
8381 // Reconstruct if inflowFrame is parent's only child, and parent is, or has,
8382 // a non-fluid continuation, i.e. it was split by bidi resolution
8383 if (!inFlowFrame->GetPrevSibling() && !inFlowFrame->GetNextSibling() &&
8384 ((parent->GetPrevContinuation() && !parent->GetPrevInFlow()) ||
8385 (parent->GetNextContinuation() && !parent->GetNextInFlow()))) {
8386 TRACE("Removing last child of non-fluid split parent");
8387 RecreateFramesForContent(parent->GetContent(), InsertionKind::Async);
8388 return true;
8391 // We might still need to reconstruct things if the parent of inFlowFrame is
8392 // ib-split, since in that case the removal of aFrame might affect the
8393 // splitting of its parent.
8394 if (!IsFramePartOfIBSplit(parent)) {
8395 return false;
8398 // If inFlowFrame is not the only in-flow child of |parent|, then removing
8399 // it will change nothing about the {ib} split.
8400 if (inFlowFrame != parent->PrincipalChildList().FirstChild() ||
8401 inFlowFrame->LastContinuation()->GetNextSibling()) {
8402 return false;
8405 // If the parent is the first or last part of the {ib} split, then
8406 // removing one of its kids will have no effect on the splitting.
8407 // Get the first continuation up front so we don't have to do it twice.
8408 nsIFrame* parentFirstContinuation = parent->FirstContinuation();
8409 if (!GetIBSplitSibling(parentFirstContinuation) ||
8410 !GetIBSplitPrevSibling(parentFirstContinuation)) {
8411 return false;
8414 TRACE("IB split parent");
8415 ReframeContainingBlock(parent);
8416 return true;
8417 #undef TRACE
8420 void nsCSSFrameConstructor::UpdateTableCellSpans(nsIContent* aContent) {
8421 nsTableCellFrame* cellFrame = do_QueryFrame(aContent->GetPrimaryFrame());
8423 // It's possible that this warning could fire if some other style change
8424 // simultaneously changes the 'display' of the element and makes it no
8425 // longer be a table cell.
8426 NS_WARNING_ASSERTION(cellFrame, "Hint should only be posted on table cells!");
8428 if (cellFrame) {
8429 cellFrame->GetTableFrame()->RowOrColSpanChanged(cellFrame);
8433 static nsIContent* GetTopmostMathMLElement(nsIContent* aMathMLContent) {
8434 MOZ_ASSERT(aMathMLContent->IsMathMLElement());
8435 MOZ_ASSERT(aMathMLContent->GetPrimaryFrame());
8436 MOZ_ASSERT(
8437 aMathMLContent->GetPrimaryFrame()->IsFrameOfType(nsIFrame::eMathML));
8438 nsIContent* root = aMathMLContent;
8440 for (nsIContent* parent = aMathMLContent->GetFlattenedTreeParent(); parent;
8441 parent = parent->GetFlattenedTreeParent()) {
8442 nsIFrame* frame = parent->GetPrimaryFrame();
8443 if (!frame || !frame->IsFrameOfType(nsIFrame::eMathML)) {
8444 break;
8446 root = parent;
8449 return root;
8452 // We don't know how to re-insert an anonymous subtree root, so recreate the
8453 // closest non-generated ancestor instead, except for a few special cases...
8454 static bool ShouldRecreateContainerForNativeAnonymousContentRoot(
8455 nsIContent* aContent) {
8456 if (!aContent->IsRootOfNativeAnonymousSubtree()) {
8457 return false;
8459 if (ManualNACPtr::IsManualNAC(aContent)) {
8460 // Editor NAC, would enter an infinite loop, and we sorta get away with it
8461 // because it's all abspos.
8462 return false;
8464 if (auto* el = Element::FromNode(aContent)) {
8465 if (auto* classes = el->GetClasses()) {
8466 if (classes->Contains(nsGkAtoms::mozCustomContentContainer,
8467 eCaseMatters)) {
8468 // Canvas anonymous content (like the custom content container) is also
8469 // fine, because its only sibling is a tooltip which is also abspos, so
8470 // relative insertion order doesn't really matter.
8472 // This is important because the inspector uses it, and we don't want
8473 // inspecting the page to change behavior heavily (and reframing
8474 // unfortunately has side-effects sometimes, even though they're bugs).
8475 return false;
8480 return true;
8483 void nsCSSFrameConstructor::RecreateFramesForContent(
8484 nsIContent* aContent, InsertionKind aInsertionKind) {
8485 MOZ_ASSERT(aContent);
8487 // If there is no document, we don't want to recreate frames for it. (You
8488 // shouldn't generally be giving this method content without a document
8489 // anyway).
8490 // Rebuilding the frame tree can have bad effects, especially if it's the
8491 // frame tree for chrome (see bug 157322).
8492 if (NS_WARN_IF(!aContent->GetComposedDoc())) {
8493 return;
8496 // TODO(emilio): We technically can find the right insertion point nowadays
8497 // using StyleChildrenIterator rather than FlattenedTreeIterator. But we'd
8498 // need to tweak the setup to insert into replaced elements to filter which
8499 // anonymous roots can be allowed, and which can't.
8501 // TODO(emilio, 2022): Is this true? If we have a replaced element we wouldn't
8502 // have generated e.g., a ::before/::after pseudo-element to begin with (which
8503 // is what this code is about, so maybe we can just remove this piece of code
8504 // altogether).
8505 if (ShouldRecreateContainerForNativeAnonymousContentRoot(aContent)) {
8506 do {
8507 aContent = aContent->GetParent();
8508 } while (ShouldRecreateContainerForNativeAnonymousContentRoot(aContent));
8509 return RecreateFramesForContent(aContent, InsertionKind::Async);
8512 nsIFrame* frame = aContent->GetPrimaryFrame();
8513 if (frame && frame->IsFrameOfType(nsIFrame::eMathML)) {
8514 // Reframe the topmost MathML element to prevent exponential blowup
8515 // (see bug 397518).
8516 aContent = GetTopmostMathMLElement(aContent);
8517 frame = aContent->GetPrimaryFrame();
8520 if (frame) {
8521 nsIFrame* parent = frame->GetParent();
8522 nsIContent* parentContent = parent ? parent->GetContent() : nullptr;
8523 // If the parent frame is a leaf then the subsequent insert will fail to
8524 // create a frame, so we need to recreate the parent content. This happens
8525 // with native anonymous content from the editor.
8526 if (parent && parent->IsLeaf() && parentContent &&
8527 parentContent != aContent) {
8528 return RecreateFramesForContent(parentContent, InsertionKind::Async);
8532 if (frame && MaybeRecreateContainerForFrameRemoval(frame)) {
8533 return;
8536 MOZ_ASSERT(aContent->GetParentNode());
8538 // Remove the frames associated with the content object.
8539 nsIContent* nextSibling = aContent->IsRootOfNativeAnonymousSubtree()
8540 ? nullptr
8541 : aContent->GetNextSibling();
8542 bool didReconstruct =
8543 ContentRemoved(aContent, nextSibling, REMOVE_FOR_RECONSTRUCTION);
8545 if (!didReconstruct) {
8546 if (aInsertionKind == InsertionKind::Async && aContent->IsElement()) {
8547 // FIXME(emilio, bug 1397239): There's nothing removing the frame state
8548 // for elements that go away before we come back to the frame
8549 // constructor.
8551 // Also, it'd be nice to just use the `ContentRangeInserted` path for
8552 // both elements and non-elements, but we need to make lazy frame
8553 // construction to apply to all elements first.
8554 RestyleManager()->PostRestyleEvent(aContent->AsElement(), RestyleHint{0},
8555 nsChangeHint_ReconstructFrame);
8556 } else {
8557 // Now, recreate the frames associated with this content object. If
8558 // ContentRemoved triggered reconstruction, then we don't need to do this
8559 // because the frames will already have been built.
8560 ContentRangeInserted(aContent, aContent->GetNextSibling(),
8561 aInsertionKind);
8566 bool nsCSSFrameConstructor::DestroyFramesFor(nsIContent* aContent) {
8567 MOZ_ASSERT(aContent && aContent->GetParentNode());
8569 nsIContent* nextSibling = aContent->IsRootOfNativeAnonymousSubtree()
8570 ? nullptr
8571 : aContent->GetNextSibling();
8573 return ContentRemoved(aContent, nextSibling, REMOVE_FOR_RECONSTRUCTION);
8576 //////////////////////////////////////////////////////////////////////
8578 // Block frame construction code
8580 already_AddRefed<ComputedStyle> nsCSSFrameConstructor::GetFirstLetterStyle(
8581 nsIContent* aContent, ComputedStyle* aComputedStyle) {
8582 if (aContent) {
8583 return mPresShell->StyleSet()->ResolvePseudoElementStyle(
8584 *aContent->AsElement(), PseudoStyleType::firstLetter, aComputedStyle);
8586 return nullptr;
8589 already_AddRefed<ComputedStyle> nsCSSFrameConstructor::GetFirstLineStyle(
8590 nsIContent* aContent, ComputedStyle* aComputedStyle) {
8591 if (aContent) {
8592 return mPresShell->StyleSet()->ResolvePseudoElementStyle(
8593 *aContent->AsElement(), PseudoStyleType::firstLine, aComputedStyle);
8595 return nullptr;
8598 // Predicate to see if a given content (block element) has
8599 // first-letter style applied to it.
8600 bool nsCSSFrameConstructor::ShouldHaveFirstLetterStyle(
8601 nsIContent* aContent, ComputedStyle* aComputedStyle) {
8602 return nsLayoutUtils::HasPseudoStyle(aContent, aComputedStyle,
8603 PseudoStyleType::firstLetter,
8604 mPresShell->GetPresContext());
8607 bool nsCSSFrameConstructor::HasFirstLetterStyle(nsIFrame* aBlockFrame) {
8608 MOZ_ASSERT(aBlockFrame, "Need a frame");
8609 NS_ASSERTION(aBlockFrame->IsBlockFrameOrSubclass(), "Not a block frame?");
8611 return aBlockFrame->HasAnyStateBits(NS_BLOCK_HAS_FIRST_LETTER_STYLE);
8614 bool nsCSSFrameConstructor::ShouldHaveFirstLineStyle(
8615 nsIContent* aContent, ComputedStyle* aComputedStyle) {
8616 bool hasFirstLine = nsLayoutUtils::HasPseudoStyle(
8617 aContent, aComputedStyle, PseudoStyleType::firstLine,
8618 mPresShell->GetPresContext());
8619 return hasFirstLine && !aContent->IsHTMLElement(nsGkAtoms::fieldset);
8622 void nsCSSFrameConstructor::ShouldHaveSpecialBlockStyle(
8623 nsIContent* aContent, ComputedStyle* aComputedStyle,
8624 bool* aHaveFirstLetterStyle, bool* aHaveFirstLineStyle) {
8625 *aHaveFirstLetterStyle = ShouldHaveFirstLetterStyle(aContent, aComputedStyle);
8626 *aHaveFirstLineStyle = ShouldHaveFirstLineStyle(aContent, aComputedStyle);
8629 /* static */
8630 const nsCSSFrameConstructor::PseudoParentData
8631 nsCSSFrameConstructor::sPseudoParentData[eParentTypeCount] = {
8632 // Cell
8633 {{&nsCSSFrameConstructor::ConstructTableCell,
8634 FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET | FCDATA_USE_CHILD_ITEMS |
8635 FCDATA_IS_WRAPPER_ANON_BOX |
8636 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRow)},
8637 PseudoStyleType::tableCell},
8638 // Row
8639 {{&nsCSSFrameConstructor::ConstructTableRowOrRowGroup,
8640 FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET | FCDATA_USE_CHILD_ITEMS |
8641 FCDATA_IS_WRAPPER_ANON_BOX |
8642 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRowGroup)},
8643 PseudoStyleType::tableRow},
8644 // Row group
8645 {{&nsCSSFrameConstructor::ConstructTableRowOrRowGroup,
8646 FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET | FCDATA_USE_CHILD_ITEMS |
8647 FCDATA_IS_WRAPPER_ANON_BOX |
8648 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable)},
8649 PseudoStyleType::tableRowGroup},
8650 // Column group
8651 {{ToCreationFunc(NS_NewTableColGroupFrame),
8652 FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET |
8653 FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_USE_CHILD_ITEMS |
8654 FCDATA_SKIP_ABSPOS_PUSH |
8655 // Not FCDATA_IS_WRAPPER_ANON_BOX, because we don't need to
8656 // restyle these: they have non-inheriting styles.
8657 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable)},
8658 PseudoStyleType::tableColGroup},
8659 // Table
8660 {{&nsCSSFrameConstructor::ConstructTable,
8661 FCDATA_SKIP_FRAMESET | FCDATA_USE_CHILD_ITEMS |
8662 FCDATA_IS_WRAPPER_ANON_BOX},
8663 PseudoStyleType::table},
8664 // Ruby
8665 {{ToCreationFunc(NS_NewRubyFrame),
8666 FCDATA_IS_LINE_PARTICIPANT | FCDATA_USE_CHILD_ITEMS |
8667 FCDATA_IS_WRAPPER_ANON_BOX | FCDATA_SKIP_FRAMESET},
8668 PseudoStyleType::ruby},
8669 // Ruby Base
8670 {{ToCreationFunc(NS_NewRubyBaseFrame),
8671 FCDATA_USE_CHILD_ITEMS | FCDATA_IS_LINE_PARTICIPANT |
8672 FCDATA_IS_WRAPPER_ANON_BOX |
8673 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRubyBaseContainer) |
8674 FCDATA_SKIP_FRAMESET},
8675 PseudoStyleType::rubyBase},
8676 // Ruby Base Container
8677 {{ToCreationFunc(NS_NewRubyBaseContainerFrame),
8678 FCDATA_USE_CHILD_ITEMS | FCDATA_IS_LINE_PARTICIPANT |
8679 FCDATA_IS_WRAPPER_ANON_BOX |
8680 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRuby) |
8681 FCDATA_SKIP_FRAMESET},
8682 PseudoStyleType::rubyBaseContainer},
8683 // Ruby Text
8684 {{ToCreationFunc(NS_NewRubyTextFrame),
8685 FCDATA_USE_CHILD_ITEMS | FCDATA_IS_LINE_PARTICIPANT |
8686 FCDATA_IS_WRAPPER_ANON_BOX |
8687 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRubyTextContainer) |
8688 FCDATA_SKIP_FRAMESET},
8689 PseudoStyleType::rubyText},
8690 // Ruby Text Container
8691 {{ToCreationFunc(NS_NewRubyTextContainerFrame),
8692 FCDATA_USE_CHILD_ITEMS | FCDATA_IS_WRAPPER_ANON_BOX |
8693 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRuby) |
8694 FCDATA_SKIP_FRAMESET},
8695 PseudoStyleType::rubyTextContainer}};
8697 void nsCSSFrameConstructor::CreateNeededAnonFlexOrGridItems(
8698 nsFrameConstructorState& aState, FrameConstructionItemList& aItems,
8699 nsIFrame* aParentFrame) {
8700 if (aItems.IsEmpty()) {
8701 return;
8704 if (!aParentFrame->IsFlexOrGridContainer()) {
8705 return;
8708 const bool isLegacyWebKitBox =
8709 IsFlexContainerForLegacyWebKitBox(aParentFrame);
8710 FCItemIterator iter(aItems);
8711 do {
8712 // Advance iter past children that don't want to be wrapped
8713 if (iter.SkipItemsThatDontNeedAnonFlexOrGridItem(aState,
8714 isLegacyWebKitBox)) {
8715 // Hit the end of the items without finding any remaining children that
8716 // need to be wrapped. We're finished!
8717 return;
8720 // If our next potentially-wrappable child is whitespace, then see if
8721 // there's anything wrappable immediately after it. If not, we just drop
8722 // the whitespace and move on. (We're not supposed to create any anonymous
8723 // flex/grid items that _only_ contain whitespace).
8724 // (BUT if this is generated content, then we don't give whitespace nodes
8725 // any special treatment, because they're probably not really whitespace --
8726 // they're just temporarily empty, waiting for their generated text.)
8727 // XXXdholbert If this node's generated text will *actually end up being
8728 // entirely whitespace*, then we technically should still skip over it, per
8729 // the CSS grid & flexbox specs. I'm not bothering with that at this point,
8730 // since it's a pretty extreme edge case.
8731 if (!aParentFrame->IsGeneratedContentFrame() &&
8732 iter.item().IsWhitespace(aState)) {
8733 FCItemIterator afterWhitespaceIter(iter);
8734 bool hitEnd = afterWhitespaceIter.SkipWhitespace(aState);
8735 bool nextChildNeedsAnonItem =
8736 !hitEnd && afterWhitespaceIter.item().NeedsAnonFlexOrGridItem(
8737 aState, isLegacyWebKitBox);
8739 if (!nextChildNeedsAnonItem) {
8740 // There's nothing after the whitespace that we need to wrap, so we
8741 // just drop this run of whitespace.
8742 iter.DeleteItemsTo(this, afterWhitespaceIter);
8743 if (hitEnd) {
8744 // Nothing left to do -- we're finished!
8745 return;
8747 // else, we have a next child and it does not want to be wrapped. So,
8748 // we jump back to the beginning of the loop to skip over that child
8749 // (and anything else non-wrappable after it)
8750 MOZ_ASSERT(!iter.IsDone() && !iter.item().NeedsAnonFlexOrGridItem(
8751 aState, isLegacyWebKitBox),
8752 "hitEnd and/or nextChildNeedsAnonItem lied");
8753 continue;
8757 // Now |iter| points to the first child that needs to be wrapped in an
8758 // anonymous flex/grid item. Now we see how many children after it also want
8759 // to be wrapped in an anonymous flex/grid item.
8760 FCItemIterator endIter(iter); // iterator to find the end of the group
8761 endIter.SkipItemsThatNeedAnonFlexOrGridItem(aState, isLegacyWebKitBox);
8763 NS_ASSERTION(iter != endIter,
8764 "Should've had at least one wrappable child to seek past");
8766 // Now, we create the anonymous flex or grid item to contain the children
8767 // between |iter| and |endIter|.
8768 nsIContent* parentContent = aParentFrame->GetContent();
8769 RefPtr<ComputedStyle> wrapperStyle =
8770 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
8771 PseudoStyleType::anonymousItem, aParentFrame->Style());
8773 static constexpr FrameConstructionData sBlockFormattingContextFCData(
8774 ToCreationFunc(NS_NewBlockFormattingContext),
8775 FCDATA_SKIP_FRAMESET | FCDATA_USE_CHILD_ITEMS |
8776 FCDATA_IS_WRAPPER_ANON_BOX);
8778 FrameConstructionItem* newItem = new (this)
8779 FrameConstructionItem(&sBlockFormattingContextFCData,
8780 // Use the content of our parent frame
8781 parentContent, wrapperStyle.forget(), true);
8783 newItem->mIsAllInline =
8784 newItem->mComputedStyle->StyleDisplay()->IsInlineOutsideStyle();
8785 newItem->mIsBlock = !newItem->mIsAllInline;
8787 MOZ_ASSERT(!newItem->mIsAllInline && newItem->mIsBlock,
8788 "expecting anonymous flex/grid items to be block-level "
8789 "(this will make a difference when we encounter "
8790 "'align-items: baseline')");
8792 // Anonymous flex and grid items induce line boundaries around their
8793 // contents.
8794 newItem->mChildItems.SetLineBoundaryAtStart(true);
8795 newItem->mChildItems.SetLineBoundaryAtEnd(true);
8796 // The parent of the items in aItems is also the parent of the items
8797 // in mChildItems
8798 newItem->mChildItems.SetParentHasNoShadowDOM(aItems.ParentHasNoShadowDOM());
8800 // Eat up all items between |iter| and |endIter| and put them in our
8801 // wrapper. This advances |iter| to point to |endIter|.
8802 iter.AppendItemsToList(this, endIter, newItem->mChildItems);
8804 iter.InsertItem(newItem);
8805 } while (!iter.IsDone());
8808 /* static */ nsCSSFrameConstructor::RubyWhitespaceType
8809 nsCSSFrameConstructor::ComputeRubyWhitespaceType(StyleDisplay aPrevDisplay,
8810 StyleDisplay aNextDisplay) {
8811 MOZ_ASSERT(nsStyleDisplay::IsRubyDisplayType(aPrevDisplay) &&
8812 nsStyleDisplay::IsRubyDisplayType(aNextDisplay));
8813 if (aPrevDisplay == aNextDisplay &&
8814 (aPrevDisplay == StyleDisplay::RubyBase ||
8815 aPrevDisplay == StyleDisplay::RubyText)) {
8816 return eRubyInterLeafWhitespace;
8818 if (aNextDisplay == StyleDisplay::RubyText ||
8819 aNextDisplay == StyleDisplay::RubyTextContainer) {
8820 return eRubyInterLevelWhitespace;
8822 return eRubyInterSegmentWhitespace;
8826 * This function checks the content from |aStartIter| to |aEndIter|,
8827 * determines whether it contains only whitespace, and if yes,
8828 * interprets the type of whitespace. This method does not change
8829 * any of the iters.
8831 /* static */ nsCSSFrameConstructor::RubyWhitespaceType
8832 nsCSSFrameConstructor::InterpretRubyWhitespace(nsFrameConstructorState& aState,
8833 const FCItemIterator& aStartIter,
8834 const FCItemIterator& aEndIter) {
8835 if (!aStartIter.item().IsWhitespace(aState)) {
8836 return eRubyNotWhitespace;
8839 FCItemIterator spaceEndIter(aStartIter);
8840 spaceEndIter.SkipWhitespace(aState);
8841 if (spaceEndIter != aEndIter) {
8842 return eRubyNotWhitespace;
8845 // Any leading or trailing whitespace in non-pseudo ruby box
8846 // should have been trimmed, hence there should not be any
8847 // whitespace at the start or the end.
8848 MOZ_ASSERT(!aStartIter.AtStart() && !aEndIter.IsDone());
8849 FCItemIterator prevIter(aStartIter);
8850 prevIter.Prev();
8851 return ComputeRubyWhitespaceType(
8852 prevIter.item().mComputedStyle->StyleDisplay()->mDisplay,
8853 aEndIter.item().mComputedStyle->StyleDisplay()->mDisplay);
8857 * This function eats up consecutive items which do not want the current
8858 * parent into either a ruby base box or a ruby text box. When it
8859 * returns, |aIter| points to the first item it doesn't wrap.
8861 void nsCSSFrameConstructor::WrapItemsInPseudoRubyLeafBox(
8862 FCItemIterator& aIter, ComputedStyle* aParentStyle,
8863 nsIContent* aParentContent) {
8864 StyleDisplay parentDisplay = aParentStyle->StyleDisplay()->mDisplay;
8865 ParentType parentType, wrapperType;
8866 if (parentDisplay == StyleDisplay::RubyTextContainer) {
8867 parentType = eTypeRubyTextContainer;
8868 wrapperType = eTypeRubyText;
8869 } else {
8870 MOZ_ASSERT(parentDisplay == StyleDisplay::RubyBaseContainer);
8871 parentType = eTypeRubyBaseContainer;
8872 wrapperType = eTypeRubyBase;
8875 MOZ_ASSERT(aIter.item().DesiredParentType() != parentType,
8876 "Should point to something needs to be wrapped.");
8878 FCItemIterator endIter(aIter);
8879 endIter.SkipItemsNotWantingParentType(parentType);
8881 WrapItemsInPseudoParent(aParentContent, aParentStyle, wrapperType, aIter,
8882 endIter);
8886 * This function eats up consecutive items into a ruby level container.
8887 * It may create zero or one level container. When it returns, |aIter|
8888 * points to the first item it doesn't wrap.
8890 void nsCSSFrameConstructor::WrapItemsInPseudoRubyLevelContainer(
8891 nsFrameConstructorState& aState, FCItemIterator& aIter,
8892 ComputedStyle* aParentStyle, nsIContent* aParentContent) {
8893 MOZ_ASSERT(aIter.item().DesiredParentType() != eTypeRuby,
8894 "Pointing to a level container?");
8896 FrameConstructionItem& firstItem = aIter.item();
8897 ParentType wrapperType = firstItem.DesiredParentType();
8898 if (wrapperType != eTypeRubyTextContainer) {
8899 // If the first item is not ruby text,
8900 // it should be in a base container.
8901 wrapperType = eTypeRubyBaseContainer;
8904 FCItemIterator endIter(aIter);
8905 do {
8906 if (endIter.SkipItemsWantingParentType(wrapperType) ||
8907 // If the skipping above stops at some item which wants a
8908 // different ruby parent, then we have finished.
8909 IsRubyParentType(endIter.item().DesiredParentType())) {
8910 // No more items need to be wrapped in this level container.
8911 break;
8914 FCItemIterator contentEndIter(endIter);
8915 contentEndIter.SkipItemsNotWantingRubyParent();
8916 // endIter must be on something doesn't want a ruby parent.
8917 MOZ_ASSERT(contentEndIter != endIter);
8919 // InterpretRubyWhitespace depends on the fact that any leading or
8920 // trailing whitespace described in the spec have been trimmed at
8921 // this point. With this precondition, it is safe not to check
8922 // whether contentEndIter has been done.
8923 RubyWhitespaceType whitespaceType =
8924 InterpretRubyWhitespace(aState, endIter, contentEndIter);
8925 if (whitespaceType == eRubyInterLevelWhitespace) {
8926 // Remove inter-level whitespace.
8927 bool atStart = (aIter == endIter);
8928 endIter.DeleteItemsTo(this, contentEndIter);
8929 if (atStart) {
8930 aIter = endIter;
8932 } else if (whitespaceType == eRubyInterSegmentWhitespace) {
8933 // If this level container starts with inter-segment whitespaces,
8934 // wrap them. Break at contentEndIter. Otherwise, leave it here.
8935 // Break at endIter. They will be wrapped when we are here again.
8936 if (aIter == endIter) {
8937 MOZ_ASSERT(wrapperType == eTypeRubyBaseContainer,
8938 "Inter-segment whitespace should be wrapped in rbc");
8939 endIter = contentEndIter;
8941 break;
8942 } else if (wrapperType == eTypeRubyTextContainer &&
8943 whitespaceType != eRubyInterLeafWhitespace) {
8944 // Misparented inline content that's not inter-annotation
8945 // whitespace doesn't belong in a pseudo ruby text container.
8946 // Break at endIter.
8947 break;
8948 } else {
8949 endIter = contentEndIter;
8951 } while (!endIter.IsDone());
8953 // It is possible that everything our parent wants us to wrap is
8954 // simply an inter-level whitespace, which has been trimmed, or
8955 // an inter-segment whitespace, which will be wrapped later.
8956 // In those cases, don't create anything.
8957 if (aIter != endIter) {
8958 WrapItemsInPseudoParent(aParentContent, aParentStyle, wrapperType, aIter,
8959 endIter);
8964 * This function trims leading and trailing whitespaces
8965 * in the given item list.
8967 void nsCSSFrameConstructor::TrimLeadingAndTrailingWhitespaces(
8968 nsFrameConstructorState& aState, FrameConstructionItemList& aItems) {
8969 FCItemIterator iter(aItems);
8970 if (!iter.IsDone() && iter.item().IsWhitespace(aState)) {
8971 FCItemIterator spaceEndIter(iter);
8972 spaceEndIter.SkipWhitespace(aState);
8973 iter.DeleteItemsTo(this, spaceEndIter);
8976 iter.SetToEnd();
8977 if (!iter.AtStart()) {
8978 FCItemIterator spaceEndIter(iter);
8979 do {
8980 iter.Prev();
8981 if (iter.AtStart()) {
8982 // It's fine to not check the first item, because we
8983 // should have trimmed leading whitespaces above.
8984 break;
8986 } while (iter.item().IsWhitespace(aState));
8987 iter.Next();
8988 if (iter != spaceEndIter) {
8989 iter.DeleteItemsTo(this, spaceEndIter);
8995 * This function walks through the child list (aItems) and creates
8996 * needed pseudo ruby boxes to wrap misparented children.
8998 void nsCSSFrameConstructor::CreateNeededPseudoInternalRubyBoxes(
8999 nsFrameConstructorState& aState, FrameConstructionItemList& aItems,
9000 nsIFrame* aParentFrame) {
9001 const ParentType ourParentType = GetParentType(aParentFrame);
9002 if (!IsRubyParentType(ourParentType) ||
9003 aItems.AllWantParentType(ourParentType)) {
9004 return;
9007 if (!IsRubyPseudo(aParentFrame) ||
9008 ourParentType == eTypeRuby /* for 'display:block ruby' */) {
9009 // Normally, ruby pseudo frames start from and end at some elements,
9010 // which means they don't have leading and trailing whitespaces at
9011 // all. But there are two cases where they do actually have leading
9012 // or trailing whitespaces:
9013 // 1. It is an inter-segment whitespace which in an individual ruby
9014 // base container.
9015 // 2. The pseudo frame starts from or ends at consecutive inline
9016 // content, which is not pure whitespace, but includes some.
9017 // In either case, the whitespaces are not the leading or trailing
9018 // whitespaces defined in the spec, and thus should not be trimmed.
9019 TrimLeadingAndTrailingWhitespaces(aState, aItems);
9022 FCItemIterator iter(aItems);
9023 nsIContent* parentContent = aParentFrame->GetContent();
9024 ComputedStyle* parentStyle = aParentFrame->Style();
9025 while (!iter.IsDone()) {
9026 if (!iter.SkipItemsWantingParentType(ourParentType)) {
9027 if (ourParentType == eTypeRuby) {
9028 WrapItemsInPseudoRubyLevelContainer(aState, iter, parentStyle,
9029 parentContent);
9030 } else {
9031 WrapItemsInPseudoRubyLeafBox(iter, parentStyle, parentContent);
9038 * This function works as follows: we walk through the child list (aItems) and
9039 * find items that cannot have aParentFrame as their parent. We wrap
9040 * continuous runs of such items into a FrameConstructionItem for a frame that
9041 * gets them closer to their desired parents. For example, a run of non-row
9042 * children of a row-group will get wrapped in a row. When we later construct
9043 * the frame for this wrapper (in this case for the row), it'll be the correct
9044 * parent for the cells in the set of items we wrapped or we'll wrap cells
9045 * around everything else. At the end of this method, aItems is guaranteed to
9046 * contain only items for frames that can be direct kids of aParentFrame.
9048 void nsCSSFrameConstructor::CreateNeededPseudoContainers(
9049 nsFrameConstructorState& aState, FrameConstructionItemList& aItems,
9050 nsIFrame* aParentFrame) {
9051 ParentType ourParentType = GetParentType(aParentFrame);
9052 if (IsRubyParentType(ourParentType) ||
9053 aItems.AllWantParentType(ourParentType)) {
9054 // Nothing to do here
9055 return;
9058 FCItemIterator iter(aItems);
9059 do {
9060 if (iter.SkipItemsWantingParentType(ourParentType)) {
9061 // Nothing else to do here; we're finished
9062 return;
9065 // Now we're pointing to the first child that wants a different parent
9066 // type.
9068 // Now try to figure out what kids we can group together. We can generally
9069 // group everything that has a different desired parent type from us. Two
9070 // exceptions to this:
9071 // 1) If our parent type is table, we can't group columns with anything
9072 // else other than whitespace.
9073 // 2) Whitespace that lies between two things we can group which both want
9074 // a non-block parent should be dropped, even if we can't group them
9075 // with each other and even if the whitespace wants a parent of
9076 // ourParentType. Ends of the list count as things that don't want a
9077 // block parent (so that for example we'll drop a whitespace-only list).
9079 FCItemIterator endIter(iter); /* iterator to find the end of the group */
9080 ParentType groupingParentType = endIter.item().DesiredParentType();
9081 if (aItems.AllWantParentType(groupingParentType) &&
9082 groupingParentType != eTypeBlock) {
9083 // Just group them all and be done with it. We need the check for
9084 // eTypeBlock here to catch the "all the items are whitespace" case
9085 // described above.
9086 endIter.SetToEnd();
9087 } else {
9088 // Locate the end of the group.
9090 // Keep track of the type the previous item wanted, in case we have to
9091 // deal with whitespace. Start it off with ourParentType, since that's
9092 // the last thing |iter| would have skipped over.
9093 ParentType prevParentType = ourParentType;
9094 do {
9095 // Walk an iterator past any whitespace that we might be able to drop
9096 // from the list
9097 FCItemIterator spaceEndIter(endIter);
9098 if (prevParentType != eTypeBlock &&
9099 !aParentFrame->IsGeneratedContentFrame() &&
9100 spaceEndIter.item().IsWhitespace(aState)) {
9101 bool trailingSpaces = spaceEndIter.SkipWhitespace(aState);
9103 // We drop the whitespace in the following cases:
9104 // 1) If these are not trailing spaces and the next item wants a table
9105 // or table-part parent
9106 // 2) If these are trailing spaces and aParentFrame is a
9107 // tabular container according to rule 1.3 of CSS 2.1 Sec 17.2.1.
9108 // (Being a tabular container pretty much means ourParentType is
9109 // not eTypeBlock besides the eTypeColGroup case, which won't
9110 // reach here.)
9111 if ((!trailingSpaces &&
9112 IsTableParentType(spaceEndIter.item().DesiredParentType())) ||
9113 (trailingSpaces && ourParentType != eTypeBlock)) {
9114 bool updateStart = (iter == endIter);
9115 endIter.DeleteItemsTo(this, spaceEndIter);
9116 NS_ASSERTION(trailingSpaces == endIter.IsDone(),
9117 "These should match");
9119 if (updateStart) {
9120 iter = endIter;
9123 if (trailingSpaces) {
9124 break; /* Found group end */
9127 if (updateStart) {
9128 // Update groupingParentType, since it might have been eTypeBlock
9129 // just because of the whitespace.
9130 groupingParentType = iter.item().DesiredParentType();
9135 // Now endIter points to a non-whitespace item or a non-droppable
9136 // whitespace item. In the latter case, if this is the end of the group
9137 // we'll traverse this whitespace again. But it'll all just be quick
9138 // DesiredParentType() checks which will match ourParentType (that's
9139 // what it means that this is the group end), so it's OK.
9140 // However, when we are grouping a ruby parent, and endIter points to
9141 // a non-droppable whitespace, if the next non-whitespace item also
9142 // wants a ruby parent, the whitespace should also be included into
9143 // the current ruby container.
9144 prevParentType = endIter.item().DesiredParentType();
9145 if (prevParentType == ourParentType &&
9146 (endIter == spaceEndIter || spaceEndIter.IsDone() ||
9147 !IsRubyParentType(groupingParentType) ||
9148 !IsRubyParentType(spaceEndIter.item().DesiredParentType()))) {
9149 // End the group at endIter.
9150 break;
9153 if (ourParentType == eTypeTable &&
9154 (prevParentType == eTypeColGroup) !=
9155 (groupingParentType == eTypeColGroup)) {
9156 // Either we started with columns and now found something else, or
9157 // vice versa. In any case, end the grouping.
9158 break;
9161 // If we have some whitespace that we were not able to drop and there is
9162 // an item after the whitespace that is already properly parented, then
9163 // make sure to include the spaces in our group but stop the group after
9164 // that.
9165 if (spaceEndIter != endIter && !spaceEndIter.IsDone() &&
9166 ourParentType == spaceEndIter.item().DesiredParentType()) {
9167 endIter = spaceEndIter;
9168 break;
9171 // Include the whitespace we didn't drop (if any) in the group.
9172 endIter = spaceEndIter;
9173 prevParentType = endIter.item().DesiredParentType();
9175 endIter.Next();
9176 } while (!endIter.IsDone());
9179 if (iter == endIter) {
9180 // Nothing to wrap here; just skipped some whitespace
9181 continue;
9184 // Now group together all the items between iter and endIter. The right
9185 // parent type to use depends on ourParentType.
9186 ParentType wrapperType;
9187 switch (ourParentType) {
9188 case eTypeRow:
9189 // The parent type for a cell is eTypeBlock, since that's what a cell
9190 // looks like to its kids.
9191 wrapperType = eTypeBlock;
9192 break;
9193 case eTypeRowGroup:
9194 wrapperType = eTypeRow;
9195 break;
9196 case eTypeTable:
9197 // Either colgroup or rowgroup, depending on what we're grouping.
9198 wrapperType =
9199 groupingParentType == eTypeColGroup ? eTypeColGroup : eTypeRowGroup;
9200 break;
9201 case eTypeColGroup:
9202 MOZ_CRASH("Colgroups should be suppresing non-col child items");
9203 default:
9204 NS_ASSERTION(ourParentType == eTypeBlock, "Unrecognized parent type");
9205 if (IsRubyParentType(groupingParentType)) {
9206 wrapperType = eTypeRuby;
9207 } else {
9208 NS_ASSERTION(IsTableParentType(groupingParentType),
9209 "groupingParentType should be either Ruby or table");
9210 wrapperType = eTypeTable;
9214 ComputedStyle* parentStyle = aParentFrame->Style();
9215 WrapItemsInPseudoParent(aParentFrame->GetContent(), parentStyle,
9216 wrapperType, iter, endIter);
9218 // Now |iter| points to the item that was the first one we didn't wrap;
9219 // loop and see whether we need to skip it or wrap it in something
9220 // different.
9221 } while (!iter.IsDone());
9225 * This method wraps frame construction item from |aIter| to
9226 * |aEndIter|. After it returns, aIter points to the first item
9227 * after the wrapper.
9229 void nsCSSFrameConstructor::WrapItemsInPseudoParent(
9230 nsIContent* aParentContent, ComputedStyle* aParentStyle,
9231 ParentType aWrapperType, FCItemIterator& aIter,
9232 const FCItemIterator& aEndIter) {
9233 const PseudoParentData& pseudoData = sPseudoParentData[aWrapperType];
9234 PseudoStyleType pseudoType = pseudoData.mPseudoType;
9235 auto& parentDisplay = *aParentStyle->StyleDisplay();
9236 auto parentDisplayInside = parentDisplay.DisplayInside();
9238 // XXXmats should we use IsInlineInsideStyle() here instead? seems odd to
9239 // exclude RubyBaseContainer/RubyTextContainer...
9240 if (pseudoType == PseudoStyleType::table &&
9241 (parentDisplay.IsInlineFlow() ||
9242 parentDisplayInside == StyleDisplayInside::RubyBase ||
9243 parentDisplayInside == StyleDisplayInside::RubyText)) {
9244 pseudoType = PseudoStyleType::inlineTable;
9247 RefPtr<ComputedStyle> wrapperStyle;
9248 if (pseudoData.mFCData.mBits & FCDATA_IS_WRAPPER_ANON_BOX) {
9249 wrapperStyle = mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
9250 pseudoType, aParentStyle);
9251 } else {
9252 wrapperStyle =
9253 mPresShell->StyleSet()->ResolveNonInheritingAnonymousBoxStyle(
9254 pseudoType);
9257 FrameConstructionItem* newItem = new (this)
9258 FrameConstructionItem(&pseudoData.mFCData,
9259 // Use the content of our parent frame
9260 aParentContent, wrapperStyle.forget(), true);
9262 const nsStyleDisplay* disp = newItem->mComputedStyle->StyleDisplay();
9263 // Here we're cheating a tad... technically, table-internal items should be
9264 // inline if aParentFrame is inline, but they'll get wrapped in an
9265 // inline-table in the end, so it'll all work out. In any case, arguably
9266 // we don't need to maintain this state at this point... but it's better
9267 // to, I guess.
9268 newItem->mIsAllInline = disp->IsInlineOutsideStyle();
9270 bool isRuby = disp->IsRubyDisplayType();
9271 if (!isRuby) {
9272 // Table pseudo frames always induce line boundaries around their
9273 // contents.
9274 newItem->mChildItems.SetLineBoundaryAtStart(true);
9275 newItem->mChildItems.SetLineBoundaryAtEnd(true);
9277 // The parent of the items in aItems is also the parent of the items
9278 // in mChildItems
9279 newItem->mChildItems.SetParentHasNoShadowDOM(
9280 aIter.List()->ParentHasNoShadowDOM());
9282 // Eat up all items between |aIter| and |aEndIter| and put them in our
9283 // wrapper Advances |aIter| to point to |aEndIter|.
9284 aIter.AppendItemsToList(this, aEndIter, newItem->mChildItems);
9286 aIter.InsertItem(newItem);
9289 void nsCSSFrameConstructor::CreateNeededPseudoSiblings(
9290 nsFrameConstructorState& aState, FrameConstructionItemList& aItems,
9291 nsIFrame* aParentFrame) {
9292 if (aItems.IsEmpty() || GetParentType(aParentFrame) != eTypeRuby) {
9293 return;
9296 FCItemIterator iter(aItems);
9297 StyleDisplay firstDisplay =
9298 iter.item().mComputedStyle->StyleDisplay()->mDisplay;
9299 if (firstDisplay == StyleDisplay::RubyBaseContainer) {
9300 return;
9302 NS_ASSERTION(firstDisplay == StyleDisplay::RubyTextContainer,
9303 "Child of ruby frame should either a rbc or a rtc");
9305 const PseudoParentData& pseudoData =
9306 sPseudoParentData[eTypeRubyBaseContainer];
9307 RefPtr<ComputedStyle> pseudoStyle =
9308 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
9309 pseudoData.mPseudoType, aParentFrame->Style());
9310 FrameConstructionItem* newItem = new (this) FrameConstructionItem(
9311 &pseudoData.mFCData,
9312 // Use the content of the parent frame
9313 aParentFrame->GetContent(), pseudoStyle.forget(), true);
9314 newItem->mIsAllInline = true;
9315 newItem->mChildItems.SetParentHasNoShadowDOM(true);
9316 iter.InsertItem(newItem);
9319 #ifdef DEBUG
9321 * Returns true iff aFrame should be wrapped in an anonymous flex/grid item,
9322 * rather than being a direct child of aContainerFrame.
9324 * NOTE: aContainerFrame must be a flex or grid container - this function is
9325 * purely for sanity-checking the children of these container types.
9326 * NOTE: See also NeedsAnonFlexOrGridItem(), for the non-debug version of this
9327 * logic (which operates a bit earlier, on FCData instead of frames).
9329 static bool FrameWantsToBeInAnonymousItem(const nsIFrame* aContainerFrame,
9330 const nsIFrame* aFrame) {
9331 MOZ_ASSERT(aContainerFrame->IsFlexOrGridContainer());
9333 // Any line-participant frames (e.g. text) definitely want to be wrapped in
9334 // an anonymous flex/grid item.
9335 if (aFrame->IsFrameOfType(nsIFrame::eLineParticipant)) {
9336 return true;
9339 // If the container is a -webkit-{inline-}box container, then placeholders
9340 // also need to be wrapped, for compatibility.
9341 if (IsFlexContainerForLegacyWebKitBox(aContainerFrame) &&
9342 aFrame->IsPlaceholderFrame()) {
9343 return true;
9346 return false;
9348 #endif
9350 static void VerifyGridFlexContainerChildren(nsIFrame* aParentFrame,
9351 const nsFrameList& aChildren) {
9352 #ifdef DEBUG
9353 if (!aParentFrame->IsFlexOrGridContainer()) {
9354 return;
9357 bool prevChildWasAnonItem = false;
9358 for (const nsIFrame* child : aChildren) {
9359 MOZ_ASSERT(!FrameWantsToBeInAnonymousItem(aParentFrame, child),
9360 "frame wants to be inside an anonymous item, but it isn't");
9361 if (IsAnonymousItem(child)) {
9362 AssertAnonymousFlexOrGridItemParent(child, aParentFrame);
9363 MOZ_ASSERT(!prevChildWasAnonItem, "two anon items in a row");
9364 nsIFrame* firstWrappedChild = child->PrincipalChildList().FirstChild();
9365 MOZ_ASSERT(firstWrappedChild, "anonymous item shouldn't be empty");
9366 prevChildWasAnonItem = true;
9367 } else {
9368 prevChildWasAnonItem = false;
9371 #endif
9374 static bool FrameHasOnlyPlaceholderPrevSiblings(const nsIFrame* aFrame) {
9375 // Check for prev siblings, ignoring placeholder frames.
9376 MOZ_ASSERT(aFrame, "frame must not be null");
9377 const nsIFrame* prevSibling = aFrame;
9378 do {
9379 prevSibling = prevSibling->GetPrevSibling();
9380 } while (prevSibling && prevSibling->IsPlaceholderFrame());
9381 return !prevSibling;
9384 static bool FrameHasOnlyPlaceholderNextSiblings(const nsIFrame* aFrame) {
9385 // Check for next siblings, ignoring placeholder frames.
9386 MOZ_ASSERT(aFrame, "frame must not be null");
9387 const nsIFrame* nextSibling = aFrame;
9388 do {
9389 nextSibling = nextSibling->GetNextSibling();
9390 } while (nextSibling && nextSibling->IsPlaceholderFrame());
9391 return !nextSibling;
9394 static void SetPageValues(nsIFrame* const aFrame,
9395 const nsAtom* const aAutoValue,
9396 const nsAtom* const aStartValue,
9397 const nsAtom* const aEndValue) {
9398 MOZ_ASSERT(aAutoValue, "Auto page value should never be null");
9399 MOZ_ASSERT(aStartValue || aEndValue, "Should not have called with no values");
9400 nsIFrame::PageValues* pageValues =
9401 aFrame->GetProperty(nsIFrame::PageValuesProperty());
9403 if (aStartValue) {
9404 if (aStartValue == aAutoValue) {
9405 // If the page value struct already exists, set the start value to null
9406 // to indicate the auto value.
9407 if (pageValues) {
9408 pageValues->mStartPageValue = nullptr;
9410 } else {
9411 // The start value is not auto, so we need to store it, creating the
9412 // page values struct if it does not already exist.
9413 if (!pageValues) {
9414 pageValues = new nsIFrame::PageValues();
9415 aFrame->SetProperty(nsIFrame::PageValuesProperty(), pageValues);
9417 pageValues->mStartPageValue = aStartValue;
9420 if (aEndValue) {
9421 if (aEndValue == aAutoValue) {
9422 // If the page value struct already exists, set the end value to null
9423 // to indicate the auto value.
9424 if (pageValues) {
9425 pageValues->mEndPageValue = nullptr;
9427 } else {
9428 // The end value is not auto, so we need to store it, creating the
9429 // page values struct if it does not already exist.
9430 if (!pageValues) {
9431 pageValues = new nsIFrame::PageValues();
9432 aFrame->SetProperty(nsIFrame::PageValuesProperty(), pageValues);
9434 pageValues->mEndPageValue = aEndValue;
9439 inline void nsCSSFrameConstructor::ConstructFramesFromItemList(
9440 nsFrameConstructorState& aState, FrameConstructionItemList& aItems,
9441 nsContainerFrame* aParentFrame, bool aParentIsWrapperAnonBox,
9442 nsFrameList& aFrameList) {
9443 #ifdef DEBUG
9444 if (aParentFrame->StyleContent()->mContent.IsNone() &&
9445 StaticPrefs::layout_css_element_content_none_enabled()) {
9446 for (FCItemIterator iter(aItems); !iter.IsDone(); iter.Next()) {
9447 MOZ_ASSERT(iter.item().mContent->IsInNativeAnonymousSubtree() ||
9448 iter.item().mComputedStyle->IsPseudoOrAnonBox());
9452 // The assertion condition should match the logic in
9453 // MaybePushFloatContainingBlock().
9454 MOZ_ASSERT(!(ShouldSuppressFloatingOfDescendants(aParentFrame) ||
9455 aParentFrame->IsFloatContainingBlock()) ||
9456 aState.mFloatCBCandidate == aParentFrame,
9457 "Our caller or ProcessChildren()'s caller should call "
9458 "MaybePushFloatContainingBlock() to handle the float containing "
9459 "block candidate!");
9460 aState.mFloatCBCandidate = nullptr;
9461 #endif
9463 // Ensure aParentIsWrapperAnonBox is correct. We _could_ compute it directly,
9464 // but it would be a bit slow, which is why we pass it from callers, who have
9465 // that information offhand in many cases.
9466 MOZ_ASSERT(ParentIsWrapperAnonBox(aParentFrame) == aParentIsWrapperAnonBox);
9468 // Note: we explicitly exclude TableColGroupFrame because it doesn't
9469 // have the FCDATA_IS_WRAPPER_ANON_BOX on pseudos so aParentIsWrapperAnonBox
9470 // is false for such pseudos (see sPseudoParentData below).
9471 if (!aParentIsWrapperAnonBox && aState.mHasRenderedLegend &&
9472 aParentFrame->GetContent()->IsHTMLElement(nsGkAtoms::fieldset) &&
9473 !aParentFrame->IsTableColGroupFrame()) {
9474 DebugOnly<bool> found = false;
9475 for (FCItemIterator iter(aItems); !iter.IsDone(); iter.Next()) {
9476 if (iter.item().mIsRenderedLegend) {
9477 // This makes the rendered legend the first frame in the fieldset child
9478 // list which makes keyboard traversal follow the visual order.
9479 nsFieldSetFrame* fieldSetFrame = GetFieldSetFrameFor(aParentFrame);
9480 nsFrameList renderedLegend;
9481 ConstructFramesFromItem(aState, iter, fieldSetFrame, renderedLegend);
9482 MOZ_ASSERT(renderedLegend.OnlyChild(),
9483 "a rendered legend should have exactly one frame");
9484 fieldSetFrame->InsertFrames(FrameChildListID::Principal, nullptr,
9485 nullptr, std::move(renderedLegend));
9486 FCItemIterator next = iter;
9487 next.Next();
9488 iter.DeleteItemsTo(this, next);
9489 found = true;
9490 break;
9493 MOZ_ASSERT(found, "should have found our rendered legend");
9496 CreateNeededPseudoContainers(aState, aItems, aParentFrame);
9497 CreateNeededAnonFlexOrGridItems(aState, aItems, aParentFrame);
9498 CreateNeededPseudoInternalRubyBoxes(aState, aItems, aParentFrame);
9499 CreateNeededPseudoSiblings(aState, aItems, aParentFrame);
9501 for (FCItemIterator iter(aItems); !iter.IsDone(); iter.Next()) {
9502 MOZ_ASSERT(!iter.item().mIsRenderedLegend,
9503 "Only one item can be the rendered legend, "
9504 "and it should've been handled above");
9505 NS_ASSERTION(iter.item().DesiredParentType() == GetParentType(aParentFrame),
9506 "Needed pseudos didn't get created; expect bad things");
9507 ConstructFramesFromItem(aState, iter, aParentFrame, aFrameList);
9510 VerifyGridFlexContainerChildren(aParentFrame, aFrameList);
9512 // Calculate and propagate page-name values for each frame in the frame list.
9513 // We do not want to compute and propagate page-name values from frames that
9514 // are children of any subclasses of block frames, but not actually a block
9515 // frame. The page-name property does not apply to frames which cannot create
9516 // class A breakpoints (currently no subclass of BlockFrame can). Because the
9517 // property does not apply, those children also cannot propagate page-name
9518 // values.
9519 // This assumption helps avoid unnecessarily handling page-names for frames
9520 // such as form controls, which also avoids bug 1819468.
9521 if (aState.mPresContext->IsPaginated() && aParentFrame->IsBlockFrame()) {
9522 // Set the start/end page values while iterating the frame list, to walk
9523 // up the frame tree only once after iterating the frame list.
9524 // This also avoids extra property lookups on these frames.
9525 MOZ_ASSERT(aState.mAutoPageNameValue == aParentFrame->GetAutoPageValue(),
9526 "aState.mAutoPageNameValue should have been equivalent to "
9527 "the auto value stored on our parent frame.");
9528 // Even though we store null for page values that equal the "auto" resolved
9529 // value on frames, we always want startPageValue/endPageValue to be the
9530 // actual atoms reflecting the start/end values. This is because when we
9531 // propagate the values up the frame tree, we will need to compare them to
9532 // the auto value for each ancestor. This value might be different than the
9533 // auto value for this frame.
9534 const nsAtom* startPageValue = nullptr;
9535 const nsAtom* endPageValue = nullptr;
9536 for (nsIFrame* f : aFrameList) {
9537 if (f->IsPlaceholderFrame()) {
9538 continue;
9540 // Resolve auto against the parent frame's used page name, which has been
9541 // determined and set on aState.mAutoPageNameValue. If this item is not
9542 // block-level then we use the value that auto resolves to.
9544 // This is to achieve the propagation behavior described in the spec:
9546 // "A start page value and end page value is determined for each box as
9547 // the value (if any) propagated from its first or last child box
9548 // (respectively), else the used value on the box itself."
9550 // "A child propagates its own start or end page value if and only if the
9551 // page property applies to it."
9553 // The page property only applies to "boxes that create class A break
9554 // points". When taken together, this means that non block-level children
9555 // do not propagate start/end page values, and instead we use "the used
9556 // value on the box itself", the "box itself" being aParentFrame. This
9557 // value has been determined and saved as aState.mAutoPageNameValue
9559 // https://www.w3.org/TR/css-page-3/#using-named-pages
9560 // https://www.w3.org/TR/css-break-3/#btw-blocks
9561 const StylePageName& pageName = f->StylePage()->mPage;
9562 const nsAtom* const pageNameAtom =
9563 (pageName.IsPageName() && f->IsBlockOutside())
9564 ? pageName.AsPageName().AsAtom()
9565 : aState.mAutoPageNameValue;
9566 nsIFrame::PageValues* pageValues =
9567 f->GetProperty(nsIFrame::PageValuesProperty());
9568 // If this frame has any children, it will already have had its page
9569 // values set at this point. However, if no page values have been set,
9570 // we must ensure that the appropriate PageValuesProperty value has been
9571 // set.
9572 // If the page name is equal to the auto value, then PageValuesProperty
9573 // should remain null to indicate that the start/end values are both
9574 // equal to the auto value.
9575 if (pageNameAtom != aState.mAutoPageNameValue && !pageValues) {
9576 pageValues = new nsIFrame::PageValues{pageNameAtom, pageNameAtom};
9577 f->SetProperty(nsIFrame::PageValuesProperty(), pageValues);
9579 // We don't want to use GetStartPageValue() or GetEndPageValue(), as each
9580 // requires a property lookup which we can avoid here.
9581 if (!startPageValue) {
9582 startPageValue = (pageValues && pageValues->mStartPageValue)
9583 ? pageValues->mStartPageValue.get()
9584 : aState.mAutoPageNameValue;
9586 endPageValue = (pageValues && pageValues->mEndPageValue)
9587 ? pageValues->mEndPageValue.get()
9588 : aState.mAutoPageNameValue;
9589 MOZ_ASSERT(startPageValue && endPageValue,
9590 "Should have found start/end page value");
9592 MOZ_ASSERT(!startPageValue == !endPageValue,
9593 "Should have set both or neither page values");
9594 if (startPageValue) {
9595 // Walk up the frame tree from our parent frame, propagating start and
9596 // end page values.
9597 // As we go, if we find that, for a frame, we are not contributing one of
9598 // the start/end page values, then our subtree will not contribute this
9599 // value from that frame onward. startPageValue/endPageValue are set to
9600 // null to indicate this.
9601 // Stop iterating when we are not contributing either start or end
9602 // values, when we hit the root frame (no parent), or when we find a
9603 // frame that is not a block frame.
9604 for (nsContainerFrame* ancestorFrame = aParentFrame;
9605 (startPageValue || endPageValue) && ancestorFrame &&
9606 ancestorFrame->IsBlockFrame();
9607 ancestorFrame = ancestorFrame->GetParent()) {
9608 MOZ_ASSERT(!ancestorFrame->GetPrevInFlow(),
9609 "Should not have fragmentation yet");
9610 MOZ_ASSERT(ancestorFrame->mWasVisitedByAutoFrameConstructionPageName,
9611 "Frame should have been visited by "
9612 "AutoFrameConstructionPageName");
9614 // Get what the auto value is, based on this frame's parent.
9615 // For the root frame, `auto` resolves to the empty atom.
9616 const nsContainerFrame* const parent = ancestorFrame->GetParent();
9617 const nsAtom* const parentAuto = MOZ_LIKELY(parent)
9618 ? parent->GetAutoPageValue()
9619 : nsGkAtoms::_empty;
9620 SetPageValues(ancestorFrame, parentAuto, startPageValue,
9621 endPageValue);
9623 // Once we stop contributing start/end values, we know there is a
9624 // sibling subtree that contributed that value to our shared parent
9625 // instead of our starting frame's subtree. This means once
9626 // startPageValue/endPageValue becomes null, indicating that we are no
9627 // longer contributing that page value, it should stay null and we no
9628 // longer need to check for siblings in that direction.
9629 if (startPageValue &&
9630 !FrameHasOnlyPlaceholderPrevSiblings(ancestorFrame)) {
9631 startPageValue = nullptr;
9633 if (endPageValue &&
9634 !FrameHasOnlyPlaceholderNextSiblings(ancestorFrame)) {
9635 endPageValue = nullptr;
9641 if (aParentIsWrapperAnonBox) {
9642 for (nsIFrame* f : aFrameList) {
9643 f->SetParentIsWrapperAnonBox();
9648 void nsCSSFrameConstructor::AddFCItemsForAnonymousContent(
9649 nsFrameConstructorState& aState, nsContainerFrame* aFrame,
9650 const nsTArray<nsIAnonymousContentCreator::ContentInfo>& aAnonymousItems,
9651 FrameConstructionItemList& aItemsToConstruct,
9652 const AutoFrameConstructionPageName&) {
9653 for (const auto& info : aAnonymousItems) {
9654 nsIContent* content = info.mContent;
9655 // Gecko-styled nodes should have no pending restyle flags.
9656 // Assert some things about this content
9657 MOZ_ASSERT(!(content->GetFlags() &
9658 (NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME)),
9659 "Should not be marked as needing frames");
9660 MOZ_ASSERT(!content->GetPrimaryFrame(), "Should have no existing frame");
9661 MOZ_ASSERT(!content->IsComment() && !content->IsProcessingInstruction(),
9662 "Why is someone creating garbage anonymous content");
9664 // Make sure we eagerly performed the servo cascade when the anonymous
9665 // nodes were created.
9666 MOZ_ASSERT(!content->IsElement() || content->AsElement()->HasServoData());
9668 RefPtr<ComputedStyle> computedStyle = ResolveComputedStyle(content);
9670 AddFrameConstructionItemsInternal(aState, content, aFrame, true,
9671 computedStyle, {ItemFlag::AllowPageBreak},
9672 aItemsToConstruct);
9676 void nsCSSFrameConstructor::ProcessChildren(
9677 nsFrameConstructorState& aState, nsIContent* aContent,
9678 ComputedStyle* aComputedStyle, nsContainerFrame* aFrame,
9679 const bool aCanHaveGeneratedContent, nsFrameList& aFrameList,
9680 const bool aAllowBlockStyles, nsIFrame* aPossiblyLeafFrame) {
9681 MOZ_ASSERT(aFrame, "Must have parent frame here");
9682 MOZ_ASSERT(aFrame->GetContentInsertionFrame() == aFrame,
9683 "Parent frame in ProcessChildren should be its own "
9684 "content insertion frame");
9686 const uint32_t kMaxDepth = 2 * MAX_REFLOW_DEPTH;
9687 static_assert(kMaxDepth <= UINT16_MAX, "mCurrentDepth type is too narrow");
9688 AutoRestore<uint16_t> savedDepth(mCurrentDepth);
9689 if (mCurrentDepth != UINT16_MAX) {
9690 ++mCurrentDepth;
9693 if (!aPossiblyLeafFrame) {
9694 aPossiblyLeafFrame = aFrame;
9697 // XXXbz ideally, this would do all the pushing of various
9698 // containing blocks as needed, so callers don't have to do it...
9700 // Check that our parent frame is a block before allowing ::first-letter/line.
9701 // E.g. <button style="display:grid"> should not allow it.
9702 const bool allowFirstPseudos =
9703 aAllowBlockStyles && aFrame->IsBlockFrameOrSubclass();
9704 bool haveFirstLetterStyle = false, haveFirstLineStyle = false;
9705 if (allowFirstPseudos) {
9706 ShouldHaveSpecialBlockStyle(aContent, aComputedStyle, &haveFirstLetterStyle,
9707 &haveFirstLineStyle);
9710 AutoFrameConstructionItemList itemsToConstruct(this);
9711 AutoFrameConstructionPageName pageNameTracker(aState, aFrame);
9713 // If we have first-letter or first-line style then frames can get
9714 // moved around so don't set these flags.
9715 if (allowFirstPseudos && !haveFirstLetterStyle && !haveFirstLineStyle) {
9716 itemsToConstruct.SetLineBoundaryAtStart(true);
9717 itemsToConstruct.SetLineBoundaryAtEnd(true);
9720 // Create any anonymous frames we need here.
9721 AutoTArray<nsIAnonymousContentCreator::ContentInfo, 4> anonymousItems;
9722 GetAnonymousContent(aContent, aPossiblyLeafFrame, anonymousItems);
9723 #ifdef DEBUG
9724 for (uint32_t i = 0; i < anonymousItems.Length(); ++i) {
9725 MOZ_ASSERT(anonymousItems[i].mContent->IsRootOfNativeAnonymousSubtree(),
9726 "Content should know it's an anonymous subtree");
9728 #endif
9729 AddFCItemsForAnonymousContent(aState, aFrame, anonymousItems,
9730 itemsToConstruct, pageNameTracker);
9732 nsBlockFrame* listItem = nullptr;
9733 bool isOutsideMarker = false;
9734 if (!aPossiblyLeafFrame->IsLeaf()) {
9735 // :before/:after content should have the same style parent as normal kids.
9737 // Note that we don't use this style for looking up things like special
9738 // block styles because in some cases involving table pseudo-frames it has
9739 // nothing to do with the parent frame's desired behavior.
9740 auto* styleParentFrame =
9741 nsIFrame::CorrectStyleParentFrame(aFrame, PseudoStyleType::NotPseudo);
9742 ComputedStyle* computedStyle = styleParentFrame->Style();
9744 if (aCanHaveGeneratedContent) {
9745 if (computedStyle->StyleDisplay()->IsListItem() &&
9746 (listItem = do_QueryFrame(aFrame)) &&
9747 !styleParentFrame->IsFieldSetFrame()) {
9748 isOutsideMarker = computedStyle->StyleList()->mListStylePosition ==
9749 StyleListStylePosition::Outside;
9750 ItemFlags extraFlags;
9751 if (isOutsideMarker) {
9752 extraFlags += ItemFlag::IsForOutsideMarker;
9754 CreateGeneratedContentItem(aState, aFrame, *aContent->AsElement(),
9755 *computedStyle, PseudoStyleType::marker,
9756 itemsToConstruct, extraFlags);
9758 // Probe for generated content before
9759 CreateGeneratedContentItem(aState, aFrame, *aContent->AsElement(),
9760 *computedStyle, PseudoStyleType::before,
9761 itemsToConstruct);
9764 const bool addChildItems = MOZ_LIKELY(mCurrentDepth < kMaxDepth);
9765 if (!addChildItems) {
9766 NS_WARNING("ProcessChildren max depth exceeded");
9769 FlattenedChildIterator iter(aContent);
9770 const InsertionPoint insertion(aFrame, aContent);
9771 for (nsIContent* child = iter.GetNextChild(); child;
9772 child = iter.GetNextChild()) {
9773 MOZ_ASSERT(insertion.mContainer == GetInsertionPoint(child).mContainer,
9774 "GetInsertionPoint should agree with us");
9775 if (addChildItems) {
9776 AddFrameConstructionItems(aState, child, iter.ShadowDOMInvolved(),
9777 *computedStyle, insertion, itemsToConstruct);
9778 } else {
9779 ClearLazyBits(child, child->GetNextSibling());
9782 itemsToConstruct.SetParentHasNoShadowDOM(!iter.ShadowDOMInvolved());
9784 if (aCanHaveGeneratedContent) {
9785 // Probe for generated content after
9786 CreateGeneratedContentItem(aState, aFrame, *aContent->AsElement(),
9787 *computedStyle, PseudoStyleType::after,
9788 itemsToConstruct);
9790 } else {
9791 ClearLazyBits(aContent->GetFirstChild(), nullptr);
9794 ConstructFramesFromItemList(aState, itemsToConstruct, aFrame,
9795 /* aParentIsWrapperAnonBox = */ false,
9796 aFrameList);
9798 if (listItem) {
9799 if (auto* markerFrame = nsLayoutUtils::GetMarkerFrame(aContent)) {
9800 for (auto* childFrame : aFrameList) {
9801 if (markerFrame == childFrame) {
9802 if (isOutsideMarker) {
9803 // SetMarkerFrameForListItem will add childFrame to the
9804 // FrameChildListID::Bullet
9805 aFrameList.RemoveFrame(childFrame);
9806 auto* grandParent = listItem->GetParent()->GetParent();
9807 if (listItem->Style()->GetPseudoType() ==
9808 PseudoStyleType::columnContent &&
9809 grandParent && grandParent->IsColumnSetWrapperFrame()) {
9810 listItem = do_QueryFrame(grandParent);
9811 MOZ_ASSERT(listItem,
9812 "ColumnSetWrapperFrame is expected to be "
9813 "a nsBlockFrame subclass");
9814 childFrame->SetParent(listItem);
9817 listItem->SetMarkerFrameForListItem(childFrame);
9818 MOZ_ASSERT(listItem->HasAnyStateBits(
9819 NS_BLOCK_FRAME_HAS_OUTSIDE_MARKER) == isOutsideMarker);
9820 #ifdef ACCESSIBILITY
9821 if (nsAccessibilityService* accService = GetAccService()) {
9822 auto* marker = markerFrame->GetContent();
9823 accService->ContentRangeInserted(mPresShell, marker, nullptr);
9825 #endif
9826 break;
9832 if (haveFirstLetterStyle) {
9833 WrapFramesInFirstLetterFrame(aFrame, aFrameList);
9835 if (haveFirstLineStyle) {
9836 WrapFramesInFirstLineFrame(aState, aContent, aFrame, nullptr, aFrameList);
9840 //----------------------------------------------------------------------
9842 // Support for :first-line style
9844 // Special routine to handle placing a list of frames into a block
9845 // frame that has first-line style. The routine ensures that the first
9846 // collection of inline frames end up in a first-line frame.
9847 // NOTE: aState may have containing block information related to a
9848 // different part of the frame tree than where the first line occurs.
9849 // In particular aState may be set up for where ContentInserted or
9850 // ContentAppended is inserting content, which may be some
9851 // non-first-in-flow continuation of the block to which the first-line
9852 // belongs. So this function needs to be careful about how it uses
9853 // aState.
9854 void nsCSSFrameConstructor::WrapFramesInFirstLineFrame(
9855 nsFrameConstructorState& aState, nsIContent* aBlockContent,
9856 nsContainerFrame* aBlockFrame, nsFirstLineFrame* aLineFrame,
9857 nsFrameList& aFrameList) {
9858 // Extract any initial inline frames from aFrameList so we can put them
9859 // in the first-line.
9860 nsFrameList firstLineChildren =
9861 aFrameList.Split([](nsIFrame* f) { return !f->IsInlineOutside(); });
9863 if (firstLineChildren.IsEmpty()) {
9864 // Nothing is supposed to go into the first-line; nothing to do
9865 return;
9868 if (!aLineFrame) {
9869 // Create line frame
9870 ComputedStyle* parentStyle = nsIFrame::CorrectStyleParentFrame(
9871 aBlockFrame, PseudoStyleType::firstLine)
9872 ->Style();
9873 RefPtr<ComputedStyle> firstLineStyle =
9874 GetFirstLineStyle(aBlockContent, parentStyle);
9876 aLineFrame = NS_NewFirstLineFrame(mPresShell, firstLineStyle);
9878 // Initialize the line frame
9879 InitAndRestoreFrame(aState, aBlockContent, aBlockFrame, aLineFrame);
9881 // The lineFrame will be the block's first child; the rest of the
9882 // frame list (after lastInlineFrame) will be the second and
9883 // subsequent children; insert lineFrame into aFrameList.
9884 aFrameList.InsertFrame(nullptr, nullptr, aLineFrame);
9886 NS_ASSERTION(aLineFrame->Style() == firstLineStyle,
9887 "Bogus style on line frame");
9890 // Give the inline frames to the lineFrame <b>after</b> reparenting them
9891 ReparentFrames(this, aLineFrame, firstLineChildren, true);
9892 if (aLineFrame->PrincipalChildList().IsEmpty() &&
9893 aLineFrame->HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
9894 aLineFrame->SetInitialChildList(FrameChildListID::Principal,
9895 std::move(firstLineChildren));
9896 } else {
9897 AppendFrames(aLineFrame, FrameChildListID::Principal,
9898 std::move(firstLineChildren));
9902 // Special routine to handle appending a new frame to a block frame's
9903 // child list. Takes care of placing the new frame into the right
9904 // place when first-line style is present.
9905 void nsCSSFrameConstructor::AppendFirstLineFrames(
9906 nsFrameConstructorState& aState, nsIContent* aBlockContent,
9907 nsContainerFrame* aBlockFrame, nsFrameList& aFrameList) {
9908 // It's possible that aBlockFrame needs to have a first-line frame
9909 // created because it doesn't currently have any children.
9910 const nsFrameList& blockKids = aBlockFrame->PrincipalChildList();
9911 if (blockKids.IsEmpty()) {
9912 WrapFramesInFirstLineFrame(aState, aBlockContent, aBlockFrame, nullptr,
9913 aFrameList);
9914 return;
9917 // Examine the last block child - if it's a first-line frame then
9918 // appended frames need special treatment.
9919 nsIFrame* lastBlockKid = blockKids.LastChild();
9920 if (!lastBlockKid->IsLineFrame()) {
9921 // No first-line frame at the end of the list, therefore there is
9922 // an intervening block between any first-line frame the frames
9923 // we are appending. Therefore, we don't need any special
9924 // treatment of the appended frames.
9925 return;
9928 nsFirstLineFrame* lineFrame = static_cast<nsFirstLineFrame*>(lastBlockKid);
9929 WrapFramesInFirstLineFrame(aState, aBlockContent, aBlockFrame, lineFrame,
9930 aFrameList);
9933 void nsCSSFrameConstructor::CheckForFirstLineInsertion(
9934 nsIFrame* aParentFrame, nsFrameList& aFrameList) {
9935 MOZ_ASSERT(aParentFrame->Style()->HasPseudoElementData(),
9936 "Why were we called?");
9938 if (aFrameList.IsEmpty()) {
9939 // Happens often enough, with the caption stuff. No need to do the ancestor
9940 // walk here.
9941 return;
9944 class RestyleManager* restyleManager = RestyleManager();
9946 // Check whether there's a ::first-line on the path up from aParentFrame.
9947 // Note that we can't stop until we've run out of ancestors with
9948 // pseudo-element data, because the first-letter might be somewhere way up the
9949 // tree; in particular it might be past our containing block.
9950 nsIFrame* ancestor = aParentFrame;
9951 while (ancestor) {
9952 if (!ancestor->Style()->HasPseudoElementData()) {
9953 // We know we won't find a ::first-line now.
9954 return;
9957 if (!ancestor->IsLineFrame()) {
9958 ancestor = ancestor->GetParent();
9959 continue;
9962 if (!ancestor->Style()->IsPseudoElement()) {
9963 // This is a continuation lineframe, not the first line; no need to do
9964 // anything to the styles.
9965 return;
9968 // Fix up the styles of aFrameList for ::first-line.
9969 for (nsIFrame* f : aFrameList) {
9970 restyleManager->ReparentComputedStyleForFirstLine(f);
9972 return;
9976 //----------------------------------------------------------------------
9978 // First-letter support
9980 // Determine how many characters in the text fragment apply to the
9981 // first letter
9982 static int32_t FirstLetterCount(const nsTextFragment* aFragment) {
9983 int32_t count = 0;
9984 int32_t firstLetterLength = 0;
9986 const uint32_t n = aFragment->GetLength();
9987 for (uint32_t i = 0; i < n; i++) {
9988 const char16_t ch = aFragment->CharAt(i);
9989 // FIXME: take content language into account when deciding whitespace.
9990 if (dom::IsSpaceCharacter(ch)) {
9991 if (firstLetterLength) {
9992 break;
9994 count++;
9995 continue;
9997 // XXX I18n
9998 if ((ch == '\'') || (ch == '\"')) {
9999 if (firstLetterLength) {
10000 break;
10002 // keep looping
10003 firstLetterLength = 1;
10004 } else {
10005 count++;
10006 break;
10010 return count;
10013 static bool NeedFirstLetterContinuation(Text* aText) {
10014 MOZ_ASSERT(aText, "null ptr");
10015 int32_t flc = FirstLetterCount(&aText->TextFragment());
10016 int32_t tl = aText->TextDataLength();
10017 return flc < tl;
10020 static bool IsFirstLetterContent(Text* aText) {
10021 return aText->TextDataLength() && !aText->TextIsOnlyWhitespace();
10025 * Create a letter frame, only make it a floating frame.
10027 nsFirstLetterFrame* nsCSSFrameConstructor::CreateFloatingLetterFrame(
10028 nsFrameConstructorState& aState, Text* aTextContent, nsIFrame* aTextFrame,
10029 nsContainerFrame* aParentFrame, ComputedStyle* aParentStyle,
10030 ComputedStyle* aComputedStyle, nsFrameList& aResult) {
10031 MOZ_ASSERT(aParentStyle);
10033 nsFirstLetterFrame* letterFrame =
10034 NS_NewFirstLetterFrame(mPresShell, aComputedStyle);
10035 // We don't want to use a text content for a non-text frame (because we want
10036 // its primary frame to be a text frame).
10037 nsIContent* letterContent = aParentFrame->GetContent();
10038 nsContainerFrame* containingBlock =
10039 aState.GetGeometricParent(*aComputedStyle->StyleDisplay(), aParentFrame);
10040 InitAndRestoreFrame(aState, letterContent, containingBlock, letterFrame);
10042 // Init the text frame to refer to the letter frame.
10044 // Make sure we get a proper style for it (the one passed in is for the letter
10045 // frame and will have the float property set on it; the text frame shouldn't
10046 // have that set).
10047 ServoStyleSet* styleSet = mPresShell->StyleSet();
10048 RefPtr<ComputedStyle> textSC =
10049 styleSet->ResolveStyleForText(aTextContent, aComputedStyle);
10050 aTextFrame->SetComputedStyleWithoutNotification(textSC);
10051 InitAndRestoreFrame(aState, aTextContent, letterFrame, aTextFrame);
10053 // And then give the text frame to the letter frame
10054 SetInitialSingleChild(letterFrame, aTextFrame);
10056 // See if we will need to continue the text frame (does it contain
10057 // more than just the first-letter text or not?) If it does, then we
10058 // create (in advance) a continuation frame for it.
10059 nsIFrame* nextTextFrame = nullptr;
10060 if (NeedFirstLetterContinuation(aTextContent)) {
10061 // Create continuation
10062 nextTextFrame = CreateContinuingFrame(aTextFrame, aParentFrame);
10063 RefPtr<ComputedStyle> newSC =
10064 styleSet->ResolveStyleForText(aTextContent, aParentStyle);
10065 nextTextFrame->SetComputedStyle(newSC);
10068 NS_ASSERTION(aResult.IsEmpty(), "aResult should be an empty nsFrameList!");
10069 // Put the new float before any of the floats in the block we're doing
10070 // first-letter for, that is, before any floats whose parent is
10071 // containingBlock.
10072 nsIFrame* prevSibling = nullptr;
10073 for (nsIFrame* f : aState.mFloatedList) {
10074 if (f->GetParent() == containingBlock) {
10075 break;
10077 prevSibling = f;
10080 aState.AddChild(letterFrame, aResult, letterContent, aParentFrame, false,
10081 true, true, prevSibling);
10083 if (nextTextFrame) {
10084 aResult.AppendFrame(nullptr, nextTextFrame);
10087 return letterFrame;
10091 * Create a new letter frame for aTextFrame. The letter frame will be
10092 * a child of aParentFrame.
10094 void nsCSSFrameConstructor::CreateLetterFrame(
10095 nsContainerFrame* aBlockFrame, nsContainerFrame* aBlockContinuation,
10096 Text* aTextContent, nsContainerFrame* aParentFrame, nsFrameList& aResult) {
10097 NS_ASSERTION(aBlockFrame->IsBlockFrameOrSubclass(), "Not a block frame?");
10099 // Get a ComputedStyle for the first-letter-frame.
10101 // Keep this in sync with nsBlockFrame::UpdatePseudoElementStyles.
10102 nsIFrame* parentFrame = nsIFrame::CorrectStyleParentFrame(
10103 aParentFrame, PseudoStyleType::firstLetter);
10105 ComputedStyle* parentComputedStyle = parentFrame->Style();
10107 // Use content from containing block so that we can actually
10108 // find a matching style rule.
10109 nsIContent* blockContent = aBlockFrame->GetContent();
10111 // Create first-letter style rule
10112 RefPtr<ComputedStyle> sc =
10113 GetFirstLetterStyle(blockContent, parentComputedStyle);
10115 if (sc) {
10116 if (parentFrame->IsLineFrame()) {
10117 nsIFrame* parentIgnoringFirstLine = nsIFrame::CorrectStyleParentFrame(
10118 aBlockFrame, PseudoStyleType::firstLetter);
10120 sc = mPresShell->StyleSet()->ReparentComputedStyle(
10121 sc, parentComputedStyle, parentIgnoringFirstLine->Style(),
10122 parentComputedStyle, blockContent->AsElement());
10125 RefPtr<ComputedStyle> textSC =
10126 mPresShell->StyleSet()->ResolveStyleForText(aTextContent, sc);
10128 // Create a new text frame (the original one will be discarded)
10129 // pass a temporary stylecontext, the correct one will be set
10130 // later. Start off by unsetting the primary frame for
10131 // aTextContent, so it's no longer pointing to the to-be-destroyed
10132 // frame.
10133 // XXXbz it would be really nice to destroy the old frame _first_,
10134 // then create the new one, so we could avoid this hack.
10135 aTextContent->SetPrimaryFrame(nullptr);
10136 nsIFrame* textFrame = NS_NewTextFrame(mPresShell, textSC);
10138 NS_ASSERTION(aBlockContinuation == GetFloatContainingBlock(aParentFrame),
10139 "Containing block is confused");
10140 nsFrameConstructorState state(
10141 mPresShell, GetAbsoluteContainingBlock(aParentFrame, FIXED_POS),
10142 GetAbsoluteContainingBlock(aParentFrame, ABS_POS), aBlockContinuation);
10144 // Create the right type of first-letter frame
10145 const nsStyleDisplay* display = sc->StyleDisplay();
10146 nsFirstLetterFrame* letterFrame;
10147 if (display->IsFloatingStyle() && !aParentFrame->IsInSVGTextSubtree()) {
10148 // Make a floating first-letter frame
10149 letterFrame = CreateFloatingLetterFrame(state, aTextContent, textFrame,
10150 aParentFrame, parentComputedStyle,
10151 sc, aResult);
10152 } else {
10153 // Make an inflow first-letter frame
10154 letterFrame = NS_NewFirstLetterFrame(mPresShell, sc);
10156 // Initialize the first-letter-frame. We don't want to use a text
10157 // content for a non-text frame (because we want its primary frame to
10158 // be a text frame).
10159 nsIContent* letterContent = aParentFrame->GetContent();
10160 letterFrame->Init(letterContent, aParentFrame, nullptr);
10162 InitAndRestoreFrame(state, aTextContent, letterFrame, textFrame);
10164 SetInitialSingleChild(letterFrame, textFrame);
10165 aResult.Clear();
10166 aResult.AppendFrame(nullptr, letterFrame);
10167 NS_ASSERTION(!aBlockFrame->GetPrevContinuation(),
10168 "should have the first continuation here");
10169 aBlockFrame->AddStateBits(NS_BLOCK_HAS_FIRST_LETTER_CHILD);
10171 MOZ_ASSERT(
10172 !aBlockFrame->GetPrevContinuation(),
10173 "Setting up a first-letter frame on a non-first block continuation?");
10174 auto parent =
10175 static_cast<nsContainerFrame*>(aParentFrame->FirstContinuation());
10176 if (MOZ_UNLIKELY(parent->IsLineFrame())) {
10177 parent = static_cast<nsContainerFrame*>(
10178 parent->GetParent()->FirstContinuation());
10180 parent->SetHasFirstLetterChild();
10181 aBlockFrame->SetProperty(nsContainerFrame::FirstLetterProperty(),
10182 letterFrame);
10183 aTextContent->SetPrimaryFrame(textFrame);
10187 void nsCSSFrameConstructor::WrapFramesInFirstLetterFrame(
10188 nsContainerFrame* aBlockFrame, nsFrameList& aBlockFrames) {
10189 aBlockFrame->AddStateBits(NS_BLOCK_HAS_FIRST_LETTER_STYLE);
10191 nsContainerFrame* parentFrame = nullptr;
10192 nsIFrame* textFrame = nullptr;
10193 nsIFrame* prevFrame = nullptr;
10194 nsFrameList letterFrames;
10195 bool stopLooking = false;
10196 WrapFramesInFirstLetterFrame(
10197 aBlockFrame, aBlockFrame, aBlockFrame, aBlockFrames.FirstChild(),
10198 &parentFrame, &textFrame, &prevFrame, letterFrames, &stopLooking);
10199 if (parentFrame) {
10200 if (parentFrame == aBlockFrame) {
10201 // Take textFrame out of the block's frame list and substitute the
10202 // letter frame(s) instead.
10203 aBlockFrames.DestroyFrame(textFrame);
10204 aBlockFrames.InsertFrames(nullptr, prevFrame, std::move(letterFrames));
10205 } else {
10206 // Take the old textFrame out of the inline parent's child list
10207 RemoveFrame(FrameChildListID::Principal, textFrame);
10209 // Insert in the letter frame(s)
10210 parentFrame->InsertFrames(FrameChildListID::Principal, prevFrame, nullptr,
10211 std::move(letterFrames));
10216 void nsCSSFrameConstructor::WrapFramesInFirstLetterFrame(
10217 nsContainerFrame* aBlockFrame, nsContainerFrame* aBlockContinuation,
10218 nsContainerFrame* aParentFrame, nsIFrame* aParentFrameList,
10219 nsContainerFrame** aModifiedParent, nsIFrame** aTextFrame,
10220 nsIFrame** aPrevFrame, nsFrameList& aLetterFrames, bool* aStopLooking) {
10221 nsIFrame* prevFrame = nullptr;
10222 nsIFrame* frame = aParentFrameList;
10224 // This loop attempts to implement "Finding the First Letter":
10225 // https://drafts.csswg.org/css-pseudo-4/#application-in-css
10226 // FIXME: we don't handle nested blocks correctly yet though (bug 214004)
10227 while (frame) {
10228 nsIFrame* nextFrame = frame->GetNextSibling();
10230 // Skip all ::markers and placeholders.
10231 if (frame->Style()->GetPseudoType() == PseudoStyleType::marker ||
10232 frame->IsPlaceholderFrame()) {
10233 prevFrame = frame;
10234 frame = nextFrame;
10235 continue;
10237 LayoutFrameType frameType = frame->Type();
10238 if (LayoutFrameType::Text == frameType) {
10239 // Wrap up first-letter content in a letter frame
10240 Text* textContent = frame->GetContent()->AsText();
10241 if (IsFirstLetterContent(textContent)) {
10242 // Create letter frame to wrap up the text
10243 CreateLetterFrame(aBlockFrame, aBlockContinuation, textContent,
10244 aParentFrame, aLetterFrames);
10246 // Provide adjustment information for parent
10247 *aModifiedParent = aParentFrame;
10248 *aTextFrame = frame;
10249 *aPrevFrame = prevFrame;
10250 *aStopLooking = true;
10251 return;
10253 } else if (IsInlineFrame(frame) && frameType != LayoutFrameType::Br) {
10254 nsIFrame* kids = frame->PrincipalChildList().FirstChild();
10255 WrapFramesInFirstLetterFrame(aBlockFrame, aBlockContinuation,
10256 static_cast<nsContainerFrame*>(frame), kids,
10257 aModifiedParent, aTextFrame, aPrevFrame,
10258 aLetterFrames, aStopLooking);
10259 if (*aStopLooking) {
10260 return;
10262 } else {
10263 // This will stop us looking to create more letter frames. For
10264 // example, maybe the frame-type is "letterFrame" or
10265 // "placeholderFrame". This keeps us from creating extra letter
10266 // frames, and also prevents us from creating letter frames when
10267 // the first real content child of a block is not text (e.g. an
10268 // image, hr, etc.)
10269 *aStopLooking = true;
10270 break;
10273 prevFrame = frame;
10274 frame = nextFrame;
10278 static nsIFrame* FindFirstLetterFrame(nsIFrame* aFrame,
10279 FrameChildListID aListID) {
10280 for (nsIFrame* f : aFrame->GetChildList(aListID)) {
10281 if (f->IsLetterFrame()) {
10282 return f;
10285 return nullptr;
10288 static void ClearHasFirstLetterChildFrom(nsContainerFrame* aParentFrame) {
10289 MOZ_ASSERT(aParentFrame);
10290 auto* parent =
10291 static_cast<nsContainerFrame*>(aParentFrame->FirstContinuation());
10292 if (MOZ_UNLIKELY(parent->IsLineFrame())) {
10293 MOZ_ASSERT(!parent->HasFirstLetterChild());
10294 parent = static_cast<nsContainerFrame*>(
10295 parent->GetParent()->FirstContinuation());
10297 MOZ_ASSERT(parent->HasFirstLetterChild());
10298 parent->ClearHasFirstLetterChild();
10301 void nsCSSFrameConstructor::RemoveFloatingFirstLetterFrames(
10302 PresShell* aPresShell, nsIFrame* aBlockFrame) {
10303 // Look for the first letter frame on the FrameChildListID::Float, then
10304 // FrameChildListID::PushedFloats.
10305 nsIFrame* floatFrame =
10306 ::FindFirstLetterFrame(aBlockFrame, FrameChildListID::Float);
10307 if (!floatFrame) {
10308 floatFrame =
10309 ::FindFirstLetterFrame(aBlockFrame, FrameChildListID::PushedFloats);
10310 if (!floatFrame) {
10311 return;
10315 // Take the text frame away from the letter frame (so it isn't
10316 // destroyed when we destroy the letter frame).
10317 nsIFrame* textFrame = floatFrame->PrincipalChildList().FirstChild();
10318 if (!textFrame) {
10319 return;
10322 // Discover the placeholder frame for the letter frame
10323 nsPlaceholderFrame* placeholderFrame = floatFrame->GetPlaceholderFrame();
10324 if (!placeholderFrame) {
10325 // Somethings really wrong
10326 return;
10328 nsContainerFrame* parentFrame = placeholderFrame->GetParent();
10329 if (!parentFrame) {
10330 // Somethings really wrong
10331 return;
10334 ClearHasFirstLetterChildFrom(parentFrame);
10336 // Create a new text frame with the right style that maps all of the content
10337 // that was previously part of the letter frame (and probably continued
10338 // elsewhere).
10339 ComputedStyle* parentSC = parentFrame->Style();
10340 nsIContent* textContent = textFrame->GetContent();
10341 if (!textContent) {
10342 return;
10344 RefPtr<ComputedStyle> newSC =
10345 aPresShell->StyleSet()->ResolveStyleForText(textContent, parentSC);
10346 nsIFrame* newTextFrame = NS_NewTextFrame(aPresShell, newSC);
10347 newTextFrame->Init(textContent, parentFrame, nullptr);
10349 // Destroy the old text frame's continuations (the old text frame
10350 // will be destroyed when its letter frame is destroyed).
10351 nsIFrame* frameToDelete = textFrame->LastContinuation();
10352 while (frameToDelete != textFrame) {
10353 nsIFrame* nextFrameToDelete = frameToDelete->GetPrevContinuation();
10354 RemoveFrame(FrameChildListID::Principal, frameToDelete);
10355 frameToDelete = nextFrameToDelete;
10358 nsIFrame* prevSibling = placeholderFrame->GetPrevSibling();
10360 // Now that everything is set...
10361 #ifdef NOISY_FIRST_LETTER
10362 printf(
10363 "RemoveFloatingFirstLetterFrames: textContent=%p oldTextFrame=%p "
10364 "newTextFrame=%p\n",
10365 textContent.get(), textFrame, newTextFrame);
10366 #endif
10368 // Remove placeholder frame and the float
10369 RemoveFrame(FrameChildListID::Principal, placeholderFrame);
10371 // Now that the old frames are gone, we can start pointing to our
10372 // new primary frame.
10373 textContent->SetPrimaryFrame(newTextFrame);
10375 // Wallpaper bug 822910.
10376 bool offsetsNeedFixing = prevSibling && prevSibling->IsTextFrame();
10377 if (offsetsNeedFixing) {
10378 prevSibling->AddStateBits(TEXT_OFFSETS_NEED_FIXING);
10381 // Insert text frame in its place
10382 InsertFrames(parentFrame, FrameChildListID::Principal, prevSibling,
10383 nsFrameList(newTextFrame, newTextFrame));
10385 if (offsetsNeedFixing) {
10386 prevSibling->RemoveStateBits(TEXT_OFFSETS_NEED_FIXING);
10390 void nsCSSFrameConstructor::RemoveFirstLetterFrames(
10391 PresShell* aPresShell, nsContainerFrame* aFrame,
10392 nsContainerFrame* aBlockFrame, bool* aStopLooking) {
10393 nsIFrame* prevSibling = nullptr;
10394 nsIFrame* kid = aFrame->PrincipalChildList().FirstChild();
10396 while (kid) {
10397 if (kid->IsLetterFrame()) {
10398 ClearHasFirstLetterChildFrom(aFrame);
10399 nsIFrame* textFrame = kid->PrincipalChildList().FirstChild();
10400 if (!textFrame) {
10401 break;
10404 // Create a new textframe
10405 ComputedStyle* parentSC = aFrame->Style();
10406 if (!parentSC) {
10407 break;
10409 nsIContent* textContent = textFrame->GetContent();
10410 if (!textContent) {
10411 break;
10413 RefPtr<ComputedStyle> newSC =
10414 aPresShell->StyleSet()->ResolveStyleForText(textContent, parentSC);
10415 textFrame = NS_NewTextFrame(aPresShell, newSC);
10416 textFrame->Init(textContent, aFrame, nullptr);
10418 // Next rip out the kid and replace it with the text frame
10419 RemoveFrame(FrameChildListID::Principal, kid);
10421 // Now that the old frames are gone, we can start pointing to our
10422 // new primary frame.
10423 textContent->SetPrimaryFrame(textFrame);
10425 // Wallpaper bug 822910.
10426 bool offsetsNeedFixing = prevSibling && prevSibling->IsTextFrame();
10427 if (offsetsNeedFixing) {
10428 prevSibling->AddStateBits(TEXT_OFFSETS_NEED_FIXING);
10431 // Insert text frame in its place
10432 InsertFrames(aFrame, FrameChildListID::Principal, prevSibling,
10433 nsFrameList(textFrame, textFrame));
10435 if (offsetsNeedFixing) {
10436 prevSibling->RemoveStateBits(TEXT_OFFSETS_NEED_FIXING);
10439 *aStopLooking = true;
10440 NS_ASSERTION(!aBlockFrame->GetPrevContinuation(),
10441 "should have the first continuation here");
10442 aBlockFrame->RemoveStateBits(NS_BLOCK_HAS_FIRST_LETTER_CHILD);
10443 break;
10444 } else if (IsInlineFrame(kid)) {
10445 nsContainerFrame* kidAsContainerFrame = do_QueryFrame(kid);
10446 if (kidAsContainerFrame) {
10447 // Look inside child inline frame for the letter frame.
10448 RemoveFirstLetterFrames(aPresShell, kidAsContainerFrame, aBlockFrame,
10449 aStopLooking);
10450 if (*aStopLooking) {
10451 break;
10455 prevSibling = kid;
10456 kid = kid->GetNextSibling();
10460 void nsCSSFrameConstructor::RemoveLetterFrames(PresShell* aPresShell,
10461 nsContainerFrame* aBlockFrame) {
10462 aBlockFrame =
10463 static_cast<nsContainerFrame*>(aBlockFrame->FirstContinuation());
10464 aBlockFrame->RemoveProperty(nsContainerFrame::FirstLetterProperty());
10465 nsContainerFrame* continuation = aBlockFrame;
10467 bool stopLooking = false;
10468 do {
10469 RemoveFloatingFirstLetterFrames(aPresShell, continuation);
10470 RemoveFirstLetterFrames(aPresShell, continuation, aBlockFrame,
10471 &stopLooking);
10472 if (stopLooking) {
10473 break;
10475 continuation =
10476 static_cast<nsContainerFrame*>(continuation->GetNextContinuation());
10477 } while (continuation);
10480 // Fixup the letter frame situation for the given block
10481 void nsCSSFrameConstructor::RecoverLetterFrames(nsContainerFrame* aBlockFrame) {
10482 aBlockFrame =
10483 static_cast<nsContainerFrame*>(aBlockFrame->FirstContinuation());
10484 nsContainerFrame* continuation = aBlockFrame;
10486 nsContainerFrame* parentFrame = nullptr;
10487 nsIFrame* textFrame = nullptr;
10488 nsIFrame* prevFrame = nullptr;
10489 nsFrameList letterFrames;
10490 bool stopLooking = false;
10491 do {
10492 // XXX shouldn't this bit be set already (bug 408493), assert instead?
10493 continuation->AddStateBits(NS_BLOCK_HAS_FIRST_LETTER_STYLE);
10494 WrapFramesInFirstLetterFrame(
10495 aBlockFrame, continuation, continuation,
10496 continuation->PrincipalChildList().FirstChild(), &parentFrame,
10497 &textFrame, &prevFrame, letterFrames, &stopLooking);
10498 if (stopLooking) {
10499 break;
10501 continuation =
10502 static_cast<nsContainerFrame*>(continuation->GetNextContinuation());
10503 } while (continuation);
10505 if (parentFrame) {
10506 // Take the old textFrame out of the parent's child list
10507 RemoveFrame(FrameChildListID::Principal, textFrame);
10509 // Insert in the letter frame(s)
10510 parentFrame->InsertFrames(FrameChildListID::Principal, prevFrame, nullptr,
10511 std::move(letterFrames));
10515 //----------------------------------------------------------------------
10517 void nsCSSFrameConstructor::ConstructBlock(
10518 nsFrameConstructorState& aState, nsIContent* aContent,
10519 nsContainerFrame* aParentFrame, nsContainerFrame* aContentParentFrame,
10520 ComputedStyle* aComputedStyle, nsContainerFrame** aNewFrame,
10521 nsFrameList& aFrameList, nsIFrame* aPositionedFrameForAbsPosContainer) {
10522 // clang-format off
10524 // If a block frame is in a multi-column subtree, its children may need to
10525 // be chopped into runs of blocks containing column-spans and runs of
10526 // blocks containing no column-spans. Each run containing column-spans
10527 // will be wrapped by an anonymous block. See CreateColumnSpanSiblings() for
10528 // the implementation.
10530 // If a block frame is a multi-column container, its children will need to
10531 // be processed as above. Moreover, it creates a ColumnSetWrapperFrame as
10532 // its outermost frame, and its children which have no
10533 // -moz-column-span-wrapper pseudo will be wrapped in ColumnSetFrames. See
10534 // FinishBuildingColumns() for the implementation.
10536 // The multi-column subtree maintains the following invariants:
10538 // 1) All the frames have the frame state bit
10539 // NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR set, except for top-level
10540 // ColumnSetWrapperFrame and those children in the column-span subtrees.
10542 // 2) The first and last frame under ColumnSetWrapperFrame are always
10543 // ColumnSetFrame.
10545 // 3) ColumnSetFrames are linked together as continuations.
10547 // 4) Those column-span wrappers are *not* linked together with themselves nor
10548 // with the original block frame. The continuation chain consists of the
10549 // original block frame and the original block's continuations wrapping
10550 // non-column-spans.
10552 // For example, this HTML
10553 // <div id="x" style="column-count: 2;">
10554 // <div style="column-span: all">a</div>
10555 // <div id="y">
10556 // b
10557 // <div style="column-span: all">c</div>
10558 // <div style="column-span: all">d</div>
10559 // e
10560 // </div>
10561 // </div>
10562 // <div style="column-span: all">f</div>
10564 // yields the following frame tree.
10566 // A) ColumnSetWrapper (original style)
10567 // B) ColumnSet (-moz-column-set) <-- always created by BeginBuildingColumns
10568 // C) Block (-moz-column-content)
10569 // D) Block (-moz-column-span-wrapper, created by x)
10570 // E) Block (div)
10571 // F) Text ("a")
10572 // G) ColumnSet (-moz-column-set)
10573 // H) Block (-moz-column-content, created by x)
10574 // I) Block (div, y)
10575 // J) Text ("b")
10576 // K) Block (-moz-column-span-wrapper, created by x)
10577 // L) Block (-moz-column-span-wrapper, created by y)
10578 // M) Block (div, new BFC)
10579 // N) Text ("c")
10580 // O) Block (div, new BFC)
10581 // P) Text ("d")
10582 // Q) ColumnSet (-moz-column-set)
10583 // R) Block (-moz-column-content, created by x)
10584 // S) Block (div, y)
10585 // T) Text ("e")
10586 // U) Block (div, new BFC) <-- not in multi-column hierarchy
10587 // V) Text ("f")
10589 // ColumnSet linkage described in 3): B -> G -> Q
10591 // Block linkage described in 4): C -> H -> R and I -> S
10593 // clang-format on
10595 nsBlockFrame* blockFrame = do_QueryFrame(*aNewFrame);
10596 MOZ_ASSERT(blockFrame && blockFrame->IsBlockFrame(), "not a block frame?");
10598 // Create column hierarchy if necessary.
10599 const bool needsColumn =
10600 aComputedStyle->StyleColumn()->IsColumnContainerStyle();
10601 if (needsColumn) {
10602 *aNewFrame = BeginBuildingColumns(aState, aContent, aParentFrame,
10603 blockFrame, aComputedStyle);
10605 if (aPositionedFrameForAbsPosContainer == blockFrame) {
10606 aPositionedFrameForAbsPosContainer = *aNewFrame;
10608 } else {
10609 // No need to create column hierarchy. Initialize block frame.
10610 blockFrame->SetComputedStyleWithoutNotification(aComputedStyle);
10611 InitAndRestoreFrame(aState, aContent, aParentFrame, blockFrame);
10614 aState.AddChild(*aNewFrame, aFrameList, aContent,
10615 aContentParentFrame ? aContentParentFrame : aParentFrame);
10616 if (!mRootElementFrame) {
10617 // The frame we're constructing will be the root element frame.
10618 SetRootElementFrameAndConstructCanvasAnonContent(*aNewFrame, aState,
10619 aFrameList);
10622 // We should make the outer frame be the absolute containing block,
10623 // if one is required. We have to do this because absolute
10624 // positioning must be computed with respect to the CSS dimensions
10625 // of the element, which are the dimensions of the outer block. But
10626 // we can't really do that because only blocks can have absolute
10627 // children. So use the block and try to compensate with hacks
10628 // in nsBlockFrame::CalculateContainingBlockSizeForAbsolutes.
10629 nsFrameConstructorSaveState absoluteSaveState;
10630 (*aNewFrame)->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
10631 if (aPositionedFrameForAbsPosContainer) {
10632 aState.PushAbsoluteContainingBlock(
10633 *aNewFrame, aPositionedFrameForAbsPosContainer, absoluteSaveState);
10636 nsFrameConstructorSaveState floatSaveState;
10637 aState.MaybePushFloatContainingBlock(blockFrame, floatSaveState);
10639 if (aParentFrame->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR) &&
10640 !ShouldSuppressColumnSpanDescendants(aParentFrame)) {
10641 blockFrame->AddStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR);
10644 // Process the child content
10645 nsFrameList childList;
10646 ProcessChildren(aState, aContent, aComputedStyle, blockFrame, true, childList,
10647 true);
10649 if (!MayNeedToCreateColumnSpanSiblings(blockFrame, childList)) {
10650 // No need to create column-span siblings.
10651 blockFrame->SetInitialChildList(FrameChildListID::Principal,
10652 std::move(childList));
10653 return;
10656 // Extract any initial non-column-span kids, and put them in block frame's
10657 // child list.
10658 nsFrameList initialNonColumnSpanKids =
10659 childList.Split([](nsIFrame* f) { return f->IsColumnSpan(); });
10660 blockFrame->SetInitialChildList(FrameChildListID::Principal,
10661 std::move(initialNonColumnSpanKids));
10663 if (childList.IsEmpty()) {
10664 // No more kids to process (there weren't any column-span kids).
10665 return;
10668 nsFrameList columnSpanSiblings = CreateColumnSpanSiblings(
10669 aState, blockFrame, childList,
10670 // If we're constructing a column container, pass nullptr as
10671 // aPositionedFrame to forbid reparenting absolute/fixed positioned frames
10672 // to column contents or column-span wrappers.
10673 needsColumn ? nullptr : aPositionedFrameForAbsPosContainer);
10675 if (needsColumn) {
10676 // We're constructing a column container; need to finish building it.
10677 FinishBuildingColumns(aState, *aNewFrame, blockFrame, columnSpanSiblings);
10678 } else {
10679 // We're constructing a normal block which has column-span children in a
10680 // column hierarchy such as "x" in the following example.
10682 // <div style="column-count: 2">
10683 // <div id="x">
10684 // <div>normal child</div>
10685 // <div style="column-span">spanner</div>
10686 // </div>
10687 // </div>
10688 aFrameList.AppendFrames(nullptr, std::move(columnSpanSiblings));
10691 MOZ_ASSERT(columnSpanSiblings.IsEmpty(),
10692 "The column-span siblings should be moved to the proper place!");
10695 nsBlockFrame* nsCSSFrameConstructor::BeginBuildingColumns(
10696 nsFrameConstructorState& aState, nsIContent* aContent,
10697 nsContainerFrame* aParentFrame, nsContainerFrame* aColumnContent,
10698 ComputedStyle* aComputedStyle) {
10699 MOZ_ASSERT(aColumnContent->IsBlockFrame(),
10700 "aColumnContent should be a block frame.");
10701 MOZ_ASSERT(aComputedStyle->StyleColumn()->IsColumnContainerStyle(),
10702 "No need to build a column hierarchy!");
10704 // The initial column hierarchy looks like this:
10706 // ColumnSetWrapper (original style)
10707 // ColumnSet (-moz-column-set)
10708 // Block (-moz-column-content)
10710 nsBlockFrame* columnSetWrapper = NS_NewColumnSetWrapperFrame(
10711 mPresShell, aComputedStyle, nsFrameState(NS_FRAME_OWNS_ANON_BOXES));
10712 InitAndRestoreFrame(aState, aContent, aParentFrame, columnSetWrapper);
10713 if (aParentFrame->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR) &&
10714 !ShouldSuppressColumnSpanDescendants(aParentFrame)) {
10715 columnSetWrapper->AddStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR);
10718 AutoFrameConstructionPageName pageNameTracker(aState, columnSetWrapper);
10719 RefPtr<ComputedStyle> columnSetStyle =
10720 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
10721 PseudoStyleType::columnSet, aComputedStyle);
10722 nsContainerFrame* columnSet = NS_NewColumnSetFrame(
10723 mPresShell, columnSetStyle, nsFrameState(NS_FRAME_OWNS_ANON_BOXES));
10724 InitAndRestoreFrame(aState, aContent, columnSetWrapper, columnSet);
10725 columnSet->AddStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR);
10727 RefPtr<ComputedStyle> blockStyle =
10728 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
10729 PseudoStyleType::columnContent, columnSetStyle);
10730 aColumnContent->SetComputedStyleWithoutNotification(blockStyle);
10731 InitAndRestoreFrame(aState, aContent, columnSet, aColumnContent);
10732 aColumnContent->AddStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR |
10733 NS_BLOCK_FORMATTING_CONTEXT_STATE_BITS);
10735 // Set up the parent-child chain.
10736 SetInitialSingleChild(columnSetWrapper, columnSet);
10737 SetInitialSingleChild(columnSet, aColumnContent);
10739 return columnSetWrapper;
10742 void nsCSSFrameConstructor::FinishBuildingColumns(
10743 nsFrameConstructorState& aState, nsContainerFrame* aColumnSetWrapper,
10744 nsContainerFrame* aColumnContent, nsFrameList& aColumnContentSiblings) {
10745 nsContainerFrame* prevColumnSet = aColumnContent->GetParent();
10747 MOZ_ASSERT(prevColumnSet->IsColumnSetFrame() &&
10748 prevColumnSet->GetParent() == aColumnSetWrapper,
10749 "Should have established column hierarchy!");
10751 // Tag the first ColumnSet to have column-span siblings so that the bit can
10752 // propagate to all the continuations. We don't want the last ColumnSet to
10753 // have this bit, so we will unset the bit for it at the end of this function.
10754 prevColumnSet->SetHasColumnSpanSiblings(true);
10756 nsFrameList finalList;
10757 while (aColumnContentSiblings.NotEmpty()) {
10758 nsIFrame* f = aColumnContentSiblings.RemoveFirstChild();
10759 if (f->IsColumnSpan()) {
10760 // Do nothing for column-span wrappers. Just move it to the final
10761 // items.
10762 finalList.AppendFrame(aColumnSetWrapper, f);
10763 } else {
10764 auto* continuingColumnSet = static_cast<nsContainerFrame*>(
10765 CreateContinuingFrame(prevColumnSet, aColumnSetWrapper, false));
10766 MOZ_ASSERT(continuingColumnSet->HasColumnSpanSiblings(),
10767 "The bit should propagate to the next continuation!");
10769 f->SetParent(continuingColumnSet);
10770 SetInitialSingleChild(continuingColumnSet, f);
10771 finalList.AppendFrame(aColumnSetWrapper, continuingColumnSet);
10772 prevColumnSet = continuingColumnSet;
10776 // Unset the bit because the last ColumnSet has no column-span siblings.
10777 prevColumnSet->SetHasColumnSpanSiblings(false);
10779 aColumnSetWrapper->AppendFrames(FrameChildListID::Principal,
10780 std::move(finalList));
10783 bool nsCSSFrameConstructor::MayNeedToCreateColumnSpanSiblings(
10784 nsContainerFrame* aBlockFrame, const nsFrameList& aChildList) {
10785 if (!aBlockFrame->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR)) {
10786 // The block frame isn't in a multi-column block formatting context.
10787 return false;
10790 if (ShouldSuppressColumnSpanDescendants(aBlockFrame)) {
10791 // No need to create column-span siblings for a frame that suppresses them.
10792 return false;
10795 if (aChildList.IsEmpty()) {
10796 // No child needs to be processed.
10797 return false;
10800 // Need to actually look into the child list.
10801 return true;
10804 nsFrameList nsCSSFrameConstructor::CreateColumnSpanSiblings(
10805 nsFrameConstructorState& aState, nsContainerFrame* aInitialBlock,
10806 nsFrameList& aChildList, nsIFrame* aPositionedFrame) {
10807 MOZ_ASSERT(aInitialBlock->IsBlockFrameOrSubclass());
10808 MOZ_ASSERT(!aPositionedFrame || aPositionedFrame->IsAbsPosContainingBlock());
10810 nsIContent* const content = aInitialBlock->GetContent();
10811 nsContainerFrame* const parentFrame = aInitialBlock->GetParent();
10812 const bool isInitialBlockFloatCB = aInitialBlock->IsFloatContainingBlock();
10814 nsFrameList siblings;
10815 nsContainerFrame* lastNonColumnSpanWrapper = aInitialBlock;
10817 // Tag the first non-column-span wrapper to have column-span siblings so that
10818 // the bit can propagate to all the continuations. We don't want the last
10819 // wrapper to have this bit, so we will unset the bit for it at the end of
10820 // this function.
10821 lastNonColumnSpanWrapper->SetHasColumnSpanSiblings(true);
10822 do {
10823 MOZ_ASSERT(aChildList.NotEmpty(), "Why call this if child list is empty?");
10824 MOZ_ASSERT(aChildList.FirstChild()->IsColumnSpan(),
10825 "Must have the child starting with column-span!");
10827 // Grab the consecutive column-span kids, and reparent them into a
10828 // block frame.
10829 RefPtr<ComputedStyle> columnSpanWrapperStyle =
10830 mPresShell->StyleSet()->ResolveNonInheritingAnonymousBoxStyle(
10831 PseudoStyleType::columnSpanWrapper);
10832 nsBlockFrame* columnSpanWrapper =
10833 NS_NewBlockFrame(mPresShell, columnSpanWrapperStyle);
10834 InitAndRestoreFrame(aState, content, parentFrame, columnSpanWrapper, false);
10835 columnSpanWrapper->AddStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR |
10836 NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
10838 nsFrameList columnSpanKids =
10839 aChildList.Split([](nsIFrame* f) { return !f->IsColumnSpan(); });
10840 columnSpanKids.ApplySetParent(columnSpanWrapper);
10841 columnSpanWrapper->SetInitialChildList(FrameChildListID::Principal,
10842 std::move(columnSpanKids));
10843 if (aPositionedFrame) {
10844 aState.ReparentAbsoluteItems(columnSpanWrapper);
10847 siblings.AppendFrame(nullptr, columnSpanWrapper);
10849 // Grab the consecutive non-column-span kids, and reparent them into a new
10850 // continuation of the last non-column-span wrapper frame.
10851 auto* nonColumnSpanWrapper = static_cast<nsContainerFrame*>(
10852 CreateContinuingFrame(lastNonColumnSpanWrapper, parentFrame, false));
10853 nonColumnSpanWrapper->AddStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR |
10854 NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
10855 MOZ_ASSERT(nonColumnSpanWrapper->HasColumnSpanSiblings(),
10856 "The bit should propagate to the next continuation!");
10858 if (aChildList.NotEmpty()) {
10859 nsFrameList nonColumnSpanKids =
10860 aChildList.Split([](nsIFrame* f) { return f->IsColumnSpan(); });
10862 nonColumnSpanKids.ApplySetParent(nonColumnSpanWrapper);
10863 nonColumnSpanWrapper->SetInitialChildList(FrameChildListID::Principal,
10864 std::move(nonColumnSpanKids));
10865 if (aPositionedFrame) {
10866 aState.ReparentAbsoluteItems(nonColumnSpanWrapper);
10868 if (isInitialBlockFloatCB) {
10869 aState.ReparentFloats(nonColumnSpanWrapper);
10873 siblings.AppendFrame(nullptr, nonColumnSpanWrapper);
10875 lastNonColumnSpanWrapper = nonColumnSpanWrapper;
10876 } while (aChildList.NotEmpty());
10878 // Unset the bit because the last non-column-span wrapper has no column-span
10879 // siblings.
10880 lastNonColumnSpanWrapper->SetHasColumnSpanSiblings(false);
10882 return siblings;
10885 bool nsCSSFrameConstructor::MaybeRecreateForColumnSpan(
10886 nsFrameConstructorState& aState, nsContainerFrame* aParentFrame,
10887 nsFrameList& aFrameList, nsIFrame* aPrevSibling) {
10888 if (!aParentFrame->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR)) {
10889 return false;
10892 if (aFrameList.IsEmpty()) {
10893 return false;
10896 MOZ_ASSERT(!IsFramePartOfIBSplit(aParentFrame),
10897 "We should have wiped aParentFrame in WipeContainingBlock if it's "
10898 "part of IB split!");
10900 nsIFrame* nextSibling = ::GetInsertNextSibling(aParentFrame, aPrevSibling);
10901 if (!nextSibling && IsLastContinuationForColumnContent(aParentFrame)) {
10902 // We are appending a list of frames to the last continuation of a
10903 // ::-moz-column-content. This is the case where we can fix the frame tree
10904 // instead of reframing the containing block. Return false and let
10905 // AppendFramesToParent() deal with this.
10906 return false;
10909 auto HasColumnSpan = [](const nsFrameList& aList) {
10910 for (nsIFrame* f : aList) {
10911 if (f->IsColumnSpan()) {
10912 return true;
10915 return false;
10918 if (HasColumnSpan(aFrameList)) {
10919 // If any frame in the frame list has "column-span:all" style, i.e. a
10920 // -moz-column-span-wrapper frame, we need to reframe the multi-column
10921 // containing block.
10923 // We can only be here if none of the new inserted nsIContent* nodes (via
10924 // ContentAppended or ContentRangeInserted) have column-span:all style, yet
10925 // some of them have column-span:all descendants. Sadly, there's no way to
10926 // detect this by checking FrameConstructionItems in WipeContainingBlock().
10927 // Otherwise, we would have already wiped the multi-column containing block.
10928 PROFILER_MARKER("Reframe multi-column after constructing frame list",
10929 LAYOUT, {}, Tracing, "Layout");
10931 // aFrameList can contain placeholder frames. In order to destroy their
10932 // associated out-of-flow frames properly, we need to manually flush all the
10933 // out-of-flow frames in aState to their container frames.
10934 aState.ProcessFrameInsertionsForAllLists();
10935 aFrameList.DestroyFrames();
10936 RecreateFramesForContent(
10937 GetMultiColumnContainingBlockFor(aParentFrame)->GetContent(),
10938 InsertionKind::Async);
10939 return true;
10942 return false;
10945 nsIFrame* nsCSSFrameConstructor::ConstructInline(
10946 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
10947 nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay,
10948 nsFrameList& aFrameList) {
10949 // If an inline frame has non-inline kids, then we chop up the child list
10950 // into runs of blocks and runs of inlines, create anonymous block frames to
10951 // contain the runs of blocks, inline frames with our style for the runs of
10952 // inlines, and put all these frames, in order, into aFrameList.
10954 // When there are column-span blocks in a run of blocks, instead of creating
10955 // an anonymous block to wrap them, we create multiple anonymous blocks,
10956 // wrapping runs of non-column-spans and runs of column-spans.
10958 // We return the the first one. The whole setup is called an {ib}
10959 // split; in what follows "frames in the split" refers to the anonymous blocks
10960 // and inlines that contain our children.
10962 // {ib} splits maintain the following invariants:
10963 // 1) All frames in the split have the NS_FRAME_PART_OF_IBSPLIT bit
10964 // set.
10966 // 2) Each frame in the split has the nsIFrame::IBSplitSibling
10967 // property pointing to the next frame in the split, except for the last
10968 // one, which does not have it set.
10970 // 3) Each frame in the split has the nsIFrame::IBSplitPrevSibling
10971 // property pointing to the previous frame in the split, except for the
10972 // first one, which does not have it set.
10974 // 4) The first and last frame in the split are always inlines.
10976 // 5) The frames wrapping runs of non-column-spans are linked together as
10977 // continuations. The frames wrapping runs of column-spans are *not*
10978 // linked with each other nor with other non-column-span wrappers.
10980 // 6) The first and last frame in the chains of blocks are always wrapping
10981 // non-column-spans. Both of them are created even if they're empty.
10983 // An invariant that is NOT maintained is that the wrappers are actually
10984 // linked via GetNextSibling linkage. A simple example is an inline
10985 // containing an inline that contains a block. The three parts of the inner
10986 // inline end up with three different parents.
10988 // For example, this HTML:
10989 // <span>
10990 // <div>a</div>
10991 // <span>
10992 // b
10993 // <div>c</div>
10994 // </span>
10995 // d
10996 // <div>e</div>
10997 // f
10998 // </span>
10999 // Gives the following frame tree:
11001 // Inline (outer span)
11002 // Block (anonymous, outer span)
11003 // Block (div)
11004 // Text("a")
11005 // Inline (outer span)
11006 // Inline (inner span)
11007 // Text("b")
11008 // Block (anonymous, outer span)
11009 // Block (anonymous, inner span)
11010 // Block (div)
11011 // Text("c")
11012 // Inline (outer span)
11013 // Inline (inner span)
11014 // Text("d")
11015 // Block (anonymous, outer span)
11016 // Block (div)
11017 // Text("e")
11018 // Inline (outer span)
11019 // Text("f")
11021 nsIContent* const content = aItem.mContent;
11022 ComputedStyle* const computedStyle = aItem.mComputedStyle;
11024 nsInlineFrame* newFrame = NS_NewInlineFrame(mPresShell, computedStyle);
11026 // Initialize the frame
11027 InitAndRestoreFrame(aState, content, aParentFrame, newFrame);
11029 // definition cannot be inside next block because the object's destructor is
11030 // significant. this is part of the fix for bug 42372
11031 nsFrameConstructorSaveState absoluteSaveState;
11033 bool isAbsPosCB = newFrame->IsAbsPosContainingBlock();
11034 newFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
11035 if (isAbsPosCB) {
11036 // Relatively positioned frames becomes a container for child
11037 // frames that are positioned
11038 aState.PushAbsoluteContainingBlock(newFrame, newFrame, absoluteSaveState);
11041 if (aParentFrame->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR) &&
11042 !ShouldSuppressColumnSpanDescendants(aParentFrame)) {
11043 newFrame->AddStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR);
11046 // Process the child content
11047 nsFrameList childList;
11048 ConstructFramesFromItemList(aState, aItem.mChildItems, newFrame,
11049 /* aParentIsWrapperAnonBox = */ false, childList);
11051 nsIFrame* firstBlock = nullptr;
11052 if (!aItem.mIsAllInline) {
11053 for (nsIFrame* f : childList) {
11054 if (f->IsBlockOutside()) {
11055 firstBlock = f;
11056 break;
11061 if (aItem.mIsAllInline || !firstBlock) {
11062 // This part is easy. We either already know we have no non-inline kids,
11063 // or haven't found any when constructing actual frames (the latter can
11064 // happen only if out-of-flows that we thought had no containing block
11065 // acquired one when ancestor inline frames and {ib} splits got
11066 // constructed). Just put all the kids into the single inline frame and
11067 // bail.
11068 newFrame->SetInitialChildList(FrameChildListID::Principal,
11069 std::move(childList));
11070 aState.AddChild(newFrame, aFrameList, content, aParentFrame);
11071 return newFrame;
11074 // This inline frame contains several types of children. Therefore this frame
11075 // has to be chopped into several pieces, as described above.
11077 // Grab the first inline's kids
11078 nsFrameList firstInlineKids = childList.TakeFramesBefore(firstBlock);
11079 newFrame->SetInitialChildList(FrameChildListID::Principal,
11080 std::move(firstInlineKids));
11082 aFrameList.AppendFrame(nullptr, newFrame);
11084 newFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
11085 CreateIBSiblings(aState, newFrame, isAbsPosCB, childList, aFrameList);
11087 return newFrame;
11090 void nsCSSFrameConstructor::CreateIBSiblings(nsFrameConstructorState& aState,
11091 nsContainerFrame* aInitialInline,
11092 bool aIsAbsPosCB,
11093 nsFrameList& aChildList,
11094 nsFrameList& aSiblings) {
11095 MOZ_ASSERT(aIsAbsPosCB == aInitialInline->IsAbsPosContainingBlock());
11097 nsIContent* content = aInitialInline->GetContent();
11098 ComputedStyle* computedStyle = aInitialInline->Style();
11099 nsContainerFrame* parentFrame = aInitialInline->GetParent();
11101 // Resolve the right style for our anonymous blocks.
11103 // The distinction in styles is needed because of CSS 2.1, section
11104 // 9.2.1.1, which says:
11106 // When such an inline box is affected by relative positioning, any
11107 // resulting translation also affects the block-level box contained
11108 // in the inline box.
11109 RefPtr<ComputedStyle> blockSC =
11110 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
11111 PseudoStyleType::mozBlockInsideInlineWrapper, computedStyle);
11113 nsContainerFrame* lastNewInline =
11114 static_cast<nsContainerFrame*>(aInitialInline->FirstContinuation());
11115 do {
11116 // On entry to this loop aChildList is not empty and the first frame in it
11117 // is block-level.
11118 MOZ_ASSERT(aChildList.NotEmpty(), "Should have child items");
11119 MOZ_ASSERT(aChildList.FirstChild()->IsBlockOutside(),
11120 "Must have list starting with block");
11122 // The initial run of blocks belongs to an anonymous block that we create
11123 // right now. The anonymous block will be the parent of these block
11124 // children of the inline.
11125 nsBlockFrame* blockFrame = NS_NewBlockFrame(mPresShell, blockSC);
11126 InitAndRestoreFrame(aState, content, parentFrame, blockFrame, false);
11127 if (aInitialInline->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR)) {
11128 blockFrame->AddStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR);
11131 // Find the first non-block child which defines the end of our block kids
11132 // and the start of our next inline's kids
11133 nsFrameList blockKids =
11134 aChildList.Split([](nsIFrame* f) { return !f->IsBlockOutside(); });
11136 if (!aInitialInline->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR)) {
11137 MoveChildrenTo(aInitialInline, blockFrame, blockKids);
11139 SetFrameIsIBSplit(lastNewInline, blockFrame);
11140 aSiblings.AppendFrame(nullptr, blockFrame);
11141 } else {
11142 // Extract any initial non-column-span frames, and put them in
11143 // blockFrame's child list.
11144 nsFrameList initialNonColumnSpanKids =
11145 blockKids.Split([](nsIFrame* f) { return f->IsColumnSpan(); });
11146 MoveChildrenTo(aInitialInline, blockFrame, initialNonColumnSpanKids);
11148 SetFrameIsIBSplit(lastNewInline, blockFrame);
11149 aSiblings.AppendFrame(nullptr, blockFrame);
11151 if (blockKids.NotEmpty()) {
11152 // Although SetFrameIsIBSplit() will add NS_FRAME_PART_OF_IBSPLIT for
11153 // blockFrame later, we manually add the bit earlier here to make all
11154 // the continuations of blockFrame created in
11155 // CreateColumnSpanSiblings(), i.e. non-column-span wrappers, have the
11156 // bit via nsIFrame::Init().
11157 blockFrame->AddStateBits(NS_FRAME_PART_OF_IBSPLIT);
11159 nsFrameList columnSpanSiblings =
11160 CreateColumnSpanSiblings(aState, blockFrame, blockKids,
11161 aIsAbsPosCB ? aInitialInline : nullptr);
11162 aSiblings.AppendFrames(nullptr, std::move(columnSpanSiblings));
11166 // Now grab the initial inlines in aChildList and put them into an inline
11167 // frame.
11168 nsInlineFrame* inlineFrame = NS_NewInlineFrame(mPresShell, computedStyle);
11169 InitAndRestoreFrame(aState, content, parentFrame, inlineFrame, false);
11170 inlineFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
11171 if (aInitialInline->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR)) {
11172 inlineFrame->AddStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR);
11175 if (aIsAbsPosCB) {
11176 inlineFrame->MarkAsAbsoluteContainingBlock();
11179 if (aChildList.NotEmpty()) {
11180 nsFrameList inlineKids =
11181 aChildList.Split([](nsIFrame* f) { return f->IsBlockOutside(); });
11182 MoveChildrenTo(aInitialInline, inlineFrame, inlineKids);
11185 SetFrameIsIBSplit(blockFrame, inlineFrame);
11186 aSiblings.AppendFrame(nullptr, inlineFrame);
11187 lastNewInline = inlineFrame;
11188 } while (aChildList.NotEmpty());
11190 SetFrameIsIBSplit(lastNewInline, nullptr);
11193 void nsCSSFrameConstructor::BuildInlineChildItems(
11194 nsFrameConstructorState& aState, FrameConstructionItem& aParentItem,
11195 bool aItemIsWithinSVGText, bool aItemAllowsTextPathChild) {
11196 ComputedStyle* const parentComputedStyle = aParentItem.mComputedStyle;
11197 nsIContent* const parentContent = aParentItem.mContent;
11199 if (!aItemIsWithinSVGText) {
11200 if (parentComputedStyle->StyleDisplay()->IsListItem()) {
11201 CreateGeneratedContentItem(aState, nullptr, *parentContent->AsElement(),
11202 *parentComputedStyle, PseudoStyleType::marker,
11203 aParentItem.mChildItems);
11205 // Probe for generated content before
11206 CreateGeneratedContentItem(aState, nullptr, *parentContent->AsElement(),
11207 *parentComputedStyle, PseudoStyleType::before,
11208 aParentItem.mChildItems);
11211 ItemFlags flags;
11212 if (aItemIsWithinSVGText) {
11213 flags += ItemFlag::IsWithinSVGText;
11215 if (aItemAllowsTextPathChild &&
11216 aParentItem.mContent->IsSVGElement(nsGkAtoms::a)) {
11217 flags += ItemFlag::AllowTextPathChild;
11220 FlattenedChildIterator iter(parentContent);
11221 for (nsIContent* content = iter.GetNextChild(); content;
11222 content = iter.GetNextChild()) {
11223 AddFrameConstructionItems(aState, content, iter.ShadowDOMInvolved(),
11224 *parentComputedStyle, InsertionPoint(),
11225 aParentItem.mChildItems, flags);
11228 if (!aItemIsWithinSVGText) {
11229 // Probe for generated content after
11230 CreateGeneratedContentItem(aState, nullptr, *parentContent->AsElement(),
11231 *parentComputedStyle, PseudoStyleType::after,
11232 aParentItem.mChildItems);
11235 aParentItem.mIsAllInline = aParentItem.mChildItems.AreAllItemsInline();
11238 // return whether it's ok to append (in the AppendFrames sense) to
11239 // aParentFrame if our nextSibling is aNextSibling. aParentFrame must
11240 // be an ib-split inline.
11241 static bool IsSafeToAppendToIBSplitInline(nsIFrame* aParentFrame,
11242 nsIFrame* aNextSibling) {
11243 MOZ_ASSERT(IsInlineFrame(aParentFrame), "Must have an inline parent here");
11245 do {
11246 NS_ASSERTION(IsFramePartOfIBSplit(aParentFrame),
11247 "How is this not part of an ib-split?");
11248 if (aNextSibling || aParentFrame->GetNextContinuation() ||
11249 GetIBSplitSibling(aParentFrame)) {
11250 return false;
11253 aNextSibling = aParentFrame->GetNextSibling();
11254 aParentFrame = aParentFrame->GetParent();
11255 } while (IsInlineFrame(aParentFrame));
11257 return true;
11260 bool nsCSSFrameConstructor::WipeInsertionParent(nsContainerFrame* aFrame) {
11261 #define TRACE(reason) \
11262 PROFILER_MARKER("WipeInsertionParent: " reason, LAYOUT, {}, Tracing, \
11263 "Layout");
11265 const LayoutFrameType frameType = aFrame->Type();
11267 // FIXME(emilio): This looks terribly inefficient if you insert elements deep
11268 // in a MathML subtree.
11269 if (aFrame->IsFrameOfType(nsIFrame::eMathML)) {
11270 TRACE("MathML");
11271 RecreateFramesForContent(aFrame->GetContent(), InsertionKind::Async);
11272 return true;
11275 // A ruby-related frame that's getting new children.
11276 // The situation for ruby is complex, especially when interacting with
11277 // spaces. It contains these two special cases apart from tables:
11278 // 1) There are effectively three types of white spaces in ruby frames
11279 // we handle differently: leading/tailing/inter-level space,
11280 // inter-base/inter-annotation space, and inter-segment space.
11281 // These three types of spaces can be converted to each other when
11282 // their sibling changes.
11283 // 2) The first effective child of a ruby frame must always be a ruby
11284 // base container. It should be created or destroyed accordingly.
11285 if (IsRubyPseudo(aFrame) || frameType == LayoutFrameType::Ruby ||
11286 RubyUtils::IsRubyContainerBox(frameType)) {
11287 // We want to optimize it better, and avoid reframing as much as
11288 // possible. But given the cases above, and the fact that a ruby
11289 // usually won't be very large, it should be fine to reframe it.
11290 TRACE("Ruby");
11291 RecreateFramesForContent(aFrame->GetContent(), InsertionKind::Async);
11292 return true;
11295 // Reframe the multi-column container whenever elements insert/append
11296 // into it because we need to reconstruct column-span split.
11297 if (aFrame->IsColumnSetWrapperFrame()) {
11298 TRACE("Multi-column");
11299 RecreateFramesForContent(aFrame->GetContent(), InsertionKind::Async);
11300 return true;
11303 return false;
11305 #undef TRACE
11308 bool nsCSSFrameConstructor::WipeContainingBlock(
11309 nsFrameConstructorState& aState, nsIFrame* aContainingBlock,
11310 nsIFrame* aFrame, FrameConstructionItemList& aItems, bool aIsAppend,
11311 nsIFrame* aPrevSibling) {
11312 #define TRACE(reason) \
11313 PROFILER_MARKER("WipeContainingBlock: " reason, LAYOUT, {}, Tracing, \
11314 "Layout");
11316 if (aItems.IsEmpty()) {
11317 return false;
11320 // Before we go and append the frames, we must check for several
11321 // special situations.
11323 if (aFrame->GetContent() == mDocument->GetRootElement()) {
11324 // Situation #1 is when we insert content that becomes the canonical body
11325 // element, and its used WritingMode is different from the root element's
11326 // used WritingMode.
11327 // We need to reframe the root element so that the root element's frames has
11328 // the correct writing-mode propagated from body element. (See
11329 // nsCSSFrameConstructor::ConstructDocElementFrame.)
11331 // Bug 1594297: When inserting a new <body>, we may need to reframe the old
11332 // <body> which has a "overflow" value other than simple "visible". But it's
11333 // tricky, see bug 1593752.
11334 nsIContent* bodyElement = mDocument->GetBodyElement();
11335 for (FCItemIterator iter(aItems); !iter.IsDone(); iter.Next()) {
11336 const WritingMode bodyWM(iter.item().mComputedStyle);
11337 if (iter.item().mContent == bodyElement &&
11338 bodyWM != aFrame->GetWritingMode()) {
11339 TRACE("Root");
11340 RecreateFramesForContent(mDocument->GetRootElement(),
11341 InsertionKind::Async);
11342 return true;
11347 nsIFrame* nextSibling = ::GetInsertNextSibling(aFrame, aPrevSibling);
11349 // Situation #2 is a flex / grid container frame into which we're inserting
11350 // new inline non-replaced children, adjacent to an existing anonymous flex or
11351 // grid item.
11352 if (aFrame->IsFlexOrGridContainer()) {
11353 FCItemIterator iter(aItems);
11355 // Check if we're adding to-be-wrapped content right *after* an existing
11356 // anonymous flex or grid item (which would need to absorb this content).
11357 const bool isLegacyWebKitBox = IsFlexContainerForLegacyWebKitBox(aFrame);
11358 if (aPrevSibling && IsAnonymousItem(aPrevSibling) &&
11359 iter.item().NeedsAnonFlexOrGridItem(aState, isLegacyWebKitBox)) {
11360 TRACE("Inserting inline after anon flex or grid item");
11361 RecreateFramesForContent(aFrame->GetContent(), InsertionKind::Async);
11362 return true;
11365 // Check if we're adding to-be-wrapped content right *before* an existing
11366 // anonymous flex or grid item (which would need to absorb this content).
11367 if (nextSibling && IsAnonymousItem(nextSibling)) {
11368 // Jump to the last entry in the list
11369 iter.SetToEnd();
11370 iter.Prev();
11371 if (iter.item().NeedsAnonFlexOrGridItem(aState, isLegacyWebKitBox)) {
11372 TRACE("Inserting inline before anon flex or grid item");
11373 RecreateFramesForContent(aFrame->GetContent(), InsertionKind::Async);
11374 return true;
11379 // Situation #3 is an anonymous flex or grid item that's getting new children
11380 // who don't want to be wrapped.
11381 if (IsAnonymousItem(aFrame)) {
11382 AssertAnonymousFlexOrGridItemParent(aFrame, aFrame->GetParent());
11384 // We need to push a null float containing block to be sure that
11385 // "NeedsAnonFlexOrGridItem" will know we're not honoring floats for this
11386 // inserted content. (In particular, this is necessary in order for
11387 // its "GetGeometricParent" call to return the correct result.)
11388 // We're not honoring floats on this content because it has the
11389 // _flex/grid container_ as its parent in the content tree.
11390 nsFrameConstructorSaveState floatSaveState;
11391 aState.PushFloatContainingBlock(nullptr, floatSaveState);
11393 FCItemIterator iter(aItems);
11394 // Skip over things that _do_ need an anonymous flex item, because
11395 // they're perfectly happy to go here -- they won't cause a reframe.
11396 nsIFrame* containerFrame = aFrame->GetParent();
11397 const bool isLegacyWebKitBox =
11398 IsFlexContainerForLegacyWebKitBox(containerFrame);
11399 if (!iter.SkipItemsThatNeedAnonFlexOrGridItem(aState, isLegacyWebKitBox)) {
11400 // We hit something that _doesn't_ need an anonymous flex item!
11401 // Rebuild the flex container to bust it out.
11402 TRACE("Inserting non-inlines inside anon flex or grid item");
11403 RecreateFramesForContent(containerFrame->GetContent(),
11404 InsertionKind::Async);
11405 return true;
11408 // If we get here, then everything in |aItems| needs to be wrapped in
11409 // an anonymous flex or grid item. That's where it's already going - good!
11412 // Situation #4 is a case when table pseudo-frames don't work out right
11413 ParentType parentType = GetParentType(aFrame);
11414 // If all the kids want a parent of the type that aFrame is, then we're all
11415 // set to go. Indeed, there won't be any table pseudo-frames created between
11416 // aFrame and the kids, so those won't need to be merged with any table
11417 // pseudo-frames that might already be kids of aFrame. If aFrame itself is a
11418 // table pseudo-frame, then all the kids in this list would have wanted a
11419 // frame of that type wrapping them anyway, so putting them inside it is ok.
11420 if (!aItems.AllWantParentType(parentType)) {
11421 // Don't give up yet. If parentType is not eTypeBlock and the parent is
11422 // not a generated content frame, then try filtering whitespace out of the
11423 // list.
11424 if (parentType != eTypeBlock && !aFrame->IsGeneratedContentFrame()) {
11425 // For leading whitespace followed by a kid that wants our parent type,
11426 // there are four cases:
11427 // 1) We have a previous sibling which is not a table pseudo. That means
11428 // that previous sibling wanted a (non-block) parent of the type we're
11429 // looking at. Then the whitespace comes between two table-internal
11430 // elements, so should be collapsed out.
11431 // 2) We have a previous sibling which is a table pseudo. It might have
11432 // kids who want this whitespace, so we need to reframe.
11433 // 3) We have no previous sibling and our parent frame is not a table
11434 // pseudo. That means that we'll be at the beginning of our actual
11435 // non-block-type parent, and the whitespace is OK to collapse out.
11436 // If something is ever inserted before us, it'll find our own parent
11437 // as its parent and if it's something that would care about the
11438 // whitespace it'll want a block parent, so it'll trigger a reframe at
11439 // that point.
11440 // 4) We have no previous sibling and our parent frame is a table pseudo.
11441 // Need to reframe.
11442 // All that is predicated on finding the correct previous sibling. We
11443 // might have to walk backwards along continuations from aFrame to do so.
11445 // It's always OK to drop whitespace between any two items that want a
11446 // parent of type parentType.
11448 // For trailing whitespace preceded by a kid that wants our parent type,
11449 // there are four cases:
11450 // 1) We have a next sibling which is not a table pseudo. That means
11451 // that next sibling wanted a (non-block) parent of the type we're
11452 // looking at. Then the whitespace comes between two table-internal
11453 // elements, so should be collapsed out.
11454 // 2) We have a next sibling which is a table pseudo. It might have
11455 // kids who want this whitespace, so we need to reframe.
11456 // 3) We have no next sibling and our parent frame is not a table
11457 // pseudo. That means that we'll be at the end of our actual
11458 // non-block-type parent, and the whitespace is OK to collapse out.
11459 // If something is ever inserted after us, it'll find our own parent
11460 // as its parent and if it's something that would care about the
11461 // whitespace it'll want a block parent, so it'll trigger a reframe at
11462 // that point.
11463 // 4) We have no next sibling and our parent frame is a table pseudo.
11464 // Need to reframe.
11465 // All that is predicated on finding the correct next sibling. We might
11466 // have to walk forward along continuations from aFrame to do so. That
11467 // said, in the case when nextSibling is null at this point and aIsAppend
11468 // is true, we know we're in case 3. Furthermore, in that case we don't
11469 // even have to worry about the table pseudo situation; we know our
11470 // parent is not a table pseudo there.
11471 FCItemIterator iter(aItems);
11472 FCItemIterator start(iter);
11473 do {
11474 if (iter.SkipItemsWantingParentType(parentType)) {
11475 break;
11478 // iter points to an item that wants a different parent. If it's not
11479 // whitespace, we're done; no more point scanning the list.
11480 if (!iter.item().IsWhitespace(aState)) {
11481 break;
11484 if (iter == start) {
11485 // Leading whitespace. How to handle this depends on our
11486 // previous sibling and aFrame. See the long comment above.
11487 nsIFrame* prevSibling = aPrevSibling;
11488 if (!prevSibling) {
11489 // Try to find one after all
11490 nsIFrame* parentPrevCont = aFrame->GetPrevContinuation();
11491 while (parentPrevCont) {
11492 prevSibling = parentPrevCont->PrincipalChildList().LastChild();
11493 if (prevSibling) {
11494 break;
11496 parentPrevCont = parentPrevCont->GetPrevContinuation();
11499 if (prevSibling) {
11500 if (IsTablePseudo(prevSibling)) {
11501 // need to reframe
11502 break;
11504 } else if (IsTablePseudo(aFrame)) {
11505 // need to reframe
11506 break;
11510 FCItemIterator spaceEndIter(iter);
11511 // Advance spaceEndIter past any whitespace
11512 bool trailingSpaces = spaceEndIter.SkipWhitespace(aState);
11514 bool okToDrop;
11515 if (trailingSpaces) {
11516 // Trailing whitespace. How to handle this depeds on aIsAppend, our
11517 // next sibling and aFrame. See the long comment above.
11518 okToDrop = aIsAppend && !nextSibling;
11519 if (!okToDrop) {
11520 if (!nextSibling) {
11521 // Try to find one after all
11522 nsIFrame* parentNextCont = aFrame->GetNextContinuation();
11523 while (parentNextCont) {
11524 nextSibling = parentNextCont->PrincipalChildList().FirstChild();
11525 if (nextSibling) {
11526 break;
11528 parentNextCont = parentNextCont->GetNextContinuation();
11532 okToDrop = (nextSibling && !IsTablePseudo(nextSibling)) ||
11533 (!nextSibling && !IsTablePseudo(aFrame));
11535 #ifdef DEBUG
11536 else {
11537 NS_ASSERTION(!IsTablePseudo(aFrame), "How did that happen?");
11539 #endif
11540 } else {
11541 okToDrop = (spaceEndIter.item().DesiredParentType() == parentType);
11544 if (okToDrop) {
11545 iter.DeleteItemsTo(this, spaceEndIter);
11546 } else {
11547 // We're done: we don't want to drop the whitespace, and it has the
11548 // wrong parent type.
11549 break;
11552 // Now loop, since |iter| points to item right after the whitespace we
11553 // removed.
11554 } while (!iter.IsDone());
11557 // We might be able to figure out some sort of optimizations here, but they
11558 // would have to depend on having a correct aPrevSibling and a correct next
11559 // sibling. For example, we can probably avoid reframing if none of
11560 // aFrame, aPrevSibling, and next sibling are table pseudo-frames. But it
11561 // doesn't seem worth it to worry about that for now, especially since we
11562 // in fact do not have a reliable aPrevSibling, nor any next sibling, in
11563 // this method.
11565 // aItems might have changed, so recheck the parent type thing. In fact,
11566 // it might be empty, so recheck that too.
11567 if (aItems.IsEmpty()) {
11568 return false;
11571 if (!aItems.AllWantParentType(parentType)) {
11572 // Reframing aFrame->GetContent() is good enough, since the content of
11573 // table pseudo-frames is the ancestor content.
11574 TRACE("Pseudo-frames going wrong");
11575 RecreateFramesForContent(aFrame->GetContent(), InsertionKind::Async);
11576 return true;
11580 // Situation #5 is a frame in multicol subtree that's getting new children.
11581 if (aFrame->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR)) {
11582 bool anyColumnSpanItems = false;
11583 for (FCItemIterator iter(aItems); !iter.IsDone(); iter.Next()) {
11584 if (iter.item().mComputedStyle->StyleColumn()->IsColumnSpanStyle()) {
11585 anyColumnSpanItems = true;
11586 break;
11590 bool needsReframe =
11591 // 1. Insert / append any column-span children.
11592 anyColumnSpanItems ||
11593 // 2. GetInsertionPrevSibling() modifies insertion parent. If the prev
11594 // sibling is a column-span, aFrame ends up being the
11595 // column-span-wrapper.
11596 aFrame->Style()->GetPseudoType() ==
11597 PseudoStyleType::columnSpanWrapper ||
11598 // 3. Append into {ib} split container. There might be room for
11599 // optimization, but let's reframe for correctness...
11600 IsFramePartOfIBSplit(aFrame);
11602 if (needsReframe) {
11603 TRACE("Multi-column");
11604 RecreateFramesForContent(
11605 GetMultiColumnContainingBlockFor(aFrame)->GetContent(),
11606 InsertionKind::Async);
11607 return true;
11610 // If we get here, then we need further check for {ib} split to decide
11611 // whether to reframe. For example, appending a block into an empty inline
11612 // that is not part of an {ib} split, but should become an {ib} split.
11615 // A <fieldset> may need to pick up a new rendered legend from aItems.
11616 // We currently can't handle this case without recreating frames for
11617 // the fieldset.
11618 // XXXmats we should be able to optimize this when the fieldset doesn't
11619 // currently have a rendered legend. ContentRangeInserted needs to be fixed
11620 // to use the inner frame as the content insertion frame in that case.
11621 if (const auto* fieldset = GetFieldSetFrameFor(aFrame)) {
11622 // Check if any item is eligible to be a rendered legend.
11623 for (FCItemIterator iter(aItems); !iter.IsDone(); iter.Next()) {
11624 const auto& item = iter.item();
11625 if (!item.mContent->IsHTMLElement(nsGkAtoms::legend)) {
11626 continue;
11628 const auto* display = item.mComputedStyle->StyleDisplay();
11629 if (display->IsFloatingStyle() ||
11630 display->IsAbsolutelyPositionedStyle()) {
11631 continue;
11633 TRACE("Fieldset with rendered legend");
11634 RecreateFramesForContent(fieldset->GetContent(), InsertionKind::Async);
11635 return true;
11639 // Now we have several cases involving {ib} splits. Put them all in a
11640 // do/while with breaks to take us to the "go and reconstruct" code.
11641 do {
11642 if (IsInlineFrame(aFrame)) {
11643 if (aItems.AreAllItemsInline()) {
11644 // We can just put the kids in.
11645 return false;
11648 if (!IsFramePartOfIBSplit(aFrame)) {
11649 // Need to go ahead and reconstruct.
11650 break;
11653 // Now we're adding kids including some blocks to an inline part of an
11654 // {ib} split. If we plan to call AppendFrames, and don't have a next
11655 // sibling for the new frames, and our parent is the last continuation of
11656 // the last part of the {ib} split, and the same is true of all our
11657 // ancestor inlines (they have no following continuations and they're the
11658 // last part of their {ib} splits and we'd be adding to the end for all
11659 // of them), then AppendFrames will handle things for us. Bail out in
11660 // that case.
11661 if (aIsAppend && IsSafeToAppendToIBSplitInline(aFrame, nextSibling)) {
11662 return false;
11665 // Need to reconstruct.
11666 break;
11669 // Now we know we have a block parent. If it's not part of an
11670 // ib-split, we're all set.
11671 if (!IsFramePartOfIBSplit(aFrame)) {
11672 return false;
11675 // We're adding some kids to a block part of an {ib} split. If all the
11676 // kids are blocks, we don't need to reconstruct.
11677 if (aItems.AreAllItemsBlock()) {
11678 return false;
11681 // We might have some inline kids for this block. Just fall out of the
11682 // loop and reconstruct.
11683 } while (0);
11685 // If we don't have a containing block, start with aFrame and look for one.
11686 if (!aContainingBlock) {
11687 aContainingBlock = aFrame;
11690 // To find the right block to reframe, just walk up the tree until we find a
11691 // frame that is:
11692 // 1) Not part of an IB split
11693 // 2) Not a pseudo-frame
11694 // 3) Not an inline frame
11695 // We're guaranteed to find one, since ComputedStyle::ApplyStyleFixups
11696 // enforces that the root is display:none, display:table, or display:block.
11697 // Note that walking up "too far" is OK in terms of correctness, even if it
11698 // might be a little inefficient. This is why we walk out of all
11699 // pseudo-frames -- telling which ones are or are not OK to walk out of is
11700 // too hard (and I suspect that we do in fact need to walk out of all of
11701 // them).
11702 while (IsFramePartOfIBSplit(aContainingBlock) ||
11703 aContainingBlock->IsInlineOutside() ||
11704 aContainingBlock->Style()->IsPseudoOrAnonBox()) {
11705 aContainingBlock = aContainingBlock->GetParent();
11706 NS_ASSERTION(aContainingBlock,
11707 "Must have non-inline, non-ib-split, non-pseudo frame as "
11708 "root (or child of root, for a table root)!");
11711 // Tell parent of the containing block to reformulate the
11712 // entire block. This is painful and definitely not optimal
11713 // but it will *always* get the right answer.
11715 nsIContent* blockContent = aContainingBlock->GetContent();
11716 TRACE("IB splits");
11717 RecreateFramesForContent(blockContent, InsertionKind::Async);
11718 return true;
11719 #undef TRACE
11722 void nsCSSFrameConstructor::ReframeContainingBlock(nsIFrame* aFrame) {
11723 // XXXbz how exactly would we get here while isReflowing anyway? Should this
11724 // whole test be ifdef DEBUG?
11725 if (mPresShell->IsReflowLocked()) {
11726 // don't ReframeContainingBlock, this will result in a crash
11727 // if we remove a tree that's in reflow - see bug 121368 for testcase
11728 NS_ERROR(
11729 "Atemptted to nsCSSFrameConstructor::ReframeContainingBlock during a "
11730 "Reflow!!!");
11731 return;
11734 // Get the first "normal" ancestor of the target frame.
11735 nsIFrame* containingBlock = GetIBContainingBlockFor(aFrame);
11736 if (containingBlock) {
11737 // From here we look for the containing block in case the target
11738 // frame is already a block (which can happen when an inline frame
11739 // wraps some of its content in an anonymous block; see
11740 // ConstructInline)
11742 // NOTE: We used to get the FloatContainingBlock here, but it was often
11743 // wrong. GetIBContainingBlock works much better and provides the correct
11744 // container in all cases so GetFloatContainingBlock(aFrame) has been
11745 // removed
11747 // And get the containingBlock's content
11748 if (nsIContent* blockContent = containingBlock->GetContent()) {
11749 #ifdef DEBUG
11750 if (gNoisyContentUpdates) {
11751 printf(" ==> blockContent=%p\n", blockContent);
11753 #endif
11754 RecreateFramesForContent(blockContent, InsertionKind::Async);
11755 return;
11759 // If we get here, we're screwed!
11760 RecreateFramesForContent(mPresShell->GetDocument()->GetRootElement(),
11761 InsertionKind::Async);
11764 //////////////////////////////////////////////////////////
11765 // nsCSSFrameConstructor::FrameConstructionItem methods //
11766 //////////////////////////////////////////////////////////
11767 bool nsCSSFrameConstructor::FrameConstructionItem::IsWhitespace(
11768 nsFrameConstructorState& aState) const {
11769 MOZ_ASSERT(aState.mCreatingExtraFrames || !mContent->GetPrimaryFrame(),
11770 "How did that happen?");
11771 if (!mIsText) {
11772 return false;
11774 mContent->SetFlags(NS_CREATE_FRAME_IF_NON_WHITESPACE |
11775 NS_REFRAME_IF_WHITESPACE);
11776 return mContent->TextIsOnlyWhitespace();
11779 //////////////////////////////////////////////////////////////
11780 // nsCSSFrameConstructor::FrameConstructionItemList methods //
11781 //////////////////////////////////////////////////////////////
11782 void nsCSSFrameConstructor::FrameConstructionItemList::AdjustCountsForItem(
11783 FrameConstructionItem* aItem, int32_t aDelta) {
11784 MOZ_ASSERT(aDelta == 1 || aDelta == -1, "Unexpected delta");
11785 mItemCount += aDelta;
11786 if (aItem->mIsAllInline) {
11787 mInlineCount += aDelta;
11789 if (aItem->mIsBlock) {
11790 mBlockCount += aDelta;
11792 mDesiredParentCounts[aItem->DesiredParentType()] += aDelta;
11795 ////////////////////////////////////////////////////////////////////////
11796 // nsCSSFrameConstructor::FrameConstructionItemList::Iterator methods //
11797 ////////////////////////////////////////////////////////////////////////
11798 inline bool nsCSSFrameConstructor::FrameConstructionItemList::Iterator::
11799 SkipItemsWantingParentType(ParentType aParentType) {
11800 MOZ_ASSERT(!IsDone(), "Shouldn't be done yet");
11801 while (item().DesiredParentType() == aParentType) {
11802 Next();
11803 if (IsDone()) {
11804 return true;
11807 return false;
11810 inline bool nsCSSFrameConstructor::FrameConstructionItemList::Iterator::
11811 SkipItemsNotWantingParentType(ParentType aParentType) {
11812 MOZ_ASSERT(!IsDone(), "Shouldn't be done yet");
11813 while (item().DesiredParentType() != aParentType) {
11814 Next();
11815 if (IsDone()) {
11816 return true;
11819 return false;
11822 // Note: we implement -webkit-{inline-}box using nsFlexContainerFrame, but we
11823 // use different rules for what gets wrapped in an anonymous flex item.
11824 bool nsCSSFrameConstructor::FrameConstructionItem::NeedsAnonFlexOrGridItem(
11825 const nsFrameConstructorState& aState, bool aIsLegacyWebKitBox) {
11826 if (mFCData->mBits & FCDATA_IS_LINE_PARTICIPANT) {
11827 // This will be an inline non-replaced box.
11828 return true;
11831 if (aIsLegacyWebKitBox) {
11832 if (mComputedStyle->StyleDisplay()->IsInlineOutsideStyle()) {
11833 // In an emulated legacy box, all inline-level content gets wrapped in an
11834 // anonymous flex item.
11835 return true;
11837 if (mIsPopup ||
11838 (!(mFCData->mBits & FCDATA_DISALLOW_OUT_OF_FLOW) &&
11839 aState.GetGeometricParent(*mComputedStyle->StyleDisplay(), nullptr))) {
11840 // We're abspos or fixedpos (or a XUL popup), which means we'll spawn a
11841 // placeholder which (because our container is an emulated legacy box)
11842 // we'll need to wrap in an anonymous flex item. So, we just treat
11843 // _this_ frame as if _it_ needs to be wrapped in an anonymous flex item,
11844 // and then when we spawn the placeholder, it'll end up in the right
11845 // spot.
11846 return true;
11850 return false;
11853 inline bool nsCSSFrameConstructor::FrameConstructionItemList::Iterator::
11854 SkipItemsThatNeedAnonFlexOrGridItem(const nsFrameConstructorState& aState,
11855 bool aIsLegacyWebKitBox) {
11856 MOZ_ASSERT(!IsDone(), "Shouldn't be done yet");
11857 while (item().NeedsAnonFlexOrGridItem(aState, aIsLegacyWebKitBox)) {
11858 Next();
11859 if (IsDone()) {
11860 return true;
11863 return false;
11866 inline bool nsCSSFrameConstructor::FrameConstructionItemList::Iterator::
11867 SkipItemsThatDontNeedAnonFlexOrGridItem(
11868 const nsFrameConstructorState& aState, bool aIsLegacyWebKitBox) {
11869 MOZ_ASSERT(!IsDone(), "Shouldn't be done yet");
11870 while (!(item().NeedsAnonFlexOrGridItem(aState, aIsLegacyWebKitBox))) {
11871 Next();
11872 if (IsDone()) {
11873 return true;
11876 return false;
11879 inline bool nsCSSFrameConstructor::FrameConstructionItemList::Iterator::
11880 SkipItemsNotWantingRubyParent() {
11881 MOZ_ASSERT(!IsDone(), "Shouldn't be done yet");
11882 while (!IsRubyParentType(item().DesiredParentType())) {
11883 Next();
11884 if (IsDone()) {
11885 return true;
11888 return false;
11891 inline bool
11892 nsCSSFrameConstructor::FrameConstructionItemList::Iterator::SkipWhitespace(
11893 nsFrameConstructorState& aState) {
11894 MOZ_ASSERT(!IsDone(), "Shouldn't be done yet");
11895 MOZ_ASSERT(item().IsWhitespace(aState), "Not pointing to whitespace?");
11896 do {
11897 Next();
11898 if (IsDone()) {
11899 return true;
11901 } while (item().IsWhitespace(aState));
11903 return false;
11906 void nsCSSFrameConstructor::FrameConstructionItemList::Iterator::
11907 AppendItemToList(FrameConstructionItemList& aTargetList) {
11908 NS_ASSERTION(&aTargetList != &mList, "Unexpected call");
11909 MOZ_ASSERT(!IsDone(), "should not be done");
11911 FrameConstructionItem* item = mCurrent;
11912 Next();
11913 item->remove();
11914 aTargetList.mItems.insertBack(item);
11916 mList.AdjustCountsForItem(item, -1);
11917 aTargetList.AdjustCountsForItem(item, 1);
11920 void nsCSSFrameConstructor::FrameConstructionItemList::Iterator::
11921 AppendItemsToList(nsCSSFrameConstructor* aFCtor, const Iterator& aEnd,
11922 FrameConstructionItemList& aTargetList) {
11923 NS_ASSERTION(&aTargetList != &mList, "Unexpected call");
11924 MOZ_ASSERT(&mList == &aEnd.mList, "End iterator for some other list?");
11926 // We can't just move our guts to the other list if it already has
11927 // some information or if we're not moving our entire list.
11928 if (!AtStart() || !aEnd.IsDone() || !aTargetList.IsEmpty()) {
11929 do {
11930 AppendItemToList(aTargetList);
11931 } while (*this != aEnd);
11932 return;
11935 // Move our entire list of items into the empty target list.
11936 aTargetList.mItems = std::move(mList.mItems);
11938 // Copy over the various counters
11939 aTargetList.mInlineCount = mList.mInlineCount;
11940 aTargetList.mBlockCount = mList.mBlockCount;
11941 aTargetList.mItemCount = mList.mItemCount;
11942 memcpy(aTargetList.mDesiredParentCounts, mList.mDesiredParentCounts,
11943 sizeof(aTargetList.mDesiredParentCounts));
11945 // reset mList
11946 mList.Reset(aFCtor);
11948 // Point ourselves to aEnd, as advertised
11949 SetToEnd();
11950 MOZ_ASSERT(*this == aEnd, "How did that happen?");
11953 void nsCSSFrameConstructor::FrameConstructionItemList::Iterator::InsertItem(
11954 FrameConstructionItem* aItem) {
11955 if (IsDone()) {
11956 mList.mItems.insertBack(aItem);
11957 } else {
11958 // Just insert the item before us. There's no magic here.
11959 mCurrent->setPrevious(aItem);
11961 mList.AdjustCountsForItem(aItem, 1);
11963 MOZ_ASSERT(aItem->getNext() == mCurrent, "How did that happen?");
11966 void nsCSSFrameConstructor::FrameConstructionItemList::Iterator::DeleteItemsTo(
11967 nsCSSFrameConstructor* aFCtor, const Iterator& aEnd) {
11968 MOZ_ASSERT(&mList == &aEnd.mList, "End iterator for some other list?");
11969 MOZ_ASSERT(*this != aEnd, "Shouldn't be at aEnd yet");
11971 do {
11972 NS_ASSERTION(!IsDone(), "Ran off end of list?");
11973 FrameConstructionItem* item = mCurrent;
11974 Next();
11975 item->remove();
11976 mList.AdjustCountsForItem(item, -1);
11977 item->Delete(aFCtor);
11978 } while (*this != aEnd);
11981 void nsCSSFrameConstructor::QuotesDirty() {
11982 mQuotesDirty = true;
11983 mPresShell->SetNeedLayoutFlush();
11986 void nsCSSFrameConstructor::CountersDirty() {
11987 mCountersDirty = true;
11988 mPresShell->SetNeedLayoutFlush();
11991 void* nsCSSFrameConstructor::AllocateFCItem() {
11992 void* item;
11993 if (mFirstFreeFCItem) {
11994 item = mFirstFreeFCItem;
11995 mFirstFreeFCItem = mFirstFreeFCItem->mNext;
11996 } else {
11997 item = mFCItemPool.Allocate(sizeof(FrameConstructionItem));
11999 ++mFCItemsInUse;
12000 return item;
12003 void nsCSSFrameConstructor::FreeFCItem(FrameConstructionItem* aItem) {
12004 MOZ_ASSERT(mFCItemsInUse != 0);
12005 if (--mFCItemsInUse == 0) {
12006 // The arena is now unused - clear it but retain one chunk.
12007 mFirstFreeFCItem = nullptr;
12008 mFCItemPool.Clear();
12009 } else {
12010 // Prepend it to the list of free items.
12011 FreeFCItemLink* item = reinterpret_cast<FreeFCItemLink*>(aItem);
12012 item->mNext = mFirstFreeFCItem;
12013 mFirstFreeFCItem = item;
12017 void nsCSSFrameConstructor::AddSizeOfIncludingThis(
12018 nsWindowSizes& aSizes) const {
12019 if (nsIFrame* rootFrame = GetRootFrame()) {
12020 rootFrame->AddSizeOfExcludingThisForTree(aSizes);
12021 if (RetainedDisplayListBuilder* builder =
12022 rootFrame->GetProperty(RetainedDisplayListBuilder::Cached())) {
12023 builder->AddSizeOfIncludingThis(aSizes);
12027 // This must be done after measuring from the frame tree, since frame
12028 // manager will measure sizes of staled computed values and style
12029 // structs, which only make sense after we know what are being used.
12030 nsFrameManager::AddSizeOfIncludingThis(aSizes);
12032 // Measurement of the following members may be added later if DMD finds it
12033 // is worthwhile:
12034 // - mFCItemPool
12035 // - mContainStyleScopeManager