Bug 1709347 - Add CanvasRenderingContext2D.reset(). r=lsalzman,webidl,smaug
[gecko.git] / layout / base / nsCSSFrameConstructor.cpp
blobeb8c112a28390f2bff83eb12061e73465fd17d06
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 "nsHTMLParts.h"
56 #include "nsUnicharUtils.h"
57 #include "nsViewManager.h"
58 #include "nsStyleConsts.h"
59 #include "nsXULElement.h"
60 #include "nsContainerFrame.h"
61 #include "nsNameSpaceManager.h"
62 #include "nsComboboxControlFrame.h"
63 #include "nsListControlFrame.h"
64 #include "nsPlaceholderFrame.h"
65 #include "nsTableRowGroupFrame.h"
66 #include "nsIFormControl.h"
67 #include "nsCSSAnonBoxes.h"
68 #include "nsTextFragment.h"
69 #include "nsTextBoxFrame.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 "nsBoxFrame.h"
80 #include "nsBoxLayout.h"
81 #include "nsFlexContainerFrame.h"
82 #include "nsGridContainerFrame.h"
83 #include "RubyUtils.h"
84 #include "nsRubyFrame.h"
85 #include "nsRubyBaseFrame.h"
86 #include "nsRubyBaseContainerFrame.h"
87 #include "nsRubyTextFrame.h"
88 #include "nsRubyTextContainerFrame.h"
89 #include "nsImageFrame.h"
90 #include "nsIObjectLoadingContent.h"
91 #include "nsTArray.h"
92 #include "mozilla/dom/CharacterData.h"
93 #include "mozilla/dom/Element.h"
94 #include "mozilla/dom/ElementInlines.h"
95 #include "mozilla/dom/HTMLInputElement.h"
96 #include "nsAutoLayoutPhase.h"
97 #include "nsStyleStructInlines.h"
98 #include "nsPageContentFrame.h"
99 #include "mozilla/RestyleManager.h"
100 #include "StickyScrollContainer.h"
101 #include "nsFieldSetFrame.h"
102 #include "nsInlineFrame.h"
103 #include "nsBlockFrame.h"
104 #include "nsCanvasFrame.h"
105 #include "nsFirstLetterFrame.h"
106 #include "nsGfxScrollFrame.h"
107 #include "nsPageFrame.h"
108 #include "nsPageSequenceFrame.h"
109 #include "nsTableWrapperFrame.h"
110 #include "nsIScrollableFrame.h"
111 #include "nsBackdropFrame.h"
112 #include "nsTransitionManager.h"
114 #include "nsIPopupContainer.h"
115 #ifdef ACCESSIBILITY
116 # include "nsAccessibilityService.h"
117 #endif
119 #undef NOISY_FIRST_LETTER
121 #include "nsMathMLParts.h"
122 #include "mozilla/dom/SVGAnimationElement.h"
123 #include "mozilla/dom/SVGFilters.h"
124 #include "mozilla/dom/SVGTests.h"
125 #include "mozilla/SVGGradientFrame.h"
126 #include "mozilla/SVGUtils.h"
128 #include "nsRefreshDriver.h"
129 #include "nsTextNode.h"
130 #include "ActiveLayerTracker.h"
132 using namespace mozilla;
133 using namespace mozilla::dom;
135 nsIFrame* NS_NewHTMLCanvasFrame(PresShell* aPresShell, ComputedStyle* aStyle);
137 nsIFrame* NS_NewHTMLVideoFrame(PresShell* aPresShell, ComputedStyle* aStyle);
139 nsContainerFrame* NS_NewSVGOuterSVGFrame(PresShell* aPresShell,
140 ComputedStyle* aStyle);
141 nsContainerFrame* NS_NewSVGOuterSVGAnonChildFrame(PresShell* aPresShell,
142 ComputedStyle* aStyle);
143 nsIFrame* NS_NewSVGInnerSVGFrame(PresShell* aPresShell, ComputedStyle* aStyle);
144 nsIFrame* NS_NewSVGGeometryFrame(PresShell* aPresShell, ComputedStyle* aStyle);
145 nsIFrame* NS_NewSVGGFrame(PresShell* aPresShell, ComputedStyle* aStyle);
146 nsContainerFrame* NS_NewSVGForeignObjectFrame(PresShell* aPresShell,
147 ComputedStyle* aStyle);
148 nsIFrame* NS_NewSVGAFrame(PresShell* aPresShell, ComputedStyle* aStyle);
149 nsIFrame* NS_NewSVGSwitchFrame(PresShell* aPresShell, ComputedStyle* aStyle);
150 nsIFrame* NS_NewSVGSymbolFrame(PresShell* aPresShell, ComputedStyle* aStyle);
151 nsIFrame* NS_NewSVGTextFrame(PresShell* aPresShell, ComputedStyle* aStyle);
152 nsIFrame* NS_NewSVGContainerFrame(PresShell* aPresShell, ComputedStyle* aStyle);
153 nsIFrame* NS_NewSVGUseFrame(PresShell* aPresShell, ComputedStyle* aStyle);
154 nsIFrame* NS_NewSVGViewFrame(PresShell* aPresShell, ComputedStyle* aStyle);
155 extern nsIFrame* NS_NewSVGLinearGradientFrame(PresShell* aPresShell,
156 ComputedStyle* aStyle);
157 extern nsIFrame* NS_NewSVGRadialGradientFrame(PresShell* aPresShell,
158 ComputedStyle* aStyle);
159 extern nsIFrame* NS_NewSVGStopFrame(PresShell* aPresShell,
160 ComputedStyle* aStyle);
161 nsContainerFrame* NS_NewSVGMarkerFrame(PresShell* aPresShell,
162 ComputedStyle* aStyle);
163 nsContainerFrame* NS_NewSVGMarkerAnonChildFrame(PresShell* aPresShell,
164 ComputedStyle* aStyle);
165 extern nsIFrame* NS_NewSVGImageFrame(PresShell* aPresShell,
166 ComputedStyle* aStyle);
167 nsIFrame* NS_NewSVGClipPathFrame(PresShell* aPresShell, ComputedStyle* aStyle);
168 nsIFrame* NS_NewSVGFilterFrame(PresShell* aPresShell, ComputedStyle* aStyle);
169 nsIFrame* NS_NewSVGPatternFrame(PresShell* aPresShell, ComputedStyle* aStyle);
170 nsIFrame* NS_NewSVGMaskFrame(PresShell* aPresShell, ComputedStyle* aStyle);
171 nsIFrame* NS_NewSVGFEContainerFrame(PresShell* aPresShell,
172 ComputedStyle* aStyle);
173 nsIFrame* NS_NewSVGFELeafFrame(PresShell* aPresShell, ComputedStyle* aStyle);
174 nsIFrame* NS_NewSVGFEImageFrame(PresShell* aPresShell, ComputedStyle* aStyle);
175 nsIFrame* NS_NewSVGFEUnstyledLeafFrame(PresShell* aPresShell,
176 ComputedStyle* aStyle);
178 #include "mozilla/dom/NodeInfo.h"
179 #include "prenv.h"
180 #include "nsNodeInfoManager.h"
181 #include "nsContentCreatorFunctions.h"
183 #ifdef DEBUG
184 // Set the environment variable GECKO_FRAMECTOR_DEBUG_FLAGS to one or
185 // more of the following flags (comma separated) for handy debug
186 // output.
187 static bool gNoisyContentUpdates = false;
188 static bool gReallyNoisyContentUpdates = false;
189 static bool gNoisyInlineConstruction = false;
191 struct FrameCtorDebugFlags {
192 const char* name;
193 bool* on;
196 static FrameCtorDebugFlags gFlags[] = {
197 {"content-updates", &gNoisyContentUpdates},
198 {"really-noisy-content-updates", &gReallyNoisyContentUpdates},
199 {"noisy-inline", &gNoisyInlineConstruction}};
201 # define NUM_DEBUG_FLAGS (sizeof(gFlags) / sizeof(gFlags[0]))
202 #endif
204 //------------------------------------------------------------------
206 nsIFrame* NS_NewLeafBoxFrame(PresShell* aPresShell, ComputedStyle* aStyle);
208 nsIFrame* NS_NewRangeFrame(PresShell* aPresShell, ComputedStyle* aStyle);
210 nsIFrame* NS_NewTextBoxFrame(PresShell* aPresShell, ComputedStyle* aStyle);
212 nsIFrame* NS_NewSplitterFrame(PresShell* aPresShell, ComputedStyle* aStyle);
214 nsIFrame* NS_NewMenuPopupFrame(PresShell* aPresShell, ComputedStyle* aStyle);
216 nsIFrame* NS_NewMenuFrame(PresShell* aPresShell, ComputedStyle* aStyle,
217 uint32_t aFlags);
219 nsIFrame* NS_NewTreeBodyFrame(PresShell* aPresShell, ComputedStyle* aStyle);
221 nsHTMLScrollFrame* NS_NewHTMLScrollFrame(PresShell* aPresShell,
222 ComputedStyle* aStyle, bool aIsRoot);
224 nsXULScrollFrame* NS_NewXULScrollFrame(PresShell* aPresShell,
225 ComputedStyle* aStyle, bool aIsRoot);
227 nsIFrame* NS_NewSliderFrame(PresShell* aPresShell, ComputedStyle* aStyle);
229 nsIFrame* NS_NewScrollbarFrame(PresShell* aPresShell, ComputedStyle* aStyle);
231 nsIFrame* NS_NewScrollbarButtonFrame(PresShell* aPresShell,
232 ComputedStyle* aStyle);
234 nsIFrame* NS_NewXULImageFrame(PresShell*, ComputedStyle*);
235 nsIFrame* NS_NewImageFrameForContentProperty(PresShell*, ComputedStyle*);
236 nsIFrame* NS_NewImageFrameForGeneratedContentIndex(PresShell*, ComputedStyle*);
237 nsIFrame* NS_NewImageFrameForListStyleImage(PresShell*, ComputedStyle*);
239 // Returns true if aFrame is an anonymous flex/grid item.
240 static inline bool IsAnonymousItem(const nsIFrame* aFrame) {
241 return aFrame->Style()->GetPseudoType() == PseudoStyleType::anonymousItem;
244 // Returns true IFF the given nsIFrame is a nsFlexContainerFrame and represents
245 // a -webkit-{inline-}box container.
246 static inline bool IsFlexContainerForLegacyWebKitBox(const nsIFrame* aFrame) {
247 return aFrame->IsFlexContainerFrame() &&
248 aFrame->HasAnyStateBits(NS_STATE_FLEX_IS_EMULATING_LEGACY_WEBKIT_BOX);
251 #if DEBUG
252 static void AssertAnonymousFlexOrGridItemParent(const nsIFrame* aChild,
253 const nsIFrame* aParent) {
254 MOZ_ASSERT(IsAnonymousItem(aChild), "expected an anonymous item child frame");
255 MOZ_ASSERT(aParent, "expected a parent frame");
256 MOZ_ASSERT(aParent->IsFlexContainerFrame() ||
257 aParent->IsGridContainerFrame() || aParent->IsXULBoxFrame(),
258 "anonymous items should only exist as children of "
259 "flex/grid/-moz-box container frames");
261 #else
262 # define AssertAnonymousFlexOrGridItemParent(x, y) PR_BEGIN_MACRO PR_END_MACRO
263 #endif
265 #define ToCreationFunc(_func) \
266 [](PresShell* aPs, ComputedStyle* aStyle) -> nsIFrame* { \
267 return _func(aPs, aStyle); \
271 * True if aFrame is an actual inline frame in the sense of non-replaced
272 * display:inline CSS boxes. In other words, it can be affected by {ib}
273 * splitting and can contain first-letter frames. Basically, this is either an
274 * inline frame (positioned or otherwise) or an line frame (this last because
275 * it can contain first-letter and because inserting blocks in the middle of it
276 * needs to terminate it).
278 static bool IsInlineFrame(const nsIFrame* aFrame) {
279 return aFrame->IsFrameOfType(nsIFrame::eLineParticipant);
283 * True for display: contents elements.
285 static inline bool IsDisplayContents(const Element* aElement) {
286 return aElement->IsDisplayContents();
289 static inline bool IsDisplayContents(const nsIContent* aContent) {
290 return aContent->IsElement() && IsDisplayContents(aContent->AsElement());
294 * True if aFrame is an instance of an SVG frame class or is an inline/block
295 * frame being used for SVG text.
297 static bool IsFrameForSVG(const nsIFrame* aFrame) {
298 return aFrame->IsFrameOfType(nsIFrame::eSVG) ||
299 SVGUtils::IsInSVGTextSubtree(aFrame);
302 static bool IsLastContinuationForColumnContent(const nsIFrame* aFrame) {
303 MOZ_ASSERT(aFrame);
304 return aFrame->Style()->GetPseudoType() == PseudoStyleType::columnContent &&
305 !aFrame->GetNextContinuation();
309 * Returns true iff aFrame explicitly prevents its descendants from floating
310 * (at least, down to the level of descendants which themselves are
311 * float-containing blocks -- those will manage the floating status of any
312 * lower-level descendents inside them, of course).
314 static bool ShouldSuppressFloatingOfDescendants(nsIFrame* aFrame) {
315 return aFrame->IsFlexOrGridContainer() || aFrame->IsXULBoxFrame() ||
316 aFrame->IsFrameOfType(nsIFrame::eMathML);
319 // Return true if column-span descendants should be suppressed under aFrame's
320 // subtree (until a multi-column container re-establishing a block formatting
321 // context). Basically, this is testing whether aFrame establishes a new block
322 // formatting context or not.
323 static bool ShouldSuppressColumnSpanDescendants(nsIFrame* aFrame) {
324 if (aFrame->Style()->GetPseudoType() == PseudoStyleType::columnContent) {
325 // Never suppress column-span under ::-moz-column-content frames.
326 return false;
329 if (aFrame->IsInlineFrame()) {
330 // Allow inline frames to have column-span block children.
331 return false;
334 if (!aFrame->IsBlockFrameOrSubclass() ||
335 aFrame->HasAnyStateBits(NS_BLOCK_FLOAT_MGR | NS_FRAME_OUT_OF_FLOW) ||
336 aFrame->IsFixedPosContainingBlock()) {
337 // Need to suppress column-span if we:
338 // - Are a different block formatting context,
339 // - Are an out-of-flow frame, OR
340 // - Establish a containing block for fixed-position descendants
342 // For example, the children of a column-span never need to be further
343 // processed even if there is a nested column-span child. Because a
344 // column-span always creates its own block formatting context, a nested
345 // column-span child won't be in the same block formatting context with the
346 // nearest multi-column ancestor. This is the same case as if the
347 // column-span is outside of a multi-column hierarchy.
348 return true;
351 return false;
354 // Reparent a frame into a wrapper frame that is a child of its old parent.
355 static void ReparentFrame(RestyleManager* aRestyleManager,
356 nsContainerFrame* aNewParentFrame, nsIFrame* aFrame,
357 bool aForceStyleReparent) {
358 aFrame->SetParent(aNewParentFrame);
359 // We reparent frames for two reasons: to put them inside ::first-line, and to
360 // put them inside some wrapper anonymous boxes.
361 if (aForceStyleReparent) {
362 aRestyleManager->ReparentComputedStyleForFirstLine(aFrame);
366 static void ReparentFrames(nsCSSFrameConstructor* aFrameConstructor,
367 nsContainerFrame* aNewParentFrame,
368 const nsFrameList& aFrameList,
369 bool aForceStyleReparent) {
370 RestyleManager* restyleManager = aFrameConstructor->RestyleManager();
371 for (nsIFrame* f : aFrameList) {
372 ReparentFrame(restyleManager, aNewParentFrame, f, aForceStyleReparent);
376 //----------------------------------------------------------------------
378 // When inline frames get weird and have block frames in them, we
379 // annotate them to help us respond to incremental content changes
380 // more easily.
382 static inline bool IsFramePartOfIBSplit(nsIFrame* aFrame) {
383 bool result = aFrame->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT);
384 MOZ_ASSERT(!result || static_cast<nsBlockFrame*>(do_QueryFrame(aFrame)) ||
385 static_cast<nsInlineFrame*>(do_QueryFrame(aFrame)),
386 "only block/inline frames can have NS_FRAME_PART_OF_IBSPLIT");
387 return result;
390 static nsContainerFrame* GetIBSplitSibling(nsIFrame* aFrame) {
391 MOZ_ASSERT(IsFramePartOfIBSplit(aFrame), "Shouldn't call this");
393 // We only store the "ib-split sibling" annotation with the first
394 // frame in the continuation chain. Walk back to find that frame now.
395 return aFrame->FirstContinuation()->GetProperty(nsIFrame::IBSplitSibling());
398 static nsContainerFrame* GetIBSplitPrevSibling(nsIFrame* aFrame) {
399 MOZ_ASSERT(IsFramePartOfIBSplit(aFrame), "Shouldn't call this");
401 // We only store the ib-split sibling annotation with the first
402 // frame in the continuation chain. Walk back to find that frame now.
403 return aFrame->FirstContinuation()->GetProperty(
404 nsIFrame::IBSplitPrevSibling());
407 static nsContainerFrame* GetLastIBSplitSibling(nsIFrame* aFrame) {
408 for (nsIFrame *frame = aFrame, *next;; frame = next) {
409 next = GetIBSplitSibling(frame);
410 if (!next) {
411 return static_cast<nsContainerFrame*>(frame);
414 MOZ_ASSERT_UNREACHABLE("unreachable code");
415 return nullptr;
418 static void SetFrameIsIBSplit(nsContainerFrame* aFrame,
419 nsContainerFrame* aIBSplitSibling) {
420 MOZ_ASSERT(aFrame, "bad args!");
422 // We should be the only continuation
423 NS_ASSERTION(!aFrame->GetPrevContinuation(),
424 "assigning ib-split sibling to other than first continuation!");
425 NS_ASSERTION(!aFrame->GetNextContinuation() ||
426 IsFramePartOfIBSplit(aFrame->GetNextContinuation()),
427 "should have no non-ib-split continuations here");
429 // Mark the frame as ib-split.
430 aFrame->AddStateBits(NS_FRAME_PART_OF_IBSPLIT);
432 if (aIBSplitSibling) {
433 NS_ASSERTION(!aIBSplitSibling->GetPrevContinuation(),
434 "assigning something other than the first continuation as the "
435 "ib-split sibling");
437 // Store the ib-split sibling (if we were given one) with the
438 // first frame in the flow.
439 aFrame->SetProperty(nsIFrame::IBSplitSibling(), aIBSplitSibling);
440 aIBSplitSibling->SetProperty(nsIFrame::IBSplitPrevSibling(), aFrame);
444 static nsIFrame* GetIBContainingBlockFor(nsIFrame* aFrame) {
445 MOZ_ASSERT(
446 IsFramePartOfIBSplit(aFrame),
447 "GetIBContainingBlockFor() should only be called on known IB frames");
449 // Get the first "normal" ancestor of the target frame.
450 nsIFrame* parentFrame;
451 do {
452 parentFrame = aFrame->GetParent();
454 if (!parentFrame) {
455 NS_ERROR("no unsplit block frame in IB hierarchy");
456 return aFrame;
459 // Note that we ignore non-ib-split frames which have a pseudo on their
460 // ComputedStyle -- they're not the frames we're looking for! In
461 // particular, they may be hiding a real parent that _is_ in an ib-split.
462 if (!IsFramePartOfIBSplit(parentFrame) &&
463 !parentFrame->Style()->IsPseudoOrAnonBox())
464 break;
466 aFrame = parentFrame;
467 } while (1);
469 // post-conditions
470 NS_ASSERTION(parentFrame,
471 "no normal ancestor found for ib-split frame "
472 "in GetIBContainingBlockFor");
473 NS_ASSERTION(parentFrame != aFrame,
474 "parentFrame is actually the child frame - bogus reslt");
476 return parentFrame;
479 // Find the multicol containing block suitable for reframing.
481 // Note: this function may not return a ColumnSetWrapperFrame. For example, if
482 // the multicol containing block has "overflow:scroll" style, HTMLScrollFrame is
483 // returned because ColumnSetWrapperFrame is the scrolled frame which has the
484 // -moz-scrolled-content pseudo style. We may walk up "too far", but in terms of
485 // correctness of reframing, it's OK.
486 static nsContainerFrame* GetMultiColumnContainingBlockFor(nsIFrame* aFrame) {
487 MOZ_ASSERT(aFrame->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR),
488 "Should only be called if the frame has a multi-column ancestor!");
490 nsContainerFrame* current = aFrame->GetParent();
491 while (current &&
492 (current->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR) ||
493 current->Style()->IsPseudoOrAnonBox())) {
494 current = current->GetParent();
497 MOZ_ASSERT(current,
498 "No multicol containing block in a valid column hierarchy?");
500 return current;
503 // This is a bit slow, but sometimes we need it.
504 static bool ParentIsWrapperAnonBox(nsIFrame* aParent) {
505 nsIFrame* maybeAnonBox = aParent;
506 if (maybeAnonBox->Style()->GetPseudoType() == PseudoStyleType::cellContent) {
507 // The thing that would maybe be a wrapper anon box is the cell.
508 maybeAnonBox = maybeAnonBox->GetParent();
510 return maybeAnonBox->Style()->IsWrapperAnonBox();
513 //----------------------------------------------------------------------
515 // Block/inline frame construction logic. We maintain a few invariants here:
517 // 1. Block frames contain block and inline frames.
519 // 2. Inline frames only contain inline frames. If an inline parent has a block
520 // child then the block child is migrated upward until it lands in a block
521 // parent (the inline frames containing block is where it will end up).
523 inline void SetInitialSingleChild(nsContainerFrame* aParent, nsIFrame* aFrame) {
524 MOZ_ASSERT(!aFrame->GetNextSibling(), "Should be using a frame list");
525 aParent->SetInitialChildList(FrameChildListID::Principal,
526 nsFrameList(aFrame, aFrame));
529 // -----------------------------------------------------------
531 // Structure used when constructing formatting object trees. Contains
532 // state information needed for absolutely positioned elements
533 namespace mozilla {
534 struct AbsoluteFrameList final : public nsFrameList {
535 // Containing block for absolutely positioned elements.
536 nsContainerFrame* mContainingBlock;
538 explicit AbsoluteFrameList(nsContainerFrame* aContainingBlock = nullptr)
539 : mContainingBlock(aContainingBlock) {}
541 // Transfer frames in aOther to this list. aOther becomes empty after this
542 // operation.
543 AbsoluteFrameList(AbsoluteFrameList&& aOther) = default;
544 AbsoluteFrameList& operator=(AbsoluteFrameList&& aOther) = default;
546 #ifdef DEBUG
547 // XXXbz Does this need a debug-only assignment operator that nulls out the
548 // childList in the AbsoluteFrameList we're copying? Introducing a difference
549 // between debug and non-debug behavior seems bad, so I guess not...
550 ~AbsoluteFrameList() {
551 NS_ASSERTION(!FirstChild(),
552 "Dangling child list. Someone forgot to insert it?");
554 #endif
556 } // namespace mozilla
558 // -----------------------------------------------------------
560 // Structure for saving the existing state when pushing/poping containing
561 // blocks. The destructor restores the state to its previous state
562 class MOZ_STACK_CLASS nsFrameConstructorSaveState {
563 public:
564 ~nsFrameConstructorSaveState();
566 private:
567 // Pointer to struct whose data we save/restore.
568 AbsoluteFrameList* mList = nullptr;
570 // Copy of original frame list. This can be the original absolute list or a
571 // float list. If we're saving the abs-pos state for a transformed element,
572 // i.e. when mSavedFixedPosIsAbsPos is true, this is the original fixed list.
573 AbsoluteFrameList mSavedList;
575 // The name of the child list in which our frames would belong.
576 mozilla::FrameChildListID mChildListID = FrameChildListID::Principal;
577 nsFrameConstructorState* mState = nullptr;
579 // Only used when saving an absolute list. See the description of
580 // nsFrameConstructorState::mFixedPosIsAbsPos for its meaning.
581 bool mSavedFixedPosIsAbsPos = false;
583 friend class nsFrameConstructorState;
586 // Structure used for maintaining state information during the
587 // frame construction process
588 class MOZ_STACK_CLASS nsFrameConstructorState {
589 public:
590 nsPresContext* mPresContext;
591 PresShell* mPresShell;
592 nsFrameManager* mFrameManager;
594 // Containing block information for out-of-flow frames.
595 AbsoluteFrameList mFixedList;
596 AbsoluteFrameList mAbsoluteList;
597 AbsoluteFrameList mFloatedList;
598 // The containing block of a frame in the top layer is defined by the
599 // spec: fixed-positioned frames are children of the viewport frame,
600 // and absolutely-positioned frames are children of the initial
601 // containing block. They would not be caught by any other containing
602 // block, e.g. frames with transform or filter.
603 AbsoluteFrameList mTopLayerFixedList;
604 AbsoluteFrameList mTopLayerAbsoluteList;
606 // What `page: auto` resolves to. This is the used page-name of the parent
607 // frame. Updated by AutoFrameConstructionPageName.
608 const nsAtom* mAutoPageNameValue;
610 nsCOMPtr<nsILayoutHistoryState> mFrameState;
611 // These bits will be added to the state bits of any frame we construct
612 // using this state.
613 nsFrameState mAdditionalStateBits;
615 // When working with transform / filter properties, we want to hook the
616 // abs-pos and fixed-pos lists together, since such elements are fixed-pos
617 // containing blocks.
619 // Similarly when restricting absolute positioning (for e.g. mathml).
621 // This flag determines whether or not we want to wire the fixed-pos and
622 // abs-pos lists together.
623 bool mFixedPosIsAbsPos;
625 // If false (which is the default) then call SetPrimaryFrame() as needed
626 // during frame construction. If true, don't make any SetPrimaryFrame()
627 // calls, except for generated content which doesn't have a primary frame
628 // yet. The mCreatingExtraFrames == true mode is meant to be used for
629 // construction of random "extra" frames for elements via normal frame
630 // construction APIs (e.g. replication of things across pages in paginated
631 // mode).
632 bool mCreatingExtraFrames;
634 // This keeps track of whether we have found a "rendered legend" for
635 // the current FieldSetFrame.
636 bool mHasRenderedLegend;
638 nsTArray<RefPtr<nsIContent>> mGeneratedContentWithInitializer;
640 #ifdef DEBUG
641 // Record the float containing block candidate passed into
642 // MaybePushFloatContainingBlock() to keep track that we've call the method to
643 // handle the float CB scope before processing the CB's children. It is reset
644 // in ConstructFramesFromItemList().
645 nsContainerFrame* mFloatCBCandidate = nullptr;
646 #endif
648 // Constructor
649 // Use the passed-in history state.
650 nsFrameConstructorState(
651 PresShell* aPresShell, nsContainerFrame* aFixedContainingBlock,
652 nsContainerFrame* aAbsoluteContainingBlock,
653 nsContainerFrame* aFloatContainingBlock,
654 already_AddRefed<nsILayoutHistoryState> aHistoryState);
655 // Get the history state from the pres context's pres shell.
656 nsFrameConstructorState(PresShell* aPresShell,
657 nsContainerFrame* aFixedContainingBlock,
658 nsContainerFrame* aAbsoluteContainingBlock,
659 nsContainerFrame* aFloatContainingBlock);
661 ~nsFrameConstructorState();
663 // Process the frame insertions for all the out-of-flow nsAbsoluteItems.
664 void ProcessFrameInsertionsForAllLists();
666 // Function to push the existing absolute containing block state and
667 // create a new scope. Code that uses this function should get matching
668 // logic in GetAbsoluteContainingBlock.
669 // Also makes aNewAbsoluteContainingBlock the containing block for
670 // fixed-pos elements if necessary.
671 // aPositionedFrame is the frame whose style actually makes
672 // aNewAbsoluteContainingBlock a containing block. E.g. for a scrollable
673 // element aPositionedFrame is the element's primary frame and
674 // aNewAbsoluteContainingBlock is the scrolled frame.
675 void PushAbsoluteContainingBlock(
676 nsContainerFrame* aNewAbsoluteContainingBlock, nsIFrame* aPositionedFrame,
677 nsFrameConstructorSaveState& aSaveState);
679 // Function to forbid floats descendants under aFloatCBCandidate, or open a
680 // new float containing block scope for aFloatCBCandidate. The current
681 // state is saved in aSaveState if a new scope is pushed.
682 void MaybePushFloatContainingBlock(nsContainerFrame* aFloatCBCandidate,
683 nsFrameConstructorSaveState& aSaveState);
685 // Helper function for MaybePushFloatContainingBlock().
686 void PushFloatContainingBlock(nsContainerFrame* aNewFloatContainingBlock,
687 nsFrameConstructorSaveState& aSaveState);
689 // Function to return the proper geometric parent for a frame with display
690 // struct given by aStyleDisplay and parent's frame given by
691 // aContentParentFrame.
692 nsContainerFrame* GetGeometricParent(
693 const nsStyleDisplay& aStyleDisplay,
694 nsContainerFrame* aContentParentFrame) const;
696 // Collect absolute frames in mAbsoluteList which are proper descendants
697 // of aNewParent, and reparent them to aNewParent.
699 // Note: This function does something unusual that moves absolute items
700 // after their frames are constructed under a column hierarchy which has
701 // column-span elements. Do not use this if you're not dealing with
702 // columns.
703 void ReparentAbsoluteItems(nsContainerFrame* aNewParent);
705 // Collect floats in mFloatedList which are proper descendants of aNewParent,
706 // and reparent them to aNewParent.
708 // Note: This function does something unusual that moves floats after their
709 // frames are constructed under a column hierarchy which has column-span
710 // elements. Do not use this if you're not dealing with columns.
711 void ReparentFloats(nsContainerFrame* aNewParent);
714 * Function to add a new frame to the right frame list. This MUST be called
715 * on frames before their children have been processed if the frames might
716 * conceivably be out-of-flow; otherwise cleanup in error cases won't work
717 * right. Also, this MUST be called on frames after they have been
718 * initialized.
719 * @param aNewFrame the frame to add
720 * @param aFrameList the list to add in-flow frames to
721 * @param aContent the content pointer for aNewFrame
722 * @param aParentFrame the parent frame for the content if it were in-flow
723 * @param aCanBePositioned pass false if the frame isn't allowed to be
724 * positioned
725 * @param aCanBeFloated pass false if the frame isn't allowed to be
726 * floated
728 void AddChild(nsIFrame* aNewFrame, nsFrameList& aFrameList,
729 nsIContent* aContent, nsContainerFrame* aParentFrame,
730 bool aCanBePositioned = true, bool aCanBeFloated = true,
731 bool aInsertAfter = false,
732 nsIFrame* aInsertAfterFrame = nullptr);
735 * Function to return the fixed-pos element list. Normally this will just
736 * hand back the fixed-pos element list, but in case we're dealing with a
737 * transformed element that's acting as an abs-pos and fixed-pos container,
738 * we'll hand back the abs-pos list. Callers should use this function if they
739 * want to get the list acting as the fixed-pos item parent.
741 AbsoluteFrameList& GetFixedList() {
742 return mFixedPosIsAbsPos ? mAbsoluteList : mFixedList;
744 const AbsoluteFrameList& GetFixedList() const {
745 return mFixedPosIsAbsPos ? mAbsoluteList : mFixedList;
748 protected:
749 friend class nsFrameConstructorSaveState;
752 * ProcessFrameInsertions takes the frames in aFrameList and adds them as
753 * kids to the aChildListID child list of |aFrameList.containingBlock|.
755 void ProcessFrameInsertions(AbsoluteFrameList& aFrameList,
756 mozilla::FrameChildListID aChildListID);
759 * GetOutOfFlowFrameList selects the out-of-flow frame list the new
760 * frame should be added to. If the frame shouldn't be added to any
761 * out-of-flow list, it returns nullptr. The corresponding type of
762 * placeholder is also returned via the aPlaceholderType parameter
763 * if this method doesn't return nullptr. The caller should check
764 * whether the returned list really has a containing block.
766 AbsoluteFrameList* GetOutOfFlowFrameList(nsIFrame* aNewFrame,
767 bool aCanBePositioned,
768 bool aCanBeFloated,
769 nsFrameState* aPlaceholderType);
771 void ConstructBackdropFrameFor(nsIContent* aContent, nsIFrame* aFrame);
774 nsFrameConstructorState::nsFrameConstructorState(
775 PresShell* aPresShell, nsContainerFrame* aFixedContainingBlock,
776 nsContainerFrame* aAbsoluteContainingBlock,
777 nsContainerFrame* aFloatContainingBlock,
778 already_AddRefed<nsILayoutHistoryState> aHistoryState)
779 : mPresContext(aPresShell->GetPresContext()),
780 mPresShell(aPresShell),
781 mFrameManager(aPresShell->FrameConstructor()),
782 mFixedList(aFixedContainingBlock),
783 mAbsoluteList(aAbsoluteContainingBlock),
784 mFloatedList(aFloatContainingBlock),
785 mTopLayerFixedList(
786 static_cast<nsContainerFrame*>(mFrameManager->GetRootFrame())),
787 mTopLayerAbsoluteList(
788 aPresShell->FrameConstructor()->GetDocElementContainingBlock()),
789 // Will be set by AutoFrameConstructionPageName
790 mAutoPageNameValue(nullptr),
791 // See PushAbsoluteContaningBlock below
792 mFrameState(aHistoryState),
793 mAdditionalStateBits(nsFrameState(0)),
794 // If the fixed-pos containing block is equal to the abs-pos containing
795 // block, use the abs-pos containing block's abs-pos list for fixed-pos
796 // frames.
797 mFixedPosIsAbsPos(aFixedContainingBlock == aAbsoluteContainingBlock),
798 mCreatingExtraFrames(false),
799 mHasRenderedLegend(false) {
800 MOZ_COUNT_CTOR(nsFrameConstructorState);
803 nsFrameConstructorState::nsFrameConstructorState(
804 PresShell* aPresShell, nsContainerFrame* aFixedContainingBlock,
805 nsContainerFrame* aAbsoluteContainingBlock,
806 nsContainerFrame* aFloatContainingBlock)
807 : nsFrameConstructorState(
808 aPresShell, aFixedContainingBlock, aAbsoluteContainingBlock,
809 aFloatContainingBlock,
810 aPresShell->GetDocument()->GetLayoutHistoryState()) {}
812 nsFrameConstructorState::~nsFrameConstructorState() {
813 MOZ_COUNT_DTOR(nsFrameConstructorState);
814 ProcessFrameInsertionsForAllLists();
815 for (auto& content : Reversed(mGeneratedContentWithInitializer)) {
816 content->RemoveProperty(nsGkAtoms::genConInitializerProperty);
820 void nsFrameConstructorState::ProcessFrameInsertionsForAllLists() {
821 ProcessFrameInsertions(mTopLayerFixedList, FrameChildListID::Fixed);
822 ProcessFrameInsertions(mTopLayerAbsoluteList, FrameChildListID::Absolute);
823 ProcessFrameInsertions(mFloatedList, FrameChildListID::Float);
824 ProcessFrameInsertions(mAbsoluteList, FrameChildListID::Absolute);
825 ProcessFrameInsertions(mFixedList, FrameChildListID::Fixed);
828 void nsFrameConstructorState::PushAbsoluteContainingBlock(
829 nsContainerFrame* aNewAbsoluteContainingBlock, nsIFrame* aPositionedFrame,
830 nsFrameConstructorSaveState& aSaveState) {
831 MOZ_ASSERT(!!aNewAbsoluteContainingBlock == !!aPositionedFrame,
832 "We should have both or none");
833 aSaveState.mList = &mAbsoluteList;
834 aSaveState.mChildListID = FrameChildListID::Absolute;
835 aSaveState.mState = this;
836 aSaveState.mSavedFixedPosIsAbsPos = mFixedPosIsAbsPos;
838 if (mFixedPosIsAbsPos) {
839 // Since we're going to replace mAbsoluteList, we need to save it into
840 // mFixedList now (and save the current value of mFixedList).
841 aSaveState.mSavedList = std::move(mFixedList);
842 mFixedList = std::move(mAbsoluteList);
843 } else {
844 // Otherwise, we just save mAbsoluteList.
845 aSaveState.mSavedList = std::move(mAbsoluteList);
848 mAbsoluteList = AbsoluteFrameList(aNewAbsoluteContainingBlock);
850 /* See if we're wiring the fixed-pos and abs-pos lists together. This happens
851 * if we're a transformed/filtered/etc element, or if we force a null abspos
852 * containing block (for mathml for example).
854 mFixedPosIsAbsPos =
855 !aPositionedFrame || aPositionedFrame->IsFixedPosContainingBlock();
857 if (aNewAbsoluteContainingBlock) {
858 aNewAbsoluteContainingBlock->MarkAsAbsoluteContainingBlock();
862 void nsFrameConstructorState::MaybePushFloatContainingBlock(
863 nsContainerFrame* aFloatCBCandidate,
864 nsFrameConstructorSaveState& aSaveState) {
865 // The logic here needs to match the logic in GetFloatContainingBlock().
866 if (ShouldSuppressFloatingOfDescendants(aFloatCBCandidate)) {
867 // Pushing a null float containing block forbids any frames from being
868 // floated until a new float containing block is pushed. See implementation
869 // of nsFrameConstructorState::AddChild().
871 // XXX we should get rid of null float containing blocks and teach the
872 // various frame classes to deal with floats instead.
873 PushFloatContainingBlock(nullptr, aSaveState);
874 } else if (aFloatCBCandidate->IsFloatContainingBlock()) {
875 PushFloatContainingBlock(aFloatCBCandidate, aSaveState);
878 #ifdef DEBUG
879 mFloatCBCandidate = aFloatCBCandidate;
880 #endif
883 void nsFrameConstructorState::PushFloatContainingBlock(
884 nsContainerFrame* aNewFloatContainingBlock,
885 nsFrameConstructorSaveState& aSaveState) {
886 MOZ_ASSERT(!aNewFloatContainingBlock ||
887 aNewFloatContainingBlock->IsFloatContainingBlock(),
888 "Please push a real float containing block!");
889 NS_ASSERTION(
890 !aNewFloatContainingBlock ||
891 !ShouldSuppressFloatingOfDescendants(aNewFloatContainingBlock),
892 "We should not push a frame that is supposed to _suppress_ "
893 "floats as a float containing block!");
894 aSaveState.mList = &mFloatedList;
895 aSaveState.mSavedList = std::move(mFloatedList);
896 aSaveState.mChildListID = FrameChildListID::Float;
897 aSaveState.mState = this;
898 mFloatedList = AbsoluteFrameList(aNewFloatContainingBlock);
901 nsContainerFrame* nsFrameConstructorState::GetGeometricParent(
902 const nsStyleDisplay& aStyleDisplay,
903 nsContainerFrame* aContentParentFrame) const {
904 // If there is no container for a fixed, absolute, or floating root
905 // frame, we will ignore the positioning. This hack is originally
906 // brought to you by the letter T: tables, since other roots don't
907 // even call into this code. See bug 178855.
909 // XXX Disabling positioning in this case is a hack. If one was so inclined,
910 // one could support this either by (1) inserting a dummy block between the
911 // table and the canvas or (2) teaching the canvas how to reflow positioned
912 // elements. (1) has the usual problems when multiple frames share the same
913 // content (notice all the special cases in this file dealing with inner
914 // tables and table wrappers which share the same content). (2) requires some
915 // work and possible factoring.
917 // XXXbz couldn't we just force position to "static" on roots and
918 // float to "none"? That's OK per CSS 2.1, as far as I can tell.
920 if (aContentParentFrame &&
921 SVGUtils::IsInSVGTextSubtree(aContentParentFrame)) {
922 return aContentParentFrame;
925 if (aStyleDisplay.IsFloatingStyle() && mFloatedList.mContainingBlock) {
926 NS_ASSERTION(!aStyleDisplay.IsAbsolutelyPositionedStyle(),
927 "Absolutely positioned _and_ floating?");
928 return mFloatedList.mContainingBlock;
931 if (aStyleDisplay.mTopLayer != StyleTopLayer::None) {
932 MOZ_ASSERT(aStyleDisplay.mTopLayer == StyleTopLayer::Top,
933 "-moz-top-layer should be either none or top");
934 MOZ_ASSERT(aStyleDisplay.IsAbsolutelyPositionedStyle(),
935 "Top layer items should always be absolutely positioned");
936 if (aStyleDisplay.mPosition == StylePositionProperty::Fixed) {
937 MOZ_ASSERT(mTopLayerFixedList.mContainingBlock, "No root frame?");
938 return mTopLayerFixedList.mContainingBlock;
940 MOZ_ASSERT(aStyleDisplay.mPosition == StylePositionProperty::Absolute);
941 MOZ_ASSERT(mTopLayerAbsoluteList.mContainingBlock);
942 return mTopLayerAbsoluteList.mContainingBlock;
945 if (aStyleDisplay.mPosition == StylePositionProperty::Absolute &&
946 mAbsoluteList.mContainingBlock) {
947 return mAbsoluteList.mContainingBlock;
950 if (aStyleDisplay.mPosition == StylePositionProperty::Fixed &&
951 GetFixedList().mContainingBlock) {
952 return GetFixedList().mContainingBlock;
955 return aContentParentFrame;
958 void nsFrameConstructorState::ReparentAbsoluteItems(
959 nsContainerFrame* aNewParent) {
960 // Bug 1491727: This function might not conform to the spec. See
961 // https://github.com/w3c/csswg-drafts/issues/1894.
963 MOZ_ASSERT(aNewParent->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR),
964 "Restrict the usage under column hierarchy.");
966 AbsoluteFrameList newAbsoluteItems(aNewParent);
968 nsIFrame* current = mAbsoluteList.FirstChild();
969 while (current) {
970 nsIFrame* placeholder = current->GetPlaceholderFrame();
972 if (nsLayoutUtils::IsProperAncestorFrame(aNewParent, placeholder)) {
973 nsIFrame* next = current->GetNextSibling();
974 mAbsoluteList.RemoveFrame(current);
975 newAbsoluteItems.AppendFrame(aNewParent, current);
976 current = next;
977 } else {
978 current = current->GetNextSibling();
982 if (newAbsoluteItems.NotEmpty()) {
983 // ~nsFrameConstructorSaveState() will move newAbsoluteItems to
984 // aNewParent's absolute child list.
985 nsFrameConstructorSaveState absoluteSaveState;
987 // It doesn't matter whether aNewParent has position style or not. Caller
988 // won't call us if we can't have absolute children.
989 PushAbsoluteContainingBlock(aNewParent, aNewParent, absoluteSaveState);
990 mAbsoluteList = std::move(newAbsoluteItems);
994 void nsFrameConstructorState::ReparentFloats(nsContainerFrame* aNewParent) {
995 MOZ_ASSERT(aNewParent->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR),
996 "Restrict the usage under column hierarchy.");
997 MOZ_ASSERT(
998 aNewParent->IsFloatContainingBlock(),
999 "Why calling this method if aNewParent is not a float containing block?");
1001 // Gather floats that should reparent under aNewParent.
1002 AbsoluteFrameList floats(aNewParent);
1003 nsIFrame* current = mFloatedList.FirstChild();
1004 while (current) {
1005 nsIFrame* placeholder = current->GetPlaceholderFrame();
1006 nsIFrame* next = current->GetNextSibling();
1007 if (nsLayoutUtils::IsProperAncestorFrame(aNewParent, placeholder)) {
1008 mFloatedList.RemoveFrame(current);
1009 floats.AppendFrame(aNewParent, current);
1011 current = next;
1014 if (floats.NotEmpty()) {
1015 // Make floats move into aNewParent's float child list in
1016 // ~nsFrameConstructorSaveState() when destructing floatSaveState.
1017 nsFrameConstructorSaveState floatSaveState;
1018 PushFloatContainingBlock(aNewParent, floatSaveState);
1019 mFloatedList = std::move(floats);
1023 AbsoluteFrameList* nsFrameConstructorState::GetOutOfFlowFrameList(
1024 nsIFrame* aNewFrame, bool aCanBePositioned, bool aCanBeFloated,
1025 nsFrameState* aPlaceholderType) {
1026 const nsStyleDisplay* disp = aNewFrame->StyleDisplay();
1027 if (aCanBeFloated && disp->IsFloatingStyle()) {
1028 *aPlaceholderType = PLACEHOLDER_FOR_FLOAT;
1029 return &mFloatedList;
1032 if (aCanBePositioned) {
1033 if (disp->mTopLayer != StyleTopLayer::None) {
1034 *aPlaceholderType = PLACEHOLDER_FOR_TOPLAYER;
1035 if (disp->mPosition == StylePositionProperty::Fixed) {
1036 *aPlaceholderType |= PLACEHOLDER_FOR_FIXEDPOS;
1037 return &mTopLayerFixedList;
1039 *aPlaceholderType |= PLACEHOLDER_FOR_ABSPOS;
1040 return &mTopLayerAbsoluteList;
1042 if (disp->mPosition == StylePositionProperty::Absolute) {
1043 *aPlaceholderType = PLACEHOLDER_FOR_ABSPOS;
1044 return &mAbsoluteList;
1046 if (disp->mPosition == StylePositionProperty::Fixed) {
1047 *aPlaceholderType = PLACEHOLDER_FOR_FIXEDPOS;
1048 return &GetFixedList();
1051 return nullptr;
1054 void nsFrameConstructorState::ConstructBackdropFrameFor(nsIContent* aContent,
1055 nsIFrame* aFrame) {
1056 MOZ_ASSERT(aFrame->StyleDisplay()->mTopLayer == StyleTopLayer::Top);
1057 nsContainerFrame* frame = do_QueryFrame(aFrame);
1058 if (!frame) {
1059 NS_WARNING("Cannot create backdrop frame for non-container frame");
1060 return;
1063 RefPtr<ComputedStyle> style =
1064 mPresShell->StyleSet()->ResolvePseudoElementStyle(
1065 *aContent->AsElement(), PseudoStyleType::backdrop,
1066 /* aParentStyle */ nullptr);
1067 MOZ_ASSERT(style->StyleDisplay()->mTopLayer == StyleTopLayer::Top);
1068 nsContainerFrame* parentFrame =
1069 GetGeometricParent(*style->StyleDisplay(), nullptr);
1071 nsBackdropFrame* backdropFrame =
1072 new (mPresShell) nsBackdropFrame(style, mPresShell->GetPresContext());
1073 backdropFrame->Init(aContent, parentFrame, nullptr);
1075 nsFrameState placeholderType;
1076 AbsoluteFrameList* frameList =
1077 GetOutOfFlowFrameList(backdropFrame, true, true, &placeholderType);
1078 MOZ_ASSERT(placeholderType & PLACEHOLDER_FOR_TOPLAYER);
1080 nsIFrame* placeholder = nsCSSFrameConstructor::CreatePlaceholderFrameFor(
1081 mPresShell, aContent, backdropFrame, frame, nullptr, placeholderType);
1082 frame->SetInitialChildList(FrameChildListID::Backdrop,
1083 nsFrameList(placeholder, placeholder));
1085 frameList->AppendFrame(nullptr, backdropFrame);
1088 void nsFrameConstructorState::AddChild(
1089 nsIFrame* aNewFrame, nsFrameList& aFrameList, nsIContent* aContent,
1090 nsContainerFrame* aParentFrame, bool aCanBePositioned, bool aCanBeFloated,
1091 bool aInsertAfter, nsIFrame* aInsertAfterFrame) {
1092 MOZ_ASSERT(!aNewFrame->GetNextSibling(), "Shouldn't happen");
1094 nsFrameState placeholderType;
1095 AbsoluteFrameList* outOfFlowFrameList = GetOutOfFlowFrameList(
1096 aNewFrame, aCanBePositioned, aCanBeFloated, &placeholderType);
1098 // The comments in GetGeometricParent regarding root table frames
1099 // all apply here, unfortunately. Thus, we need to check whether
1100 // the returned frame items really has containing block.
1101 nsFrameList* frameList;
1102 if (outOfFlowFrameList && outOfFlowFrameList->mContainingBlock) {
1103 MOZ_ASSERT(aNewFrame->GetParent() == outOfFlowFrameList->mContainingBlock,
1104 "Parent of the frame is not the containing block?");
1105 frameList = outOfFlowFrameList;
1106 } else {
1107 frameList = &aFrameList;
1108 placeholderType = nsFrameState(0);
1111 if (placeholderType) {
1112 NS_ASSERTION(frameList != &aFrameList,
1113 "Putting frame in-flow _and_ want a placeholder?");
1114 nsIFrame* placeholderFrame =
1115 nsCSSFrameConstructor::CreatePlaceholderFrameFor(
1116 mPresShell, aContent, aNewFrame, aParentFrame, nullptr,
1117 placeholderType);
1119 placeholderFrame->AddStateBits(mAdditionalStateBits);
1120 // Add the placeholder frame to the flow
1121 aFrameList.AppendFrame(nullptr, placeholderFrame);
1123 if (placeholderType & PLACEHOLDER_FOR_TOPLAYER) {
1124 ConstructBackdropFrameFor(aContent, aNewFrame);
1127 #ifdef DEBUG
1128 else {
1129 NS_ASSERTION(aNewFrame->GetParent() == aParentFrame,
1130 "In-flow frame has wrong parent");
1132 #endif
1134 if (aInsertAfter) {
1135 frameList->InsertFrame(nullptr, aInsertAfterFrame, aNewFrame);
1136 } else {
1137 frameList->AppendFrame(nullptr, aNewFrame);
1141 // Some of this function's callers recurse 1000 levels deep in crashtests. On
1142 // platforms where stack limits are low, we can't afford to incorporate this
1143 // function's `AutoTArray`s into its callers' stack frames, so disable inlining.
1144 MOZ_NEVER_INLINE void nsFrameConstructorState::ProcessFrameInsertions(
1145 AbsoluteFrameList& aFrameList, FrameChildListID aChildListID) {
1146 #define NS_NONXUL_LIST_TEST \
1147 (&aFrameList == &mFloatedList && aChildListID == FrameChildListID::Float) || \
1148 ((&aFrameList == &mAbsoluteList || \
1149 &aFrameList == &mTopLayerAbsoluteList) && \
1150 aChildListID == FrameChildListID::Absolute) || \
1151 ((&aFrameList == &mFixedList || &aFrameList == &mTopLayerFixedList) && \
1152 aChildListID == FrameChildListID::Fixed)
1153 MOZ_ASSERT(NS_NONXUL_LIST_TEST,
1154 "Unexpected aFrameList/aChildListID combination");
1156 if (aFrameList.IsEmpty()) {
1157 return;
1160 nsContainerFrame* containingBlock = aFrameList.mContainingBlock;
1162 NS_ASSERTION(containingBlock, "Child list without containing block?");
1164 if (aChildListID == FrameChildListID::Fixed) {
1165 // Put this frame on the transformed-frame's abs-pos list instead, if
1166 // it has abs-pos children instead of fixed-pos children.
1167 aChildListID = containingBlock->GetAbsoluteListID();
1170 // Insert the frames hanging out in aItems. We can use SetInitialChildList()
1171 // if the containing block hasn't been reflowed yet (so NS_FRAME_FIRST_REFLOW
1172 // is set) and doesn't have any frames in the aChildListID child list yet.
1173 const nsFrameList& childList = containingBlock->GetChildList(aChildListID);
1174 if (childList.IsEmpty() &&
1175 containingBlock->HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
1176 // If we're injecting absolutely positioned frames, inject them on the
1177 // absolute containing block
1178 if (aChildListID == containingBlock->GetAbsoluteListID()) {
1179 containingBlock->GetAbsoluteContainingBlock()->SetInitialChildList(
1180 containingBlock, aChildListID, std::move(aFrameList));
1181 } else {
1182 containingBlock->SetInitialChildList(aChildListID, std::move(aFrameList));
1184 } else if (aChildListID == FrameChildListID::Fixed ||
1185 aChildListID == FrameChildListID::Absolute) {
1186 // The order is not important for abs-pos/fixed-pos frame list, just
1187 // append the frame items to the list directly.
1188 mFrameManager->AppendFrames(containingBlock, aChildListID,
1189 std::move(aFrameList));
1190 } else {
1191 // Note that whether the frame construction context is doing an append or
1192 // not is not helpful here, since it could be appending to some frame in
1193 // the middle of the document, which means we're not necessarily
1194 // appending to the children of the containing block.
1196 // We need to make sure the 'append to the end of document' case is fast.
1197 // So first test the last child of the containing block
1198 nsIFrame* lastChild = childList.LastChild();
1200 // CompareTreePosition uses placeholder hierarchy for out of flow frames,
1201 // so this will make out-of-flows respect the ordering of placeholders,
1202 // which is great because it takes care of anonymous content.
1203 nsIFrame* firstNewFrame = aFrameList.FirstChild();
1205 // Cache the ancestor chain so that we can reuse it if needed.
1206 AutoTArray<nsIFrame*, 20> firstNewFrameAncestors;
1207 nsIFrame* notCommonAncestor = nullptr;
1208 if (lastChild) {
1209 notCommonAncestor = nsLayoutUtils::FillAncestors(
1210 firstNewFrame, containingBlock, &firstNewFrameAncestors);
1213 if (!lastChild || nsLayoutUtils::CompareTreePosition(
1214 lastChild, firstNewFrame, firstNewFrameAncestors,
1215 notCommonAncestor ? containingBlock : nullptr) < 0) {
1216 // no lastChild, or lastChild comes before the new children, so just
1217 // append
1218 mFrameManager->AppendFrames(containingBlock, aChildListID,
1219 std::move(aFrameList));
1220 } else {
1221 // Try the other children. First collect them to an array so that a
1222 // reasonable fast binary search can be used to find the insertion point.
1223 AutoTArray<nsIFrame*, 128> children;
1224 for (nsIFrame* f = childList.FirstChild(); f != lastChild;
1225 f = f->GetNextSibling()) {
1226 children.AppendElement(f);
1229 nsIFrame* insertionPoint = nullptr;
1230 int32_t imin = 0;
1231 int32_t max = children.Length();
1232 while (max > imin) {
1233 int32_t imid = imin + ((max - imin) / 2);
1234 nsIFrame* f = children[imid];
1235 int32_t compare = nsLayoutUtils::CompareTreePosition(
1236 f, firstNewFrame, firstNewFrameAncestors,
1237 notCommonAncestor ? containingBlock : nullptr);
1238 if (compare > 0) {
1239 // f is after the new frame.
1240 max = imid;
1241 insertionPoint = imid > 0 ? children[imid - 1] : nullptr;
1242 } else if (compare < 0) {
1243 // f is before the new frame.
1244 imin = imid + 1;
1245 insertionPoint = f;
1246 } else {
1247 // This is for the old behavior. Should be removed once it is
1248 // guaranteed that CompareTreePosition can't return 0!
1249 // See bug 928645.
1250 NS_WARNING("Something odd happening???");
1251 insertionPoint = nullptr;
1252 for (uint32_t i = 0; i < children.Length(); ++i) {
1253 nsIFrame* f = children[i];
1254 if (nsLayoutUtils::CompareTreePosition(
1255 f, firstNewFrame, firstNewFrameAncestors,
1256 notCommonAncestor ? containingBlock : nullptr) > 0) {
1257 break;
1259 insertionPoint = f;
1261 break;
1264 mFrameManager->InsertFrames(containingBlock, aChildListID, insertionPoint,
1265 std::move(aFrameList));
1269 MOZ_ASSERT(aFrameList.IsEmpty(), "How did that happen?");
1272 nsFrameConstructorSaveState::~nsFrameConstructorSaveState() {
1273 // Restore the state
1274 if (mList) {
1275 MOZ_ASSERT(mState, "Can't have mList set without having a state!");
1276 mState->ProcessFrameInsertions(*mList, mChildListID);
1278 if (mList == &mState->mAbsoluteList) {
1279 mState->mFixedPosIsAbsPos = mSavedFixedPosIsAbsPos;
1280 // mAbsoluteList was moved to mFixedList, so move mFixedList back
1281 // and repair the old mFixedList now.
1282 if (mSavedFixedPosIsAbsPos) {
1283 mState->mAbsoluteList = std::move(mState->mFixedList);
1284 mState->mFixedList = std::move(mSavedList);
1285 } else {
1286 mState->mAbsoluteList = std::move(mSavedList);
1288 } else {
1289 mState->mFloatedList = std::move(mSavedList);
1292 MOZ_ASSERT(mSavedList.IsEmpty(),
1293 "Frames in mSavedList should've moved back into mState!");
1294 MOZ_ASSERT(!mList->LastChild() || !mList->LastChild()->GetNextSibling(),
1295 "Something corrupted our list!");
1300 * Moves aFrameList from aOldParent to aNewParent. This updates the parent
1301 * pointer of the frames in the list, and reparents their views as needed.
1302 * nsIFrame::SetParent sets the NS_FRAME_HAS_VIEW bit on aNewParent and its
1303 * ancestors as needed. Then it sets the list as the initial child list
1304 * on aNewParent, unless aNewParent either already has kids or has been
1305 * reflowed; in that case it appends the new frames. Note that this
1306 * method differs from ReparentFrames in that it doesn't change the kids'
1307 * style.
1309 // XXXbz Since this is only used for {ib} splits, could we just copy the view
1310 // bits from aOldParent to aNewParent and then use the
1311 // nsFrameList::ApplySetParent? That would still leave us doing two passes
1312 // over the list, of course; if we really wanted to we could factor out the
1313 // relevant part of ReparentFrameViewList, I suppose... Or just get rid of
1314 // views, which would make most of this function go away.
1315 static void MoveChildrenTo(nsIFrame* aOldParent, nsContainerFrame* aNewParent,
1316 nsFrameList& aFrameList) {
1317 bool sameGrandParent = aOldParent->GetParent() == aNewParent->GetParent();
1319 if (aNewParent->HasView() || aOldParent->HasView() || !sameGrandParent) {
1320 // Move the frames into the new view
1321 nsContainerFrame::ReparentFrameViewList(aFrameList, aOldParent, aNewParent);
1324 aFrameList.ApplySetParent(aNewParent);
1326 if (aNewParent->PrincipalChildList().IsEmpty() &&
1327 aNewParent->HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
1328 aNewParent->SetInitialChildList(FrameChildListID::Principal,
1329 std::move(aFrameList));
1330 } else {
1331 aNewParent->AppendFrames(FrameChildListID::Principal,
1332 std::move(aFrameList));
1336 static bool MaybeApplyPageName(nsFrameConstructorState& aState,
1337 const StylePageName& aPageName) {
1338 if (aPageName.IsPageName()) {
1339 aState.mAutoPageNameValue = aPageName.AsPageName().AsAtom();
1340 return true;
1342 MOZ_ASSERT(aPageName.IsAuto(), "Impossible page name");
1343 return false;
1346 static void EnsureAutoPageName(nsFrameConstructorState& aState,
1347 const nsContainerFrame* const aFrame) {
1348 // Check if we need to figure out our used page name.
1349 // When building the entire document, this should only happen for the
1350 // root, which will mean the loop will immediately end. Either way, this will
1351 // only happen once for each time the frame constructor is run.
1352 if (aState.mAutoPageNameValue) {
1353 return;
1356 for (const nsContainerFrame* frame = aFrame; frame;
1357 frame = frame->GetParent()) {
1358 const StylePageName& pageName = frame->StylePage()->mPage;
1359 if (MaybeApplyPageName(aState, pageName)) {
1360 return;
1363 // Ensure that a root with `page: auto` gets an empty page name
1364 // https://drafts.csswg.org/css-page-3/#using-named-pages
1365 aState.mAutoPageNameValue = nsGkAtoms::_empty;
1368 nsCSSFrameConstructor::AutoFrameConstructionPageName::
1369 AutoFrameConstructionPageName(nsFrameConstructorState& aState,
1370 nsIFrame* const aFrame)
1371 : mState(aState), mNameToRestore(nullptr) {
1372 if (!aState.mPresContext->IsPaginated() ||
1373 !StaticPrefs::layout_css_named_pages_enabled()) {
1374 MOZ_ASSERT(!aState.mAutoPageNameValue,
1375 "Page name should not have been set");
1376 return;
1378 #ifdef DEBUG
1379 MOZ_ASSERT(!aFrame->mWasVisitedByAutoFrameConstructionPageName,
1380 "Frame should only have been visited once");
1381 aFrame->mWasVisitedByAutoFrameConstructionPageName = true;
1382 #endif
1384 EnsureAutoPageName(aState, aFrame->GetParent());
1385 mNameToRestore = aState.mAutoPageNameValue;
1387 MOZ_ASSERT(mNameToRestore,
1388 "Page name should have been found by EnsureAutoPageName");
1389 MaybeApplyPageName(aState, aFrame->StylePage()->mPage);
1390 aFrame->SetAutoPageValue(aState.mAutoPageNameValue);
1393 nsCSSFrameConstructor::AutoFrameConstructionPageName::
1394 ~AutoFrameConstructionPageName() {
1395 // This isn't actually useful when not in paginated layout or when
1396 // layout.css.named-pages.enabled is false, but it's very likely cheaper to
1397 // unconditionally write this pointer than to test for paginated layout and
1398 // the value of the pref and then branch on the result.
1399 mState.mAutoPageNameValue = mNameToRestore;
1402 //----------------------------------------------------------------------
1404 nsCSSFrameConstructor::nsCSSFrameConstructor(Document* aDocument,
1405 PresShell* aPresShell)
1406 : nsFrameManager(aPresShell),
1407 mDocument(aDocument),
1408 mRootElementFrame(nullptr),
1409 mRootElementStyleFrame(nullptr),
1410 mDocElementContainingBlock(nullptr),
1411 mPageSequenceFrame(nullptr),
1412 mFirstFreeFCItem(nullptr),
1413 mFCItemsInUse(0),
1414 mCurrentDepth(0),
1415 mQuotesDirty(false),
1416 mCountersDirty(false),
1417 mAlwaysCreateFramesForIgnorableWhitespace(false) {
1418 #ifdef DEBUG
1419 static bool gFirstTime = true;
1420 if (gFirstTime) {
1421 gFirstTime = false;
1422 char* flags = PR_GetEnv("GECKO_FRAMECTOR_DEBUG_FLAGS");
1423 if (flags) {
1424 bool error = false;
1425 for (;;) {
1426 char* comma = strchr(flags, ',');
1427 if (comma) *comma = '\0';
1429 bool found = false;
1430 FrameCtorDebugFlags* flag = gFlags;
1431 FrameCtorDebugFlags* limit = gFlags + NUM_DEBUG_FLAGS;
1432 while (flag < limit) {
1433 if (nsCRT::strcasecmp(flag->name, flags) == 0) {
1434 *(flag->on) = true;
1435 printf("nsCSSFrameConstructor: setting %s debug flag on\n",
1436 flag->name);
1437 found = true;
1438 break;
1440 ++flag;
1443 if (!found) error = true;
1445 if (!comma) break;
1447 *comma = ',';
1448 flags = comma + 1;
1451 if (error) {
1452 printf("Here are the available GECKO_FRAMECTOR_DEBUG_FLAGS:\n");
1453 FrameCtorDebugFlags* flag = gFlags;
1454 FrameCtorDebugFlags* limit = gFlags + NUM_DEBUG_FLAGS;
1455 while (flag < limit) {
1456 printf(" %s\n", flag->name);
1457 ++flag;
1459 printf(
1460 "Note: GECKO_FRAMECTOR_DEBUG_FLAGS is a comma separated list of "
1461 "flag\n");
1462 printf("names (no whitespace)\n");
1466 #endif
1469 void nsCSSFrameConstructor::NotifyDestroyingFrame(nsIFrame* aFrame) {
1470 if (aFrame->HasAnyStateBits(NS_FRAME_GENERATED_CONTENT) &&
1471 mContainStyleScopeManager.DestroyQuoteNodesFor(aFrame)) {
1472 QuotesDirty();
1475 if (aFrame->HasAnyStateBits(NS_FRAME_HAS_CSS_COUNTER_STYLE) &&
1476 mContainStyleScopeManager.DestroyCounterNodesFor(aFrame)) {
1477 // Technically we don't need to update anything if we destroyed only
1478 // USE nodes. However, this is unlikely to happen in the real world
1479 // since USE nodes generally go along with INCREMENT nodes.
1480 CountersDirty();
1483 if (aFrame->StyleDisplay()->IsContainStyle()) {
1484 mContainStyleScopeManager.DestroyScopesFor(aFrame);
1487 RestyleManager()->NotifyDestroyingFrame(aFrame);
1490 struct nsGenConInitializer {
1491 UniquePtr<nsGenConNode> mNode;
1492 nsGenConList* mList;
1493 void (nsCSSFrameConstructor::*mDirtyAll)();
1495 nsGenConInitializer(UniquePtr<nsGenConNode> aNode, nsGenConList* aList,
1496 void (nsCSSFrameConstructor::*aDirtyAll)())
1497 : mNode(std::move(aNode)), mList(aList), mDirtyAll(aDirtyAll) {}
1500 already_AddRefed<nsIContent> nsCSSFrameConstructor::CreateGenConTextNode(
1501 nsFrameConstructorState& aState, const nsAString& aString,
1502 UniquePtr<nsGenConInitializer> aInitializer) {
1503 RefPtr<nsTextNode> content = new (mDocument->NodeInfoManager())
1504 nsTextNode(mDocument->NodeInfoManager());
1505 content->SetText(aString, false);
1506 if (aInitializer) {
1507 aInitializer->mNode->mText = content;
1508 content->SetProperty(nsGkAtoms::genConInitializerProperty,
1509 aInitializer.release(),
1510 nsINode::DeleteProperty<nsGenConInitializer>);
1511 aState.mGeneratedContentWithInitializer.AppendElement(content);
1513 return content.forget();
1516 void nsCSSFrameConstructor::CreateGeneratedContent(
1517 nsFrameConstructorState& aState, Element& aOriginatingElement,
1518 ComputedStyle& aPseudoStyle, uint32_t aContentIndex,
1519 const FunctionRef<void(nsIContent*)> aAddChild) {
1520 using Type = StyleContentItem::Tag;
1521 // Get the content value
1522 const auto& item = aPseudoStyle.StyleContent()->ContentAt(aContentIndex);
1523 const Type type = item.tag;
1525 switch (type) {
1526 case Type::Image: {
1527 RefPtr c = GeneratedImageContent::Create(*mDocument, aContentIndex);
1528 aAddChild(c);
1529 return;
1532 case Type::String: {
1533 RefPtr text = CreateGenConTextNode(
1534 aState, NS_ConvertUTF8toUTF16(item.AsString().AsString()), nullptr);
1535 aAddChild(text);
1536 return;
1539 case Type::Attr: {
1540 const auto& attr = item.AsAttr();
1541 RefPtr<nsAtom> attrName = attr.attribute.AsAtom();
1542 int32_t attrNameSpace = kNameSpaceID_None;
1543 RefPtr<nsAtom> ns = attr.namespace_url.AsAtom();
1544 if (!ns->IsEmpty()) {
1545 nsresult rv = nsNameSpaceManager::GetInstance()->RegisterNameSpace(
1546 ns.forget(), attrNameSpace);
1547 NS_ENSURE_SUCCESS_VOID(rv);
1550 if (mDocument->IsHTMLDocument() && aOriginatingElement.IsHTMLElement()) {
1551 ToLowerCaseASCII(attrName);
1554 nsCOMPtr<nsIContent> content;
1555 NS_NewAttributeContent(mDocument->NodeInfoManager(), attrNameSpace,
1556 attrName, getter_AddRefs(content));
1557 aAddChild(content);
1558 return;
1561 case Type::Counter:
1562 case Type::Counters: {
1563 RefPtr<nsAtom> name;
1564 CounterStylePtr ptr;
1565 nsString separator;
1566 if (type == Type::Counter) {
1567 auto& counter = item.AsCounter();
1568 name = counter._0.AsAtom();
1569 ptr = CounterStylePtr::FromStyle(counter._1);
1570 } else {
1571 auto& counters = item.AsCounters();
1572 name = counters._0.AsAtom();
1573 CopyUTF8toUTF16(counters._1.AsString(), separator);
1574 ptr = CounterStylePtr::FromStyle(counters._2);
1577 auto* counterList = mContainStyleScopeManager.GetOrCreateCounterList(
1578 aOriginatingElement, name);
1579 auto node = MakeUnique<nsCounterUseNode>(
1580 std::move(ptr), std::move(separator), aContentIndex,
1581 /* aAllCounters = */ type == Type::Counters);
1583 auto initializer = MakeUnique<nsGenConInitializer>(
1584 std::move(node), counterList, &nsCSSFrameConstructor::CountersDirty);
1585 RefPtr c = CreateGenConTextNode(aState, u""_ns, std::move(initializer));
1586 aAddChild(c);
1587 return;
1589 case Type::OpenQuote:
1590 case Type::CloseQuote:
1591 case Type::NoOpenQuote:
1592 case Type::NoCloseQuote: {
1593 auto node = MakeUnique<nsQuoteNode>(type, aContentIndex);
1594 auto* quoteList =
1595 mContainStyleScopeManager.QuoteListFor(aOriginatingElement);
1596 auto initializer = MakeUnique<nsGenConInitializer>(
1597 std::move(node), quoteList, &nsCSSFrameConstructor::QuotesDirty);
1598 RefPtr c = CreateGenConTextNode(aState, u""_ns, std::move(initializer));
1599 aAddChild(c);
1600 return;
1603 case Type::MozLabelContent: {
1604 nsAutoString accesskey;
1605 if (!aOriginatingElement.GetAttr(nsGkAtoms::accesskey, accesskey) ||
1606 accesskey.IsEmpty() || !LookAndFeel::GetMenuAccessKey()) {
1607 // Easy path: just return a regular value attribute content.
1608 nsCOMPtr<nsIContent> content;
1609 NS_NewAttributeContent(mDocument->NodeInfoManager(), kNameSpaceID_None,
1610 nsGkAtoms::value, getter_AddRefs(content));
1611 aAddChild(content);
1612 return;
1615 nsAutoString value;
1616 aOriginatingElement.GetAttr(nsGkAtoms::value, value);
1618 auto AppendAccessKeyLabel = [&] {
1619 // Always append accesskey text in uppercase, see bug 1806167.
1620 ToUpperCase(accesskey);
1621 nsAutoString accessKeyLabel = u"("_ns + accesskey + u")"_ns;
1622 if (!StringEndsWith(value, accessKeyLabel)) {
1623 if (nsTextBoxFrame::InsertSeparatorBeforeAccessKey() &&
1624 !value.IsEmpty() && !NS_IS_SPACE(value.Last())) {
1625 value.Append(' ');
1627 value.Append(accessKeyLabel);
1630 if (nsTextBoxFrame::AlwaysAppendAccessKey()) {
1631 AppendAccessKeyLabel();
1632 RefPtr c = CreateGenConTextNode(aState, value, nullptr);
1633 aAddChild(c);
1634 return;
1637 const auto accessKeyStart = [&]() -> Maybe<size_t> {
1638 nsAString::const_iterator start, end;
1639 value.BeginReading(start);
1640 value.EndReading(end);
1642 const auto originalStart = start;
1643 // not appending access key - do case-sensitive search
1644 // first
1645 bool found = true;
1646 if (!FindInReadable(accesskey, start, end)) {
1647 start = originalStart;
1648 // didn't find it - perform a case-insensitive search
1649 found = FindInReadable(accesskey, start, end,
1650 nsCaseInsensitiveStringComparator);
1652 if (!found) {
1653 return Nothing();
1655 return Some(Distance(originalStart, start));
1656 }();
1658 if (accessKeyStart.isNothing()) {
1659 AppendAccessKeyLabel();
1660 RefPtr c = CreateGenConTextNode(aState, value, nullptr);
1661 aAddChild(c);
1662 return;
1665 if (*accessKeyStart != 0) {
1666 RefPtr beginning = CreateGenConTextNode(
1667 aState, Substring(value, 0, *accessKeyStart), nullptr);
1668 aAddChild(beginning);
1672 RefPtr accessKeyText = CreateGenConTextNode(
1673 aState, Substring(value, *accessKeyStart, accesskey.Length()),
1674 nullptr);
1675 RefPtr<nsIContent> underline =
1676 mDocument->CreateHTMLElement(nsGkAtoms::u);
1677 underline->AppendChildTo(accessKeyText, /* aNotify = */ false,
1678 IgnoreErrors());
1679 aAddChild(underline);
1682 size_t accessKeyEnd = *accessKeyStart + accesskey.Length();
1683 if (accessKeyEnd != value.Length()) {
1684 RefPtr valueEnd = CreateGenConTextNode(
1685 aState, Substring(value, *accessKeyStart + accesskey.Length()),
1686 nullptr);
1687 aAddChild(valueEnd);
1689 break;
1691 case Type::MozAltContent: {
1692 // Use the "alt" attribute; if that fails and the node is an HTML
1693 // <input>, try the value attribute and then fall back to some default
1694 // localized text we have.
1695 // XXX what if the 'alt' attribute is added later, how will we
1696 // detect that and do the right thing here?
1697 if (aOriginatingElement.HasAttr(nsGkAtoms::alt)) {
1698 nsCOMPtr<nsIContent> content;
1699 NS_NewAttributeContent(mDocument->NodeInfoManager(), kNameSpaceID_None,
1700 nsGkAtoms::alt, getter_AddRefs(content));
1701 aAddChild(content);
1702 return;
1705 if (aOriginatingElement.IsHTMLElement(nsGkAtoms::input)) {
1706 if (aOriginatingElement.HasAttr(nsGkAtoms::value)) {
1707 nsCOMPtr<nsIContent> content;
1708 NS_NewAttributeContent(mDocument->NodeInfoManager(),
1709 kNameSpaceID_None, nsGkAtoms::value,
1710 getter_AddRefs(content));
1711 aAddChild(content);
1712 return;
1715 nsAutoString temp;
1716 nsContentUtils::GetMaybeLocalizedString(
1717 nsContentUtils::eFORMS_PROPERTIES, "Submit", mDocument, temp);
1718 RefPtr c = CreateGenConTextNode(aState, temp, nullptr);
1719 aAddChild(c);
1720 return;
1722 break;
1726 return;
1729 void nsCSSFrameConstructor::CreateGeneratedContentFromListStyle(
1730 nsFrameConstructorState& aState, Element& aOriginatingElement,
1731 const ComputedStyle& aPseudoStyle,
1732 const FunctionRef<void(nsIContent*)> aAddChild) {
1733 const nsStyleList* styleList = aPseudoStyle.StyleList();
1734 if (!styleList->mListStyleImage.IsNone()) {
1735 RefPtr<nsIContent> child =
1736 GeneratedImageContent::CreateForListStyleImage(*mDocument);
1737 aAddChild(child);
1738 child = CreateGenConTextNode(aState, u" "_ns, nullptr);
1739 aAddChild(child);
1740 return;
1742 CreateGeneratedContentFromListStyleType(aState, aOriginatingElement,
1743 aPseudoStyle, aAddChild);
1746 void nsCSSFrameConstructor::CreateGeneratedContentFromListStyleType(
1747 nsFrameConstructorState& aState, Element& aOriginatingElement,
1748 const ComputedStyle& aPseudoStyle,
1749 const FunctionRef<void(nsIContent*)> aAddChild) {
1750 const nsStyleList* styleList = aPseudoStyle.StyleList();
1751 CounterStyle* counterStyle =
1752 mPresShell->GetPresContext()->CounterStyleManager()->ResolveCounterStyle(
1753 styleList->mCounterStyle);
1754 bool needUseNode = false;
1755 switch (counterStyle->GetStyle()) {
1756 case ListStyle::None:
1757 return;
1758 case ListStyle::Disc:
1759 case ListStyle::Circle:
1760 case ListStyle::Square:
1761 case ListStyle::DisclosureClosed:
1762 case ListStyle::DisclosureOpen:
1763 break;
1764 default:
1765 const auto* anonStyle = counterStyle->AsAnonymous();
1766 if (!anonStyle || !anonStyle->IsSingleString()) {
1767 needUseNode = true;
1771 auto node = MakeUnique<nsCounterUseNode>(nsCounterUseNode::ForLegacyBullet,
1772 styleList->mCounterStyle);
1773 if (!needUseNode) {
1774 nsAutoString text;
1775 node->GetText(WritingMode(&aPseudoStyle), counterStyle, text);
1776 // Note that we're done with 'node' in this case. It's not inserted into
1777 // any list so it's deleted when we return.
1778 RefPtr<nsIContent> child = CreateGenConTextNode(aState, text, nullptr);
1779 aAddChild(child);
1780 return;
1783 auto* counterList = mContainStyleScopeManager.GetOrCreateCounterList(
1784 aOriginatingElement, nsGkAtoms::list_item);
1785 auto initializer = MakeUnique<nsGenConInitializer>(
1786 std::move(node), counterList, &nsCSSFrameConstructor::CountersDirty);
1787 RefPtr<nsIContent> child =
1788 CreateGenConTextNode(aState, EmptyString(), std::move(initializer));
1789 aAddChild(child);
1792 // Frames for these may not be leaves in the proper sense, but we still don't
1793 // want to expose generated content on them. For the purposes of the page they
1794 // should be leaves.
1795 static bool HasUAWidget(const Element& aOriginatingElement) {
1796 const ShadowRoot* sr = aOriginatingElement.GetShadowRoot();
1797 return sr && sr->IsUAWidget();
1801 * aParentFrame - the frame that should be the parent of the generated
1802 * content. This is the frame for the corresponding content node,
1803 * which must not be a leaf frame.
1805 * Any items created are added to aItems.
1807 * We create an XML element (tag _moz_generated_content_before/after/marker)
1808 * representing the pseudoelement. We create a DOM node for each 'content'
1809 * item and make those nodes the children of the XML element. Then we create
1810 * a frame subtree for the XML element as if it were a regular child of
1811 * aParentFrame/aParentContent, giving the XML element the ::before, ::after
1812 * or ::marker style.
1814 void nsCSSFrameConstructor::CreateGeneratedContentItem(
1815 nsFrameConstructorState& aState, nsContainerFrame* aParentFrame,
1816 Element& aOriginatingElement, ComputedStyle& aStyle,
1817 PseudoStyleType aPseudoElement, FrameConstructionItemList& aItems,
1818 ItemFlags aExtraFlags) {
1819 MOZ_ASSERT(aPseudoElement == PseudoStyleType::before ||
1820 aPseudoElement == PseudoStyleType::after ||
1821 aPseudoElement == PseudoStyleType::marker,
1822 "unexpected aPseudoElement");
1824 if (HasUAWidget(aOriginatingElement) &&
1825 !aOriginatingElement.IsHTMLElement(nsGkAtoms::details)) {
1826 return;
1829 ServoStyleSet* styleSet = mPresShell->StyleSet();
1831 // Probe for the existence of the pseudo-element.
1832 // |ProbePseudoElementStyle| checks the relevant properties for the pseudo.
1833 // It only returns a non-null value if the pseudo should exist.
1834 RefPtr<ComputedStyle> pseudoStyle = styleSet->ProbePseudoElementStyle(
1835 aOriginatingElement, aPseudoElement, &aStyle);
1836 if (!pseudoStyle) {
1837 return;
1840 nsAtom* elemName = nullptr;
1841 nsAtom* property = nullptr;
1842 switch (aPseudoElement) {
1843 case PseudoStyleType::before:
1844 elemName = nsGkAtoms::mozgeneratedcontentbefore;
1845 property = nsGkAtoms::beforePseudoProperty;
1846 break;
1847 case PseudoStyleType::after:
1848 elemName = nsGkAtoms::mozgeneratedcontentafter;
1849 property = nsGkAtoms::afterPseudoProperty;
1850 break;
1851 case PseudoStyleType::marker:
1852 // We want to get a marker style even if we match no rules, but we still
1853 // want to check the result of GeneratedContentPseudoExists.
1854 elemName = nsGkAtoms::mozgeneratedcontentmarker;
1855 property = nsGkAtoms::markerPseudoProperty;
1856 break;
1857 default:
1858 MOZ_ASSERT_UNREACHABLE("unexpected aPseudoElement");
1861 RefPtr<NodeInfo> nodeInfo = mDocument->NodeInfoManager()->GetNodeInfo(
1862 elemName, nullptr, kNameSpaceID_None, nsINode::ELEMENT_NODE);
1863 RefPtr<Element> container;
1864 nsresult rv = NS_NewXMLElement(getter_AddRefs(container), nodeInfo.forget());
1865 if (NS_FAILED(rv)) {
1866 return;
1869 // Cleared when the pseudo is unbound from the tree, so no need to store a
1870 // strong reference, nor a destructor.
1871 aOriginatingElement.SetProperty(property, container.get());
1873 container->SetIsNativeAnonymousRoot();
1874 container->SetPseudoElementType(aPseudoElement);
1876 BindContext context(aOriginatingElement, BindContext::ForNativeAnonymous);
1877 rv = container->BindToTree(context, aOriginatingElement);
1878 if (NS_FAILED(rv)) {
1879 container->UnbindFromTree();
1880 return;
1883 // Servo has already eagerly computed the style for the container, so we can
1884 // just stick the style on the element and avoid an additional traversal.
1886 // We don't do this for pseudos that may trigger animations or transitions,
1887 // since those need to be kicked off by the traversal machinery.
1889 // Note that when a pseudo-element animates, we flag the originating element,
1890 // so we check that flag, but we could also a more expensive (but exhaustive)
1891 // check using EffectSet::GetEffectSet, for example.
1892 if (!Servo_ComputedValues_SpecifiesAnimationsOrTransitions(pseudoStyle) &&
1893 !aOriginatingElement.MayHaveAnimations()) {
1894 Servo_SetExplicitStyle(container, pseudoStyle);
1895 } else {
1896 // If animations are involved, we avoid the SetExplicitStyle optimization
1897 // above. We need to grab style with animations from the pseudo element and
1898 // replace old one.
1899 mPresShell->StyleSet()->StyleNewSubtree(container);
1900 pseudoStyle = ServoStyleSet::ResolveServoStyle(*container);
1903 auto AppendChild = [&container, this](nsIContent* aChild) {
1904 // We don't strictly have to set NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE
1905 // here; it would get set under AppendChildTo. But AppendChildTo might
1906 // think that we're going from not being anonymous to being anonymous and
1907 // do some extra work; setting the flag here avoids that.
1908 aChild->SetFlags(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE);
1909 container->AppendChildTo(aChild, false, IgnoreErrors());
1910 if (auto* childElement = Element::FromNode(aChild)) {
1911 // If we created any children elements, Servo needs to traverse them, but
1912 // the root is already set up.
1913 mPresShell->StyleSet()->StyleNewSubtree(childElement);
1916 const uint32_t contentCount = pseudoStyle->StyleContent()->ContentCount();
1917 for (uint32_t contentIndex = 0; contentIndex < contentCount; contentIndex++) {
1918 CreateGeneratedContent(aState, aOriginatingElement, *pseudoStyle,
1919 contentIndex, AppendChild);
1921 // If a ::marker has no 'content' then generate it from its 'list-style-*'.
1922 if (contentCount == 0 && aPseudoElement == PseudoStyleType::marker) {
1923 CreateGeneratedContentFromListStyle(aState, aOriginatingElement,
1924 *pseudoStyle, AppendChild);
1926 auto flags = ItemFlags{ItemFlag::IsGeneratedContent} + aExtraFlags;
1927 AddFrameConstructionItemsInternal(aState, container, aParentFrame, true,
1928 pseudoStyle, flags, aItems);
1931 /****************************************************
1932 ** BEGIN TABLE SECTION
1933 ****************************************************/
1935 // The term pseudo frame is being used instead of anonymous frame, since
1936 // anonymous frame has been used elsewhere to refer to frames that have
1937 // generated content
1939 // Return whether the given frame is a table pseudo-frame. Note that
1940 // cell-content and table-outer frames have pseudo-types, but are always
1941 // created, even for non-anonymous cells and tables respectively. So for those
1942 // we have to examine the cell or table frame to see whether it's a pseudo
1943 // frame. In particular, a lone table caption will have a table wrapper as its
1944 // parent, but will also trigger construction of an empty inner table, which
1945 // will be the one we can examine to see whether the wrapper was a pseudo-frame.
1946 static bool IsTablePseudo(nsIFrame* aFrame) {
1947 auto pseudoType = aFrame->Style()->GetPseudoType();
1948 return pseudoType != PseudoStyleType::NotPseudo &&
1949 (pseudoType == PseudoStyleType::table ||
1950 pseudoType == PseudoStyleType::inlineTable ||
1951 pseudoType == PseudoStyleType::tableColGroup ||
1952 pseudoType == PseudoStyleType::tableRowGroup ||
1953 pseudoType == PseudoStyleType::tableRow ||
1954 pseudoType == PseudoStyleType::tableCell ||
1955 (pseudoType == PseudoStyleType::cellContent &&
1956 aFrame->GetParent()->Style()->GetPseudoType() ==
1957 PseudoStyleType::tableCell) ||
1958 (pseudoType == PseudoStyleType::tableWrapper &&
1959 (aFrame->PrincipalChildList()
1960 .FirstChild()
1961 ->Style()
1962 ->GetPseudoType() == PseudoStyleType::table ||
1963 aFrame->PrincipalChildList()
1964 .FirstChild()
1965 ->Style()
1966 ->GetPseudoType() == PseudoStyleType::inlineTable)));
1969 static bool IsRubyPseudo(nsIFrame* aFrame) {
1970 return RubyUtils::IsRubyPseudo(aFrame->Style()->GetPseudoType());
1973 static bool IsTableOrRubyPseudo(nsIFrame* aFrame) {
1974 return IsTablePseudo(aFrame) || IsRubyPseudo(aFrame);
1977 /* static */
1978 nsCSSFrameConstructor::ParentType nsCSSFrameConstructor::GetParentType(
1979 LayoutFrameType aFrameType) {
1980 if (aFrameType == LayoutFrameType::Table) {
1981 return eTypeTable;
1983 if (aFrameType == LayoutFrameType::TableRowGroup) {
1984 return eTypeRowGroup;
1986 if (aFrameType == LayoutFrameType::TableRow) {
1987 return eTypeRow;
1989 if (aFrameType == LayoutFrameType::TableColGroup) {
1990 return eTypeColGroup;
1992 if (aFrameType == LayoutFrameType::RubyBaseContainer) {
1993 return eTypeRubyBaseContainer;
1995 if (aFrameType == LayoutFrameType::RubyTextContainer) {
1996 return eTypeRubyTextContainer;
1998 if (aFrameType == LayoutFrameType::Ruby) {
1999 return eTypeRuby;
2002 return eTypeBlock;
2005 // Pull all the captions present in aItems out into aCaptions.
2006 static void PullOutCaptionFrames(nsFrameList& aList, nsFrameList& aCaptions) {
2007 nsIFrame* child = aList.FirstChild();
2008 while (child) {
2009 nsIFrame* nextSibling = child->GetNextSibling();
2010 if (child->StyleDisplay()->mDisplay == StyleDisplay::TableCaption) {
2011 aList.RemoveFrame(child);
2012 aCaptions.AppendFrame(nullptr, child);
2014 child = nextSibling;
2018 // Construct the outer, inner table frames and the children frames for the
2019 // table.
2020 // XXX Page break frames for pseudo table frames are not constructed to avoid
2021 // the risk associated with revising the pseudo frame mechanism. The long term
2022 // solution of having frames handle page-break-before/after will solve the
2023 // problem.
2024 nsIFrame* nsCSSFrameConstructor::ConstructTable(nsFrameConstructorState& aState,
2025 FrameConstructionItem& aItem,
2026 nsContainerFrame* aParentFrame,
2027 const nsStyleDisplay* aDisplay,
2028 nsFrameList& aFrameList) {
2029 MOZ_ASSERT(aDisplay->mDisplay == StyleDisplay::Table ||
2030 aDisplay->mDisplay == StyleDisplay::InlineTable,
2031 "Unexpected call");
2033 nsIContent* const content = aItem.mContent;
2034 ComputedStyle* const computedStyle = aItem.mComputedStyle;
2035 const bool isMathMLContent = content->IsMathMLElement();
2037 // create the pseudo SC for the table wrapper as a child of the inner SC
2038 RefPtr<ComputedStyle> outerComputedStyle =
2039 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
2040 PseudoStyleType::tableWrapper, computedStyle);
2042 // Create the table wrapper frame which holds the caption and inner table
2043 // frame
2044 nsContainerFrame* newFrame;
2045 if (isMathMLContent)
2046 newFrame = NS_NewMathMLmtableOuterFrame(mPresShell, outerComputedStyle);
2047 else
2048 newFrame = NS_NewTableWrapperFrame(mPresShell, outerComputedStyle);
2050 nsContainerFrame* geometricParent = aState.GetGeometricParent(
2051 *outerComputedStyle->StyleDisplay(), aParentFrame);
2053 // Init the table wrapper frame
2054 InitAndRestoreFrame(aState, content, geometricParent, newFrame);
2056 // Create the inner table frame
2057 nsContainerFrame* innerFrame;
2058 if (isMathMLContent)
2059 innerFrame = NS_NewMathMLmtableFrame(mPresShell, computedStyle);
2060 else
2061 innerFrame = NS_NewTableFrame(mPresShell, computedStyle);
2063 InitAndRestoreFrame(aState, content, newFrame, innerFrame);
2064 innerFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
2066 // Put the newly created frames into the right child list
2067 SetInitialSingleChild(newFrame, innerFrame);
2069 aState.AddChild(newFrame, aFrameList, content, aParentFrame);
2071 if (!mRootElementFrame) {
2072 // The frame we're constructing will be the root element frame.
2073 SetRootElementFrameAndConstructCanvasAnonContent(newFrame, aState,
2074 aFrameList);
2077 nsFrameList childList;
2079 // Process children
2080 nsFrameConstructorSaveState absoluteSaveState;
2082 // Mark the table frame as an absolute container if needed
2083 newFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
2084 if (newFrame->IsAbsPosContainingBlock()) {
2085 aState.PushAbsoluteContainingBlock(newFrame, newFrame, absoluteSaveState);
2088 nsFrameConstructorSaveState floatSaveState;
2089 aState.MaybePushFloatContainingBlock(innerFrame, floatSaveState);
2091 if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) {
2092 ConstructFramesFromItemList(
2093 aState, aItem.mChildItems, innerFrame,
2094 aItem.mFCData->mBits & FCDATA_IS_WRAPPER_ANON_BOX, childList);
2095 } else {
2096 ProcessChildren(aState, content, computedStyle, innerFrame, true, childList,
2097 false);
2100 nsFrameList captionList;
2101 PullOutCaptionFrames(childList, captionList);
2103 // Set the inner table frame's initial primary list
2104 innerFrame->SetInitialChildList(FrameChildListID::Principal,
2105 std::move(childList));
2107 // Set the table wrapper frame's secondary childlist lists
2108 if (captionList.NotEmpty()) {
2109 captionList.ApplySetParent(newFrame);
2110 newFrame->SetInitialChildList(FrameChildListID::Caption,
2111 std::move(captionList));
2114 return newFrame;
2117 static void MakeTablePartAbsoluteContainingBlock(
2118 nsFrameConstructorState& aState, nsFrameConstructorSaveState& aAbsSaveState,
2119 nsContainerFrame* aFrame) {
2120 // If we're positioned, then we need to become an absolute containing block
2121 // for any absolutely positioned children.
2122 aFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
2123 if (aFrame->IsAbsPosContainingBlock()) {
2124 aState.PushAbsoluteContainingBlock(aFrame, aFrame, aAbsSaveState);
2128 nsIFrame* nsCSSFrameConstructor::ConstructTableRowOrRowGroup(
2129 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
2130 nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay,
2131 nsFrameList& aFrameList) {
2132 MOZ_ASSERT(aDisplay->mDisplay == StyleDisplay::TableRow ||
2133 aDisplay->mDisplay == StyleDisplay::TableRowGroup ||
2134 aDisplay->mDisplay == StyleDisplay::TableFooterGroup ||
2135 aDisplay->mDisplay == StyleDisplay::TableHeaderGroup,
2136 "Not a row or row group");
2137 MOZ_ASSERT(aItem.mComputedStyle->StyleDisplay() == aDisplay,
2138 "Display style doesn't match style");
2139 nsIContent* const content = aItem.mContent;
2140 ComputedStyle* const computedStyle = aItem.mComputedStyle;
2142 nsContainerFrame* newFrame;
2143 if (aDisplay->mDisplay == StyleDisplay::TableRow) {
2144 if (content->IsMathMLElement())
2145 newFrame = NS_NewMathMLmtrFrame(mPresShell, computedStyle);
2146 else
2147 newFrame = NS_NewTableRowFrame(mPresShell, computedStyle);
2148 } else {
2149 newFrame = NS_NewTableRowGroupFrame(mPresShell, computedStyle);
2152 InitAndRestoreFrame(aState, content, aParentFrame, newFrame);
2154 nsFrameConstructorSaveState absoluteSaveState;
2155 MakeTablePartAbsoluteContainingBlock(aState, absoluteSaveState, newFrame);
2157 nsFrameConstructorSaveState floatSaveState;
2158 aState.MaybePushFloatContainingBlock(newFrame, floatSaveState);
2160 nsFrameList childList;
2161 if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) {
2162 ConstructFramesFromItemList(
2163 aState, aItem.mChildItems, newFrame,
2164 aItem.mFCData->mBits & FCDATA_IS_WRAPPER_ANON_BOX, childList);
2165 } else {
2166 ProcessChildren(aState, content, computedStyle, newFrame, true, childList,
2167 false);
2170 newFrame->SetInitialChildList(FrameChildListID::Principal,
2171 std::move(childList));
2172 aFrameList.AppendFrame(nullptr, newFrame);
2173 return newFrame;
2176 nsIFrame* nsCSSFrameConstructor::ConstructTableCol(
2177 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
2178 nsContainerFrame* aParentFrame, const nsStyleDisplay* aStyleDisplay,
2179 nsFrameList& aFrameList) {
2180 nsIContent* const content = aItem.mContent;
2181 ComputedStyle* const computedStyle = aItem.mComputedStyle;
2183 nsTableColFrame* colFrame = NS_NewTableColFrame(mPresShell, computedStyle);
2184 InitAndRestoreFrame(aState, content, aParentFrame, colFrame);
2186 NS_ASSERTION(colFrame->Style() == computedStyle, "Unexpected style");
2188 aFrameList.AppendFrame(nullptr, colFrame);
2190 // construct additional col frames if the col frame has a span > 1
2191 int32_t span = colFrame->GetSpan();
2192 for (int32_t spanX = 1; spanX < span; spanX++) {
2193 nsTableColFrame* newCol = NS_NewTableColFrame(mPresShell, computedStyle);
2194 InitAndRestoreFrame(aState, content, aParentFrame, newCol, false);
2195 aFrameList.LastChild()->SetNextContinuation(newCol);
2196 newCol->SetPrevContinuation(aFrameList.LastChild());
2197 aFrameList.AppendFrame(nullptr, newCol);
2198 newCol->SetColType(eColAnonymousCol);
2201 return colFrame;
2204 nsIFrame* nsCSSFrameConstructor::ConstructTableCell(
2205 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
2206 nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay,
2207 nsFrameList& aFrameList) {
2208 MOZ_ASSERT(aDisplay->mDisplay == StyleDisplay::TableCell, "Unexpected call");
2210 nsIContent* const content = aItem.mContent;
2211 ComputedStyle* const computedStyle = aItem.mComputedStyle;
2212 const bool isMathMLContent = content->IsMathMLElement();
2214 nsTableFrame* tableFrame =
2215 static_cast<nsTableRowFrame*>(aParentFrame)->GetTableFrame();
2216 nsContainerFrame* newFrame;
2217 // <mtable> is border separate in mathml.css and the MathML code doesn't
2218 // implement border collapse. For those users who style <mtable> with border
2219 // collapse, give them the default non-MathML table frames that understand
2220 // border collapse. This won't break us because MathML table frames are all
2221 // subclasses of the default table code, and so we can freely mix <mtable>
2222 // with <mtr> or <tr>, <mtd> or <td>. What will happen is just that non-MathML
2223 // frames won't understand MathML attributes and will therefore miss the
2224 // special handling that the MathML code does.
2225 if (isMathMLContent && !tableFrame->IsBorderCollapse()) {
2226 newFrame = NS_NewMathMLmtdFrame(mPresShell, computedStyle, tableFrame);
2227 } else {
2228 // Warning: If you change this and add a wrapper frame around table cell
2229 // frames, make sure Bug 368554 doesn't regress!
2230 // See IsInAutoWidthTableCellForQuirk() in nsImageFrame.cpp.
2231 newFrame = NS_NewTableCellFrame(mPresShell, computedStyle, tableFrame);
2234 // Initialize the table cell frame
2235 InitAndRestoreFrame(aState, content, aParentFrame, newFrame);
2236 newFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
2238 // Resolve pseudo style and initialize the body cell frame
2239 RefPtr<ComputedStyle> innerPseudoStyle =
2240 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
2241 PseudoStyleType::cellContent, computedStyle);
2243 // Create a block frame that will format the cell's content
2244 nsContainerFrame* cellInnerFrame;
2245 if (isMathMLContent) {
2246 cellInnerFrame = NS_NewMathMLmtdInnerFrame(mPresShell, innerPseudoStyle);
2247 } else {
2248 cellInnerFrame = NS_NewBlockFormattingContext(mPresShell, innerPseudoStyle);
2251 InitAndRestoreFrame(aState, content, newFrame, cellInnerFrame);
2253 nsFrameConstructorSaveState absoluteSaveState;
2254 MakeTablePartAbsoluteContainingBlock(aState, absoluteSaveState, newFrame);
2256 nsFrameConstructorSaveState floatSaveState;
2257 aState.MaybePushFloatContainingBlock(cellInnerFrame, floatSaveState);
2259 nsFrameList childList;
2260 if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) {
2261 AutoFrameConstructionPageName pageNameTracker(aState, cellInnerFrame);
2262 ConstructFramesFromItemList(
2263 aState, aItem.mChildItems, cellInnerFrame,
2264 aItem.mFCData->mBits & FCDATA_IS_WRAPPER_ANON_BOX, childList);
2265 } else {
2266 // Process the child content
2267 ProcessChildren(aState, content, computedStyle, cellInnerFrame, true,
2268 childList, !isMathMLContent);
2271 cellInnerFrame->SetInitialChildList(FrameChildListID::Principal,
2272 std::move(childList));
2273 SetInitialSingleChild(newFrame, cellInnerFrame);
2274 aFrameList.AppendFrame(nullptr, newFrame);
2275 return newFrame;
2278 static inline bool NeedFrameFor(const nsFrameConstructorState& aState,
2279 nsContainerFrame* aParentFrame,
2280 nsIContent* aChildContent) {
2281 // XXX the GetContent() != aChildContent check is needed due to bug 135040.
2282 // Remove it once that's fixed.
2283 MOZ_ASSERT(
2284 !aChildContent->GetPrimaryFrame() || aState.mCreatingExtraFrames ||
2285 aChildContent->GetPrimaryFrame()->GetContent() != aChildContent,
2286 "Why did we get called?");
2288 // don't create a whitespace frame if aParentFrame doesn't want it.
2289 // always create frames for children in generated content. counter(),
2290 // quotes, and attr() content can easily change dynamically and we don't
2291 // want to be reconstructing frames. It's not even clear that these
2292 // should be considered ignorable just because they evaluate to
2293 // whitespace.
2295 // We could handle all this in CreateNeededPseudoContainers or some other
2296 // place after we build our frame construction items, but that would involve
2297 // creating frame construction items for whitespace kids that ignores
2298 // white-space, where we know we'll be dropping them all anyway, and involve
2299 // an extra walk down the frame construction item list.
2300 auto excludesIgnorableWhitespace = [](nsIFrame* aParentFrame) {
2301 return aParentFrame->IsFrameOfType(nsIFrame::eXULBox) ||
2302 aParentFrame->IsFrameOfType(nsIFrame::eMathML);
2304 if (!aParentFrame || !excludesIgnorableWhitespace(aParentFrame) ||
2305 aParentFrame->IsGeneratedContentFrame() || !aChildContent->IsText()) {
2306 return true;
2309 aChildContent->SetFlags(NS_CREATE_FRAME_IF_NON_WHITESPACE |
2310 NS_REFRAME_IF_WHITESPACE);
2311 return !aChildContent->TextIsOnlyWhitespace();
2314 /***********************************************
2315 * END TABLE SECTION
2316 ***********************************************/
2318 void nsCSSFrameConstructor::SetRootElementFrameAndConstructCanvasAnonContent(
2319 nsContainerFrame* aRootElementFrame, nsFrameConstructorState& aState,
2320 nsFrameList& aFrameList) {
2321 MOZ_DIAGNOSTIC_ASSERT(!mRootElementFrame);
2322 mRootElementFrame = aRootElementFrame;
2323 if (mDocElementContainingBlock->IsCanvasFrame()) {
2324 // NOTE(emilio): This is in the reverse order compared to normal anonymous
2325 // children. We usually generate anonymous kids first, then non-anonymous,
2326 // but we generate the doc element frame the other way around. This is fine
2327 // either way, but generating anonymous children in a different order
2328 // requires changing nsCanvasFrame (and a whole lot of other potentially
2329 // unknown code) to look at the last child to find the root frame rather
2330 // than the first child.
2331 ConstructAnonymousContentForCanvas(aState, mDocElementContainingBlock,
2332 aRootElementFrame->GetContent(),
2333 aFrameList);
2337 nsIFrame* nsCSSFrameConstructor::ConstructDocElementFrame(
2338 Element* aDocElement) {
2339 MOZ_ASSERT(GetRootFrame(),
2340 "No viewport? Someone forgot to call ConstructRootFrame!");
2341 MOZ_ASSERT(!mDocElementContainingBlock,
2342 "Shouldn't have a doc element containing block here");
2344 // Resolve a new style for the viewport since it may be affected by a new root
2345 // element style (e.g. a propagated 'direction').
2347 // @see ComputedStyle::ApplyStyleFixups
2349 RefPtr<ComputedStyle> sc =
2350 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
2351 PseudoStyleType::viewport, nullptr);
2352 GetRootFrame()->SetComputedStyleWithoutNotification(sc);
2355 // Ensure the document element is styled at this point.
2356 if (!aDocElement->HasServoData()) {
2357 mPresShell->StyleSet()->StyleNewSubtree(aDocElement);
2359 aDocElement->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
2361 // Make sure to call UpdateViewportScrollStylesOverride before
2362 // SetUpDocElementContainingBlock, since it sets up our scrollbar state
2363 // properly.
2364 DebugOnly<nsIContent*> propagatedScrollFrom;
2365 if (nsPresContext* presContext = mPresShell->GetPresContext()) {
2366 propagatedScrollFrom = presContext->UpdateViewportScrollStylesOverride();
2369 SetUpDocElementContainingBlock(aDocElement);
2371 // This has the side-effect of getting `mFrameTreeState` from our docshell.
2373 // FIXME(emilio): There may be a more sensible time to do this.
2374 if (!mFrameTreeState) {
2375 mPresShell->CaptureHistoryState(getter_AddRefs(mFrameTreeState));
2378 NS_ASSERTION(mDocElementContainingBlock, "Should have parent by now");
2379 nsFrameConstructorState state(
2380 mPresShell,
2381 GetAbsoluteContainingBlock(mDocElementContainingBlock, FIXED_POS),
2382 nullptr, nullptr, do_AddRef(mFrameTreeState));
2384 RefPtr<ComputedStyle> computedStyle =
2385 ServoStyleSet::ResolveServoStyle(*aDocElement);
2387 const nsStyleDisplay* display = computedStyle->StyleDisplay();
2389 // --------- IF SCROLLABLE WRAP IN SCROLLFRAME --------
2391 NS_ASSERTION(!display->IsScrollableOverflow() ||
2392 state.mPresContext->IsPaginated() ||
2393 propagatedScrollFrom == aDocElement,
2394 "Scrollbars should have been propagated to the viewport");
2396 if (MOZ_UNLIKELY(display->mDisplay == StyleDisplay::None)) {
2397 return nullptr;
2400 if (mDocElementContainingBlock->IsCanvasFrame()) {
2401 // This implements "The Principal Writing Mode".
2402 // https://drafts.csswg.org/css-writing-modes-3/#principal-flow
2404 // If there's a <body> element in an HTML document, its writing-mode,
2405 // direction, and text-orientation override the root element's used value.
2407 // We need to copy <body>'s WritingMode to mDocElementContainingBlock before
2408 // construct mRootElementFrame so that anonymous internal frames such as
2409 // <html> with table style can copy their parent frame's mWritingMode in
2410 // nsIFrame::Init().
2411 MOZ_ASSERT(!mRootElementFrame,
2412 "We need to copy <body>'s principal writing-mode before "
2413 "constructing mRootElementFrame.");
2415 const WritingMode propagatedWM = [&] {
2416 const WritingMode rootWM(computedStyle);
2417 if (computedStyle->StyleDisplay()->IsContainAny()) {
2418 return rootWM;
2420 Element* body = mDocument->GetBodyElement();
2421 if (!body) {
2422 return rootWM;
2424 RefPtr<ComputedStyle> bodyStyle = ResolveComputedStyle(body);
2425 if (bodyStyle->StyleDisplay()->IsContainAny()) {
2426 return rootWM;
2428 const WritingMode bodyWM(bodyStyle);
2429 if (bodyWM != rootWM) {
2430 nsContentUtils::ReportToConsole(
2431 nsIScriptError::warningFlag, "Layout"_ns, mDocument,
2432 nsContentUtils::eLAYOUT_PROPERTIES,
2433 "PrincipalWritingModePropagationWarning");
2435 return bodyWM;
2436 }();
2438 mDocElementContainingBlock->PropagateWritingModeToSelfAndAncestors(
2439 propagatedWM);
2442 nsFrameConstructorSaveState docElementContainingBlockAbsoluteSaveState;
2443 // Push the absolute containing block now so we can absolutely position
2444 // the root element
2445 mDocElementContainingBlock->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
2446 state.PushAbsoluteContainingBlock(mDocElementContainingBlock,
2447 mDocElementContainingBlock,
2448 docElementContainingBlockAbsoluteSaveState);
2450 // The rules from CSS 2.1, section 9.2.4, have already been applied
2451 // by the style system, so we can assume that display->mDisplay is
2452 // either NONE, BLOCK, or TABLE.
2454 // contentFrame is the primary frame for the root element. frameList contains
2455 // the children of the initial containing block.
2457 // The first of those frames is usually `contentFrame`, but it can be
2458 // different, in particular if the root frame is positioned, in which case
2459 // contentFrame is the out-of-flow frame and frameList.FirstChild() is the
2460 // placeholder.
2462 // The rest of the frames in frameList are the anonymous content of the canvas
2463 // frame.
2464 nsContainerFrame* contentFrame;
2465 nsFrameList frameList;
2466 bool processChildren = false;
2468 nsFrameConstructorSaveState absoluteSaveState;
2470 if (aDocElement->IsSVGElement()) {
2471 if (!aDocElement->IsSVGElement(nsGkAtoms::svg)) {
2472 return nullptr;
2474 // We're going to call the right function ourselves, so no need to give a
2475 // function to this FrameConstructionData.
2477 // XXXbz on the other hand, if we converted this whole function to
2478 // FrameConstructionData/Item, then we'd need the right function
2479 // here... but would probably be able to get away with less code in this
2480 // function in general.
2481 static constexpr FrameConstructionData rootSVGData;
2482 AutoFrameConstructionItem item(this, &rootSVGData, aDocElement,
2483 do_AddRef(computedStyle), true);
2485 contentFrame = static_cast<nsContainerFrame*>(ConstructOuterSVG(
2486 state, item, mDocElementContainingBlock, display, frameList));
2487 } else if (display->mDisplay == StyleDisplay::Flex ||
2488 display->mDisplay == StyleDisplay::WebkitBox ||
2489 display->mDisplay == StyleDisplay::Grid ||
2490 display->mDisplay == StyleDisplay::MozBox) {
2491 auto func = [&] {
2492 if (display->mDisplay == StyleDisplay::Grid) {
2493 return NS_NewGridContainerFrame;
2495 if (display->mDisplay == StyleDisplay::MozBox &&
2496 !computedStyle->StyleVisibility()->EmulateMozBoxWithFlex()) {
2497 return NS_NewBoxFrame;
2499 return NS_NewFlexContainerFrame;
2500 }();
2501 contentFrame = func(mPresShell, computedStyle);
2502 InitAndRestoreFrame(
2503 state, aDocElement,
2504 state.GetGeometricParent(*display, mDocElementContainingBlock),
2505 contentFrame);
2506 state.AddChild(contentFrame, frameList, aDocElement,
2507 mDocElementContainingBlock);
2508 processChildren = true;
2510 contentFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
2511 if (contentFrame->IsAbsPosContainingBlock()) {
2512 state.PushAbsoluteContainingBlock(contentFrame, contentFrame,
2513 absoluteSaveState);
2515 } else if (display->mDisplay == StyleDisplay::Table) {
2516 // We're going to call the right function ourselves, so no need to give a
2517 // function to this FrameConstructionData.
2519 // XXXbz on the other hand, if we converted this whole function to
2520 // FrameConstructionData/Item, then we'd need the right function
2521 // here... but would probably be able to get away with less code in this
2522 // function in general.
2523 static constexpr FrameConstructionData rootTableData;
2524 AutoFrameConstructionItem item(this, &rootTableData, aDocElement,
2525 do_AddRef(computedStyle), true);
2527 // if the document is a table then just populate it.
2528 contentFrame = static_cast<nsContainerFrame*>(ConstructTable(
2529 state, item, mDocElementContainingBlock, display, frameList));
2530 } else if (display->DisplayInside() == StyleDisplayInside::Ruby) {
2531 static constexpr FrameConstructionData data(
2532 &nsCSSFrameConstructor::ConstructBlockRubyFrame);
2533 AutoFrameConstructionItem item(this, &data, aDocElement,
2534 do_AddRef(computedStyle), true);
2535 contentFrame = static_cast<nsContainerFrame*>(ConstructBlockRubyFrame(
2536 state, item,
2537 state.GetGeometricParent(*display, mDocElementContainingBlock), display,
2538 frameList));
2539 } else {
2540 MOZ_ASSERT(display->mDisplay == StyleDisplay::Block ||
2541 display->mDisplay == StyleDisplay::FlowRoot,
2542 "Unhandled display type for root element");
2543 contentFrame = NS_NewBlockFormattingContext(mPresShell, computedStyle);
2544 ConstructBlock(
2545 state, aDocElement,
2546 state.GetGeometricParent(*display, mDocElementContainingBlock),
2547 mDocElementContainingBlock, computedStyle, &contentFrame, frameList,
2548 contentFrame->IsAbsPosContainingBlock() ? contentFrame : nullptr);
2551 MOZ_ASSERT(frameList.FirstChild());
2552 MOZ_ASSERT(frameList.FirstChild()->GetContent() == aDocElement);
2553 MOZ_ASSERT(contentFrame);
2555 MOZ_ASSERT(
2556 processChildren ? !mRootElementFrame : mRootElementFrame == contentFrame,
2557 "unexpected mRootElementFrame");
2558 if (processChildren) {
2559 SetRootElementFrameAndConstructCanvasAnonContent(contentFrame, state,
2560 frameList);
2563 // Figure out which frame has the main style for the document element,
2564 // assigning it to mRootElementStyleFrame.
2565 // Backgrounds should be propagated from that frame to the viewport.
2566 contentFrame->GetParentComputedStyle(&mRootElementStyleFrame);
2567 bool isChild = mRootElementStyleFrame &&
2568 mRootElementStyleFrame->GetParent() == contentFrame;
2569 if (!isChild) {
2570 mRootElementStyleFrame = mRootElementFrame;
2573 if (processChildren) {
2574 // Still need to process the child content
2575 nsFrameList childList;
2577 NS_ASSERTION(!contentFrame->IsBlockFrameOrSubclass() &&
2578 !contentFrame->IsFrameOfType(nsIFrame::eSVG),
2579 "Only XUL frames should reach here");
2581 nsFrameConstructorSaveState floatSaveState;
2582 state.MaybePushFloatContainingBlock(contentFrame, floatSaveState);
2584 ProcessChildren(state, aDocElement, computedStyle, contentFrame, true,
2585 childList, false);
2587 // Set the initial child lists
2588 contentFrame->SetInitialChildList(FrameChildListID::Principal,
2589 std::move(childList));
2592 nsIFrame* newFrame = frameList.FirstChild();
2593 // set the primary frame
2594 aDocElement->SetPrimaryFrame(contentFrame);
2595 mDocElementContainingBlock->AppendFrames(FrameChildListID::Principal,
2596 std::move(frameList));
2598 return newFrame;
2601 nsIFrame* nsCSSFrameConstructor::ConstructRootFrame() {
2602 AUTO_PROFILER_LABEL("nsCSSFrameConstructor::ConstructRootFrame",
2603 LAYOUT_FrameConstruction);
2604 AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
2606 ServoStyleSet* styleSet = mPresShell->StyleSet();
2608 // --------- BUILD VIEWPORT -----------
2609 RefPtr<ComputedStyle> viewportPseudoStyle =
2610 styleSet->ResolveInheritingAnonymousBoxStyle(PseudoStyleType::viewport,
2611 nullptr);
2612 ViewportFrame* viewportFrame =
2613 NS_NewViewportFrame(mPresShell, viewportPseudoStyle);
2615 // XXXbz do we _have_ to pass a null content pointer to that frame?
2616 // Would it really kill us to pass in the root element or something?
2617 // What would that break?
2618 viewportFrame->Init(nullptr, nullptr, nullptr);
2620 viewportFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
2622 // Bind the viewport frame to the root view
2623 if (nsView* rootView = mPresShell->GetViewManager()->GetRootView()) {
2624 viewportFrame->SetView(rootView);
2625 viewportFrame->SyncFrameViewProperties(rootView);
2626 rootView->SetNeedsWindowPropertiesSync();
2629 // Make it an absolute container for fixed-pos elements
2630 viewportFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
2631 viewportFrame->MarkAsAbsoluteContainingBlock();
2633 return viewportFrame;
2636 void nsCSSFrameConstructor::SetUpDocElementContainingBlock(
2637 nsIContent* aDocElement) {
2638 MOZ_ASSERT(aDocElement, "No element?");
2639 MOZ_ASSERT(!aDocElement->GetParent(), "Not root content?");
2640 MOZ_ASSERT(aDocElement->GetUncomposedDoc(), "Not in a document?");
2641 MOZ_ASSERT(aDocElement->GetUncomposedDoc()->GetRootElement() == aDocElement,
2642 "Not the root of the document?");
2645 how the root frame hierarchy should look
2647 Galley presentation, with scrolling:
2649 ViewportFrame [fixed-cb]
2650 nsHTMLScrollFrame (if needed)
2651 nsCanvasFrame [abs-cb]
2652 root element frame (nsBlockFrame, SVGOuterSVGFrame,
2653 nsTableWrapperFrame, nsPlaceholderFrame,
2654 nsBoxFrame)
2656 Print presentation, non-XUL
2658 ViewportFrame
2659 nsCanvasFrame
2660 nsPageSequenceFrame
2661 PrintedSheetFrame
2662 nsPageFrame
2663 nsPageContentFrame [fixed-cb]
2664 nsCanvasFrame [abs-cb]
2665 root element frame (nsBlockFrame, SVGOuterSVGFrame,
2666 nsTableWrapperFrame, nsPlaceholderFrame)
2668 Print-preview presentation, non-XUL
2670 ViewportFrame
2671 nsHTMLScrollFrame
2672 nsCanvasFrame
2673 nsPageSequenceFrame
2674 PrintedSheetFrame
2675 nsPageFrame
2676 nsPageContentFrame [fixed-cb]
2677 nsCanvasFrame [abs-cb]
2678 root element frame (nsBlockFrame, SVGOuterSVGFrame,
2679 nsTableWrapperFrame,
2680 nsPlaceholderFrame)
2682 Print/print preview of XUL is not supported.
2683 [fixed-cb]: the default containing block for fixed-pos content
2684 [abs-cb]: the default containing block for abs-pos content
2686 Meaning of nsCSSFrameConstructor fields:
2687 mRootElementFrame is "root element frame". This is the primary frame for
2688 the root element.
2689 mDocElementContainingBlock is the parent of mRootElementFrame
2690 (i.e. nsCanvasFrame)
2691 mPageSequenceFrame is the nsPageSequenceFrame, or null if there isn't
2695 // --------- CREATE ROOT FRAME -------
2697 // Create the root frame. The document element's frame is a child of the
2698 // root frame.
2700 // The root frame serves two purposes:
2701 // - reserves space for any margins needed for the document element's frame
2702 // - renders the document element's background. This ensures the background
2703 // covers the entire canvas as specified by the CSS2 spec
2705 nsPresContext* presContext = mPresShell->GetPresContext();
2706 const bool isPaginated = presContext->IsRootPaginatedDocument();
2708 const bool isHTML = aDocElement->IsHTMLElement();
2709 const bool isXUL = !isHTML && aDocElement->IsXULElement();
2711 const bool isScrollable = [&] {
2712 if (isPaginated) {
2713 return presContext->HasPaginatedScrolling();
2715 // Never create scrollbars for XUL documents or top level XHTML documents
2716 // that disable scrolling.
2717 if (isXUL) {
2718 return false;
2720 if (aDocElement->OwnerDoc()->ChromeRulesEnabled() &&
2721 aDocElement->AsElement()->AttrValueIs(
2722 kNameSpaceID_None, nsGkAtoms::scrolling, nsGkAtoms::_false,
2723 eCaseMatters)) {
2724 return false;
2726 return true;
2727 }();
2729 nsContainerFrame* viewportFrame =
2730 static_cast<nsContainerFrame*>(GetRootFrame());
2731 ComputedStyle* viewportPseudoStyle = viewportFrame->Style();
2733 nsContainerFrame* rootFrame =
2734 NS_NewCanvasFrame(mPresShell, viewportPseudoStyle);
2735 PseudoStyleType rootPseudo = PseudoStyleType::canvas;
2736 mDocElementContainingBlock = rootFrame;
2738 // --------- IF SCROLLABLE WRAP IN SCROLLFRAME --------
2740 // If the device supports scrolling (e.g., in galley mode on the screen and
2741 // for print-preview, but not when printing), then create a scroll frame that
2742 // will act as the scrolling mechanism for the viewport.
2743 // XXX Do we even need a viewport when printing to a printer?
2745 // We no longer need to do overflow propagation here. It's taken care of
2746 // when we construct frames for the element whose overflow might be
2747 // propagated
2748 NS_ASSERTION(!isScrollable || !isXUL,
2749 "XUL documents should never be scrollable - see above");
2751 nsContainerFrame* newFrame = rootFrame;
2752 RefPtr<ComputedStyle> rootPseudoStyle;
2753 // we must create a state because if the scrollbars are GFX it needs the
2754 // state to build the scrollbar frames.
2755 nsFrameConstructorState state(mPresShell, nullptr, nullptr, nullptr);
2757 // Start off with the viewport as parent; we'll adjust it as needed.
2758 nsContainerFrame* parentFrame = viewportFrame;
2760 ServoStyleSet* styleSet = mPresShell->StyleSet();
2761 // If paginated, make sure we don't put scrollbars in
2762 if (!isScrollable) {
2763 rootPseudoStyle = styleSet->ResolveInheritingAnonymousBoxStyle(
2764 rootPseudo, viewportPseudoStyle);
2765 } else {
2766 rootPseudo = PseudoStyleType::scrolledCanvas;
2768 // Build the frame. We give it the content we are wrapping which is the
2769 // document element, the root frame, the parent view port frame, and we
2770 // should get back the new frame and the scrollable view if one was
2771 // created.
2773 // resolve a context for the scrollframe
2774 RefPtr<ComputedStyle> computedStyle =
2775 styleSet->ResolveInheritingAnonymousBoxStyle(
2776 PseudoStyleType::viewportScroll, viewportPseudoStyle);
2778 // Note that the viewport scrollframe is always built with
2779 // overflow:auto style. This forces the scroll frame to create
2780 // anonymous content for both scrollbars. This is necessary even
2781 // if the HTML or BODY elements are overriding the viewport
2782 // scroll style to 'hidden' --- dynamic style changes might put
2783 // scrollbars back on the viewport and we don't want to have to
2784 // reframe the viewport to create the scrollbar content.
2785 newFrame = nullptr;
2786 rootPseudoStyle =
2787 BeginBuildingScrollFrame(state, aDocElement, computedStyle,
2788 viewportFrame, rootPseudo, true, newFrame);
2789 parentFrame = newFrame;
2792 rootFrame->SetComputedStyleWithoutNotification(rootPseudoStyle);
2793 rootFrame->Init(aDocElement, parentFrame, nullptr);
2795 if (isScrollable) {
2796 FinishBuildingScrollFrame(parentFrame, rootFrame);
2799 if (isPaginated) {
2800 // Create a page sequence frame
2802 RefPtr<ComputedStyle> pageSequenceStyle =
2803 styleSet->ResolveInheritingAnonymousBoxStyle(
2804 PseudoStyleType::pageSequence, viewportPseudoStyle);
2805 mPageSequenceFrame =
2806 NS_NewPageSequenceFrame(mPresShell, pageSequenceStyle);
2807 mPageSequenceFrame->Init(aDocElement, rootFrame, nullptr);
2808 SetInitialSingleChild(rootFrame, mPageSequenceFrame);
2811 // Create the first printed sheet frame, as the sole child (for now) of our
2812 // page sequence frame (mPageSequenceFrame).
2813 auto* printedSheetFrame =
2814 ConstructPrintedSheetFrame(mPresShell, mPageSequenceFrame, nullptr);
2815 SetInitialSingleChild(mPageSequenceFrame, printedSheetFrame);
2817 MOZ_ASSERT(!mNextPageContentFramePageName,
2818 "Next page name should not have been set.");
2820 // Create the first page, as the sole child (for now) of the printed sheet
2821 // frame that we just created.
2822 nsContainerFrame* canvasFrame;
2823 nsContainerFrame* pageFrame =
2824 ConstructPageFrame(mPresShell, printedSheetFrame, nullptr, canvasFrame);
2825 pageFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
2826 SetInitialSingleChild(printedSheetFrame, pageFrame);
2828 // The eventual parent of the document element frame.
2829 // XXX should this be set for every new page (in ConstructPageFrame)?
2830 mDocElementContainingBlock = canvasFrame;
2833 if (viewportFrame->HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
2834 SetInitialSingleChild(viewportFrame, newFrame);
2835 } else {
2836 viewportFrame->AppendFrames(FrameChildListID::Principal,
2837 nsFrameList(newFrame, newFrame));
2841 void nsCSSFrameConstructor::ConstructAnonymousContentForCanvas(
2842 nsFrameConstructorState& aState, nsContainerFrame* aFrame,
2843 nsIContent* aDocElement, nsFrameList& aFrameList) {
2844 NS_ASSERTION(aFrame->IsCanvasFrame(), "aFrame should be canvas frame!");
2845 MOZ_ASSERT(mRootElementFrame->GetContent() == aDocElement);
2847 AutoTArray<nsIAnonymousContentCreator::ContentInfo, 4> anonymousItems;
2848 GetAnonymousContent(aDocElement, aFrame, anonymousItems);
2849 if (anonymousItems.IsEmpty()) {
2850 return;
2853 AutoFrameConstructionItemList itemsToConstruct(this);
2854 AutoFrameConstructionPageName pageNameTracker(aState, aFrame);
2855 AddFCItemsForAnonymousContent(aState, aFrame, anonymousItems,
2856 itemsToConstruct, pageNameTracker);
2857 ConstructFramesFromItemList(aState, itemsToConstruct, aFrame,
2858 /* aParentIsWrapperAnonBox = */ false,
2859 aFrameList);
2862 PrintedSheetFrame* nsCSSFrameConstructor::ConstructPrintedSheetFrame(
2863 PresShell* aPresShell, nsContainerFrame* aParentFrame,
2864 nsIFrame* aPrevSheetFrame) {
2865 RefPtr<ComputedStyle> printedSheetPseudoStyle =
2866 aPresShell->StyleSet()->ResolveNonInheritingAnonymousBoxStyle(
2867 PseudoStyleType::printedSheet);
2869 auto* printedSheetFrame =
2870 NS_NewPrintedSheetFrame(aPresShell, printedSheetPseudoStyle);
2872 printedSheetFrame->Init(nullptr, aParentFrame, aPrevSheetFrame);
2874 return printedSheetFrame;
2877 nsContainerFrame* nsCSSFrameConstructor::ConstructPageFrame(
2878 PresShell* aPresShell, nsContainerFrame* aParentFrame,
2879 nsIFrame* aPrevPageFrame, nsContainerFrame*& aCanvasFrame) {
2880 ServoStyleSet* styleSet = aPresShell->StyleSet();
2882 RefPtr<ComputedStyle> pagePseudoStyle =
2883 styleSet->ResolveNonInheritingAnonymousBoxStyle(PseudoStyleType::page);
2885 nsContainerFrame* pageFrame = NS_NewPageFrame(aPresShell, pagePseudoStyle);
2887 // Initialize the page frame and force it to have a view. This makes printing
2888 // of the pages easier and faster.
2889 pageFrame->Init(nullptr, aParentFrame, aPrevPageFrame);
2891 RefPtr<const nsAtom> pageName;
2892 if (mNextPageContentFramePageName) {
2893 pageName = mNextPageContentFramePageName.forget();
2894 } else if (aPrevPageFrame) {
2895 pageName = aPrevPageFrame->ComputePageValue();
2896 MOZ_ASSERT(pageName,
2897 "Page name from prev-in-flow should not have been null");
2899 RefPtr<ComputedStyle> pageContentPseudoStyle =
2900 styleSet->ResolvePageContentStyle(pageName);
2902 nsContainerFrame* pageContentFrame = NS_NewPageContentFrame(
2903 aPresShell, pageContentPseudoStyle, pageName.forget());
2905 // Initialize the page content frame and force it to have a view. Also make it
2906 // the containing block for fixed elements which are repeated on every page.
2907 nsIFrame* prevPageContentFrame = nullptr;
2908 if (aPrevPageFrame) {
2909 prevPageContentFrame = aPrevPageFrame->PrincipalChildList().FirstChild();
2910 NS_ASSERTION(prevPageContentFrame, "missing page content frame");
2912 pageContentFrame->Init(nullptr, pageFrame, prevPageContentFrame);
2913 if (!prevPageContentFrame) {
2914 // The canvas is an inheriting anon box, so needs to be "owned" by the page
2915 // content.
2916 pageContentFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
2918 SetInitialSingleChild(pageFrame, pageContentFrame);
2919 // Make it an absolute container for fixed-pos elements
2920 pageContentFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
2921 pageContentFrame->MarkAsAbsoluteContainingBlock();
2923 RefPtr<ComputedStyle> canvasPseudoStyle =
2924 styleSet->ResolveInheritingAnonymousBoxStyle(PseudoStyleType::canvas,
2925 pageContentPseudoStyle);
2927 aCanvasFrame = NS_NewCanvasFrame(aPresShell, canvasPseudoStyle);
2929 nsIFrame* prevCanvasFrame = nullptr;
2930 if (prevPageContentFrame) {
2931 prevCanvasFrame = prevPageContentFrame->PrincipalChildList().FirstChild();
2932 NS_ASSERTION(prevCanvasFrame, "missing canvas frame");
2934 aCanvasFrame->Init(nullptr, pageContentFrame, prevCanvasFrame);
2935 SetInitialSingleChild(pageContentFrame, aCanvasFrame);
2936 return pageFrame;
2939 /* static */
2940 nsIFrame* nsCSSFrameConstructor::CreatePlaceholderFrameFor(
2941 PresShell* aPresShell, nsIContent* aContent, nsIFrame* aFrame,
2942 nsContainerFrame* aParentFrame, nsIFrame* aPrevInFlow,
2943 nsFrameState aTypeBit) {
2944 RefPtr<ComputedStyle> placeholderStyle =
2945 aPresShell->StyleSet()->ResolveStyleForPlaceholder();
2947 // The placeholder frame gets a pseudo style.
2948 nsPlaceholderFrame* placeholderFrame =
2949 NS_NewPlaceholderFrame(aPresShell, placeholderStyle, aTypeBit);
2951 placeholderFrame->Init(aContent, aParentFrame, aPrevInFlow);
2953 // Associate the placeholder/out-of-flow with each other.
2954 placeholderFrame->SetOutOfFlowFrame(aFrame);
2955 aFrame->SetProperty(nsIFrame::PlaceholderFrameProperty(), placeholderFrame);
2957 aFrame->AddStateBits(NS_FRAME_OUT_OF_FLOW);
2959 return placeholderFrame;
2962 // Clears any lazy bits set in the range [aStartContent, aEndContent). If
2963 // aEndContent is null, that means to clear bits in all siblings starting with
2964 // aStartContent. aStartContent must not be null unless aEndContent is also
2965 // null. We do this so that when new children are inserted under elements whose
2966 // frame is a leaf the new children don't cause us to try to construct frames
2967 // for the existing children again.
2968 static inline void ClearLazyBits(nsIContent* aStartContent,
2969 nsIContent* aEndContent) {
2970 MOZ_ASSERT(aStartContent || !aEndContent,
2971 "Must have start child if we have an end child");
2973 for (nsIContent* cur = aStartContent; cur != aEndContent;
2974 cur = cur->GetNextSibling()) {
2975 cur->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
2979 nsIFrame* nsCSSFrameConstructor::ConstructSelectFrame(
2980 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
2981 nsContainerFrame* aParentFrame, const nsStyleDisplay* aStyleDisplay,
2982 nsFrameList& aFrameList) {
2983 nsIContent* const content = aItem.mContent;
2984 ComputedStyle* const computedStyle = aItem.mComputedStyle;
2986 // Construct a frame-based listbox or combobox
2987 dom::HTMLSelectElement* sel = dom::HTMLSelectElement::FromNode(content);
2988 MOZ_ASSERT(sel);
2989 if (sel->IsCombobox()) {
2990 // Construct a frame-based combo box.
2991 // The frame-based combo box is built out of three parts. A display area, a
2992 // button and a dropdown list. The display area and button are created
2993 // through anonymous content. The drop-down list's frame is created
2994 // explicitly. The combobox frame shares its content with the drop-down
2995 // list.
2996 nsFrameState flags = NS_BLOCK_FLOAT_MGR;
2997 nsComboboxControlFrame* comboboxFrame =
2998 NS_NewComboboxControlFrame(mPresShell, computedStyle, flags);
3000 // Save the history state so we don't restore during construction
3001 // since the complete tree is required before we restore.
3002 nsILayoutHistoryState* historyState = aState.mFrameState;
3003 aState.mFrameState = nullptr;
3004 // Initialize the combobox frame
3005 InitAndRestoreFrame(aState, content,
3006 aState.GetGeometricParent(*aStyleDisplay, aParentFrame),
3007 comboboxFrame);
3009 comboboxFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
3011 aState.AddChild(comboboxFrame, aFrameList, content, aParentFrame);
3013 // Resolve pseudo element style for the dropdown list
3014 RefPtr<ComputedStyle> listStyle =
3015 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
3016 PseudoStyleType::dropDownList, computedStyle);
3018 // child frames of combobox frame
3019 nsFrameList childList;
3021 // Create display and button frames from the combobox's anonymous content.
3022 // The anonymous content is appended to existing anonymous content for this
3023 // element (the scrollbars).
3025 // nsComboboxControlFrame needs special frame creation behavior for its
3026 // first piece of anonymous content, which means that we can't take the
3027 // normal ProcessChildren path.
3028 AutoTArray<nsIAnonymousContentCreator::ContentInfo, 2> newAnonymousItems;
3029 DebugOnly<nsresult> rv =
3030 GetAnonymousContent(content, comboboxFrame, newAnonymousItems);
3031 MOZ_ASSERT(NS_SUCCEEDED(rv));
3032 MOZ_ASSERT(!newAnonymousItems.IsEmpty());
3034 // Manually create a frame for the special NAC.
3035 MOZ_ASSERT(newAnonymousItems[0].mContent ==
3036 comboboxFrame->GetDisplayNode());
3037 newAnonymousItems.RemoveElementAt(0);
3038 nsIFrame* customFrame = comboboxFrame->CreateFrameForDisplayNode();
3039 MOZ_ASSERT(customFrame);
3040 childList.AppendFrame(nullptr, customFrame);
3042 nsFrameConstructorSaveState floatSaveState;
3043 aState.MaybePushFloatContainingBlock(comboboxFrame, floatSaveState);
3045 // The other piece of NAC can take the normal path.
3046 AutoFrameConstructionItemList fcItems(this);
3047 AutoFrameConstructionPageName pageNameTracker(aState, comboboxFrame);
3048 AddFCItemsForAnonymousContent(aState, comboboxFrame, newAnonymousItems,
3049 fcItems, pageNameTracker);
3050 ConstructFramesFromItemList(aState, fcItems, comboboxFrame,
3051 /* aParentIsWrapperAnonBox = */ false,
3052 childList);
3054 comboboxFrame->SetInitialChildList(FrameChildListID::Principal,
3055 std::move(childList));
3057 aState.mFrameState = historyState;
3058 if (aState.mFrameState) {
3059 // Restore frame state for the entire subtree of |comboboxFrame|.
3060 RestoreFrameState(comboboxFrame, aState.mFrameState);
3062 return comboboxFrame;
3065 // Listbox, not combobox
3066 nsContainerFrame* listFrame =
3067 NS_NewListControlFrame(mPresShell, computedStyle);
3069 nsContainerFrame* scrolledFrame =
3070 NS_NewSelectsAreaFrame(mPresShell, computedStyle, NS_BLOCK_FLOAT_MGR);
3072 // ******* this code stolen from Initialze ScrollFrame ********
3073 // please adjust this code to use BuildScrollFrame.
3075 InitializeListboxSelect(aState, listFrame, scrolledFrame, content,
3076 aParentFrame, computedStyle, aFrameList);
3078 return listFrame;
3081 void nsCSSFrameConstructor::InitializeListboxSelect(
3082 nsFrameConstructorState& aState, nsContainerFrame* scrollFrame,
3083 nsContainerFrame* scrolledFrame, nsIContent* aContent,
3084 nsContainerFrame* aParentFrame, ComputedStyle* aComputedStyle,
3085 nsFrameList& aFrameList) {
3086 // Initialize it
3087 nsContainerFrame* geometricParent =
3088 aState.GetGeometricParent(*aComputedStyle->StyleDisplay(), aParentFrame);
3090 // We don't call InitAndRestoreFrame for scrollFrame because we can only
3091 // restore the frame state after its parts have been created (in particular,
3092 // the scrollable view). So we have to split Init and Restore.
3094 scrollFrame->Init(aContent, geometricParent, nullptr);
3095 aState.AddChild(scrollFrame, aFrameList, aContent, aParentFrame);
3096 BuildScrollFrame(aState, aContent, aComputedStyle, scrolledFrame,
3097 geometricParent, scrollFrame);
3098 if (aState.mFrameState) {
3099 // Restore frame state for the scroll frame
3100 RestoreFrameStateFor(scrollFrame, aState.mFrameState);
3103 nsFrameConstructorSaveState floatSaveState;
3104 aState.MaybePushFloatContainingBlock(scrolledFrame, floatSaveState);
3106 // Process children
3107 nsFrameList childList;
3109 ProcessChildren(aState, aContent, aComputedStyle, scrolledFrame, false,
3110 childList, false);
3112 // Set the scrolled frame's initial child lists
3113 scrolledFrame->SetInitialChildList(FrameChildListID::Principal,
3114 std::move(childList));
3117 nsIFrame* nsCSSFrameConstructor::ConstructFieldSetFrame(
3118 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
3119 nsContainerFrame* aParentFrame, const nsStyleDisplay* aStyleDisplay,
3120 nsFrameList& aFrameList) {
3121 AutoRestore<bool> savedHasRenderedLegend(aState.mHasRenderedLegend);
3122 aState.mHasRenderedLegend = false;
3123 nsIContent* const content = aItem.mContent;
3124 ComputedStyle* const computedStyle = aItem.mComputedStyle;
3126 nsContainerFrame* fieldsetFrame =
3127 NS_NewFieldSetFrame(mPresShell, computedStyle);
3129 // Initialize it
3130 InitAndRestoreFrame(aState, content,
3131 aState.GetGeometricParent(*aStyleDisplay, aParentFrame),
3132 fieldsetFrame);
3134 fieldsetFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
3136 // Resolve style and initialize the frame
3137 RefPtr<ComputedStyle> fieldsetContentStyle =
3138 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
3139 PseudoStyleType::fieldsetContent, computedStyle);
3141 const nsStyleDisplay* fieldsetContentDisplay =
3142 fieldsetContentStyle->StyleDisplay();
3143 bool isScrollable = fieldsetContentDisplay->IsScrollableOverflow();
3144 nsContainerFrame* scrollFrame = nullptr;
3145 if (isScrollable) {
3146 fieldsetContentStyle = BeginBuildingScrollFrame(
3147 aState, content, fieldsetContentStyle, fieldsetFrame,
3148 PseudoStyleType::scrolledContent, false, scrollFrame);
3151 // Create the inner ::-moz-fieldset-content frame.
3152 nsContainerFrame* contentFrameTop;
3153 nsContainerFrame* contentFrame;
3154 auto parent = scrollFrame ? scrollFrame : fieldsetFrame;
3155 MOZ_ASSERT(fieldsetContentDisplay->DisplayOutside() ==
3156 StyleDisplayOutside::Block);
3157 switch (fieldsetContentDisplay->DisplayInside()) {
3158 case StyleDisplayInside::Flex:
3159 contentFrame = NS_NewFlexContainerFrame(mPresShell, fieldsetContentStyle);
3160 InitAndRestoreFrame(aState, content, parent, contentFrame);
3161 contentFrameTop = contentFrame;
3162 break;
3163 case StyleDisplayInside::Grid:
3164 contentFrame = NS_NewGridContainerFrame(mPresShell, fieldsetContentStyle);
3165 InitAndRestoreFrame(aState, content, parent, contentFrame);
3166 contentFrameTop = contentFrame;
3167 break;
3168 default: {
3169 MOZ_ASSERT(fieldsetContentDisplay->mDisplay == StyleDisplay::Block,
3170 "bug in StyleAdjuster::adjust_for_fieldset_content?");
3172 contentFrame =
3173 NS_NewBlockFormattingContext(mPresShell, fieldsetContentStyle);
3174 if (fieldsetContentStyle->StyleColumn()->IsColumnContainerStyle()) {
3175 contentFrameTop = BeginBuildingColumns(
3176 aState, content, parent, contentFrame, fieldsetContentStyle);
3177 } else {
3178 // No need to create column container. Initialize content frame.
3179 InitAndRestoreFrame(aState, content, parent, contentFrame);
3180 contentFrameTop = contentFrame;
3183 break;
3187 aState.AddChild(fieldsetFrame, aFrameList, content, aParentFrame);
3189 // Process children
3190 nsFrameConstructorSaveState absoluteSaveState;
3191 nsFrameList childList;
3193 contentFrameTop->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
3194 if (fieldsetFrame->IsAbsPosContainingBlock()) {
3195 aState.PushAbsoluteContainingBlock(contentFrameTop, fieldsetFrame,
3196 absoluteSaveState);
3199 nsFrameConstructorSaveState floatSaveState;
3200 aState.MaybePushFloatContainingBlock(contentFrame, floatSaveState);
3202 ProcessChildren(aState, content, computedStyle, contentFrame, true, childList,
3203 true);
3204 nsFrameList fieldsetKids;
3205 fieldsetKids.AppendFrame(nullptr,
3206 scrollFrame ? scrollFrame : contentFrameTop);
3208 if (!MayNeedToCreateColumnSpanSiblings(contentFrame, childList)) {
3209 // Set the inner frame's initial child lists.
3210 contentFrame->SetInitialChildList(FrameChildListID::Principal,
3211 std::move(childList));
3212 } else {
3213 // Extract any initial non-column-span kids, and put them in inner frame's
3214 // child list.
3215 nsFrameList initialNonColumnSpanKids =
3216 childList.Split([](nsIFrame* f) { return f->IsColumnSpan(); });
3217 contentFrame->SetInitialChildList(FrameChildListID::Principal,
3218 std::move(initialNonColumnSpanKids));
3220 if (childList.NotEmpty()) {
3221 nsFrameList columnSpanSiblings = CreateColumnSpanSiblings(
3222 aState, contentFrame, childList,
3223 // Column content should never be a absolute/fixed positioned
3224 // containing block. Pass nullptr as aPositionedFrame.
3225 nullptr);
3226 FinishBuildingColumns(aState, contentFrameTop, contentFrame,
3227 columnSpanSiblings);
3231 if (isScrollable) {
3232 FinishBuildingScrollFrame(scrollFrame, contentFrameTop);
3235 // We use AppendFrames here because the rendered legend will already
3236 // be present in the principal child list if it exists.
3237 fieldsetFrame->AppendFrames(FrameChildListID::NoReflowPrincipal,
3238 std::move(fieldsetKids));
3240 return fieldsetFrame;
3243 nsIFrame* nsCSSFrameConstructor::ConstructDetails(
3244 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
3245 nsContainerFrame* aParentFrame, const nsStyleDisplay* aStyleDisplay,
3246 nsFrameList& aFrameList) {
3247 if (!aStyleDisplay->IsScrollableOverflow()) {
3248 return ConstructNonScrollableBlock(aState, aItem, aParentFrame,
3249 aStyleDisplay, aFrameList);
3252 // Build a scroll frame if necessary.
3253 return ConstructScrollableBlock(aState, aItem, aParentFrame, aStyleDisplay,
3254 aFrameList);
3257 nsIFrame* nsCSSFrameConstructor::ConstructBlockRubyFrame(
3258 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
3259 nsContainerFrame* aParentFrame, const nsStyleDisplay* aStyleDisplay,
3260 nsFrameList& aFrameList) {
3261 nsIContent* const content = aItem.mContent;
3262 ComputedStyle* const computedStyle = aItem.mComputedStyle;
3264 nsBlockFrame* blockFrame = NS_NewBlockFrame(mPresShell, computedStyle);
3265 nsContainerFrame* newFrame = blockFrame;
3266 nsContainerFrame* geometricParent =
3267 aState.GetGeometricParent(*aStyleDisplay, aParentFrame);
3268 AutoFrameConstructionPageName pageNameTracker(aState, blockFrame);
3269 if ((aItem.mFCData->mBits & FCDATA_MAY_NEED_SCROLLFRAME) &&
3270 aStyleDisplay->IsScrollableOverflow()) {
3271 nsContainerFrame* scrollframe = nullptr;
3272 BuildScrollFrame(aState, content, computedStyle, blockFrame,
3273 geometricParent, scrollframe);
3274 newFrame = scrollframe;
3275 } else {
3276 InitAndRestoreFrame(aState, content, geometricParent, blockFrame);
3279 RefPtr<ComputedStyle> rubyStyle =
3280 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
3281 PseudoStyleType::blockRubyContent, computedStyle);
3282 nsContainerFrame* rubyFrame = NS_NewRubyFrame(mPresShell, rubyStyle);
3283 InitAndRestoreFrame(aState, content, blockFrame, rubyFrame);
3284 SetInitialSingleChild(blockFrame, rubyFrame);
3285 blockFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
3287 aState.AddChild(newFrame, aFrameList, content, aParentFrame);
3289 if (!mRootElementFrame) {
3290 // The frame we're constructing will be the root element frame.
3291 SetRootElementFrameAndConstructCanvasAnonContent(newFrame, aState,
3292 aFrameList);
3295 nsFrameConstructorSaveState absoluteSaveState;
3296 blockFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
3297 if (newFrame->IsAbsPosContainingBlock()) {
3298 aState.PushAbsoluteContainingBlock(blockFrame, blockFrame,
3299 absoluteSaveState);
3301 nsFrameConstructorSaveState floatSaveState;
3302 aState.MaybePushFloatContainingBlock(blockFrame, floatSaveState);
3304 nsFrameList childList;
3305 ProcessChildren(aState, content, rubyStyle, rubyFrame, true, childList, false,
3306 nullptr);
3307 rubyFrame->SetInitialChildList(FrameChildListID::Principal,
3308 std::move(childList));
3310 return newFrame;
3313 static nsIFrame* FindAncestorWithGeneratedContentPseudo(nsIFrame* aFrame) {
3314 for (nsIFrame* f = aFrame->GetParent(); f; f = f->GetParent()) {
3315 NS_ASSERTION(f->IsGeneratedContentFrame(),
3316 "should not have exited generated content");
3317 auto pseudo = f->Style()->GetPseudoType();
3318 if (pseudo == PseudoStyleType::before || pseudo == PseudoStyleType::after ||
3319 pseudo == PseudoStyleType::marker)
3320 return f;
3322 return nullptr;
3325 /* static */
3326 const nsCSSFrameConstructor::FrameConstructionData*
3327 nsCSSFrameConstructor::FindTextData(const Text& aTextContent,
3328 nsIFrame* aParentFrame) {
3329 if (aParentFrame && IsFrameForSVG(aParentFrame)) {
3330 nsIFrame* ancestorFrame = SVGUtils::GetFirstNonAAncestorFrame(aParentFrame);
3331 if (!ancestorFrame || !SVGUtils::IsInSVGTextSubtree(ancestorFrame)) {
3332 return nullptr;
3335 // FIXME(bug 1588477) Don't render stuff in display: contents / Shadow DOM
3336 // subtrees, because TextCorrespondenceRecorder in the SVG text code doesn't
3337 // really know how to deal with it. This kinda sucks. :(
3338 if (aParentFrame->GetContent() != aTextContent.GetParent()) {
3339 return nullptr;
3342 static constexpr FrameConstructionData sSVGTextData(
3343 NS_NewTextFrame, FCDATA_IS_LINE_PARTICIPANT | FCDATA_IS_SVG_TEXT);
3344 return &sSVGTextData;
3347 static constexpr FrameConstructionData sTextData(NS_NewTextFrame,
3348 FCDATA_IS_LINE_PARTICIPANT);
3349 return &sTextData;
3352 void nsCSSFrameConstructor::ConstructTextFrame(
3353 const FrameConstructionData* aData, nsFrameConstructorState& aState,
3354 nsIContent* aContent, nsContainerFrame* aParentFrame,
3355 ComputedStyle* aComputedStyle, nsFrameList& aFrameList) {
3356 MOZ_ASSERT(aData, "Must have frame construction data");
3358 nsIFrame* newFrame =
3359 (*aData->mFunc.mCreationFunc)(mPresShell, aComputedStyle);
3361 InitAndRestoreFrame(aState, aContent, aParentFrame, newFrame);
3363 // We never need to create a view for a text frame.
3365 if (newFrame->IsGeneratedContentFrame()) {
3366 UniquePtr<nsGenConInitializer> initializer(
3367 static_cast<nsGenConInitializer*>(
3368 aContent->TakeProperty(nsGkAtoms::genConInitializerProperty)));
3369 if (initializer) {
3370 if (initializer->mNode.release()->InitTextFrame(
3371 initializer->mList,
3372 FindAncestorWithGeneratedContentPseudo(newFrame), newFrame)) {
3373 (this->*(initializer->mDirtyAll))();
3378 // Add the newly constructed frame to the flow
3379 aFrameList.AppendFrame(nullptr, newFrame);
3381 if (!aState.mCreatingExtraFrames || (aContent->IsInNativeAnonymousSubtree() &&
3382 !aContent->GetPrimaryFrame())) {
3383 aContent->SetPrimaryFrame(newFrame);
3387 /* static */
3388 const nsCSSFrameConstructor::FrameConstructionData*
3389 nsCSSFrameConstructor::FindDataByInt(int32_t aInt, const Element& aElement,
3390 ComputedStyle& aComputedStyle,
3391 const FrameConstructionDataByInt* aDataPtr,
3392 uint32_t aDataLength) {
3393 for (const FrameConstructionDataByInt *curData = aDataPtr,
3394 *endData = aDataPtr + aDataLength;
3395 curData != endData; ++curData) {
3396 if (curData->mInt == aInt) {
3397 const FrameConstructionData* data = &curData->mData;
3398 if (data->mBits & FCDATA_FUNC_IS_DATA_GETTER) {
3399 return data->mFunc.mDataGetter(aElement, aComputedStyle);
3402 return data;
3406 return nullptr;
3409 /* static */
3410 const nsCSSFrameConstructor::FrameConstructionData*
3411 nsCSSFrameConstructor::FindDataByTag(const Element& aElement,
3412 ComputedStyle& aStyle,
3413 const FrameConstructionDataByTag* aDataPtr,
3414 uint32_t aDataLength) {
3415 const nsAtom* tag = aElement.NodeInfo()->NameAtom();
3416 for (const FrameConstructionDataByTag *curData = aDataPtr,
3417 *endData = aDataPtr + aDataLength;
3418 curData != endData; ++curData) {
3419 if (curData->mTag == tag) {
3420 const FrameConstructionData* data = &curData->mData;
3421 if (data->mBits & FCDATA_FUNC_IS_DATA_GETTER) {
3422 return data->mFunc.mDataGetter(aElement, aStyle);
3425 return data;
3429 return nullptr;
3432 #define SUPPRESS_FCDATA() FrameConstructionData(nullptr, FCDATA_SUPPRESS_FRAME)
3433 #define SIMPLE_INT_CREATE(_int, _func) \
3434 { int32_t(_int), FrameConstructionData(_func) }
3435 #define SIMPLE_INT_CHAIN(_int, _func) \
3436 { int32_t(_int), FrameConstructionData(_func) }
3437 #define COMPLEX_INT_CREATE(_int, _func) \
3438 { int32_t(_int), FrameConstructionData(_func) }
3440 #define SIMPLE_TAG_CREATE(_tag, _func) \
3441 { nsGkAtoms::_tag, FrameConstructionData(_func) }
3442 #define SIMPLE_TAG_CHAIN(_tag, _func) \
3443 { nsGkAtoms::_tag, FrameConstructionData(_func) }
3444 #define COMPLEX_TAG_CREATE(_tag, _func) \
3445 { nsGkAtoms::_tag, FrameConstructionData(_func) }
3447 static nsFieldSetFrame* GetFieldSetFrameFor(nsIFrame* aFrame) {
3448 auto pseudo = aFrame->Style()->GetPseudoType();
3449 if (pseudo == PseudoStyleType::fieldsetContent ||
3450 pseudo == PseudoStyleType::scrolledContent ||
3451 pseudo == PseudoStyleType::columnSet ||
3452 pseudo == PseudoStyleType::columnContent) {
3453 return GetFieldSetFrameFor(aFrame->GetParent());
3455 return do_QueryFrame(aFrame);
3458 /* static */
3459 const nsCSSFrameConstructor::FrameConstructionData*
3460 nsCSSFrameConstructor::FindHTMLData(const Element& aElement,
3461 nsIFrame* aParentFrame,
3462 ComputedStyle& aStyle) {
3463 MOZ_ASSERT(aElement.IsHTMLElement());
3464 NS_ASSERTION(!aParentFrame ||
3465 aParentFrame->Style()->GetPseudoType() !=
3466 PseudoStyleType::fieldsetContent ||
3467 aParentFrame->GetParent()->IsFieldSetFrame(),
3468 "Unexpected parent for fieldset content anon box");
3469 static constexpr FrameConstructionDataByTag sHTMLData[] = {
3470 SIMPLE_TAG_CHAIN(img, nsCSSFrameConstructor::FindImgData),
3471 SIMPLE_TAG_CHAIN(mozgeneratedcontentimage,
3472 nsCSSFrameConstructor::FindGeneratedImageData),
3473 {nsGkAtoms::br,
3474 {NS_NewBRFrame, FCDATA_IS_LINE_PARTICIPANT | FCDATA_IS_LINE_BREAK}},
3475 SIMPLE_TAG_CREATE(wbr, NS_NewWBRFrame),
3476 SIMPLE_TAG_CHAIN(input, nsCSSFrameConstructor::FindInputData),
3477 SIMPLE_TAG_CREATE(textarea, NS_NewTextControlFrame),
3478 COMPLEX_TAG_CREATE(select, &nsCSSFrameConstructor::ConstructSelectFrame),
3479 SIMPLE_TAG_CHAIN(object, nsCSSFrameConstructor::FindObjectData),
3480 SIMPLE_TAG_CHAIN(embed, nsCSSFrameConstructor::FindObjectData),
3481 COMPLEX_TAG_CREATE(fieldset,
3482 &nsCSSFrameConstructor::ConstructFieldSetFrame),
3483 SIMPLE_TAG_CREATE(frameset, NS_NewHTMLFramesetFrame),
3484 SIMPLE_TAG_CREATE(iframe, NS_NewSubDocumentFrame),
3485 {nsGkAtoms::button,
3486 {ToCreationFunc(NS_NewHTMLButtonControlFrame),
3487 FCDATA_ALLOW_BLOCK_STYLES | FCDATA_ALLOW_GRID_FLEX_COLUMN,
3488 PseudoStyleType::buttonContent}},
3489 SIMPLE_TAG_CHAIN(canvas, nsCSSFrameConstructor::FindCanvasData),
3490 SIMPLE_TAG_CREATE(video, NS_NewHTMLVideoFrame),
3491 SIMPLE_TAG_CREATE(audio, NS_NewHTMLVideoFrame),
3492 SIMPLE_TAG_CREATE(progress, NS_NewProgressFrame),
3493 SIMPLE_TAG_CREATE(meter, NS_NewMeterFrame),
3494 COMPLEX_TAG_CREATE(details, &nsCSSFrameConstructor::ConstructDetails)};
3496 return FindDataByTag(aElement, aStyle, sHTMLData, ArrayLength(sHTMLData));
3499 /* static */
3500 const nsCSSFrameConstructor::FrameConstructionData*
3501 nsCSSFrameConstructor::FindGeneratedImageData(const Element& aElement,
3502 ComputedStyle&) {
3503 if (!aElement.IsInNativeAnonymousSubtree()) {
3504 return nullptr;
3507 auto& generatedContent = static_cast<const GeneratedImageContent&>(aElement);
3508 if (generatedContent.IsForListStyleImageMarker()) {
3509 static constexpr FrameConstructionData sImgData(
3510 NS_NewImageFrameForListStyleImage);
3511 return &sImgData;
3514 static constexpr FrameConstructionData sImgData(
3515 NS_NewImageFrameForGeneratedContentIndex);
3516 return &sImgData;
3519 /* static */
3520 const nsCSSFrameConstructor::FrameConstructionData*
3521 nsCSSFrameConstructor::FindImgData(const Element& aElement,
3522 ComputedStyle& aStyle) {
3523 if (nsImageFrame::ImageFrameTypeFor(aElement, aStyle) !=
3524 nsImageFrame::ImageFrameType::ForElementRequest) {
3525 // content: url gets handled by the generic code-path.
3526 return nullptr;
3529 static constexpr FrameConstructionData sImgData(NS_NewImageFrame);
3530 return &sImgData;
3533 /* static */
3534 const nsCSSFrameConstructor::FrameConstructionData*
3535 nsCSSFrameConstructor::FindImgControlData(const Element& aElement,
3536 ComputedStyle& aStyle) {
3537 if (nsImageFrame::ImageFrameTypeFor(aElement, aStyle) !=
3538 nsImageFrame::ImageFrameType::ForElementRequest) {
3539 return nullptr;
3542 static constexpr FrameConstructionData sImgControlData(
3543 NS_NewImageControlFrame);
3544 return &sImgControlData;
3547 /* static */
3548 const nsCSSFrameConstructor::FrameConstructionData*
3549 nsCSSFrameConstructor::FindSearchControlData(const Element& aElement,
3550 ComputedStyle& aStyle) {
3551 if (StaticPrefs::layout_forms_input_type_search_enabled()) {
3552 static constexpr FrameConstructionData sSearchControlData(
3553 NS_NewSearchControlFrame);
3554 return &sSearchControlData;
3557 static constexpr FrameConstructionData sTextControlData(
3558 NS_NewTextControlFrame);
3559 return &sTextControlData;
3562 /* static */
3563 const nsCSSFrameConstructor::FrameConstructionData*
3564 nsCSSFrameConstructor::FindInputData(const Element& aElement,
3565 ComputedStyle& aStyle) {
3566 static constexpr FrameConstructionDataByInt sInputData[] = {
3567 SIMPLE_INT_CREATE(FormControlType::InputCheckbox,
3568 ToCreationFunc(NS_NewCheckboxRadioFrame)),
3569 SIMPLE_INT_CREATE(FormControlType::InputRadio,
3570 ToCreationFunc(NS_NewCheckboxRadioFrame)),
3571 SIMPLE_INT_CREATE(FormControlType::InputFile, NS_NewFileControlFrame),
3572 SIMPLE_INT_CHAIN(FormControlType::InputImage,
3573 nsCSSFrameConstructor::FindImgControlData),
3574 SIMPLE_INT_CREATE(FormControlType::InputEmail, NS_NewTextControlFrame),
3575 SIMPLE_INT_CREATE(FormControlType::InputText, NS_NewTextControlFrame),
3576 SIMPLE_INT_CREATE(FormControlType::InputTel, NS_NewTextControlFrame),
3577 SIMPLE_INT_CREATE(FormControlType::InputUrl, NS_NewTextControlFrame),
3578 SIMPLE_INT_CREATE(FormControlType::InputRange, NS_NewRangeFrame),
3579 SIMPLE_INT_CREATE(FormControlType::InputPassword, NS_NewTextControlFrame),
3580 {int32_t(FormControlType::InputColor),
3581 {NS_NewColorControlFrame, 0, PseudoStyleType::buttonContent}},
3583 SIMPLE_INT_CHAIN(FormControlType::InputSearch,
3584 nsCSSFrameConstructor::FindSearchControlData),
3585 SIMPLE_INT_CREATE(FormControlType::InputNumber, NS_NewNumberControlFrame),
3586 SIMPLE_INT_CREATE(FormControlType::InputTime, NS_NewDateTimeControlFrame),
3587 SIMPLE_INT_CREATE(FormControlType::InputDate, NS_NewDateTimeControlFrame),
3588 SIMPLE_INT_CREATE(FormControlType::InputDatetimeLocal,
3589 NS_NewDateTimeControlFrame),
3590 // TODO: this is temporary until a frame is written: bug 888320
3591 SIMPLE_INT_CREATE(FormControlType::InputMonth, NS_NewTextControlFrame),
3592 // TODO: this is temporary until a frame is written: bug 888320
3593 SIMPLE_INT_CREATE(FormControlType::InputWeek, NS_NewTextControlFrame),
3594 {int32_t(FormControlType::InputSubmit),
3595 {ToCreationFunc(NS_NewGfxButtonControlFrame), 0,
3596 PseudoStyleType::buttonContent}},
3597 {int32_t(FormControlType::InputReset),
3598 {ToCreationFunc(NS_NewGfxButtonControlFrame), 0,
3599 PseudoStyleType::buttonContent}},
3600 {int32_t(FormControlType::InputButton),
3601 {ToCreationFunc(NS_NewGfxButtonControlFrame), 0,
3602 PseudoStyleType::buttonContent}}
3603 // Keeping hidden inputs out of here on purpose for so they get frames by
3604 // display (in practice, none).
3607 auto controlType = HTMLInputElement::FromNode(aElement)->ControlType();
3609 // radio and checkbox inputs with appearance:none should be constructed
3610 // by display type. (Note that we're not checking that appearance is
3611 // not (respectively) StyleAppearance::Radio and StyleAppearance::Checkbox.)
3612 if ((controlType == FormControlType::InputCheckbox ||
3613 controlType == FormControlType::InputRadio) &&
3614 !aStyle.StyleDisplay()->HasAppearance()) {
3615 return nullptr;
3618 return FindDataByInt(int32_t(controlType), aElement, aStyle, sInputData,
3619 ArrayLength(sInputData));
3622 static nsIFrame* NS_NewSubDocumentOrImageFrame(mozilla::PresShell* aPresShell,
3623 mozilla::ComputedStyle* aStyle) {
3624 return StaticPrefs::
3625 browser_opaqueResponseBlocking_syntheticBrowsingContext_AtStartup()
3626 ? NS_NewSubDocumentFrame(aPresShell, aStyle)
3627 : NS_NewImageFrame(aPresShell, aStyle);
3630 /* static */
3631 const nsCSSFrameConstructor::FrameConstructionData*
3632 nsCSSFrameConstructor::FindObjectData(const Element& aElement,
3633 ComputedStyle& aStyle) {
3634 // GetDisplayedType isn't necessarily nsIObjectLoadingContent::TYPE_NULL for
3635 // cases when the object is broken/suppressed/etc (e.g. a broken image), but
3636 // we want to treat those cases as TYPE_NULL
3637 uint32_t type;
3638 if (aElement.State().HasState(ElementState::BROKEN)) {
3639 type = nsIObjectLoadingContent::TYPE_NULL;
3640 } else {
3641 nsCOMPtr<nsIObjectLoadingContent> objContent =
3642 do_QueryInterface(const_cast<Element*>(&aElement));
3643 NS_ASSERTION(objContent,
3644 "embed and object must implement "
3645 "nsIObjectLoadingContent!");
3647 objContent->GetDisplayedType(&type);
3650 if (type == nsIObjectLoadingContent::TYPE_FALLBACK &&
3651 !StaticPrefs::layout_use_plugin_fallback()) {
3652 type = nsIObjectLoadingContent::TYPE_NULL;
3655 static constexpr FrameConstructionDataByInt sObjectData[] = {
3656 SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_LOADING,
3657 NS_NewEmptyFrame),
3658 SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_FALLBACK,
3659 ToCreationFunc(NS_NewBlockFrame)),
3660 SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_IMAGE,
3661 NS_NewSubDocumentOrImageFrame),
3662 SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_DOCUMENT,
3663 NS_NewSubDocumentFrame),
3664 // Fake plugin handlers load as documents
3665 // XXXmats is TYPE_FAKE_PLUGIN something we need?
3666 SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_FAKE_PLUGIN,
3667 NS_NewSubDocumentFrame)
3668 // Nothing for TYPE_NULL so we'll construct frames by display there
3671 return FindDataByInt((int32_t)type, aElement, aStyle, sObjectData,
3672 ArrayLength(sObjectData));
3675 /* static */
3676 const nsCSSFrameConstructor::FrameConstructionData*
3677 nsCSSFrameConstructor::FindCanvasData(const Element& aElement,
3678 ComputedStyle& aStyle) {
3679 // We want to check whether script is enabled on the document that
3680 // could be painting to the canvas. That's the owner document of
3681 // the canvas, except when the owner document is a static document,
3682 // in which case it's the original document it was cloned from.
3683 Document* doc = aElement.OwnerDoc();
3684 if (doc->IsStaticDocument()) {
3685 doc = doc->GetOriginalDocument();
3687 if (!doc->IsScriptEnabled()) {
3688 return nullptr;
3691 static constexpr FrameConstructionData sCanvasData(
3692 NS_NewHTMLCanvasFrame, 0, PseudoStyleType::htmlCanvasContent);
3693 return &sCanvasData;
3696 void nsCSSFrameConstructor::ConstructFrameFromItemInternal(
3697 FrameConstructionItem& aItem, nsFrameConstructorState& aState,
3698 nsContainerFrame* aParentFrame, nsFrameList& aFrameList) {
3699 const FrameConstructionData* data = aItem.mFCData;
3700 NS_ASSERTION(data, "Must have frame construction data");
3702 uint32_t bits = data->mBits;
3704 NS_ASSERTION(!(bits & FCDATA_FUNC_IS_DATA_GETTER),
3705 "Should have dealt with this inside the data finder");
3707 // Some sets of bits are not compatible with each other
3708 #define CHECK_ONLY_ONE_BIT(_bit1, _bit2) \
3709 NS_ASSERTION(!(bits & _bit1) || !(bits & _bit2), \
3710 "Only one of these bits should be set")
3711 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR,
3712 FCDATA_FORCE_NULL_ABSPOS_CONTAINER);
3713 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_WRAP_KIDS_IN_BLOCKS);
3714 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_IS_POPUP);
3715 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_SKIP_ABSPOS_PUSH);
3716 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR,
3717 FCDATA_DISALLOW_GENERATED_CONTENT);
3718 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_ALLOW_BLOCK_STYLES);
3719 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR,
3720 FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS);
3721 CHECK_ONLY_ONE_BIT(FCDATA_WRAP_KIDS_IN_BLOCKS,
3722 FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS);
3723 #undef CHECK_ONLY_ONE_BIT
3724 NS_ASSERTION(!(bits & FCDATA_FORCED_NON_SCROLLABLE_BLOCK) ||
3725 ((bits & FCDATA_FUNC_IS_FULL_CTOR) &&
3726 data->mFunc.mFullConstructor ==
3727 &nsCSSFrameConstructor::ConstructNonScrollableBlock),
3728 "Unexpected FCDATA_FORCED_NON_SCROLLABLE_BLOCK flag");
3729 MOZ_ASSERT(
3730 !(bits & FCDATA_IS_WRAPPER_ANON_BOX) || (bits & FCDATA_USE_CHILD_ITEMS),
3731 "Wrapper anon boxes should always have FCDATA_USE_CHILD_ITEMS");
3732 MOZ_ASSERT(!(bits & FCDATA_ALLOW_GRID_FLEX_COLUMN) ||
3733 (bits & FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS),
3734 "Need the block wrapper bit to create grid/flex/column.");
3736 // Don't create a subdocument frame for iframes if we're creating extra frames
3737 if (aState.mCreatingExtraFrames &&
3738 aItem.mContent->IsHTMLElement(nsGkAtoms::iframe)) {
3739 return;
3742 nsIContent* const content = aItem.mContent;
3743 nsIFrame* newFrame;
3744 nsIFrame* primaryFrame;
3745 ComputedStyle* const computedStyle = aItem.mComputedStyle;
3746 const nsStyleDisplay* display = computedStyle->StyleDisplay();
3747 if (bits & FCDATA_FUNC_IS_FULL_CTOR) {
3748 newFrame = (this->*(data->mFunc.mFullConstructor))(
3749 aState, aItem, aParentFrame, display, aFrameList);
3750 MOZ_ASSERT(newFrame, "Full constructor failed");
3751 primaryFrame = newFrame;
3752 } else {
3753 newFrame = (*data->mFunc.mCreationFunc)(mPresShell, computedStyle);
3755 const bool allowOutOfFlow = !(bits & FCDATA_DISALLOW_OUT_OF_FLOW);
3756 const bool isPopup = aItem.mIsPopup;
3758 nsContainerFrame* geometricParent =
3759 (isPopup || allowOutOfFlow)
3760 ? aState.GetGeometricParent(*display, aParentFrame)
3761 : aParentFrame;
3763 // In the non-scrollframe case, primaryFrame and newFrame are equal; in the
3764 // scrollframe case, newFrame is the scrolled frame while primaryFrame is
3765 // the scrollframe.
3766 if ((bits & FCDATA_MAY_NEED_SCROLLFRAME) &&
3767 display->IsScrollableOverflow()) {
3768 nsContainerFrame* scrollframe = nullptr;
3769 BuildScrollFrame(aState, content, computedStyle, newFrame,
3770 geometricParent, scrollframe);
3771 primaryFrame = scrollframe;
3772 } else {
3773 InitAndRestoreFrame(aState, content, geometricParent, newFrame);
3774 primaryFrame = newFrame;
3777 // If we need to create a block formatting context to wrap our
3778 // kids, do it now.
3779 nsIFrame* maybeAbsoluteContainingBlockStyleFrame = primaryFrame;
3780 nsIFrame* maybeAbsoluteContainingBlock = newFrame;
3781 nsIFrame* possiblyLeafFrame = newFrame;
3782 nsContainerFrame* outerFrame = nullptr;
3783 if (bits & FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS) {
3784 RefPtr<ComputedStyle> outerStyle =
3785 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
3786 data->mAnonBoxPseudo, computedStyle);
3787 #ifdef DEBUG
3788 nsContainerFrame* containerFrame = do_QueryFrame(newFrame);
3789 MOZ_ASSERT(containerFrame);
3790 #endif
3791 nsContainerFrame* container = static_cast<nsContainerFrame*>(newFrame);
3792 nsContainerFrame* innerFrame;
3793 if (bits & FCDATA_ALLOW_GRID_FLEX_COLUMN) {
3794 switch (display->DisplayInside()) {
3795 case StyleDisplayInside::Flex:
3796 outerFrame = NS_NewFlexContainerFrame(mPresShell, outerStyle);
3797 InitAndRestoreFrame(aState, content, container, outerFrame);
3798 innerFrame = outerFrame;
3799 break;
3800 case StyleDisplayInside::Grid:
3801 outerFrame = NS_NewGridContainerFrame(mPresShell, outerStyle);
3802 InitAndRestoreFrame(aState, content, container, outerFrame);
3803 innerFrame = outerFrame;
3804 break;
3805 default: {
3806 innerFrame = NS_NewBlockFormattingContext(mPresShell, outerStyle);
3807 if (outerStyle->StyleColumn()->IsColumnContainerStyle()) {
3808 outerFrame = BeginBuildingColumns(aState, content, container,
3809 innerFrame, outerStyle);
3810 } else {
3811 // No need to create column container. Initialize innerFrame.
3812 InitAndRestoreFrame(aState, content, container, innerFrame);
3813 outerFrame = innerFrame;
3815 break;
3818 } else {
3819 innerFrame = NS_NewBlockFormattingContext(mPresShell, outerStyle);
3820 InitAndRestoreFrame(aState, content, container, innerFrame);
3821 outerFrame = innerFrame;
3824 SetInitialSingleChild(container, outerFrame);
3826 container->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
3828 // Now figure out whether newFrame or outerFrame should be the
3829 // absolute container.
3830 if (outerFrame->IsAbsPosContainingBlock()) {
3831 maybeAbsoluteContainingBlock = outerFrame;
3832 maybeAbsoluteContainingBlockStyleFrame = outerFrame;
3833 innerFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
3836 // Our kids should go into the innerFrame.
3837 newFrame = innerFrame;
3840 aState.AddChild(primaryFrame, aFrameList, content, aParentFrame,
3841 allowOutOfFlow, allowOutOfFlow);
3843 nsContainerFrame* newFrameAsContainer = do_QueryFrame(newFrame);
3844 if (newFrameAsContainer) {
3845 // Process the child content if requested
3846 nsFrameList childList;
3847 nsFrameConstructorSaveState absoluteSaveState;
3849 if (bits & FCDATA_FORCE_NULL_ABSPOS_CONTAINER) {
3850 aState.PushAbsoluteContainingBlock(nullptr, nullptr, absoluteSaveState);
3851 } else if (!(bits & FCDATA_SKIP_ABSPOS_PUSH)) {
3852 maybeAbsoluteContainingBlock->AddStateBits(
3853 NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
3854 if (maybeAbsoluteContainingBlockStyleFrame->IsAbsPosContainingBlock()) {
3855 auto* cf =
3856 static_cast<nsContainerFrame*>(maybeAbsoluteContainingBlock);
3857 aState.PushAbsoluteContainingBlock(
3858 cf, maybeAbsoluteContainingBlockStyleFrame, absoluteSaveState);
3862 nsFrameConstructorSaveState floatSaveState;
3863 aState.MaybePushFloatContainingBlock(newFrameAsContainer, floatSaveState);
3865 if (bits & FCDATA_USE_CHILD_ITEMS) {
3866 // At this point, we have not set up the auto value for this frame, and
3867 // no caller will have set it so it is not redundant and therefor will
3868 // not assert.
3869 AutoFrameConstructionPageName pageNameTracker(aState,
3870 newFrameAsContainer);
3871 ConstructFramesFromItemList(
3872 aState, aItem.mChildItems, newFrameAsContainer,
3873 bits & FCDATA_IS_WRAPPER_ANON_BOX, childList);
3874 } else {
3875 // Process the child frames.
3876 ProcessChildren(aState, content, computedStyle, newFrameAsContainer,
3877 !(bits & FCDATA_DISALLOW_GENERATED_CONTENT), childList,
3878 (bits & FCDATA_ALLOW_BLOCK_STYLES) != 0,
3879 possiblyLeafFrame);
3882 if (bits & FCDATA_WRAP_KIDS_IN_BLOCKS) {
3883 nsFrameList newList;
3884 nsFrameList currentBlockList;
3885 nsIFrame* f;
3886 while ((f = childList.FirstChild()) != nullptr) {
3887 bool wrapFrame = IsInlineFrame(f) || IsFramePartOfIBSplit(f);
3888 if (!wrapFrame) {
3889 FlushAccumulatedBlock(aState, content, newFrameAsContainer,
3890 currentBlockList, newList);
3893 childList.RemoveFrame(f);
3894 if (wrapFrame) {
3895 currentBlockList.AppendFrame(nullptr, f);
3896 } else {
3897 newList.AppendFrame(nullptr, f);
3900 FlushAccumulatedBlock(aState, content, newFrameAsContainer,
3901 currentBlockList, newList);
3903 if (childList.NotEmpty()) {
3904 // an error must have occurred, delete unprocessed frames
3905 childList.DestroyFrames();
3908 childList = std::move(newList);
3911 if (!(bits & FCDATA_ALLOW_GRID_FLEX_COLUMN) ||
3912 !MayNeedToCreateColumnSpanSiblings(newFrameAsContainer, childList)) {
3913 // Set the frame's initial child list. Note that MathML depends on this
3914 // being called even if childList is empty!
3915 newFrameAsContainer->SetInitialChildList(FrameChildListID::Principal,
3916 std::move(childList));
3917 } else {
3918 // Extract any initial non-column-span kids, and put them in inner
3919 // frame's child list.
3920 nsFrameList initialNonColumnSpanKids =
3921 childList.Split([](nsIFrame* f) { return f->IsColumnSpan(); });
3922 newFrameAsContainer->SetInitialChildList(
3923 FrameChildListID::Principal, std::move(initialNonColumnSpanKids));
3925 if (childList.NotEmpty()) {
3926 nsFrameList columnSpanSiblings = CreateColumnSpanSiblings(
3927 aState, newFrameAsContainer, childList,
3928 // Column content should never be a absolute/fixed positioned
3929 // containing block. Pass nullptr as aPositionedFrame.
3930 nullptr);
3932 MOZ_ASSERT(outerFrame,
3933 "outerFrame should be non-null if multi-column container "
3934 "is created.");
3935 FinishBuildingColumns(aState, outerFrame, newFrameAsContainer,
3936 columnSpanSiblings);
3942 NS_ASSERTION(newFrame->IsFrameOfType(nsIFrame::eLineParticipant) ==
3943 ((bits & FCDATA_IS_LINE_PARTICIPANT) != 0),
3944 "Incorrectly set FCDATA_IS_LINE_PARTICIPANT bits");
3946 // Even if mCreatingExtraFrames is set, we may need to SetPrimaryFrame for
3947 // generated content that doesn't have one yet. Note that we have to examine
3948 // the frame bit, because by this point mIsGeneratedContent has been cleared
3949 // on aItem.
3950 if ((!aState.mCreatingExtraFrames ||
3951 (aItem.mContent->IsRootOfNativeAnonymousSubtree() &&
3952 !aItem.mContent->GetPrimaryFrame())) &&
3953 !(bits & FCDATA_SKIP_FRAMESET)) {
3954 aItem.mContent->SetPrimaryFrame(primaryFrame);
3955 ActiveLayerTracker::TransferActivityToFrame(aItem.mContent, primaryFrame);
3959 static void GatherSubtreeElements(Element* aElement,
3960 nsTArray<Element*>& aElements) {
3961 aElements.AppendElement(aElement);
3962 StyleChildrenIterator iter(aElement);
3963 for (nsIContent* c = iter.GetNextChild(); c; c = iter.GetNextChild()) {
3964 if (!c->IsElement()) {
3965 continue;
3967 GatherSubtreeElements(c->AsElement(), aElements);
3971 nsresult nsCSSFrameConstructor::GetAnonymousContent(
3972 nsIContent* aParent, nsIFrame* aParentFrame,
3973 nsTArray<nsIAnonymousContentCreator::ContentInfo>& aContent) {
3974 nsIAnonymousContentCreator* creator = do_QueryFrame(aParentFrame);
3975 if (!creator) {
3976 return NS_OK;
3979 nsresult rv = creator->CreateAnonymousContent(aContent);
3980 if (NS_FAILED(rv)) {
3981 // CreateAnonymousContent failed, e.g. because the page has a <use> loop.
3982 return rv;
3985 MOZ_ASSERT(aParent->IsElement());
3986 for (const auto& info : aContent) {
3987 // get our child's content and set its parent to our content
3988 nsIContent* content = info.mContent;
3989 content->SetIsNativeAnonymousRoot();
3991 BindContext context(*aParent->AsElement(), BindContext::ForNativeAnonymous);
3992 rv = content->BindToTree(context, *aParent);
3994 if (NS_FAILED(rv)) {
3995 content->UnbindFromTree();
3996 return rv;
4000 // Some situations where we don't cache anonymous content styles:
4002 // * when visibility or pointer-events is anything other than the initial
4003 // value; we rely on visibility and pointer-events inheriting into anonymous
4004 // content, but don't bother adding this state to the AnonymousContentKey,
4005 // since it's not so common. Note that with overlay scrollbars, scrollbars
4006 // always start off with pointer-events: none so we don't need to check for
4007 // that in that case.
4009 // * when the medium is anything other than screen; some UA style sheet rules
4010 // apply in e.g. print medium, and will give different results from the
4011 // cached styles
4012 Maybe<bool> computedAllowStyleCaching;
4013 auto ComputeAllowStyleCaching = [&] {
4014 if (!StaticPrefs::layout_css_cached_scrollbar_styles_enabled()) {
4015 return false;
4017 if (aParentFrame->StyleVisibility()->mVisible != StyleVisibility::Visible) {
4018 return false;
4020 nsPresContext* pc = mPresShell->GetPresContext();
4021 if (!pc->UseOverlayScrollbars() &&
4022 aParentFrame->StyleUI()->ComputedPointerEvents() !=
4023 StylePointerEvents::Auto) {
4024 return false;
4026 if (pc->Medium() != nsGkAtoms::screen) {
4027 return false;
4029 return true;
4032 auto AllowStyleCaching = [&] {
4033 if (computedAllowStyleCaching.isNothing()) {
4034 computedAllowStyleCaching.emplace(ComputeAllowStyleCaching());
4036 return computedAllowStyleCaching.value();
4039 // Compute styles for the anonymous content tree.
4040 ServoStyleSet* styleSet = mPresShell->StyleSet();
4041 for (auto& info : aContent) {
4042 Element* e = Element::FromNode(info.mContent);
4043 if (!e) {
4044 continue;
4047 if (info.mKey == AnonymousContentKey::None || !AllowStyleCaching()) {
4048 // Most NAC subtrees do not use caching of computed styles. Just go
4049 // ahead and eagerly style the subtree.
4050 styleSet->StyleNewSubtree(e);
4051 continue;
4054 // We have a NAC subtree for which we can use cached styles.
4055 AutoTArray<RefPtr<ComputedStyle>, 2> cachedStyles;
4056 AutoTArray<Element*, 2> elements;
4058 GatherSubtreeElements(e, elements);
4059 styleSet->GetCachedAnonymousContentStyles(info.mKey, cachedStyles);
4061 if (cachedStyles.IsEmpty()) {
4062 // We haven't stored cached styles for this kind of NAC subtree yet.
4063 // Eagerly compute those styles, then cache them for later.
4064 styleSet->StyleNewSubtree(e);
4065 for (Element* e : elements) {
4066 if (e->HasServoData()) {
4067 cachedStyles.AppendElement(ServoStyleSet::ResolveServoStyle(*e));
4068 } else {
4069 cachedStyles.AppendElement(nullptr);
4072 styleSet->PutCachedAnonymousContentStyles(info.mKey,
4073 std::move(cachedStyles));
4074 continue;
4077 // We previously stored cached styles for this kind of NAC subtree.
4078 // Iterate over them and set them on the subtree's elements.
4079 MOZ_ASSERT(cachedStyles.Length() == elements.Length(),
4080 "should always produce the same size NAC subtree");
4081 for (size_t i = 0, len = cachedStyles.Length(); i != len; ++i) {
4082 if (cachedStyles[i]) {
4083 #ifdef DEBUG
4084 // Assert that our cached style is the same as one we could compute.
4085 RefPtr<ComputedStyle> cs = styleSet->ResolveStyleLazily(*elements[i]);
4086 MOZ_ASSERT(
4087 cachedStyles[i]->EqualForCachedAnonymousContentStyle(*cs),
4088 "cached anonymous content styles should be identical to those we "
4089 "would compute normally");
4090 // All overlay scrollbars start off as inactive, so we can rely on their
4091 // pointer-events value being always none.
4092 MOZ_ASSERT(!mPresShell->GetPresContext()->UseOverlayScrollbars() ||
4093 cs->StyleUI()->ComputedPointerEvents() ==
4094 StylePointerEvents::None);
4095 #endif
4096 Servo_SetExplicitStyle(elements[i], cachedStyles[i]);
4101 return NS_OK;
4104 // XUL frames are not allowed to be out of flow.
4105 #define SIMPLE_XUL_FCDATA(_func) \
4106 FrameConstructionData(_func, \
4107 FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_SKIP_ABSPOS_PUSH)
4108 #define SCROLLABLE_XUL_FCDATA(_func) \
4109 FrameConstructionData(_func, FCDATA_DISALLOW_OUT_OF_FLOW | \
4110 FCDATA_SKIP_ABSPOS_PUSH | \
4111 FCDATA_MAY_NEED_SCROLLFRAME)
4112 // .. but we allow some XUL frames to be _containers_ for out-of-flow content
4113 // (This is the same as SCROLLABLE_XUL_FCDATA, but w/o FCDATA_SKIP_ABSPOS_PUSH)
4114 #define SCROLLABLE_ABSPOS_CONTAINER_XUL_FCDATA(_func) \
4115 FrameConstructionData( \
4116 _func, FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_MAY_NEED_SCROLLFRAME)
4118 #define SIMPLE_XUL_CREATE(_tag, _func) \
4119 { nsGkAtoms::_tag, SIMPLE_XUL_FCDATA(_func) }
4120 #define SCROLLABLE_XUL_CREATE(_tag, _func) \
4121 { nsGkAtoms::_tag, SCROLLABLE_XUL_FCDATA(_func) }
4123 /* static */
4124 const nsCSSFrameConstructor::FrameConstructionData*
4125 nsCSSFrameConstructor::FindXULTagData(const Element& aElement,
4126 ComputedStyle& aStyle) {
4127 MOZ_ASSERT(aElement.IsXULElement());
4128 static constexpr FrameConstructionData kPopupData(
4129 NS_NewMenuPopupFrame, FCDATA_IS_POPUP | FCDATA_SKIP_ABSPOS_PUSH);
4131 static constexpr FrameConstructionDataByTag sXULTagData[] = {
4132 SIMPLE_XUL_CREATE(image, NS_NewXULImageFrame),
4133 SIMPLE_XUL_CREATE(treechildren, NS_NewTreeBodyFrame),
4134 SIMPLE_TAG_CHAIN(label,
4135 nsCSSFrameConstructor::FindXULLabelOrDescriptionData),
4136 SIMPLE_TAG_CHAIN(description,
4137 nsCSSFrameConstructor::FindXULLabelOrDescriptionData),
4138 #ifdef XP_MACOSX
4139 SIMPLE_TAG_CHAIN(menubar, nsCSSFrameConstructor::FindXULMenubarData),
4140 #endif /* XP_MACOSX */
4141 SIMPLE_XUL_CREATE(iframe, NS_NewSubDocumentFrame),
4142 SIMPLE_XUL_CREATE(editor, NS_NewSubDocumentFrame),
4143 SIMPLE_XUL_CREATE(browser, NS_NewSubDocumentFrame),
4144 SIMPLE_XUL_CREATE(splitter, NS_NewSplitterFrame),
4145 SIMPLE_XUL_CREATE(slider, NS_NewSliderFrame),
4146 SIMPLE_XUL_CREATE(scrollbar, NS_NewScrollbarFrame),
4147 SIMPLE_XUL_CREATE(scrollbarbutton, NS_NewScrollbarButtonFrame),
4148 {nsGkAtoms::panel, kPopupData},
4149 {nsGkAtoms::menupopup, kPopupData},
4150 {nsGkAtoms::tooltip, kPopupData},
4153 return FindDataByTag(aElement, aStyle, sXULTagData, ArrayLength(sXULTagData));
4156 /* static */
4157 const nsCSSFrameConstructor::FrameConstructionData*
4158 nsCSSFrameConstructor::FindXULLabelOrDescriptionData(const Element& aElement,
4159 ComputedStyle&) {
4160 // Follow CSS display value if no value attribute
4161 if (!aElement.HasAttr(nsGkAtoms::value)) {
4162 return nullptr;
4165 // Follow CSS display if there's no crop="center".
4166 if (!aElement.AttrValueIs(kNameSpaceID_None, nsGkAtoms::crop,
4167 nsGkAtoms::center, eCaseMatters)) {
4168 return nullptr;
4171 static constexpr FrameConstructionData sXULTextBoxData =
4172 SIMPLE_XUL_FCDATA(NS_NewTextBoxFrame);
4173 return &sXULTextBoxData;
4176 #ifdef XP_MACOSX
4177 /* static */
4178 const nsCSSFrameConstructor::FrameConstructionData*
4179 nsCSSFrameConstructor::FindXULMenubarData(const Element& aElement,
4180 ComputedStyle&) {
4181 if (aElement.OwnerDoc()->IsInChromeDocShell()) {
4182 BrowsingContext* bc = aElement.OwnerDoc()->GetBrowsingContext();
4183 bool isRoot = bc && !bc->GetParent();
4184 if (isRoot) {
4185 // This is the root. Suppress the menubar, since on Mac
4186 // window menus are not attached to the window.
4187 static constexpr FrameConstructionData sSuppressData = SUPPRESS_FCDATA();
4188 return &sSuppressData;
4192 return nullptr;
4194 #endif /* XP_MACOSX */
4196 already_AddRefed<ComputedStyle> nsCSSFrameConstructor::BeginBuildingScrollFrame(
4197 nsFrameConstructorState& aState, nsIContent* aContent,
4198 ComputedStyle* aContentStyle, nsContainerFrame* aParentFrame,
4199 PseudoStyleType aScrolledPseudo, bool aIsRoot,
4200 nsContainerFrame*& aNewFrame) {
4201 nsContainerFrame* gfxScrollFrame = aNewFrame;
4203 nsFrameList anonymousList;
4205 if (!gfxScrollFrame) {
4206 const bool useXULScrollFrame = [&] {
4207 const auto& disp = *aContentStyle->StyleDisplay();
4208 if (disp.DisplayOutside() == StyleDisplayOutside::XUL) {
4209 // XXX Should this be emulated?
4210 return true;
4212 if (disp.DisplayInside() == StyleDisplayInside::MozBox) {
4213 return !aContentStyle->StyleVisibility()->EmulateMozBoxWithFlex();
4215 return false;
4216 }();
4217 // Build a XULScrollFrame when the child is a box, otherwise an
4218 // HTMLScrollFrame
4219 if (useXULScrollFrame) {
4220 gfxScrollFrame = NS_NewXULScrollFrame(mPresShell, aContentStyle, aIsRoot);
4221 } else {
4222 gfxScrollFrame =
4223 NS_NewHTMLScrollFrame(mPresShell, aContentStyle, aIsRoot);
4226 InitAndRestoreFrame(aState, aContent, aParentFrame, gfxScrollFrame);
4229 MOZ_ASSERT(gfxScrollFrame);
4231 // if there are any anonymous children for the scroll frame, create
4232 // frames for them.
4234 // We can't take the normal ProcessChildren path, because the NAC needs to
4235 // be parented to the scrollframe, and everything else needs to be parented
4236 // to the scrolledframe.
4237 AutoTArray<nsIAnonymousContentCreator::ContentInfo, 4> scrollNAC;
4238 DebugOnly<nsresult> rv =
4239 GetAnonymousContent(aContent, gfxScrollFrame, scrollNAC);
4240 MOZ_ASSERT(NS_SUCCEEDED(rv));
4241 if (scrollNAC.Length() > 0) {
4242 nsFrameConstructorSaveState floatSaveState;
4243 aState.MaybePushFloatContainingBlock(gfxScrollFrame, floatSaveState);
4245 AutoFrameConstructionItemList items(this);
4246 AutoFrameConstructionPageName pageNameTracker(aState, gfxScrollFrame);
4247 AddFCItemsForAnonymousContent(aState, gfxScrollFrame, scrollNAC, items,
4248 pageNameTracker);
4249 ConstructFramesFromItemList(aState, items, gfxScrollFrame,
4250 /* aParentIsWrapperAnonBox = */ false,
4251 anonymousList);
4254 aNewFrame = gfxScrollFrame;
4255 gfxScrollFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
4257 // we used the style that was passed in. So resolve another one.
4258 ServoStyleSet* styleSet = mPresShell->StyleSet();
4259 RefPtr<ComputedStyle> scrolledChildStyle =
4260 styleSet->ResolveInheritingAnonymousBoxStyle(aScrolledPseudo,
4261 aContentStyle);
4263 gfxScrollFrame->SetInitialChildList(FrameChildListID::Principal,
4264 std::move(anonymousList));
4266 return scrolledChildStyle.forget();
4269 void nsCSSFrameConstructor::FinishBuildingScrollFrame(
4270 nsContainerFrame* aScrollFrame, nsIFrame* aScrolledFrame) {
4271 aScrollFrame->AppendFrames(FrameChildListID::Principal,
4272 nsFrameList(aScrolledFrame, aScrolledFrame));
4276 * Called to wrap a gfx scrollframe around a frame. The hierarchy will look like
4277 * this
4279 * ------- for gfx scrollbars ------
4282 * ScrollFrame
4285 * Frame (scrolled frame you passed in)
4288 * -----------------------------------
4289 * LEGEND:
4291 * ScrollFrame: This is a frame that manages gfx cross platform frame based
4292 * scrollbars.
4294 * @param aContent the content node of the child to wrap.
4296 * @param aScrolledFrame The frame of the content to wrap. This should not be
4297 * Initialized. This method will initialize it with a scrolled pseudo and no
4298 * nsIContent. The content will be attached to the scrollframe returned.
4300 * @param aContentStyle the style that has already been resolved for the content
4301 * being passed in.
4303 * @param aParentFrame The parent to attach the scroll frame to
4305 * @param aNewFrame The new scrollframe or gfx scrollframe that we create. It
4306 * will contain the scrolled frame you passed in. (returned) If this is not
4307 * null, we'll just use it
4309 * @param aScrolledContentStyle the style that was resolved for the scrolled
4310 * frame. (returned)
4312 void nsCSSFrameConstructor::BuildScrollFrame(nsFrameConstructorState& aState,
4313 nsIContent* aContent,
4314 ComputedStyle* aContentStyle,
4315 nsIFrame* aScrolledFrame,
4316 nsContainerFrame* aParentFrame,
4317 nsContainerFrame*& aNewFrame) {
4318 RefPtr<ComputedStyle> scrolledContentStyle = BeginBuildingScrollFrame(
4319 aState, aContent, aContentStyle, aParentFrame,
4320 PseudoStyleType::scrolledContent, false, aNewFrame);
4322 aScrolledFrame->SetComputedStyleWithoutNotification(scrolledContentStyle);
4323 InitAndRestoreFrame(aState, aContent, aNewFrame, aScrolledFrame);
4325 FinishBuildingScrollFrame(aNewFrame, aScrolledFrame);
4328 const nsCSSFrameConstructor::FrameConstructionData*
4329 nsCSSFrameConstructor::FindDisplayData(const nsStyleDisplay& aDisplay,
4330 const StyleMozBoxLayout aMozBoxLayout,
4331 const Element& aElement) {
4332 static_assert(eParentTypeCount < (1 << (32 - FCDATA_PARENT_TYPE_OFFSET)),
4333 "Check eParentTypeCount should not overflow");
4335 // The style system ensures that floated and positioned frames are
4336 // block-level.
4337 NS_ASSERTION(
4338 !(aDisplay.IsFloatingStyle() || aDisplay.IsAbsolutelyPositionedStyle()) ||
4339 aDisplay.IsBlockOutsideStyle() ||
4340 aDisplay.DisplayOutside() == StyleDisplayOutside::XUL,
4341 "Style system did not apply CSS2.1 section 9.7 fixups");
4343 // If this is "body", try propagating its scroll style to the viewport
4344 // Note that we need to do this even if the body is NOT scrollable;
4345 // it might have dynamically changed from scrollable to not scrollable,
4346 // and that might need to be propagated.
4347 // XXXbz is this the right place to do this? If this code moves,
4348 // make this function static.
4349 bool propagatedScrollToViewport = false;
4350 if (aElement.IsHTMLElement(nsGkAtoms::body)) {
4351 if (nsPresContext* presContext = mPresShell->GetPresContext()) {
4352 propagatedScrollToViewport =
4353 presContext->UpdateViewportScrollStylesOverride() == &aElement;
4354 MOZ_ASSERT(!propagatedScrollToViewport ||
4355 !mPresShell->GetPresContext()->IsPaginated(),
4356 "Shouldn't propagate scroll in paginated contexts");
4360 switch (aDisplay.DisplayInside()) {
4361 case StyleDisplayInside::Flow:
4362 case StyleDisplayInside::FlowRoot: {
4363 if (aDisplay.IsInlineFlow()) {
4364 static constexpr FrameConstructionData data(
4365 &nsCSSFrameConstructor::ConstructInline,
4366 FCDATA_IS_INLINE | FCDATA_IS_LINE_PARTICIPANT);
4367 return &data;
4370 // If the frame is a block-level frame and is scrollable, then wrap it in
4371 // a scroll frame. Except we don't want to do that for paginated contexts
4372 // for frames that are block-outside and aren't frames for native
4373 // anonymous stuff.
4374 // XXX Ignore tables for the time being (except caption)
4375 const uint32_t kCaptionCtorFlags =
4376 FCDATA_IS_TABLE_PART | FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable);
4377 bool caption = aDisplay.mDisplay == StyleDisplay::TableCaption;
4378 bool suppressScrollFrame = false;
4379 bool needScrollFrame =
4380 aDisplay.IsScrollableOverflow() && !propagatedScrollToViewport;
4381 if (needScrollFrame) {
4382 suppressScrollFrame = mPresShell->GetPresContext()->IsPaginated() &&
4383 aDisplay.IsBlockOutsideStyle() &&
4384 !aElement.IsInNativeAnonymousSubtree();
4385 if (!suppressScrollFrame) {
4386 static constexpr FrameConstructionData sScrollableBlockData[2] = {
4387 {&nsCSSFrameConstructor::ConstructScrollableBlock},
4388 {&nsCSSFrameConstructor::ConstructScrollableBlock,
4389 kCaptionCtorFlags}};
4390 return &sScrollableBlockData[caption];
4393 // If the scrollable frame would have propagated its scrolling to the
4394 // viewport, we still want to construct a regular block rather than a
4395 // scrollframe so that it paginates correctly, but we don't want to set
4396 // the bit on the block that tells it to clip at paint time.
4397 if (mPresShell->GetPresContext()->ElementWouldPropagateScrollStyles(
4398 aElement)) {
4399 suppressScrollFrame = false;
4403 // Handle various non-scrollable blocks.
4404 static constexpr FrameConstructionData sNonScrollableBlockData[2][2] = {
4405 {{&nsCSSFrameConstructor::ConstructNonScrollableBlock},
4406 {&nsCSSFrameConstructor::ConstructNonScrollableBlock,
4407 kCaptionCtorFlags}},
4408 {{&nsCSSFrameConstructor::ConstructNonScrollableBlock,
4409 FCDATA_FORCED_NON_SCROLLABLE_BLOCK},
4410 {&nsCSSFrameConstructor::ConstructNonScrollableBlock,
4411 FCDATA_FORCED_NON_SCROLLABLE_BLOCK | kCaptionCtorFlags}}};
4412 return &sNonScrollableBlockData[suppressScrollFrame][caption];
4414 case StyleDisplayInside::Table: {
4415 static constexpr FrameConstructionData data(
4416 &nsCSSFrameConstructor::ConstructTable);
4417 return &data;
4419 // NOTE: In the unlikely event that we add another table-part here that
4420 // has a desired-parent-type (& hence triggers table fixup), we'll need to
4421 // also update the flexbox chunk in ComputedStyle::ApplyStyleFixups().
4422 case StyleDisplayInside::TableRowGroup: {
4423 static constexpr FrameConstructionData data(
4424 &nsCSSFrameConstructor::ConstructTableRowOrRowGroup,
4425 FCDATA_IS_TABLE_PART |
4426 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable));
4427 return &data;
4429 case StyleDisplayInside::TableColumn: {
4430 static constexpr FrameConstructionData data(
4431 &nsCSSFrameConstructor::ConstructTableCol,
4432 FCDATA_IS_TABLE_PART |
4433 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeColGroup));
4434 return &data;
4436 case StyleDisplayInside::TableColumnGroup: {
4437 static constexpr FrameConstructionData data(
4438 ToCreationFunc(NS_NewTableColGroupFrame),
4439 FCDATA_IS_TABLE_PART | FCDATA_DISALLOW_OUT_OF_FLOW |
4440 FCDATA_SKIP_ABSPOS_PUSH |
4441 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable));
4442 return &data;
4444 case StyleDisplayInside::TableHeaderGroup: {
4445 static constexpr FrameConstructionData data(
4446 &nsCSSFrameConstructor::ConstructTableRowOrRowGroup,
4447 FCDATA_IS_TABLE_PART |
4448 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable));
4449 return &data;
4451 case StyleDisplayInside::TableFooterGroup: {
4452 static constexpr FrameConstructionData data(
4453 &nsCSSFrameConstructor::ConstructTableRowOrRowGroup,
4454 FCDATA_IS_TABLE_PART |
4455 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable));
4456 return &data;
4458 case StyleDisplayInside::TableRow: {
4459 static constexpr FrameConstructionData data(
4460 &nsCSSFrameConstructor::ConstructTableRowOrRowGroup,
4461 FCDATA_IS_TABLE_PART |
4462 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRowGroup));
4463 return &data;
4465 case StyleDisplayInside::TableCell: {
4466 static constexpr FrameConstructionData data(
4467 &nsCSSFrameConstructor::ConstructTableCell,
4468 FCDATA_IS_TABLE_PART | FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRow));
4469 return &data;
4471 case StyleDisplayInside::MozBox: {
4472 if (!aElement.IsInNativeAnonymousSubtree() &&
4473 aElement.OwnerDoc()->IsContentDocument()) {
4474 aElement.OwnerDoc()->WarnOnceAbout(
4475 DeprecatedOperations::eMozBoxOrInlineBoxDisplay);
4478 // If we're emulating -moz-box with flexbox, then treat it as non-XUL and
4479 // fall through.
4480 if (aMozBoxLayout == StyleMozBoxLayout::Legacy) {
4481 static constexpr FrameConstructionData data =
4482 SCROLLABLE_ABSPOS_CONTAINER_XUL_FCDATA(
4483 ToCreationFunc(NS_NewBoxFrame));
4484 return &data;
4486 [[fallthrough]];
4488 case StyleDisplayInside::Flex:
4489 case StyleDisplayInside::WebkitBox: {
4490 static constexpr FrameConstructionData nonScrollableData(
4491 ToCreationFunc(NS_NewFlexContainerFrame));
4492 static constexpr FrameConstructionData data(
4493 ToCreationFunc(NS_NewFlexContainerFrame),
4494 FCDATA_MAY_NEED_SCROLLFRAME);
4495 return MOZ_UNLIKELY(propagatedScrollToViewport) ? &nonScrollableData
4496 : &data;
4498 case StyleDisplayInside::Grid: {
4499 static constexpr FrameConstructionData nonScrollableData(
4500 ToCreationFunc(NS_NewGridContainerFrame));
4501 static constexpr FrameConstructionData data(
4502 ToCreationFunc(NS_NewGridContainerFrame),
4503 FCDATA_MAY_NEED_SCROLLFRAME);
4504 return MOZ_UNLIKELY(propagatedScrollToViewport) ? &nonScrollableData
4505 : &data;
4507 case StyleDisplayInside::Ruby: {
4508 static constexpr FrameConstructionData data[] = {
4509 {&nsCSSFrameConstructor::ConstructBlockRubyFrame,
4510 FCDATA_MAY_NEED_SCROLLFRAME},
4511 {ToCreationFunc(NS_NewRubyFrame), FCDATA_IS_LINE_PARTICIPANT}};
4512 bool isInline = aDisplay.DisplayOutside() == StyleDisplayOutside::Inline;
4513 return &data[isInline];
4515 case StyleDisplayInside::RubyBase: {
4516 static constexpr FrameConstructionData data(
4517 ToCreationFunc(NS_NewRubyBaseFrame),
4518 FCDATA_IS_LINE_PARTICIPANT |
4519 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRubyBaseContainer));
4520 return &data;
4522 case StyleDisplayInside::RubyBaseContainer: {
4523 static constexpr FrameConstructionData data(
4524 ToCreationFunc(NS_NewRubyBaseContainerFrame),
4525 FCDATA_IS_LINE_PARTICIPANT |
4526 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRuby));
4527 return &data;
4529 case StyleDisplayInside::RubyText: {
4530 static constexpr FrameConstructionData data(
4531 ToCreationFunc(NS_NewRubyTextFrame),
4532 FCDATA_IS_LINE_PARTICIPANT |
4533 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRubyTextContainer));
4534 return &data;
4536 case StyleDisplayInside::RubyTextContainer: {
4537 static constexpr FrameConstructionData data(
4538 ToCreationFunc(NS_NewRubyTextContainerFrame),
4539 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRuby));
4540 return &data;
4542 default:
4543 MOZ_ASSERT_UNREACHABLE("unknown 'display' value");
4544 return nullptr;
4548 nsIFrame* nsCSSFrameConstructor::ConstructScrollableBlock(
4549 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
4550 nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay,
4551 nsFrameList& aFrameList) {
4552 nsIContent* const content = aItem.mContent;
4553 ComputedStyle* const computedStyle = aItem.mComputedStyle;
4555 nsContainerFrame* newFrame = nullptr;
4556 RefPtr<ComputedStyle> scrolledContentStyle = BeginBuildingScrollFrame(
4557 aState, content, computedStyle,
4558 aState.GetGeometricParent(*aDisplay, aParentFrame),
4559 PseudoStyleType::scrolledContent, false, newFrame);
4561 // Create our block frame
4562 // pass a temporary stylecontext, the correct one will be set later
4563 nsContainerFrame* scrolledFrame =
4564 NS_NewBlockFormattingContext(mPresShell, computedStyle);
4566 // Make sure to AddChild before we call ConstructBlock so that we
4567 // end up before our descendants in fixed-pos lists as needed.
4568 aState.AddChild(newFrame, aFrameList, content, aParentFrame);
4570 nsFrameList blockList;
4571 ConstructBlock(aState, content, newFrame, newFrame, scrolledContentStyle,
4572 &scrolledFrame, blockList,
4573 newFrame->IsAbsPosContainingBlock() ? newFrame : nullptr);
4575 MOZ_ASSERT(blockList.OnlyChild() == scrolledFrame,
4576 "Scrollframe's frameList should be exactly the scrolled frame!");
4577 FinishBuildingScrollFrame(newFrame, scrolledFrame);
4579 return newFrame;
4582 nsIFrame* nsCSSFrameConstructor::ConstructNonScrollableBlock(
4583 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
4584 nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay,
4585 nsFrameList& aFrameList) {
4586 ComputedStyle* const computedStyle = aItem.mComputedStyle;
4588 // We want a block formatting context root in paginated contexts for
4589 // every block that would be scrollable in a non-paginated context.
4590 // We mark our blocks with a bit here if this condition is true, so
4591 // we can check it later in nsIFrame::ApplyPaginatedOverflowClipping.
4592 bool clipPaginatedOverflow =
4593 (aItem.mFCData->mBits & FCDATA_FORCED_NON_SCROLLABLE_BLOCK) != 0;
4594 nsFrameState flags = nsFrameState(0);
4595 if ((aDisplay->IsAbsolutelyPositionedStyle() || aDisplay->IsFloatingStyle() ||
4596 aDisplay->DisplayInside() == StyleDisplayInside::FlowRoot ||
4597 clipPaginatedOverflow) &&
4598 !SVGUtils::IsInSVGTextSubtree(aParentFrame)) {
4599 flags = NS_BLOCK_FORMATTING_CONTEXT_STATE_BITS;
4600 if (clipPaginatedOverflow) {
4601 flags |= NS_BLOCK_CLIP_PAGINATED_OVERFLOW;
4605 nsContainerFrame* newFrame = NS_NewBlockFrame(mPresShell, computedStyle);
4606 newFrame->AddStateBits(flags);
4607 ConstructBlock(aState, aItem.mContent,
4608 aState.GetGeometricParent(*aDisplay, aParentFrame),
4609 aParentFrame, computedStyle, &newFrame, aFrameList,
4610 newFrame->IsAbsPosContainingBlock() ? newFrame : nullptr);
4611 return newFrame;
4614 void nsCSSFrameConstructor::InitAndRestoreFrame(
4615 const nsFrameConstructorState& aState, nsIContent* aContent,
4616 nsContainerFrame* aParentFrame, nsIFrame* aNewFrame, bool aAllowCounters) {
4617 MOZ_ASSERT(aNewFrame, "Null frame cannot be initialized");
4619 // Initialize the frame
4620 aNewFrame->Init(aContent, aParentFrame, nullptr);
4621 aNewFrame->AddStateBits(aState.mAdditionalStateBits);
4623 if (aState.mFrameState) {
4624 // Restore frame state for just the newly created frame.
4625 RestoreFrameStateFor(aNewFrame, aState.mFrameState);
4628 if (aAllowCounters &&
4629 mContainStyleScopeManager.AddCounterChanges(aNewFrame)) {
4630 CountersDirty();
4634 already_AddRefed<ComputedStyle> nsCSSFrameConstructor::ResolveComputedStyle(
4635 nsIContent* aContent) {
4636 if (auto* element = Element::FromNode(aContent)) {
4637 return ServoStyleSet::ResolveServoStyle(*element);
4640 MOZ_ASSERT(aContent->IsText(),
4641 "shouldn't waste time creating ComputedStyles for "
4642 "comments and processing instructions");
4644 Element* parent = aContent->GetFlattenedTreeParentElement();
4645 MOZ_ASSERT(parent, "Text out of the flattened tree?");
4647 // FIXME(emilio): The const_cast is unfortunate, but it's not worse than what
4648 // we did before.
4650 // We could use ResolveServoStyle, but that would involve extra unnecessary
4651 // refcount traffic...
4652 auto* parentStyle =
4653 const_cast<ComputedStyle*>(Servo_Element_GetMaybeOutOfDateStyle(parent));
4654 MOZ_ASSERT(parentStyle,
4655 "How are we inserting text frames in an unstyled element?");
4656 return mPresShell->StyleSet()->ResolveStyleForText(aContent, parentStyle);
4659 // MathML Mod - RBS
4660 void nsCSSFrameConstructor::FlushAccumulatedBlock(
4661 nsFrameConstructorState& aState, nsIContent* aContent,
4662 nsContainerFrame* aParentFrame, nsFrameList& aBlockList,
4663 nsFrameList& aNewList) {
4664 if (aBlockList.IsEmpty()) {
4665 // Nothing to do
4666 return;
4669 auto anonPseudo = PseudoStyleType::mozMathMLAnonymousBlock;
4671 ComputedStyle* parentContext =
4672 nsIFrame::CorrectStyleParentFrame(aParentFrame, anonPseudo)->Style();
4673 ServoStyleSet* styleSet = mPresShell->StyleSet();
4674 RefPtr<ComputedStyle> blockContext =
4675 styleSet->ResolveInheritingAnonymousBoxStyle(anonPseudo, parentContext);
4677 // then, create a block frame that will wrap the child frames. Make it a
4678 // MathML frame so that Get(Absolute/Float)ContainingBlockFor know that this
4679 // is not a suitable block.
4680 nsContainerFrame* blockFrame =
4681 NS_NewMathMLmathBlockFrame(mPresShell, blockContext);
4683 InitAndRestoreFrame(aState, aContent, aParentFrame, blockFrame);
4684 ReparentFrames(this, blockFrame, aBlockList, false);
4685 // We have to walk over aBlockList before we hand it over to blockFrame.
4686 for (nsIFrame* f : aBlockList) {
4687 f->SetParentIsWrapperAnonBox();
4689 // abs-pos and floats are disabled in MathML children so we don't have to
4690 // worry about messing up those.
4691 blockFrame->SetInitialChildList(FrameChildListID::Principal,
4692 std::move(aBlockList));
4693 aNewList.AppendFrame(nullptr, blockFrame);
4696 // Only <math> elements can be floated or positioned. All other MathML
4697 // should be in-flow.
4698 #define SIMPLE_MATHML_CREATE(_tag, _func) \
4700 nsGkAtoms::_tag, { \
4701 _func, FCDATA_DISALLOW_OUT_OF_FLOW | \
4702 FCDATA_FORCE_NULL_ABSPOS_CONTAINER | \
4703 FCDATA_WRAP_KIDS_IN_BLOCKS \
4707 /* static */
4708 const nsCSSFrameConstructor::FrameConstructionData*
4709 nsCSSFrameConstructor::FindMathMLData(const Element& aElement,
4710 ComputedStyle& aStyle) {
4711 MOZ_ASSERT(aElement.IsMathMLElement());
4713 nsAtom* tag = aElement.NodeInfo()->NameAtom();
4715 // Handle <math> specially, because it sometimes produces inlines
4716 if (tag == nsGkAtoms::math) {
4717 // The IsBlockOutsideStyle() check must match what
4718 // specified::Display::equivalent_block_display is checking for
4719 // already-block-outside things. Though the behavior here for the
4720 // display:table case is pretty weird...
4721 if (aStyle.StyleDisplay()->IsBlockOutsideStyle()) {
4722 static constexpr FrameConstructionData sBlockMathData(
4723 ToCreationFunc(NS_NewMathMLmathBlockFrame),
4724 FCDATA_FORCE_NULL_ABSPOS_CONTAINER | FCDATA_WRAP_KIDS_IN_BLOCKS);
4725 return &sBlockMathData;
4728 static constexpr FrameConstructionData sInlineMathData(
4729 ToCreationFunc(NS_NewMathMLmathInlineFrame),
4730 FCDATA_FORCE_NULL_ABSPOS_CONTAINER | FCDATA_IS_LINE_PARTICIPANT |
4731 FCDATA_WRAP_KIDS_IN_BLOCKS);
4732 return &sInlineMathData;
4735 if (!StaticPrefs::
4736 mathml_legacy_maction_and_semantics_implementations_disabled()) {
4737 static constexpr FrameConstructionDataByTag sMactionAndSemanticsData[] = {
4738 SIMPLE_MATHML_CREATE(maction_, NS_NewMathMLmactionFrame),
4739 SIMPLE_MATHML_CREATE(semantics_, NS_NewMathMLsemanticsFrame)};
4740 const FrameConstructionData* data =
4741 FindDataByTag(aElement, aStyle, sMactionAndSemanticsData,
4742 ArrayLength(sMactionAndSemanticsData));
4743 if (data) {
4744 return data;
4748 static constexpr FrameConstructionDataByTag sMathMLData[] = {
4749 SIMPLE_MATHML_CREATE(annotation_, NS_NewMathMLTokenFrame),
4750 SIMPLE_MATHML_CREATE(annotation_xml_, NS_NewMathMLmrowFrame),
4751 SIMPLE_MATHML_CREATE(mi_, NS_NewMathMLTokenFrame),
4752 SIMPLE_MATHML_CREATE(mn_, NS_NewMathMLTokenFrame),
4753 SIMPLE_MATHML_CREATE(ms_, NS_NewMathMLTokenFrame),
4754 SIMPLE_MATHML_CREATE(mtext_, NS_NewMathMLTokenFrame),
4755 SIMPLE_MATHML_CREATE(mo_, NS_NewMathMLmoFrame),
4756 SIMPLE_MATHML_CREATE(mfrac_, NS_NewMathMLmfracFrame),
4757 SIMPLE_MATHML_CREATE(msup_, NS_NewMathMLmmultiscriptsFrame),
4758 SIMPLE_MATHML_CREATE(msub_, NS_NewMathMLmmultiscriptsFrame),
4759 SIMPLE_MATHML_CREATE(msubsup_, NS_NewMathMLmmultiscriptsFrame),
4760 SIMPLE_MATHML_CREATE(munder_, NS_NewMathMLmunderoverFrame),
4761 SIMPLE_MATHML_CREATE(mover_, NS_NewMathMLmunderoverFrame),
4762 SIMPLE_MATHML_CREATE(munderover_, NS_NewMathMLmunderoverFrame),
4763 SIMPLE_MATHML_CREATE(mphantom_, NS_NewMathMLmrowFrame),
4764 SIMPLE_MATHML_CREATE(mpadded_, NS_NewMathMLmpaddedFrame),
4765 SIMPLE_MATHML_CREATE(mspace_, NS_NewMathMLmspaceFrame),
4766 SIMPLE_MATHML_CREATE(none, NS_NewMathMLmrowFrame),
4767 SIMPLE_MATHML_CREATE(mprescripts_, NS_NewMathMLmrowFrame),
4768 SIMPLE_MATHML_CREATE(mfenced_, NS_NewMathMLmrowFrame),
4769 SIMPLE_MATHML_CREATE(mmultiscripts_, NS_NewMathMLmmultiscriptsFrame),
4770 SIMPLE_MATHML_CREATE(mstyle_, NS_NewMathMLmrowFrame),
4771 SIMPLE_MATHML_CREATE(msqrt_, NS_NewMathMLmsqrtFrame),
4772 SIMPLE_MATHML_CREATE(mroot_, NS_NewMathMLmrootFrame),
4773 SIMPLE_MATHML_CREATE(maction_, NS_NewMathMLmrowFrame),
4774 SIMPLE_MATHML_CREATE(mrow_, NS_NewMathMLmrowFrame),
4775 SIMPLE_MATHML_CREATE(merror_, NS_NewMathMLmrowFrame),
4776 SIMPLE_MATHML_CREATE(menclose_, NS_NewMathMLmencloseFrame),
4777 SIMPLE_MATHML_CREATE(semantics_, NS_NewMathMLmrowFrame)};
4779 return FindDataByTag(aElement, aStyle, sMathMLData, ArrayLength(sMathMLData));
4782 nsContainerFrame* nsCSSFrameConstructor::ConstructFrameWithAnonymousChild(
4783 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
4784 nsContainerFrame* aParentFrame, nsFrameList& aFrameList,
4785 ContainerFrameCreationFunc aConstructor,
4786 ContainerFrameCreationFunc aInnerConstructor, PseudoStyleType aInnerPseudo,
4787 bool aCandidateRootFrame) {
4788 nsIContent* const content = aItem.mContent;
4789 ComputedStyle* const computedStyle = aItem.mComputedStyle;
4791 // Create the outer frame:
4792 nsContainerFrame* newFrame = aConstructor(mPresShell, computedStyle);
4794 InitAndRestoreFrame(aState, content,
4795 aCandidateRootFrame
4796 ? aState.GetGeometricParent(
4797 *computedStyle->StyleDisplay(), aParentFrame)
4798 : aParentFrame,
4799 newFrame);
4800 newFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
4802 // Create the pseudo SC for the anonymous wrapper child as a child of the SC:
4803 RefPtr<ComputedStyle> scForAnon =
4804 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(aInnerPseudo,
4805 computedStyle);
4807 // Create the anonymous inner wrapper frame
4808 nsContainerFrame* innerFrame = aInnerConstructor(mPresShell, scForAnon);
4810 InitAndRestoreFrame(aState, content, newFrame, innerFrame);
4812 // Put the newly created frames into the right child list
4813 SetInitialSingleChild(newFrame, innerFrame);
4815 aState.AddChild(newFrame, aFrameList, content, aParentFrame,
4816 aCandidateRootFrame, aCandidateRootFrame);
4818 if (!mRootElementFrame && aCandidateRootFrame) {
4819 // The frame we're constructing will be the root element frame.
4820 SetRootElementFrameAndConstructCanvasAnonContent(newFrame, aState,
4821 aFrameList);
4824 nsFrameConstructorSaveState floatSaveState;
4825 aState.MaybePushFloatContainingBlock(innerFrame, floatSaveState);
4827 nsFrameList childList;
4829 // Process children
4830 if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) {
4831 ConstructFramesFromItemList(
4832 aState, aItem.mChildItems, innerFrame,
4833 aItem.mFCData->mBits & FCDATA_IS_WRAPPER_ANON_BOX, childList);
4834 } else {
4835 ProcessChildren(aState, content, computedStyle, innerFrame, true, childList,
4836 false);
4839 // Set the inner wrapper frame's initial primary list
4840 innerFrame->SetInitialChildList(FrameChildListID::Principal,
4841 std::move(childList));
4843 return newFrame;
4846 nsIFrame* nsCSSFrameConstructor::ConstructOuterSVG(
4847 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
4848 nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay,
4849 nsFrameList& aFrameList) {
4850 return ConstructFrameWithAnonymousChild(
4851 aState, aItem, aParentFrame, aFrameList, NS_NewSVGOuterSVGFrame,
4852 NS_NewSVGOuterSVGAnonChildFrame, PseudoStyleType::mozSVGOuterSVGAnonChild,
4853 true);
4856 nsIFrame* nsCSSFrameConstructor::ConstructMarker(
4857 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
4858 nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay,
4859 nsFrameList& aFrameList) {
4860 return ConstructFrameWithAnonymousChild(
4861 aState, aItem, aParentFrame, aFrameList, NS_NewSVGMarkerFrame,
4862 NS_NewSVGMarkerAnonChildFrame, PseudoStyleType::mozSVGMarkerAnonChild,
4863 false);
4866 // Only outer <svg> elements can be floated or positioned. All other SVG
4867 // should be in-flow.
4868 #define SIMPLE_SVG_FCDATA(_func) \
4869 FrameConstructionData(ToCreationFunc(_func), \
4870 FCDATA_DISALLOW_OUT_OF_FLOW | \
4871 FCDATA_SKIP_ABSPOS_PUSH | \
4872 FCDATA_DISALLOW_GENERATED_CONTENT)
4873 #define SIMPLE_SVG_CREATE(_tag, _func) \
4874 { nsGkAtoms::_tag, SIMPLE_SVG_FCDATA(_func) }
4876 static bool IsFilterPrimitiveChildTag(const nsAtom* aTag) {
4877 return aTag == nsGkAtoms::feDistantLight || aTag == nsGkAtoms::fePointLight ||
4878 aTag == nsGkAtoms::feSpotLight || aTag == nsGkAtoms::feFuncR ||
4879 aTag == nsGkAtoms::feFuncG || aTag == nsGkAtoms::feFuncB ||
4880 aTag == nsGkAtoms::feFuncA || aTag == nsGkAtoms::feMergeNode;
4883 /* static */
4884 const nsCSSFrameConstructor::FrameConstructionData*
4885 nsCSSFrameConstructor::FindSVGData(const Element& aElement,
4886 nsIFrame* aParentFrame,
4887 bool aIsWithinSVGText,
4888 bool aAllowsTextPathChild,
4889 ComputedStyle& aStyle) {
4890 MOZ_ASSERT(aElement.IsSVGElement());
4892 static constexpr FrameConstructionData sSuppressData = SUPPRESS_FCDATA();
4893 static constexpr FrameConstructionData sContainerData =
4894 SIMPLE_SVG_FCDATA(NS_NewSVGContainerFrame);
4896 bool parentIsSVG = aIsWithinSVGText;
4897 nsIContent* parentContent =
4898 aParentFrame ? aParentFrame->GetContent() : nullptr;
4900 nsAtom* tag = aElement.NodeInfo()->NameAtom();
4902 // XXXbz should this really be based on the tag of the parent frame's content?
4903 // Should it not be based on the type of the parent frame (e.g. whether it's
4904 // an SVG frame)?
4905 if (parentContent) {
4906 // It's not clear whether the SVG spec intends to allow any SVG
4907 // content within svg:foreignObject at all (SVG 1.1, section
4908 // 23.2), but if it does, it better be svg:svg. So given that
4909 // we're allowing it, treat it as a non-SVG parent.
4910 parentIsSVG =
4911 parentContent->IsSVGElement() &&
4912 parentContent->NodeInfo()->NameAtom() != nsGkAtoms::foreignObject;
4915 if ((tag != nsGkAtoms::svg && !parentIsSVG) ||
4916 (tag == nsGkAtoms::desc || tag == nsGkAtoms::title ||
4917 tag == nsGkAtoms::metadata)) {
4918 // Sections 5.1 and G.4 of SVG 1.1 say that SVG elements other than
4919 // svg:svg not contained within svg:svg are incorrect, although they
4920 // don't seem to specify error handling. Ignore them, since many of
4921 // our frame classes can't deal. It *may* be that the document
4922 // should at that point be considered in error according to F.2, but
4923 // it's hard to tell.
4925 // Style mutation can't change this situation, so don't bother
4926 // adding to the undisplayed content map.
4928 // We don't currently handle any UI for desc/title/metadata
4929 return &sSuppressData;
4932 // We don't need frames for animation elements
4933 if (nsCOMPtr<SVGAnimationElement> animationElement =
4934 do_QueryInterface(const_cast<Element*>(&aElement))) {
4935 return &sSuppressData;
4938 if (tag == nsGkAtoms::svg && !parentIsSVG) {
4939 // We need outer <svg> elements to have an SVGOuterSVGFrame regardless
4940 // of whether they fail conditional processing attributes, since various
4941 // SVG frames assume that one exists. We handle the non-rendering
4942 // of failing outer <svg> element contents like <switch> statements,
4943 // and do the PassesConditionalProcessingTests call in
4944 // SVGOuterSVGFrame::Init.
4945 static constexpr FrameConstructionData sOuterSVGData(
4946 &nsCSSFrameConstructor::ConstructOuterSVG);
4947 return &sOuterSVGData;
4950 if (tag == nsGkAtoms::marker) {
4951 static constexpr FrameConstructionData sMarkerSVGData(
4952 &nsCSSFrameConstructor::ConstructMarker);
4953 return &sMarkerSVGData;
4956 nsCOMPtr<SVGTests> tests = do_QueryInterface(const_cast<Element*>(&aElement));
4957 if (tests && !tests->PassesConditionalProcessingTests()) {
4958 // Elements with failing conditional processing attributes never get
4959 // rendered. Note that this is not where we select which frame in a
4960 // <switch> to render! That happens in SVGSwitchFrame::PaintSVG.
4961 if (aIsWithinSVGText) {
4962 // SVGTextFrame doesn't handle conditional processing attributes,
4963 // so don't create frames for descendants of <text> with failing
4964 // attributes. We need frames not to be created so that text layout
4965 // is correct.
4966 return &sSuppressData;
4968 // If we're not inside <text>, create an SVGContainerFrame (which is a
4969 // frame that doesn't render) so that paint servers can still be referenced,
4970 // even if they live inside an element with failing conditional processing
4971 // attributes.
4972 return &sContainerData;
4975 // Ensure that a stop frame is a child of a gradient and that gradients
4976 // can only have stop children.
4977 bool parentIsGradient = aParentFrame && static_cast<SVGGradientFrame*>(
4978 do_QueryFrame(aParentFrame));
4979 bool stop = (tag == nsGkAtoms::stop);
4980 if ((parentIsGradient && !stop) || (!parentIsGradient && stop)) {
4981 return &sSuppressData;
4984 // Prevent bad frame types being children of filters or parents of filter
4985 // primitives. If aParentFrame is null, we know that the frame that will
4986 // be created will be an nsInlineFrame, so it can never be a filter.
4987 bool parentIsFilter = aParentFrame && aParentFrame->IsSVGFilterFrame();
4988 nsCOMPtr<SVGFE> filterPrimitive =
4989 do_QueryInterface(const_cast<Element*>(&aElement));
4990 if ((parentIsFilter && !filterPrimitive) ||
4991 (!parentIsFilter && filterPrimitive)) {
4992 return &sSuppressData;
4995 // Prevent bad frame types being children of filter primitives or parents of
4996 // filter primitive children. If aParentFrame is null, we know that the frame
4997 // that will be created will be an nsInlineFrame, so it can never be a filter
4998 // primitive.
4999 bool parentIsFEContainerFrame =
5000 aParentFrame && aParentFrame->IsSVGFEContainerFrame();
5001 if ((parentIsFEContainerFrame && !IsFilterPrimitiveChildTag(tag)) ||
5002 (!parentIsFEContainerFrame && IsFilterPrimitiveChildTag(tag))) {
5003 return &sSuppressData;
5006 // Special cases for text/tspan/textPath, because the kind of frame
5007 // they get depends on the parent frame. We ignore 'a' elements when
5008 // determining the parent, however.
5009 if (aIsWithinSVGText) {
5010 // If aIsWithinSVGText is true, then we know that the "SVG text uses
5011 // CSS frames" pref was true when this SVG fragment was first constructed.
5013 // FIXME(bug 1588477) Don't render stuff in display: contents / Shadow DOM
5014 // subtrees, because TextCorrespondenceRecorder in the SVG text code doesn't
5015 // really know how to deal with it. This kinda sucks. :(
5016 if (aParentFrame && aParentFrame->GetContent() != aElement.GetParent()) {
5017 return &sSuppressData;
5020 // We don't use ConstructInline because we want different behavior
5021 // for generated content.
5022 static constexpr FrameConstructionData sTSpanData(
5023 ToCreationFunc(NS_NewInlineFrame),
5024 FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_SKIP_ABSPOS_PUSH |
5025 FCDATA_DISALLOW_GENERATED_CONTENT | FCDATA_IS_LINE_PARTICIPANT |
5026 FCDATA_IS_INLINE | FCDATA_USE_CHILD_ITEMS);
5027 if (tag == nsGkAtoms::textPath) {
5028 if (aAllowsTextPathChild) {
5029 return &sTSpanData;
5031 } else if (tag == nsGkAtoms::tspan || tag == nsGkAtoms::a) {
5032 return &sTSpanData;
5034 return &sSuppressData;
5035 } else if (tag == nsGkAtoms::tspan || tag == nsGkAtoms::textPath) {
5036 return &sSuppressData;
5039 static constexpr FrameConstructionDataByTag sSVGData[] = {
5040 SIMPLE_SVG_CREATE(svg, NS_NewSVGInnerSVGFrame),
5041 SIMPLE_SVG_CREATE(g, NS_NewSVGGFrame),
5042 SIMPLE_SVG_CREATE(svgSwitch, NS_NewSVGSwitchFrame),
5043 SIMPLE_SVG_CREATE(symbol, NS_NewSVGSymbolFrame),
5044 SIMPLE_SVG_CREATE(polygon, NS_NewSVGGeometryFrame),
5045 SIMPLE_SVG_CREATE(polyline, NS_NewSVGGeometryFrame),
5046 SIMPLE_SVG_CREATE(circle, NS_NewSVGGeometryFrame),
5047 SIMPLE_SVG_CREATE(ellipse, NS_NewSVGGeometryFrame),
5048 SIMPLE_SVG_CREATE(line, NS_NewSVGGeometryFrame),
5049 SIMPLE_SVG_CREATE(rect, NS_NewSVGGeometryFrame),
5050 SIMPLE_SVG_CREATE(path, NS_NewSVGGeometryFrame),
5051 SIMPLE_SVG_CREATE(defs, NS_NewSVGContainerFrame),
5052 {nsGkAtoms::text,
5053 {NS_NewSVGTextFrame,
5054 FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_ALLOW_BLOCK_STYLES,
5055 PseudoStyleType::mozSVGText}},
5056 {nsGkAtoms::foreignObject,
5057 {ToCreationFunc(NS_NewSVGForeignObjectFrame),
5058 FCDATA_DISALLOW_OUT_OF_FLOW, PseudoStyleType::mozSVGForeignContent}},
5059 SIMPLE_SVG_CREATE(a, NS_NewSVGAFrame),
5060 SIMPLE_SVG_CREATE(linearGradient, NS_NewSVGLinearGradientFrame),
5061 SIMPLE_SVG_CREATE(radialGradient, NS_NewSVGRadialGradientFrame),
5062 SIMPLE_SVG_CREATE(stop, NS_NewSVGStopFrame),
5063 SIMPLE_SVG_CREATE(use, NS_NewSVGUseFrame),
5064 SIMPLE_SVG_CREATE(view, NS_NewSVGViewFrame),
5065 SIMPLE_SVG_CREATE(image, NS_NewSVGImageFrame),
5066 SIMPLE_SVG_CREATE(clipPath, NS_NewSVGClipPathFrame),
5067 SIMPLE_SVG_CREATE(filter, NS_NewSVGFilterFrame),
5068 SIMPLE_SVG_CREATE(pattern, NS_NewSVGPatternFrame),
5069 SIMPLE_SVG_CREATE(mask, NS_NewSVGMaskFrame),
5070 SIMPLE_SVG_CREATE(feDistantLight, NS_NewSVGFEUnstyledLeafFrame),
5071 SIMPLE_SVG_CREATE(fePointLight, NS_NewSVGFEUnstyledLeafFrame),
5072 SIMPLE_SVG_CREATE(feSpotLight, NS_NewSVGFEUnstyledLeafFrame),
5073 SIMPLE_SVG_CREATE(feBlend, NS_NewSVGFELeafFrame),
5074 SIMPLE_SVG_CREATE(feColorMatrix, NS_NewSVGFELeafFrame),
5075 SIMPLE_SVG_CREATE(feFuncR, NS_NewSVGFEUnstyledLeafFrame),
5076 SIMPLE_SVG_CREATE(feFuncG, NS_NewSVGFEUnstyledLeafFrame),
5077 SIMPLE_SVG_CREATE(feFuncB, NS_NewSVGFEUnstyledLeafFrame),
5078 SIMPLE_SVG_CREATE(feFuncA, NS_NewSVGFEUnstyledLeafFrame),
5079 SIMPLE_SVG_CREATE(feComposite, NS_NewSVGFELeafFrame),
5080 SIMPLE_SVG_CREATE(feComponentTransfer, NS_NewSVGFEContainerFrame),
5081 SIMPLE_SVG_CREATE(feConvolveMatrix, NS_NewSVGFELeafFrame),
5082 SIMPLE_SVG_CREATE(feDiffuseLighting, NS_NewSVGFEContainerFrame),
5083 SIMPLE_SVG_CREATE(feDisplacementMap, NS_NewSVGFELeafFrame),
5084 SIMPLE_SVG_CREATE(feDropShadow, NS_NewSVGFELeafFrame),
5085 SIMPLE_SVG_CREATE(feFlood, NS_NewSVGFELeafFrame),
5086 SIMPLE_SVG_CREATE(feGaussianBlur, NS_NewSVGFELeafFrame),
5087 SIMPLE_SVG_CREATE(feImage, NS_NewSVGFEImageFrame),
5088 SIMPLE_SVG_CREATE(feMerge, NS_NewSVGFEContainerFrame),
5089 SIMPLE_SVG_CREATE(feMergeNode, NS_NewSVGFEUnstyledLeafFrame),
5090 SIMPLE_SVG_CREATE(feMorphology, NS_NewSVGFELeafFrame),
5091 SIMPLE_SVG_CREATE(feOffset, NS_NewSVGFELeafFrame),
5092 SIMPLE_SVG_CREATE(feSpecularLighting, NS_NewSVGFEContainerFrame),
5093 SIMPLE_SVG_CREATE(feTile, NS_NewSVGFELeafFrame),
5094 SIMPLE_SVG_CREATE(feTurbulence, NS_NewSVGFELeafFrame)};
5096 const FrameConstructionData* data =
5097 FindDataByTag(aElement, aStyle, sSVGData, ArrayLength(sSVGData));
5099 if (!data) {
5100 data = &sContainerData;
5103 return data;
5106 void nsCSSFrameConstructor::InsertPageBreakItem(
5107 nsIContent* aContent, FrameConstructionItemList& aItems,
5108 InsertPageBreakLocation location) {
5109 RefPtr<ComputedStyle> pseudoStyle =
5110 mPresShell->StyleSet()->ResolveNonInheritingAnonymousBoxStyle(
5111 PseudoStyleType::pageBreak);
5113 MOZ_ASSERT(pseudoStyle->StyleDisplay()->mDisplay == StyleDisplay::Block,
5114 "Unexpected display");
5116 static constexpr FrameConstructionData sPageBreakData(NS_NewPageBreakFrame,
5117 FCDATA_SKIP_FRAMESET);
5118 if (location == InsertPageBreakLocation::eBefore) {
5119 aItems.PrependItem(this, &sPageBreakData, aContent, pseudoStyle.forget(),
5120 true);
5121 } else {
5122 aItems.AppendItem(this, &sPageBreakData, aContent, pseudoStyle.forget(),
5123 true);
5127 bool nsCSSFrameConstructor::ShouldCreateItemsForChild(
5128 nsFrameConstructorState& aState, nsIContent* aContent,
5129 nsContainerFrame* aParentFrame) {
5130 aContent->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
5131 // XXX the GetContent() != aContent check is needed due to bug 135040.
5132 // Remove it once that's fixed.
5133 if (aContent->GetPrimaryFrame() &&
5134 aContent->GetPrimaryFrame()->GetContent() == aContent &&
5135 !aState.mCreatingExtraFrames) {
5136 MOZ_ASSERT(false,
5137 "asked to create frame construction item for a node that "
5138 "already has a frame");
5139 return false;
5142 // don't create a whitespace frame if aParent doesn't want it
5143 if (!NeedFrameFor(aState, aParentFrame, aContent)) {
5144 return false;
5147 // never create frames for comments or PIs
5148 if (aContent->IsComment() || aContent->IsProcessingInstruction()) {
5149 return false;
5152 return true;
5155 void nsCSSFrameConstructor::AddFrameConstructionItems(
5156 nsFrameConstructorState& aState, nsIContent* aContent,
5157 bool aSuppressWhiteSpaceOptimizations, const ComputedStyle& aParentStyle,
5158 const InsertionPoint& aInsertion, FrameConstructionItemList& aItems,
5159 ItemFlags aFlags) {
5160 nsContainerFrame* parentFrame = aInsertion.mParentFrame;
5161 if (!ShouldCreateItemsForChild(aState, aContent, parentFrame)) {
5162 return;
5164 if (MOZ_UNLIKELY(aParentStyle.StyleContent()->mContent.IsNone()) &&
5165 StaticPrefs::layout_css_element_content_none_enabled()) {
5166 return;
5169 RefPtr<ComputedStyle> computedStyle = ResolveComputedStyle(aContent);
5170 auto flags = aFlags + ItemFlag::AllowPageBreak;
5171 if (parentFrame) {
5172 if (SVGUtils::IsInSVGTextSubtree(parentFrame)) {
5173 flags += ItemFlag::IsWithinSVGText;
5175 if (parentFrame->IsBlockFrame() && parentFrame->GetParent() &&
5176 parentFrame->GetParent()->IsSVGTextFrame()) {
5177 flags += ItemFlag::AllowTextPathChild;
5180 AddFrameConstructionItemsInternal(aState, aContent, parentFrame,
5181 aSuppressWhiteSpaceOptimizations,
5182 computedStyle, flags, aItems);
5185 // Whether we should suppress frames for a child under a <select> frame.
5187 // Never create frames for non-option/optgroup kids of <select> and non-option
5188 // kids of <optgroup> inside a <select>.
5189 static bool ShouldSuppressFrameInSelect(const nsIContent* aParent,
5190 const nsIContent& aChild) {
5191 if (!aParent ||
5192 !aParent->IsAnyOfHTMLElements(nsGkAtoms::select, nsGkAtoms::optgroup,
5193 nsGkAtoms::option)) {
5194 return false;
5197 // Options with labels have their label text added in ::before by forms.css.
5198 // Suppress frames for their child text.
5199 if (aParent->IsHTMLElement(nsGkAtoms::option) &&
5200 !aChild.IsRootOfNativeAnonymousSubtree()) {
5201 return aParent->AsElement()->HasNonEmptyAttr(nsGkAtoms::label);
5204 // If we're in any display: contents subtree, just suppress the frame.
5206 // We can't be regular NAC, since display: contents has no frame to generate
5207 // them off.
5208 if (aChild.GetParent() != aParent) {
5209 return true;
5212 // Option is always fine.
5213 if (aChild.IsHTMLElement(nsGkAtoms::option)) {
5214 return false;
5217 // <optgroup> is OK in <select> but not in <optgroup>.
5218 if (aChild.IsHTMLElement(nsGkAtoms::optgroup) &&
5219 aParent->IsHTMLElement(nsGkAtoms::select)) {
5220 return false;
5223 // Allow native anonymous content no matter what.
5224 if (aChild.IsRootOfNativeAnonymousSubtree()) {
5225 return false;
5228 return true;
5231 const nsCSSFrameConstructor::FrameConstructionData*
5232 nsCSSFrameConstructor::FindDataForContent(nsIContent& aContent,
5233 ComputedStyle& aStyle,
5234 nsIFrame* aParentFrame,
5235 ItemFlags aFlags) {
5236 MOZ_ASSERT(aStyle.StyleDisplay()->mDisplay != StyleDisplay::None &&
5237 aStyle.StyleDisplay()->mDisplay != StyleDisplay::Contents,
5238 "These two special display values should be handled earlier");
5240 if (auto* text = Text::FromNode(aContent)) {
5241 return FindTextData(*text, aParentFrame);
5244 return FindElementData(*aContent.AsElement(), aStyle, aParentFrame, aFlags);
5247 const nsCSSFrameConstructor::FrameConstructionData*
5248 nsCSSFrameConstructor::FindElementData(const Element& aElement,
5249 ComputedStyle& aStyle,
5250 nsIFrame* aParentFrame,
5251 ItemFlags aFlags) {
5252 // Don't create frames for non-SVG element children of SVG elements.
5253 if (!aElement.IsSVGElement()) {
5254 if (aParentFrame && IsFrameForSVG(aParentFrame) &&
5255 !aParentFrame->IsSVGForeignObjectFrame()) {
5256 return nullptr;
5258 if (aFlags.contains(ItemFlag::IsWithinSVGText)) {
5259 return nullptr;
5263 if (auto* data = FindElementTagData(aElement, aStyle, aParentFrame, aFlags)) {
5264 return data;
5267 // Check for 'content: <image-url>' on the element (which makes us ignore
5268 // 'display' values other than 'none' or 'contents').
5269 if (nsImageFrame::ShouldCreateImageFrameForContentProperty(aElement,
5270 aStyle)) {
5271 static constexpr FrameConstructionData sImgData(
5272 NS_NewImageFrameForContentProperty);
5273 return &sImgData;
5276 const auto boxLayout = aStyle.StyleVisibility()->mMozBoxLayout;
5277 const bool shouldBlockify = aFlags.contains(ItemFlag::IsForRenderedLegend) ||
5278 aFlags.contains(ItemFlag::IsForOutsideMarker);
5279 if (shouldBlockify && !aStyle.StyleDisplay()->IsBlockOutsideStyle()) {
5280 // Make a temp copy of StyleDisplay and blockify its mDisplay value.
5281 auto display = *aStyle.StyleDisplay();
5282 bool isRootElement = false;
5283 uint16_t rawDisplayValue =
5284 Servo_ComputedValues_BlockifiedDisplay(&aStyle, isRootElement);
5285 display.mDisplay = StyleDisplay(rawDisplayValue);
5286 return FindDisplayData(display, boxLayout, aElement);
5289 const auto& display = *aStyle.StyleDisplay();
5290 return FindDisplayData(display, boxLayout, aElement);
5293 const nsCSSFrameConstructor::FrameConstructionData*
5294 nsCSSFrameConstructor::FindElementTagData(const Element& aElement,
5295 ComputedStyle& aStyle,
5296 nsIFrame* aParentFrame,
5297 ItemFlags aFlags) {
5298 switch (aElement.GetNameSpaceID()) {
5299 case kNameSpaceID_XHTML:
5300 return FindHTMLData(aElement, aParentFrame, aStyle);
5301 case kNameSpaceID_MathML:
5302 return FindMathMLData(aElement, aStyle);
5303 case kNameSpaceID_SVG:
5304 return FindSVGData(aElement, aParentFrame,
5305 aFlags.contains(ItemFlag::IsWithinSVGText),
5306 aFlags.contains(ItemFlag::AllowTextPathChild), aStyle);
5307 case kNameSpaceID_XUL:
5308 return FindXULTagData(aElement, aStyle);
5309 default:
5310 return nullptr;
5314 void nsCSSFrameConstructor::AddFrameConstructionItemsInternal(
5315 nsFrameConstructorState& aState, nsIContent* aContent,
5316 nsContainerFrame* aParentFrame, bool aSuppressWhiteSpaceOptimizations,
5317 ComputedStyle* aComputedStyle, ItemFlags aFlags,
5318 FrameConstructionItemList& aItems) {
5319 MOZ_ASSERT(aContent->IsText() || aContent->IsElement(),
5320 "Shouldn't get anything else here!");
5321 MOZ_ASSERT(aContent->IsInComposedDoc());
5322 MOZ_ASSERT(!aContent->GetPrimaryFrame() || aState.mCreatingExtraFrames ||
5323 aContent->NodeInfo()->NameAtom() == nsGkAtoms::area);
5325 const bool withinSVGText = aFlags.contains(ItemFlag::IsWithinSVGText);
5326 const bool isGeneratedContent = aFlags.contains(ItemFlag::IsGeneratedContent);
5327 MOZ_ASSERT(!isGeneratedContent || aComputedStyle->IsPseudoElement(),
5328 "Generated content should be a pseudo-element");
5330 FrameConstructionItem* item = nullptr;
5331 auto cleanupGeneratedContent = mozilla::MakeScopeExit([&]() {
5332 if (isGeneratedContent && !item) {
5333 MOZ_ASSERT(!IsDisplayContents(aContent),
5334 "This would need to change if we support display: contents "
5335 "in generated content");
5336 aContent->UnbindFromTree();
5340 // 'display:none' elements never creates any frames at all.
5341 const nsStyleDisplay& display = *aComputedStyle->StyleDisplay();
5342 if (display.mDisplay == StyleDisplay::None) {
5343 return;
5346 if (display.mDisplay == StyleDisplay::Contents) {
5347 // See the mDisplay fixup code in StyleAdjuster::adjust.
5348 MOZ_ASSERT(!aContent->AsElement()->IsRootOfNativeAnonymousSubtree(),
5349 "display:contents on anonymous content is unsupported");
5351 // FIXME(bug 1588477): <svg:text>'s TextNodeCorrespondenceRecorder has
5352 // trouble with everything that looks like display: contents.
5353 if (withinSVGText) {
5354 return;
5357 CreateGeneratedContentItem(aState, aParentFrame, *aContent->AsElement(),
5358 *aComputedStyle, PseudoStyleType::before,
5359 aItems);
5361 FlattenedChildIterator iter(aContent);
5362 InsertionPoint insertion(aParentFrame, aContent);
5363 for (nsIContent* child = iter.GetNextChild(); child;
5364 child = iter.GetNextChild()) {
5365 AddFrameConstructionItems(aState, child, aSuppressWhiteSpaceOptimizations,
5366 *aComputedStyle, insertion, aItems, aFlags);
5368 aItems.SetParentHasNoShadowDOM(!iter.ShadowDOMInvolved());
5370 CreateGeneratedContentItem(aState, aParentFrame, *aContent->AsElement(),
5371 *aComputedStyle, PseudoStyleType::after, aItems);
5372 return;
5375 nsIContent* parent = aParentFrame ? aParentFrame->GetContent() : nullptr;
5376 if (ShouldSuppressFrameInSelect(parent, *aContent)) {
5377 return;
5380 if (aContent->IsHTMLElement(nsGkAtoms::legend) && aParentFrame) {
5381 const nsFieldSetFrame* const fs = GetFieldSetFrameFor(aParentFrame);
5382 if (fs && !fs->GetLegend() && !aState.mHasRenderedLegend &&
5383 !aComputedStyle->StyleDisplay()->IsFloatingStyle() &&
5384 !aComputedStyle->StyleDisplay()->IsAbsolutelyPositionedStyle()) {
5385 aState.mHasRenderedLegend = true;
5386 aFlags += ItemFlag::IsForRenderedLegend;
5390 const FrameConstructionData* const data =
5391 FindDataForContent(*aContent, *aComputedStyle, aParentFrame, aFlags);
5392 if (!data || data->mBits & FCDATA_SUPPRESS_FRAME) {
5393 return;
5396 const bool isPopup = data->mBits & FCDATA_IS_POPUP;
5398 const uint32_t bits = data->mBits;
5400 // Inside colgroups, suppress everything except columns.
5401 if (aParentFrame && aParentFrame->IsTableColGroupFrame() &&
5402 (!(bits & FCDATA_IS_TABLE_PART) ||
5403 display.mDisplay != StyleDisplay::TableColumn)) {
5404 return;
5407 const bool canHavePageBreak =
5408 aFlags.contains(ItemFlag::AllowPageBreak) &&
5409 aState.mPresContext->IsPaginated() &&
5410 !display.IsAbsolutelyPositionedStyle() &&
5411 !(aParentFrame && aParentFrame->IsGridContainerFrame()) &&
5412 !(bits & FCDATA_IS_TABLE_PART) && !(bits & FCDATA_IS_SVG_TEXT);
5413 if (canHavePageBreak && display.BreakBefore()) {
5414 AppendPageBreakItem(aContent, aItems);
5417 if (!item) {
5418 item = aItems.AppendItem(this, data, aContent, do_AddRef(aComputedStyle),
5419 aSuppressWhiteSpaceOptimizations);
5420 if (aFlags.contains(ItemFlag::IsForRenderedLegend)) {
5421 item->mIsRenderedLegend = true;
5424 item->mIsText = !aContent->IsElement();
5425 item->mIsGeneratedContent = isGeneratedContent;
5426 if (isGeneratedContent) {
5427 // We need to keep this alive until the frame takes ownership.
5428 // This corresponds to the Release in ConstructFramesFromItem.
5429 item->mContent->AddRef();
5431 item->mIsPopup = isPopup;
5433 if (canHavePageBreak && display.BreakAfter()) {
5434 AppendPageBreakItem(aContent, aItems);
5437 if (bits & FCDATA_IS_INLINE) {
5438 // To correctly set item->mIsAllInline we need to build up our child items
5439 // right now.
5440 BuildInlineChildItems(aState, *item,
5441 aFlags.contains(ItemFlag::IsWithinSVGText),
5442 aFlags.contains(ItemFlag::AllowTextPathChild));
5443 item->mIsBlock = false;
5444 } else {
5445 // Compute a boolean isInline which is guaranteed to be false for blocks
5446 // (but may also be false for some inlines).
5447 const bool isInline =
5448 // Table-internal things are inline-outside if and only if they're kids
5449 // of inlines, since they'll trigger construction of inline-table
5450 // pseudos.
5451 ((bits & FCDATA_IS_TABLE_PART) &&
5452 (!aParentFrame || // No aParentFrame means inline
5453 aParentFrame->StyleDisplay()->IsInlineFlow())) ||
5454 // Things that are inline-outside but aren't inline frames are inline
5455 display.IsInlineOutsideStyle() ||
5456 // Popups that are certainly out of flow.
5457 isPopup;
5459 // Set mIsAllInline conservatively. It just might be that even an inline
5460 // that has mIsAllInline false doesn't need an {ib} split. So this is just
5461 // an optimization to keep from doing too much work in cases when we can
5462 // show that mIsAllInline is true..
5463 item->mIsAllInline =
5464 isInline ||
5465 // Figure out whether we're guaranteed this item will be out of flow.
5466 // This is not a precise test, since one of our ancestor inlines might
5467 // add an absolute containing block (if it's relatively positioned) when
5468 // there wasn't such a containing block before. But it's conservative
5469 // in the sense that anything that will really end up as an in-flow
5470 // non-inline will test false here. In other words, if this test is
5471 // true we're guaranteed to be inline; if it's false we don't know what
5472 // we'll end up as.
5474 // If we make this test precise, we can remove some of the code dealing
5475 // with the imprecision in ConstructInline and adjust the comments on
5476 // mIsAllInline and mIsBlock in the header.
5477 (!(bits & FCDATA_DISALLOW_OUT_OF_FLOW) &&
5478 aState.GetGeometricParent(display, nullptr));
5480 // Set mIsBlock conservatively. It's OK to set it false for some real
5481 // blocks, but not OK to set it true for things that aren't blocks. Since
5482 // isOutOfFlow might be false even in cases when the frame will end up
5483 // out-of-flow, we can't use it here. But we _can_ say that the frame will
5484 // for sure end up in-flow if it's not floated or absolutely positioned.
5485 item->mIsBlock = !isInline && !display.IsAbsolutelyPositionedStyle() &&
5486 !display.IsFloatingStyle() && !(bits & FCDATA_IS_SVG_TEXT);
5489 if (item->mIsAllInline) {
5490 aItems.InlineItemAdded();
5491 } else if (item->mIsBlock) {
5492 aItems.BlockItemAdded();
5497 * Return true if the frame construction item pointed to by aIter will
5498 * create a frame adjacent to a line boundary in the frame tree, and that
5499 * line boundary is induced by a content node adjacent to the frame's
5500 * content node in the content tree. The latter condition is necessary so
5501 * that ContentAppended/ContentInserted/ContentRemoved can easily find any
5502 * text nodes that were suppressed here.
5504 bool nsCSSFrameConstructor::AtLineBoundary(FCItemIterator& aIter) {
5505 if (aIter.item().mSuppressWhiteSpaceOptimizations) {
5506 return false;
5509 if (aIter.AtStart()) {
5510 if (aIter.List()->HasLineBoundaryAtStart() &&
5511 !aIter.item().mContent->GetPreviousSibling())
5512 return true;
5513 } else {
5514 FCItemIterator prev = aIter;
5515 prev.Prev();
5516 if (prev.item().IsLineBoundary() &&
5517 !prev.item().mSuppressWhiteSpaceOptimizations &&
5518 aIter.item().mContent->GetPreviousSibling() == prev.item().mContent)
5519 return true;
5522 FCItemIterator next = aIter;
5523 next.Next();
5524 if (next.IsDone()) {
5525 if (aIter.List()->HasLineBoundaryAtEnd() &&
5526 !aIter.item().mContent->GetNextSibling())
5527 return true;
5528 } else {
5529 if (next.item().IsLineBoundary() &&
5530 !next.item().mSuppressWhiteSpaceOptimizations &&
5531 aIter.item().mContent->GetNextSibling() == next.item().mContent)
5532 return true;
5535 return false;
5538 void nsCSSFrameConstructor::ConstructFramesFromItem(
5539 nsFrameConstructorState& aState, FCItemIterator& aIter,
5540 nsContainerFrame* aParentFrame, nsFrameList& aFrameList) {
5541 FrameConstructionItem& item = aIter.item();
5542 ComputedStyle* computedStyle = item.mComputedStyle;
5543 if (item.mIsText) {
5544 // If this is collapsible whitespace next to a line boundary,
5545 // don't create a frame. item.IsWhitespace() also sets the
5546 // NS_CREATE_FRAME_IF_NON_WHITESPACE flag in the text node. (If we
5547 // end up creating a frame, nsTextFrame::Init will clear the flag.)
5548 // We don't do this for generated content, because some generated
5549 // text content is empty text nodes that are about to be initialized.
5550 // (We check mAdditionalStateBits because only the generated content
5551 // container's frame construction item is marked with
5552 // mIsGeneratedContent, and we might not have an aParentFrame.)
5553 // We don't do it for content that may have Shadow DOM siblings / insertion
5554 // points, because they make it difficult to correctly create the frame due
5555 // to dynamic changes.
5556 // We don't do it for SVG text, since we might need to position and
5557 // measure the white space glyphs due to x/y/dx/dy attributes.
5558 if (AtLineBoundary(aIter) &&
5559 !computedStyle->StyleText()->WhiteSpaceOrNewlineIsSignificant() &&
5560 aIter.List()->ParentHasNoShadowDOM() &&
5561 !(aState.mAdditionalStateBits & NS_FRAME_GENERATED_CONTENT) &&
5562 (item.mFCData->mBits & FCDATA_IS_LINE_PARTICIPANT) &&
5563 !(item.mFCData->mBits & FCDATA_IS_SVG_TEXT) &&
5564 !mAlwaysCreateFramesForIgnorableWhitespace && item.IsWhitespace(aState))
5565 return;
5567 ConstructTextFrame(item.mFCData, aState, item.mContent, aParentFrame,
5568 computedStyle, aFrameList);
5569 return;
5572 AutoRestore<nsFrameState> savedStateBits(aState.mAdditionalStateBits);
5573 if (item.mIsGeneratedContent) {
5574 // Ensure that frames created here are all tagged with
5575 // NS_FRAME_GENERATED_CONTENT.
5576 aState.mAdditionalStateBits |= NS_FRAME_GENERATED_CONTENT;
5579 // XXXbz maybe just inline ConstructFrameFromItemInternal here or something?
5580 ConstructFrameFromItemInternal(item, aState, aParentFrame, aFrameList);
5582 if (item.mIsGeneratedContent) {
5583 // This corresponds to the AddRef in AddFrameConstructionItemsInternal.
5584 // The frame owns the generated content now.
5585 item.mContent->Release();
5587 // Now that we've passed ownership of item.mContent to the frame, unset
5588 // our generated content flag so we don't release or unbind it ourselves.
5589 item.mIsGeneratedContent = false;
5593 nsContainerFrame* nsCSSFrameConstructor::GetAbsoluteContainingBlock(
5594 nsIFrame* aFrame, ContainingBlockType aType) {
5595 // Starting with aFrame, look for a frame that is absolutely positioned or
5596 // relatively positioned (and transformed, if aType is FIXED)
5597 for (nsIFrame* frame = aFrame; frame; frame = frame->GetParent()) {
5598 if (frame->IsFrameOfType(nsIFrame::eMathML)) {
5599 // If it's mathml, bail out -- no absolute positioning out from inside
5600 // mathml frames. Note that we don't make this part of the loop
5601 // condition because of the stuff at the end of this method...
5602 return nullptr;
5605 // Look for the ICB.
5606 if (aType == FIXED_POS) {
5607 LayoutFrameType t = frame->Type();
5608 if (t == LayoutFrameType::Viewport || t == LayoutFrameType::PageContent) {
5609 return static_cast<nsContainerFrame*>(frame);
5613 // If the frame is positioned, we will probably return it as the containing
5614 // block (see the exceptions below). Otherwise, we'll start looking at the
5615 // parent frame, unless we're dealing with a scrollframe.
5616 // Scrollframes are special since they're not positioned, but their
5617 // scrolledframe might be. So, we need to check this special case to return
5618 // the correct containing block (the scrolledframe) in that case.
5619 // If we're looking for a fixed-pos containing block and the frame is
5620 // not transformed, skip it.
5621 if (!frame->IsAbsPosContainingBlock() ||
5622 (aType == FIXED_POS && !frame->IsFixedPosContainingBlock())) {
5623 continue;
5625 nsIFrame* absPosCBCandidate = frame;
5626 LayoutFrameType type = absPosCBCandidate->Type();
5627 if (type == LayoutFrameType::FieldSet) {
5628 absPosCBCandidate =
5629 static_cast<nsFieldSetFrame*>(absPosCBCandidate)->GetInner();
5630 if (!absPosCBCandidate) {
5631 continue;
5633 type = absPosCBCandidate->Type();
5635 if (type == LayoutFrameType::Scroll) {
5636 nsIScrollableFrame* scrollFrame = do_QueryFrame(absPosCBCandidate);
5637 absPosCBCandidate = scrollFrame->GetScrolledFrame();
5638 if (!absPosCBCandidate) {
5639 continue;
5641 type = absPosCBCandidate->Type();
5643 // Only first continuations can be containing blocks.
5644 absPosCBCandidate = absPosCBCandidate->FirstContinuation();
5645 // Is the frame really an absolute container?
5646 if (!absPosCBCandidate->IsAbsoluteContainer()) {
5647 continue;
5650 // For tables, skip the inner frame and consider the table wrapper frame.
5651 if (type == LayoutFrameType::Table) {
5652 continue;
5654 // For table wrapper frames, we can just return absPosCBCandidate.
5655 MOZ_ASSERT((nsContainerFrame*)do_QueryFrame(absPosCBCandidate),
5656 "abs.pos. containing block must be nsContainerFrame sub-class");
5657 return static_cast<nsContainerFrame*>(absPosCBCandidate);
5660 MOZ_ASSERT(aType != FIXED_POS, "no ICB in this frame tree?");
5662 // It is possible for the search for the containing block to fail, because
5663 // no absolute container can be found in the parent chain. In those cases,
5664 // we fall back to the document element's containing block.
5665 return mDocElementContainingBlock;
5668 nsContainerFrame* nsCSSFrameConstructor::GetFloatContainingBlock(
5669 nsIFrame* aFrame) {
5670 // Starting with aFrame, look for a frame that is a float containing block.
5671 // If we hit a frame which prevents its descendants from floating, bail out.
5672 // The logic here needs to match the logic in MaybePushFloatContainingBlock().
5673 for (nsIFrame* containingBlock = aFrame;
5674 containingBlock && !ShouldSuppressFloatingOfDescendants(containingBlock);
5675 containingBlock = containingBlock->GetParent()) {
5676 if (containingBlock->IsFloatContainingBlock()) {
5677 MOZ_ASSERT((nsContainerFrame*)do_QueryFrame(containingBlock),
5678 "float containing block must be nsContainerFrame sub-class");
5679 return static_cast<nsContainerFrame*>(containingBlock);
5683 // If we didn't find a containing block, then there just isn't
5684 // one.... return null
5685 return nullptr;
5689 * This function will get the previous sibling to use for an append operation.
5691 * It takes a parent frame (must not be null) and the next insertion sibling, if
5692 * the parent content is display: contents or has ::after content (may be null).
5694 static nsIFrame* FindAppendPrevSibling(nsIFrame* aParentFrame,
5695 nsIFrame* aNextSibling) {
5696 aParentFrame->DrainSelfOverflowList();
5698 if (aNextSibling) {
5699 MOZ_ASSERT(
5700 aNextSibling->GetParent()->GetContentInsertionFrame() == aParentFrame,
5701 "Wrong parent");
5702 return aNextSibling->GetPrevSibling();
5705 return aParentFrame->PrincipalChildList().LastChild();
5709 * Finds the right parent frame to append content to aParentFrame.
5711 * Cannot return or receive null.
5713 static nsContainerFrame* ContinuationToAppendTo(
5714 nsContainerFrame* aParentFrame) {
5715 MOZ_ASSERT(aParentFrame);
5717 if (IsFramePartOfIBSplit(aParentFrame)) {
5718 // If the frame we are manipulating is a ib-split frame (that is, one that's
5719 // been created as a result of a block-in-inline situation) then we need to
5720 // append to the last ib-split sibling, not to the frame itself.
5722 // Always make sure to look at the last continuation of the frame for the
5723 // {ib} case, even if that continuation is empty.
5725 // We don't do this for the non-ib-split-frame case, since in the other
5726 // cases appending to the last nonempty continuation is fine and in fact not
5727 // doing that can confuse code that doesn't know to pull kids from
5728 // continuations other than its next one.
5729 return static_cast<nsContainerFrame*>(
5730 GetLastIBSplitSibling(aParentFrame)->LastContinuation());
5733 return nsLayoutUtils::LastContinuationWithChild(aParentFrame);
5737 * This function will get the next sibling for a frame insert operation given
5738 * the parent and previous sibling. aPrevSibling may be null.
5740 static nsIFrame* GetInsertNextSibling(nsIFrame* aParentFrame,
5741 nsIFrame* aPrevSibling) {
5742 if (aPrevSibling) {
5743 return aPrevSibling->GetNextSibling();
5746 return aParentFrame->PrincipalChildList().FirstChild();
5749 void nsCSSFrameConstructor::AppendFramesToParent(
5750 nsFrameConstructorState& aState, nsContainerFrame* aParentFrame,
5751 nsFrameList& aFrameList, nsIFrame* aPrevSibling, bool aIsRecursiveCall) {
5752 MOZ_ASSERT(
5753 !IsFramePartOfIBSplit(aParentFrame) || !GetIBSplitSibling(aParentFrame) ||
5754 !GetIBSplitSibling(aParentFrame)->PrincipalChildList().FirstChild(),
5755 "aParentFrame has a ib-split sibling with kids?");
5756 MOZ_ASSERT(!aPrevSibling || aPrevSibling->GetParent() == aParentFrame,
5757 "Parent and prevsibling don't match");
5758 MOZ_ASSERT(
5759 !aParentFrame->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR) ||
5760 !IsFramePartOfIBSplit(aParentFrame),
5761 "We should have wiped aParentFrame in WipeContainingBlock() "
5762 "if it's part of an IB split!");
5764 nsIFrame* nextSibling = ::GetInsertNextSibling(aParentFrame, aPrevSibling);
5766 NS_ASSERTION(nextSibling || !aParentFrame->GetNextContinuation() ||
5767 !aParentFrame->GetNextContinuation()
5768 ->PrincipalChildList()
5769 .FirstChild() ||
5770 aIsRecursiveCall,
5771 "aParentFrame has later continuations with kids?");
5772 NS_ASSERTION(
5773 nextSibling || !IsFramePartOfIBSplit(aParentFrame) ||
5774 (IsInlineFrame(aParentFrame) && !GetIBSplitSibling(aParentFrame) &&
5775 !aParentFrame->GetNextContinuation()) ||
5776 aIsRecursiveCall,
5777 "aParentFrame is not last?");
5779 // If we're inserting a list of frames at the end of the trailing inline
5780 // of an {ib} split, we may need to create additional {ib} siblings to parent
5781 // them.
5782 if (!nextSibling && IsFramePartOfIBSplit(aParentFrame)) {
5783 // When we get here, our frame list might start with a block. If it does
5784 // so, and aParentFrame is an inline, and it and all its previous
5785 // continuations have no siblings, then put the initial blocks from the
5786 // frame list into the previous block of the {ib} split. Note that we
5787 // didn't want to stop at the block part of the split when figuring out
5788 // initial parent, because that could screw up float parenting; it's easier
5789 // to do this little fixup here instead.
5790 if (aFrameList.NotEmpty() && aFrameList.FirstChild()->IsBlockOutside()) {
5791 // See whether our trailing inline is empty
5792 nsIFrame* firstContinuation = aParentFrame->FirstContinuation();
5793 if (firstContinuation->PrincipalChildList().IsEmpty()) {
5794 // Our trailing inline is empty. Collect our starting blocks from
5795 // aFrameList, get the right parent frame for them, and put them in.
5796 nsFrameList blockKids =
5797 aFrameList.Split([](nsIFrame* f) { return !f->IsBlockOutside(); });
5798 NS_ASSERTION(blockKids.NotEmpty(), "No blocks?");
5800 nsContainerFrame* prevBlock = GetIBSplitPrevSibling(firstContinuation);
5801 prevBlock =
5802 static_cast<nsContainerFrame*>(prevBlock->LastContinuation());
5803 NS_ASSERTION(prevBlock, "Should have previous block here");
5805 MoveChildrenTo(aParentFrame, prevBlock, blockKids);
5809 // We want to put some of the frames into this inline frame.
5810 nsFrameList inlineKids =
5811 aFrameList.Split([](nsIFrame* f) { return f->IsBlockOutside(); });
5813 if (!inlineKids.IsEmpty()) {
5814 AppendFrames(aParentFrame, FrameChildListID::Principal,
5815 std::move(inlineKids));
5818 if (!aFrameList.IsEmpty()) {
5819 nsFrameList ibSiblings;
5820 CreateIBSiblings(aState, aParentFrame,
5821 aParentFrame->IsAbsPosContainingBlock(), aFrameList,
5822 ibSiblings);
5824 // Make sure to trigger reflow of the inline that used to be our
5825 // last one and now isn't anymore, since its GetSkipSides() has
5826 // changed.
5827 mPresShell->FrameNeedsReflow(aParentFrame,
5828 IntrinsicDirty::FrameAndAncestors,
5829 NS_FRAME_HAS_DIRTY_CHILDREN);
5831 // Recurse so we create new ib siblings as needed for aParentFrame's
5832 // parent
5833 return AppendFramesToParent(aState, aParentFrame->GetParent(), ibSiblings,
5834 aParentFrame, true);
5836 return;
5839 // If we're appending a list of frames to the last continuations of a
5840 // ::-moz-column-content, we may need to create column-span siblings for them.
5841 if (!nextSibling && IsLastContinuationForColumnContent(aParentFrame)) {
5842 // Extract any initial non-column-span kids, and append them to
5843 // ::-moz-column-content's child list.
5844 nsFrameList initialNonColumnSpanKids =
5845 aFrameList.Split([](nsIFrame* f) { return f->IsColumnSpan(); });
5846 AppendFrames(aParentFrame, FrameChildListID::Principal,
5847 std::move(initialNonColumnSpanKids));
5849 if (aFrameList.IsEmpty()) {
5850 // No more kids to process (there weren't any column-span kids).
5851 return;
5854 nsFrameList columnSpanSiblings = CreateColumnSpanSiblings(
5855 aState, aParentFrame, aFrameList,
5856 // Column content should never be a absolute/fixed positioned containing
5857 // block. Pass nullptr as aPositionedFrame.
5858 nullptr);
5860 nsContainerFrame* columnSetWrapper = aParentFrame->GetParent();
5861 while (!columnSetWrapper->IsColumnSetWrapperFrame()) {
5862 columnSetWrapper = columnSetWrapper->GetParent();
5864 MOZ_ASSERT(columnSetWrapper,
5865 "No ColumnSetWrapperFrame ancestor for -moz-column-content?");
5867 FinishBuildingColumns(aState, columnSetWrapper, aParentFrame,
5868 columnSpanSiblings);
5870 MOZ_ASSERT(columnSpanSiblings.IsEmpty(),
5871 "The column-span siblings should be moved to the proper place!");
5872 return;
5875 // Insert the frames after our aPrevSibling
5876 InsertFrames(aParentFrame, FrameChildListID::Principal, aPrevSibling,
5877 std::move(aFrameList));
5880 // This gets called to see if the frames corresponding to aSibling and aContent
5881 // should be siblings in the frame tree. Although (1) rows and cols, (2) row
5882 // groups and col groups, (3) row groups and captions, (4) legends and content
5883 // inside fieldsets, (5) popups and other kids of the menu are siblings from a
5884 // content perspective, they are not considered siblings in the frame tree.
5885 bool nsCSSFrameConstructor::IsValidSibling(nsIFrame* aSibling,
5886 nsIContent* aContent,
5887 Maybe<StyleDisplay>& aDisplay) {
5888 StyleDisplay siblingDisplay = aSibling->GetDisplay();
5889 if (StyleDisplay::TableColumnGroup == siblingDisplay ||
5890 StyleDisplay::TableColumn == siblingDisplay ||
5891 StyleDisplay::TableCaption == siblingDisplay ||
5892 StyleDisplay::TableHeaderGroup == siblingDisplay ||
5893 StyleDisplay::TableRowGroup == siblingDisplay ||
5894 StyleDisplay::TableFooterGroup == siblingDisplay) {
5895 // if we haven't already, resolve a style to find the display type of
5896 // aContent.
5897 if (aDisplay.isNothing()) {
5898 if (aContent->IsComment() || aContent->IsProcessingInstruction()) {
5899 // Comments and processing instructions never have frames, so we should
5900 // not try to generate styles for them.
5901 return false;
5903 // FIXME(emilio): This is buggy some times, see bug 1424656.
5904 RefPtr<ComputedStyle> computedStyle = ResolveComputedStyle(aContent);
5905 const nsStyleDisplay* display = computedStyle->StyleDisplay();
5906 aDisplay.emplace(display->mDisplay);
5909 StyleDisplay display = aDisplay.value();
5910 // To have decent performance we want to return false in cases in which
5911 // reordering the two siblings has no effect on display. To ensure
5912 // correctness, we MUST return false in cases where the two siblings have
5913 // the same desired parent type and live on different display lists.
5914 // Specificaly, columns and column groups should only consider columns and
5915 // column groups as valid siblings. Captions should only consider other
5916 // captions. All other things should consider each other as valid
5917 // siblings. The restriction in the |if| above on siblingDisplay is ok,
5918 // because for correctness the only part that really needs to happen is to
5919 // not consider captions, column groups, and row/header/footer groups
5920 // siblings of each other. Treating a column or colgroup as a valid
5921 // sibling of a non-table-related frame will just mean we end up reframing.
5922 if ((siblingDisplay == StyleDisplay::TableCaption) !=
5923 (display == StyleDisplay::TableCaption)) {
5924 // One's a caption and the other is not. Not valid siblings.
5925 return false;
5928 if ((siblingDisplay == StyleDisplay::TableColumnGroup ||
5929 siblingDisplay == StyleDisplay::TableColumn) !=
5930 (display == StyleDisplay::TableColumnGroup ||
5931 display == StyleDisplay::TableColumn)) {
5932 // One's a column or column group and the other is not. Not valid
5933 // siblings.
5934 return false;
5936 // Fall through; it's possible that the display type was overridden and
5937 // a different sort of frame was constructed, so we may need to return false
5938 // below.
5941 return true;
5944 // FIXME(emilio): If we ever kill IsValidSibling() we can simplify this quite a
5945 // bit (no need to pass aTargetContent or aTargetContentDisplay, and the
5946 // adjust() calls can be responsibility of the caller).
5947 template <nsCSSFrameConstructor::SiblingDirection aDirection>
5948 nsIFrame* nsCSSFrameConstructor::FindSiblingInternal(
5949 FlattenedChildIterator& aIter, nsIContent* aTargetContent,
5950 Maybe<StyleDisplay>& aTargetContentDisplay) {
5951 auto adjust = [&](nsIFrame* aPotentialSiblingFrame) -> nsIFrame* {
5952 return AdjustSiblingFrame(aPotentialSiblingFrame, aTargetContent,
5953 aTargetContentDisplay, aDirection);
5956 auto nextDomSibling = [](FlattenedChildIterator& aIter) -> nsIContent* {
5957 return aDirection == SiblingDirection::Forward ? aIter.GetNextChild()
5958 : aIter.GetPreviousChild();
5961 auto getInsideMarkerFrame = [](const nsIContent* aContent) -> nsIFrame* {
5962 auto* marker = nsLayoutUtils::GetMarkerFrame(aContent);
5963 const bool isInsideMarker =
5964 marker && marker->GetInFlowParent()->StyleList()->mListStylePosition ==
5965 StyleListStylePosition::Inside;
5966 return isInsideMarker ? marker : nullptr;
5969 auto getNearPseudo = [&](const nsIContent* aContent) -> nsIFrame* {
5970 if (aDirection == SiblingDirection::Forward) {
5971 if (auto* marker = getInsideMarkerFrame(aContent)) {
5972 return marker;
5974 return nsLayoutUtils::GetBeforeFrame(aContent);
5976 return nsLayoutUtils::GetAfterFrame(aContent);
5979 auto getFarPseudo = [&](const nsIContent* aContent) -> nsIFrame* {
5980 if (aDirection == SiblingDirection::Forward) {
5981 return nsLayoutUtils::GetAfterFrame(aContent);
5983 if (auto* before = nsLayoutUtils::GetBeforeFrame(aContent)) {
5984 return before;
5986 return getInsideMarkerFrame(aContent);
5989 while (nsIContent* sibling = nextDomSibling(aIter)) {
5990 // NOTE(emilio): It's important to check GetPrimaryFrame() before
5991 // IsDisplayContents to get the correct insertion point when multiple
5992 // siblings go from display: non-none to display: contents.
5993 if (nsIFrame* primaryFrame = sibling->GetPrimaryFrame()) {
5994 // XXX the GetContent() == sibling check is needed due to bug 135040.
5995 // Remove it once that's fixed.
5996 if (primaryFrame->GetContent() == sibling) {
5997 if (nsIFrame* frame = adjust(primaryFrame)) {
5998 return frame;
6003 if (IsDisplayContents(sibling)) {
6004 if (nsIFrame* frame = adjust(getNearPseudo(sibling))) {
6005 return frame;
6008 const bool startFromBeginning = aDirection == SiblingDirection::Forward;
6009 FlattenedChildIterator iter(sibling, startFromBeginning);
6010 nsIFrame* sibling = FindSiblingInternal<aDirection>(
6011 iter, aTargetContent, aTargetContentDisplay);
6012 if (sibling) {
6013 return sibling;
6018 return adjust(getFarPseudo(aIter.Parent()));
6021 nsIFrame* nsCSSFrameConstructor::AdjustSiblingFrame(
6022 nsIFrame* aSibling, nsIContent* aTargetContent,
6023 Maybe<StyleDisplay>& aTargetContentDisplay, SiblingDirection aDirection) {
6024 if (!aSibling) {
6025 return nullptr;
6028 if (aSibling->IsRenderedLegend()) {
6029 return nullptr;
6032 if (aSibling->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
6033 aSibling = aSibling->GetPlaceholderFrame();
6034 MOZ_ASSERT(aSibling);
6037 MOZ_ASSERT(!aSibling->GetPrevContinuation(), "How?");
6038 if (aDirection == SiblingDirection::Backward) {
6039 // The frame may be a ib-split frame (a split inline frame that contains a
6040 // block). Get the last part of that split.
6041 if (IsFramePartOfIBSplit(aSibling)) {
6042 aSibling = GetLastIBSplitSibling(aSibling);
6045 // The frame may have a continuation. If so, we want the last
6046 // non-overflow-container continuation as our previous sibling.
6047 aSibling = aSibling->GetTailContinuation();
6050 if (!IsValidSibling(aSibling, aTargetContent, aTargetContentDisplay)) {
6051 return nullptr;
6054 return aSibling;
6057 nsIFrame* nsCSSFrameConstructor::FindPreviousSibling(
6058 const FlattenedChildIterator& aIter,
6059 Maybe<StyleDisplay>& aTargetContentDisplay) {
6060 return FindSibling<SiblingDirection::Backward>(aIter, aTargetContentDisplay);
6063 nsIFrame* nsCSSFrameConstructor::FindNextSibling(
6064 const FlattenedChildIterator& aIter,
6065 Maybe<StyleDisplay>& aTargetContentDisplay) {
6066 return FindSibling<SiblingDirection::Forward>(aIter, aTargetContentDisplay);
6069 template <nsCSSFrameConstructor::SiblingDirection aDirection>
6070 nsIFrame* nsCSSFrameConstructor::FindSibling(
6071 const FlattenedChildIterator& aIter,
6072 Maybe<StyleDisplay>& aTargetContentDisplay) {
6073 nsIContent* targetContent = aIter.Get();
6074 FlattenedChildIterator siblingIter = aIter;
6075 nsIFrame* sibling = FindSiblingInternal<aDirection>(
6076 siblingIter, targetContent, aTargetContentDisplay);
6077 if (sibling) {
6078 return sibling;
6081 // Our siblings (if any) do not have a frame to guide us. The frame for the
6082 // target content should be inserted whereever a frame for the container would
6083 // be inserted. This is needed when inserting into display: contents nodes.
6084 const nsIContent* current = aIter.Parent();
6085 while (IsDisplayContents(current)) {
6086 const nsIContent* parent = current->GetFlattenedTreeParent();
6087 MOZ_ASSERT(parent, "No display: contents on the root");
6089 FlattenedChildIterator iter(parent);
6090 iter.Seek(current);
6091 sibling = FindSiblingInternal<aDirection>(iter, targetContent,
6092 aTargetContentDisplay);
6093 if (sibling) {
6094 return sibling;
6097 current = parent;
6100 return nullptr;
6103 // For fieldsets, returns the area frame, if the child is not a legend.
6104 static nsContainerFrame* GetAdjustedParentFrame(nsContainerFrame* aParentFrame,
6105 nsIContent* aChildContent) {
6106 MOZ_ASSERT(!aParentFrame->IsTableWrapperFrame(), "Shouldn't be happening!");
6108 nsContainerFrame* newParent = nullptr;
6109 if (aParentFrame->IsFieldSetFrame()) {
6110 // If the parent is a fieldSet, use the fieldSet's area frame as the
6111 // parent unless the new content is a legend.
6112 if (!aChildContent->IsHTMLElement(nsGkAtoms::legend)) {
6113 newParent = static_cast<nsFieldSetFrame*>(aParentFrame)->GetInner();
6114 if (newParent) {
6115 newParent = newParent->GetContentInsertionFrame();
6119 return newParent ? newParent : aParentFrame;
6122 nsIFrame* nsCSSFrameConstructor::GetInsertionPrevSibling(
6123 InsertionPoint* aInsertion, nsIContent* aChild, bool* aIsAppend,
6124 bool* aIsRangeInsertSafe, nsIContent* aStartSkipChild,
6125 nsIContent* aEndSkipChild) {
6126 MOZ_ASSERT(aInsertion->mParentFrame, "Must have parent frame to start with");
6128 *aIsAppend = false;
6130 // Find the frame that precedes the insertion point.
6131 FlattenedChildIterator iter(aInsertion->mContainer);
6132 if (iter.ShadowDOMInvolved() || !aChild->IsRootOfNativeAnonymousSubtree()) {
6133 // The check for IsRootOfNativeAnonymousSubtree() is because editor is
6134 // severely broken and calls us directly for native anonymous
6135 // nodes that it creates.
6136 if (aStartSkipChild) {
6137 iter.Seek(aStartSkipChild);
6138 } else {
6139 iter.Seek(aChild);
6141 } else {
6142 // Prime the iterator for the call to FindPreviousSibling.
6143 iter.GetNextChild();
6144 MOZ_ASSERT(aChild->GetProperty(nsGkAtoms::restylableAnonymousNode),
6145 "Someone passed native anonymous content directly into frame "
6146 "construction. Stop doing that!");
6149 // Note that FindPreviousSibling is passed the iterator by value, so that
6150 // the later usage of the iterator starts from the same place.
6151 Maybe<StyleDisplay> childDisplay;
6152 nsIFrame* prevSibling = FindPreviousSibling(iter, childDisplay);
6154 // Now, find the geometric parent so that we can handle
6155 // continuations properly. Use the prev sibling if we have it;
6156 // otherwise use the next sibling.
6157 if (prevSibling) {
6158 aInsertion->mParentFrame =
6159 prevSibling->GetParent()->GetContentInsertionFrame();
6160 } else {
6161 // If there is no previous sibling, then find the frame that follows
6163 // FIXME(emilio): This is really complex and probably shouldn't be.
6164 if (aEndSkipChild) {
6165 iter.Seek(aEndSkipChild);
6166 iter.GetPreviousChild();
6168 if (nsIFrame* nextSibling = FindNextSibling(iter, childDisplay)) {
6169 aInsertion->mParentFrame =
6170 nextSibling->GetParent()->GetContentInsertionFrame();
6171 } else {
6172 // No previous or next sibling, so treat this like an appended frame.
6173 *aIsAppend = true;
6175 // Deal with fieldsets.
6176 aInsertion->mParentFrame =
6177 ::GetAdjustedParentFrame(aInsertion->mParentFrame, aChild);
6179 aInsertion->mParentFrame =
6180 ::ContinuationToAppendTo(aInsertion->mParentFrame);
6182 prevSibling = ::FindAppendPrevSibling(aInsertion->mParentFrame, nullptr);
6186 *aIsRangeInsertSafe = childDisplay.isNothing();
6187 return prevSibling;
6190 nsContainerFrame* nsCSSFrameConstructor::GetContentInsertionFrameFor(
6191 nsIContent* aContent) {
6192 nsIFrame* frame;
6193 while (!(frame = aContent->GetPrimaryFrame())) {
6194 if (!IsDisplayContents(aContent)) {
6195 return nullptr;
6198 aContent = aContent->GetFlattenedTreeParent();
6199 if (!aContent) {
6200 return nullptr;
6204 // If the content of the frame is not the desired content then this is not
6205 // really a frame for the desired content.
6206 // XXX This check is needed due to bug 135040. Remove it once that's fixed.
6207 if (frame->GetContent() != aContent) {
6208 return nullptr;
6211 nsContainerFrame* insertionFrame = frame->GetContentInsertionFrame();
6213 NS_ASSERTION(!insertionFrame || insertionFrame == frame || !frame->IsLeaf(),
6214 "The insertion frame is the primary frame or the primary frame "
6215 "isn't a leaf");
6217 return insertionFrame;
6220 static bool IsSpecialFramesetChild(nsIContent* aContent) {
6221 // IMPORTANT: This must match the conditions in nsHTMLFramesetFrame::Init.
6222 return aContent->IsAnyOfHTMLElements(nsGkAtoms::frameset, nsGkAtoms::frame);
6225 static void InvalidateCanvasIfNeeded(PresShell* aPresShell, nsIContent* aNode);
6227 void nsCSSFrameConstructor::AddTextItemIfNeeded(
6228 nsFrameConstructorState& aState, const ComputedStyle& aParentStyle,
6229 const InsertionPoint& aInsertion, nsIContent* aPossibleTextContent,
6230 FrameConstructionItemList& aItems) {
6231 MOZ_ASSERT(aPossibleTextContent, "Must have node");
6232 if (!aPossibleTextContent->IsText() ||
6233 !aPossibleTextContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE) ||
6234 aPossibleTextContent->HasFlag(NODE_NEEDS_FRAME)) {
6235 // Not text, or not suppressed due to being all-whitespace (if it were being
6236 // suppressed, it would have the NS_CREATE_FRAME_IF_NON_WHITESPACE flag), or
6237 // going to be reframed anyway.
6238 return;
6240 MOZ_ASSERT(!aPossibleTextContent->GetPrimaryFrame(),
6241 "Text node has a frame and NS_CREATE_FRAME_IF_NON_WHITESPACE");
6242 AddFrameConstructionItems(aState, aPossibleTextContent, false, aParentStyle,
6243 aInsertion, aItems);
6246 void nsCSSFrameConstructor::ReframeTextIfNeeded(nsIContent* aContent) {
6247 if (!aContent->IsText() ||
6248 !aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE) ||
6249 aContent->HasFlag(NODE_NEEDS_FRAME)) {
6250 // Not text, or not suppressed due to being all-whitespace (if it were being
6251 // suppressed, it would have the NS_CREATE_FRAME_IF_NON_WHITESPACE flag), or
6252 // going to be reframed anyway.
6253 return;
6255 MOZ_ASSERT(!aContent->GetPrimaryFrame(),
6256 "Text node has a frame and NS_CREATE_FRAME_IF_NON_WHITESPACE");
6257 ContentInserted(aContent, InsertionKind::Async);
6260 #ifdef DEBUG
6261 void nsCSSFrameConstructor::CheckBitsForLazyFrameConstruction(
6262 nsIContent* aParent) {
6263 // If we hit a node with no primary frame, or the NODE_NEEDS_FRAME bit set
6264 // we want to assert, but leaf frames that process their own children and may
6265 // ignore anonymous children (eg framesets) make this complicated. So we set
6266 // these two booleans if we encounter these situations and unset them if we
6267 // hit a node with a leaf frame.
6269 // It's fine if one of node without primary frame is in a display:none
6270 // subtree.
6272 // Also, it's fine if one of the nodes without primary frame is a display:
6273 // contents node.
6274 bool noPrimaryFrame = false;
6275 bool needsFrameBitSet = false;
6276 nsIContent* content = aParent;
6277 while (content && !content->HasFlag(NODE_DESCENDANTS_NEED_FRAMES)) {
6278 if (content->GetPrimaryFrame() && content->GetPrimaryFrame()->IsLeaf()) {
6279 noPrimaryFrame = needsFrameBitSet = false;
6281 if (!noPrimaryFrame && !content->GetPrimaryFrame()) {
6282 noPrimaryFrame = !IsDisplayContents(content);
6284 if (!needsFrameBitSet && content->HasFlag(NODE_NEEDS_FRAME)) {
6285 needsFrameBitSet = true;
6288 content = content->GetFlattenedTreeParent();
6290 if (content && content->GetPrimaryFrame() &&
6291 content->GetPrimaryFrame()->IsLeaf()) {
6292 noPrimaryFrame = needsFrameBitSet = false;
6294 MOZ_ASSERT(!noPrimaryFrame,
6295 "Ancestors of nodes with frames to be "
6296 "constructed lazily should have frames");
6297 MOZ_ASSERT(!needsFrameBitSet,
6298 "Ancestors of nodes with frames to be "
6299 "constructed lazily should not have NEEDS_FRAME bit set");
6301 #endif
6303 // Returns true if this operation can be lazy, false if not.
6305 // FIXME(emilio, bug 1410020): This function assumes that the flattened tree
6306 // parent of all the appended children is the same, which, afaict, is not
6307 // necessarily true.
6308 void nsCSSFrameConstructor::ConstructLazily(Operation aOperation,
6309 nsIContent* aChild) {
6310 MOZ_ASSERT(aChild->GetParent());
6312 // We can construct lazily; just need to set suitable bits in the content
6313 // tree.
6314 Element* parent = aChild->GetFlattenedTreeParentElement();
6315 if (!parent) {
6316 // Not part of the flat tree, nothing to do.
6317 return;
6320 if (Servo_Element_IsDisplayNone(parent)) {
6321 // Nothing to do either.
6323 // FIXME(emilio): This should be an assert, except for weird <frameset>
6324 // stuff that does its own frame construction. Such an assert would fire in
6325 // layout/style/crashtests/1411478.html, for example.
6326 return;
6329 // Set NODE_NEEDS_FRAME on the new nodes.
6330 if (aOperation == CONTENTINSERT) {
6331 NS_ASSERTION(!aChild->GetPrimaryFrame() ||
6332 aChild->GetPrimaryFrame()->GetContent() != aChild,
6333 // XXX the aChild->GetPrimaryFrame()->GetContent() != aChild
6334 // check is needed due to bug 135040. Remove it once that's
6335 // fixed.
6336 "setting NEEDS_FRAME on a node that already has a frame?");
6337 aChild->SetFlags(NODE_NEEDS_FRAME);
6338 } else { // CONTENTAPPEND
6339 for (nsIContent* child = aChild; child; child = child->GetNextSibling()) {
6340 NS_ASSERTION(!child->GetPrimaryFrame() ||
6341 child->GetPrimaryFrame()->GetContent() != child,
6342 // XXX the child->GetPrimaryFrame()->GetContent() != child
6343 // check is needed due to bug 135040. Remove it once that's
6344 // fixed.
6345 "setting NEEDS_FRAME on a node that already has a frame?");
6346 child->SetFlags(NODE_NEEDS_FRAME);
6350 CheckBitsForLazyFrameConstruction(parent);
6351 parent->NoteDescendantsNeedFramesForServo();
6354 void nsCSSFrameConstructor::IssueSingleInsertNofications(
6355 nsIContent* aStartChild, nsIContent* aEndChild,
6356 InsertionKind aInsertionKind) {
6357 for (nsIContent* child = aStartChild; child != aEndChild;
6358 child = child->GetNextSibling()) {
6359 // XXX the GetContent() != child check is needed due to bug 135040.
6360 // Remove it once that's fixed.
6361 MOZ_ASSERT(!child->GetPrimaryFrame() ||
6362 child->GetPrimaryFrame()->GetContent() != child);
6364 // Call ContentRangeInserted with this node.
6365 ContentRangeInserted(child, child->GetNextSibling(), aInsertionKind);
6369 bool nsCSSFrameConstructor::InsertionPoint::IsMultiple() const {
6370 // Fieldset frames have multiple normal flow child frame lists so handle it
6371 // the same as if it had multiple content insertion points.
6372 return mParentFrame && mParentFrame->IsFieldSetFrame();
6375 nsCSSFrameConstructor::InsertionPoint
6376 nsCSSFrameConstructor::GetRangeInsertionPoint(nsIContent* aStartChild,
6377 nsIContent* aEndChild,
6378 InsertionKind aInsertionKind) {
6379 MOZ_ASSERT(aStartChild);
6381 nsIContent* parent = aStartChild->GetParent();
6382 if (!parent) {
6383 IssueSingleInsertNofications(aStartChild, aEndChild, aInsertionKind);
6384 return {};
6387 // If the children of the container may be distributed to different insertion
6388 // points, insert them separately and bail out, letting ContentInserted handle
6389 // the mess.
6390 if (parent->GetShadowRoot()) {
6391 IssueSingleInsertNofications(aStartChild, aEndChild, aInsertionKind);
6392 return {};
6395 #ifdef DEBUG
6397 nsIContent* expectedParent = aStartChild->GetFlattenedTreeParent();
6398 for (nsIContent* child = aStartChild->GetNextSibling(); child;
6399 child = child->GetNextSibling()) {
6400 MOZ_ASSERT(child->GetFlattenedTreeParent() == expectedParent);
6403 #endif
6405 // Now the flattened tree parent of all the siblings is the same, just use the
6406 // same insertion point and take the fast path, unless it's a multiple
6407 // insertion point.
6408 InsertionPoint ip = GetInsertionPoint(aStartChild);
6409 if (ip.IsMultiple()) {
6410 IssueSingleInsertNofications(aStartChild, aEndChild, aInsertionKind);
6411 return {};
6414 return ip;
6417 bool nsCSSFrameConstructor::MaybeRecreateForFrameset(nsIFrame* aParentFrame,
6418 nsIContent* aStartChild,
6419 nsIContent* aEndChild) {
6420 if (aParentFrame->IsFrameSetFrame()) {
6421 // Check whether we have any kids we care about.
6422 for (nsIContent* cur = aStartChild; cur != aEndChild;
6423 cur = cur->GetNextSibling()) {
6424 if (IsSpecialFramesetChild(cur)) {
6425 // Just reframe the parent, since framesets are weird like that.
6426 RecreateFramesForContent(aParentFrame->GetContent(),
6427 InsertionKind::Async);
6428 return true;
6432 return false;
6435 void nsCSSFrameConstructor::LazilyStyleNewChildRange(nsIContent* aStartChild,
6436 nsIContent* aEndChild) {
6437 for (nsIContent* child = aStartChild; child != aEndChild;
6438 child = child->GetNextSibling()) {
6439 if (child->IsElement()) {
6440 child->AsElement()->NoteDirtyForServo();
6445 #ifdef DEBUG
6446 static bool IsFlattenedTreeChild(nsIContent* aParent, nsIContent* aChild) {
6447 FlattenedChildIterator iter(aParent);
6448 for (nsIContent* node = iter.GetNextChild(); node;
6449 node = iter.GetNextChild()) {
6450 if (node == aChild) {
6451 return true;
6454 return false;
6456 #endif
6458 void nsCSSFrameConstructor::StyleNewChildRange(nsIContent* aStartChild,
6459 nsIContent* aEndChild) {
6460 ServoStyleSet* styleSet = mPresShell->StyleSet();
6462 for (nsIContent* child = aStartChild; child != aEndChild;
6463 child = child->GetNextSibling()) {
6464 if (!child->IsElement()) {
6465 continue;
6468 Element* childElement = child->AsElement();
6470 // We only come in here from non-lazy frame construction, so the children
6471 // should be unstyled.
6472 MOZ_ASSERT(!childElement->HasServoData());
6474 #ifdef DEBUG
6476 // Furthermore, all of them should have the same flattened tree parent
6477 // (GetRangeInsertionPoint ensures it). And that parent should be styled,
6478 // otherwise we would've never found an insertion point at all.
6479 Element* parent = childElement->GetFlattenedTreeParentElement();
6480 MOZ_ASSERT(parent);
6481 MOZ_ASSERT(parent->HasServoData());
6482 MOZ_ASSERT(
6483 IsFlattenedTreeChild(parent, child),
6484 "GetFlattenedTreeParent and ChildIterator don't agree, fix this!");
6486 #endif
6488 styleSet->StyleNewSubtree(childElement);
6492 nsIFrame* nsCSSFrameConstructor::FindNextSiblingForAppend(
6493 const InsertionPoint& aInsertion) {
6494 auto SlowPath = [&]() -> nsIFrame* {
6495 FlattenedChildIterator iter(aInsertion.mContainer,
6496 /* aStartAtBeginning = */ false);
6497 iter.GetPreviousChild(); // Prime the iterator.
6498 Maybe<StyleDisplay> unused;
6499 return FindNextSibling(iter, unused);
6502 if (!IsDisplayContents(aInsertion.mContainer) &&
6503 !nsLayoutUtils::GetAfterFrame(aInsertion.mContainer)) {
6504 MOZ_ASSERT(!SlowPath());
6505 return nullptr;
6508 return SlowPath();
6511 void nsCSSFrameConstructor::ContentAppended(nsIContent* aFirstNewContent,
6512 InsertionKind aInsertionKind) {
6513 MOZ_ASSERT(aInsertionKind == InsertionKind::Sync ||
6514 !RestyleManager()->IsInStyleRefresh());
6516 AUTO_PROFILER_LABEL("nsCSSFrameConstructor::ContentAppended",
6517 LAYOUT_FrameConstruction);
6518 AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
6520 #ifdef DEBUG
6521 if (gNoisyContentUpdates) {
6522 printf(
6523 "nsCSSFrameConstructor::ContentAppended container=%p "
6524 "first-child=%p lazy=%d\n",
6525 aFirstNewContent->GetParent(), aFirstNewContent,
6526 aInsertionKind == InsertionKind::Async);
6527 if (gReallyNoisyContentUpdates && aFirstNewContent->GetParent()) {
6528 aFirstNewContent->GetParent()->List(stdout, 0);
6532 for (nsIContent* child = aFirstNewContent; child;
6533 child = child->GetNextSibling()) {
6534 // XXX the GetContent() != child check is needed due to bug 135040.
6535 // Remove it once that's fixed.
6536 MOZ_ASSERT(
6537 !child->GetPrimaryFrame() ||
6538 child->GetPrimaryFrame()->GetContent() != child,
6539 "asked to construct a frame for a node that already has a frame");
6541 #endif
6543 LAYOUT_PHASE_TEMP_EXIT();
6544 InsertionPoint insertion =
6545 GetRangeInsertionPoint(aFirstNewContent, nullptr, aInsertionKind);
6546 nsContainerFrame*& parentFrame = insertion.mParentFrame;
6547 LAYOUT_PHASE_TEMP_REENTER();
6548 if (!parentFrame) {
6549 // We're punting on frame construction because there's no container frame.
6550 // The Servo-backed style system handles this case like the lazy frame
6551 // construction case, except when we're already constructing frames, in
6552 // which case we shouldn't need to do anything else.
6553 if (aInsertionKind == InsertionKind::Async) {
6554 LazilyStyleNewChildRange(aFirstNewContent, nullptr);
6556 return;
6559 if (aInsertionKind == InsertionKind::Async) {
6560 ConstructLazily(CONTENTAPPEND, aFirstNewContent);
6561 LazilyStyleNewChildRange(aFirstNewContent, nullptr);
6562 return;
6565 LAYOUT_PHASE_TEMP_EXIT();
6566 if (MaybeRecreateForFrameset(parentFrame, aFirstNewContent, nullptr)) {
6567 LAYOUT_PHASE_TEMP_REENTER();
6568 return;
6570 LAYOUT_PHASE_TEMP_REENTER();
6572 if (parentFrame->IsLeaf()) {
6573 // Nothing to do here; we shouldn't be constructing kids of leaves
6574 // Clear lazy bits so we don't try to construct again.
6575 ClearLazyBits(aFirstNewContent, nullptr);
6576 return;
6579 LAYOUT_PHASE_TEMP_EXIT();
6580 if (WipeInsertionParent(parentFrame)) {
6581 LAYOUT_PHASE_TEMP_REENTER();
6582 return;
6584 LAYOUT_PHASE_TEMP_REENTER();
6586 #ifdef DEBUG
6587 if (gNoisyContentUpdates && IsFramePartOfIBSplit(parentFrame)) {
6588 printf("nsCSSFrameConstructor::ContentAppended: parentFrame=");
6589 parentFrame->ListTag(stdout);
6590 printf(" is ib-split\n");
6592 #endif
6594 // We should never get here with fieldsets, since they have
6595 // multiple insertion points.
6596 MOZ_ASSERT(!parentFrame->IsFieldSetFrame(),
6597 "Parent frame should not be fieldset!");
6599 nsIFrame* nextSibling = FindNextSiblingForAppend(insertion);
6600 if (nextSibling) {
6601 parentFrame = nextSibling->GetParent()->GetContentInsertionFrame();
6602 } else {
6603 parentFrame = ::ContinuationToAppendTo(parentFrame);
6606 nsContainerFrame* containingBlock = GetFloatContainingBlock(parentFrame);
6608 // See if the containing block has :first-letter style applied.
6609 const bool haveFirstLetterStyle =
6610 containingBlock && HasFirstLetterStyle(containingBlock);
6612 const bool haveFirstLineStyle =
6613 containingBlock && ShouldHaveFirstLineStyle(containingBlock->GetContent(),
6614 containingBlock->Style());
6616 if (haveFirstLetterStyle) {
6617 AutoWeakFrame wf(nextSibling);
6619 // Before we get going, remove the current letter frames
6620 RemoveLetterFrames(mPresShell, containingBlock);
6622 // Reget nextSibling, since we may have killed it.
6624 // FIXME(emilio): This kinda sucks! :(
6625 if (nextSibling && !wf) {
6626 nextSibling = FindNextSiblingForAppend(insertion);
6627 if (nextSibling) {
6628 parentFrame = nextSibling->GetParent()->GetContentInsertionFrame();
6629 containingBlock = GetFloatContainingBlock(parentFrame);
6634 // Create some new frames
6635 nsFrameConstructorState state(
6636 mPresShell, GetAbsoluteContainingBlock(parentFrame, FIXED_POS),
6637 GetAbsoluteContainingBlock(parentFrame, ABS_POS), containingBlock);
6639 if (mPresShell->GetPresContext()->IsPaginated() &&
6640 StaticPrefs::layout_css_named_pages_enabled()) {
6641 // Because this function can be called outside frame construction, we need
6642 // to set state.mAutoPageNameValue based on what the parent frame's auto
6643 // value is.
6644 // Calling this from outside the frame constructor can violate many of the
6645 // expectations in AutoFrameConstructionPageName, and unlike during frame
6646 // construction we already have an auto value from parentFrame, so we do
6647 // not use AutoFrameConstructionPageName here.
6648 state.mAutoPageNameValue = parentFrame->GetAutoPageValue();
6649 #ifdef DEBUG
6650 parentFrame->mWasVisitedByAutoFrameConstructionPageName = true;
6651 #endif
6654 LayoutFrameType frameType = parentFrame->Type();
6656 RefPtr<ComputedStyle> parentStyle =
6657 ResolveComputedStyle(insertion.mContainer);
6658 FlattenedChildIterator iter(insertion.mContainer);
6659 const bool haveNoShadowDOM =
6660 !iter.ShadowDOMInvolved() || !iter.GetNextChild();
6662 AutoFrameConstructionItemList items(this);
6663 if (aFirstNewContent->GetPreviousSibling() &&
6664 GetParentType(frameType) == eTypeBlock && haveNoShadowDOM) {
6665 // If there's a text node in the normal content list just before the new
6666 // items, and it has no frame, make a frame construction item for it. If it
6667 // doesn't need a frame, ConstructFramesFromItemList below won't give it
6668 // one. No need to do all this if our parent type is not block, though,
6669 // since WipeContainingBlock already handles that situation.
6671 // Because we're appending, we don't need to worry about any text
6672 // after the appended content; there can only be generated content
6673 // (and bare text nodes are not generated). Native anonymous content
6674 // generated by frames never participates in inline layout.
6675 AddTextItemIfNeeded(state, *parentStyle, insertion,
6676 aFirstNewContent->GetPreviousSibling(), items);
6678 for (nsIContent* child = aFirstNewContent; child;
6679 child = child->GetNextSibling()) {
6680 AddFrameConstructionItems(state, child, false, *parentStyle, insertion,
6681 items);
6684 nsIFrame* prevSibling = ::FindAppendPrevSibling(parentFrame, nextSibling);
6686 // Perform special check for diddling around with the frames in
6687 // a ib-split inline frame.
6688 // If we're appending before :after content, then we're not really
6689 // appending, so let WipeContainingBlock know that.
6690 LAYOUT_PHASE_TEMP_EXIT();
6691 if (WipeContainingBlock(state, containingBlock, parentFrame, items, true,
6692 prevSibling)) {
6693 LAYOUT_PHASE_TEMP_REENTER();
6694 return;
6696 LAYOUT_PHASE_TEMP_REENTER();
6698 // If the parent is a block frame, and we're not in a special case
6699 // where frames can be moved around, determine if the list is for the
6700 // start or end of the block.
6701 if (parentFrame->IsBlockFrameOrSubclass() && !haveFirstLetterStyle &&
6702 !haveFirstLineStyle && !IsFramePartOfIBSplit(parentFrame)) {
6703 items.SetLineBoundaryAtStart(!prevSibling ||
6704 !prevSibling->IsInlineOutside() ||
6705 prevSibling->IsBrFrame());
6706 // :after content can't be <br> so no need to check it
6708 // FIXME(emilio): A display: contents sibling could! Write a test-case and
6709 // fix.
6710 items.SetLineBoundaryAtEnd(!nextSibling || !nextSibling->IsInlineOutside());
6712 // To suppress whitespace-only text frames, we have to verify that
6713 // our container's DOM child list matches its flattened tree child list.
6714 items.SetParentHasNoShadowDOM(haveNoShadowDOM);
6716 nsFrameConstructorSaveState floatSaveState;
6717 state.MaybePushFloatContainingBlock(parentFrame, floatSaveState);
6719 nsFrameList frameList;
6720 ConstructFramesFromItemList(state, items, parentFrame,
6721 ParentIsWrapperAnonBox(parentFrame), frameList);
6723 for (nsIContent* child = aFirstNewContent; child;
6724 child = child->GetNextSibling()) {
6725 // Invalidate now instead of before the WipeContainingBlock call, just in
6726 // case we do wipe; in that case we don't need to do this walk at all.
6727 // XXXbz does that matter? Would it make more sense to save some virtual
6728 // GetChildAt_Deprecated calls instead and do this during construction of
6729 // our FrameConstructionItemList?
6730 InvalidateCanvasIfNeeded(mPresShell, child);
6733 // If the container is a table and a caption was appended, it needs to be put
6734 // in the table wrapper frame's additional child list.
6735 nsFrameList captionList;
6736 if (LayoutFrameType::Table == frameType) {
6737 // Pull out the captions. Note that we don't want to do that as we go,
6738 // because processing a single caption can add a whole bunch of things to
6739 // the frame items due to pseudoframe processing. So we'd have to pull
6740 // captions from a list anyway; might as well do that here.
6741 // XXXbz this is no longer true; we could pull captions directly out of the
6742 // FrameConstructionItemList now.
6743 PullOutCaptionFrames(frameList, captionList);
6746 if (haveFirstLineStyle && parentFrame == containingBlock) {
6747 // It's possible that some of the new frames go into a
6748 // first-line frame. Look at them and see...
6749 AppendFirstLineFrames(state, containingBlock->GetContent(), containingBlock,
6750 frameList);
6751 // That moved things into line frames as needed, reparenting their
6752 // styles. Nothing else needs to be done.
6753 } else if (parentFrame->Style()->HasPseudoElementData()) {
6754 // parentFrame might be inside a ::first-line frame. Check whether it is,
6755 // and if so fix up our styles.
6756 CheckForFirstLineInsertion(parentFrame, frameList);
6757 CheckForFirstLineInsertion(parentFrame, captionList);
6760 // Notify the parent frame passing it the list of new frames
6761 // Append the flowed frames to the principal child list; captions
6762 // need special treatment
6763 if (captionList.NotEmpty()) { // append the caption to the table wrapper
6764 NS_ASSERTION(LayoutFrameType::Table == frameType, "how did that happen?");
6765 nsContainerFrame* outerTable = parentFrame->GetParent();
6766 captionList.ApplySetParent(outerTable);
6767 AppendFrames(outerTable, FrameChildListID::Caption, std::move(captionList));
6770 LAYOUT_PHASE_TEMP_EXIT();
6771 if (MaybeRecreateForColumnSpan(state, parentFrame, frameList, prevSibling)) {
6772 LAYOUT_PHASE_TEMP_REENTER();
6773 return;
6775 LAYOUT_PHASE_TEMP_REENTER();
6777 if (frameList.NotEmpty()) { // append the in-flow kids
6778 AppendFramesToParent(state, parentFrame, frameList, prevSibling);
6781 // Recover first-letter frames
6782 if (haveFirstLetterStyle) {
6783 RecoverLetterFrames(containingBlock);
6786 #ifdef DEBUG
6787 if (gReallyNoisyContentUpdates) {
6788 printf("nsCSSFrameConstructor::ContentAppended: resulting frame model:\n");
6789 parentFrame->List(stdout);
6791 #endif
6793 #ifdef ACCESSIBILITY
6794 if (nsAccessibilityService* accService = GetAccService()) {
6795 accService->ContentRangeInserted(mPresShell, aFirstNewContent, nullptr);
6797 #endif
6800 void nsCSSFrameConstructor::ContentInserted(nsIContent* aChild,
6801 InsertionKind aInsertionKind) {
6802 ContentRangeInserted(aChild, aChild->GetNextSibling(), aInsertionKind);
6805 // ContentRangeInserted handles creating frames for a range of nodes that
6806 // aren't at the end of their childlist. ContentRangeInserted isn't a real
6807 // content notification, but rather it handles regular ContentInserted calls
6808 // for a single node as well as the lazy construction of frames for a range of
6809 // nodes when called from CreateNeededFrames. For a range of nodes to be
6810 // suitable to have its frames constructed all at once they must meet the same
6811 // conditions that ContentAppended imposes (GetRangeInsertionPoint checks
6812 // these), plus more. Namely when finding the insertion prevsibling we must not
6813 // need to consult something specific to any one node in the range, so that the
6814 // insertion prevsibling would be the same for each node in the range. So we
6815 // pass the first node in the range to GetInsertionPrevSibling, and if
6816 // IsValidSibling (the only place GetInsertionPrevSibling might look at the
6817 // passed in node itself) needs to resolve style on the node we record this and
6818 // return that this range needs to be split up and inserted separately. Table
6819 // captions need extra attention as we need to determine where to insert them
6820 // in the caption list, while skipping any nodes in the range being inserted
6821 // (because when we treat the caption frames the other nodes have had their
6822 // frames constructed but not yet inserted into the frame tree).
6823 void nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aStartChild,
6824 nsIContent* aEndChild,
6825 InsertionKind aInsertionKind) {
6826 MOZ_ASSERT(aInsertionKind == InsertionKind::Sync ||
6827 !RestyleManager()->IsInStyleRefresh());
6829 AUTO_PROFILER_LABEL("nsCSSFrameConstructor::ContentRangeInserted",
6830 LAYOUT_FrameConstruction);
6831 AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
6833 MOZ_ASSERT(aStartChild, "must always pass a child");
6835 #ifdef DEBUG
6836 if (gNoisyContentUpdates) {
6837 printf(
6838 "nsCSSFrameConstructor::ContentRangeInserted container=%p "
6839 "start-child=%p end-child=%p lazy=%d\n",
6840 aStartChild->GetParent(), aStartChild, aEndChild,
6841 aInsertionKind == InsertionKind::Async);
6842 if (gReallyNoisyContentUpdates) {
6843 if (aStartChild->GetParent()) {
6844 aStartChild->GetParent()->List(stdout, 0);
6845 } else {
6846 aStartChild->List(stdout, 0);
6851 for (nsIContent* child = aStartChild; child != aEndChild;
6852 child = child->GetNextSibling()) {
6853 // XXX the GetContent() != child check is needed due to bug 135040.
6854 // Remove it once that's fixed.
6855 NS_ASSERTION(
6856 !child->GetPrimaryFrame() ||
6857 child->GetPrimaryFrame()->GetContent() != child,
6858 "asked to construct a frame for a node that already has a frame");
6860 #endif
6862 bool isSingleInsert = (aStartChild->GetNextSibling() == aEndChild);
6863 NS_ASSERTION(isSingleInsert || aInsertionKind == InsertionKind::Sync,
6864 "range insert shouldn't be lazy");
6865 NS_ASSERTION(isSingleInsert || aEndChild,
6866 "range should not include all nodes after aStartChild");
6868 // If we have a null parent, then this must be the document element being
6869 // inserted, or some other child of the document in the DOM (might be a PI,
6870 // say).
6871 if (!aStartChild->GetParent()) {
6872 MOZ_ASSERT(isSingleInsert,
6873 "root node insertion should be a single insertion");
6874 Element* docElement = mDocument->GetRootElement();
6875 if (aStartChild != docElement) {
6876 // Not the root element; just bail out
6877 return;
6880 MOZ_ASSERT(!mRootElementFrame, "root element frame already created");
6881 if (aInsertionKind == InsertionKind::Async) {
6882 docElement->SetFlags(NODE_NEEDS_FRAME);
6883 LazilyStyleNewChildRange(docElement, nullptr);
6884 return;
6887 // Create frames for the document element and its child elements
6888 if (ConstructDocElementFrame(docElement)) {
6889 InvalidateCanvasIfNeeded(mPresShell, aStartChild);
6890 #ifdef DEBUG
6891 if (gReallyNoisyContentUpdates) {
6892 printf(
6893 "nsCSSFrameConstructor::ContentRangeInserted: resulting frame "
6894 "model:\n");
6895 mRootElementFrame->List(stdout);
6897 #endif
6900 #ifdef ACCESSIBILITY
6901 if (nsAccessibilityService* accService = GetAccService()) {
6902 accService->ContentRangeInserted(mPresShell, aStartChild, aEndChild);
6904 #endif
6906 return;
6909 InsertionPoint insertion;
6910 if (isSingleInsert) {
6911 // See if we have a Shadow DOM insertion point. If so, then that's our real
6912 // parent frame; if not, then the frame hasn't been built yet and we just
6913 // bail.
6914 insertion = GetInsertionPoint(aStartChild);
6915 } else {
6916 // Get our insertion point. If we need to issue single ContentInserteds
6917 // GetRangeInsertionPoint will take care of that for us.
6918 LAYOUT_PHASE_TEMP_EXIT();
6919 insertion = GetRangeInsertionPoint(aStartChild, aEndChild, aInsertionKind);
6920 LAYOUT_PHASE_TEMP_REENTER();
6923 if (!insertion.mParentFrame) {
6924 // We're punting on frame construction because there's no container frame.
6925 // The Servo-backed style system handles this case like the lazy frame
6926 // construction case, except when we're already constructing frames, in
6927 // which case we shouldn't need to do anything else.
6928 if (aInsertionKind == InsertionKind::Async) {
6929 LazilyStyleNewChildRange(aStartChild, aEndChild);
6931 return;
6934 if (aInsertionKind == InsertionKind::Async) {
6935 ConstructLazily(CONTENTINSERT, aStartChild);
6936 LazilyStyleNewChildRange(aStartChild, aEndChild);
6937 return;
6940 bool isAppend, isRangeInsertSafe;
6941 nsIFrame* prevSibling = GetInsertionPrevSibling(
6942 &insertion, aStartChild, &isAppend, &isRangeInsertSafe);
6944 // check if range insert is safe
6945 if (!isSingleInsert && !isRangeInsertSafe) {
6946 // must fall back to a single ContertInserted for each child in the range
6947 LAYOUT_PHASE_TEMP_EXIT();
6948 IssueSingleInsertNofications(aStartChild, aEndChild, InsertionKind::Sync);
6949 LAYOUT_PHASE_TEMP_REENTER();
6950 return;
6953 LayoutFrameType frameType = insertion.mParentFrame->Type();
6954 LAYOUT_PHASE_TEMP_EXIT();
6955 if (MaybeRecreateForFrameset(insertion.mParentFrame, aStartChild,
6956 aEndChild)) {
6957 LAYOUT_PHASE_TEMP_REENTER();
6958 return;
6960 LAYOUT_PHASE_TEMP_REENTER();
6962 // We should only get here with fieldsets when doing a single insert, because
6963 // fieldsets have multiple insertion points.
6964 NS_ASSERTION(isSingleInsert || frameType != LayoutFrameType::FieldSet,
6965 "Unexpected parent");
6966 // Note that this check is insufficient if aStartChild is not a legend with
6967 // display::contents that contains a legend. We'll catch that case in
6968 // WipeContainingBlock. (That code would also catch this case, but handling
6969 // this early is slightly faster.)
6970 // XXXmats we should be able to optimize this when the fieldset doesn't
6971 // currently have a rendered legend. ContentRangeInserted needs to be fixed
6972 // to use the inner frame as the content insertion frame in that case.
6973 if (GetFieldSetFrameFor(insertion.mParentFrame) &&
6974 aStartChild->NodeInfo()->NameAtom() == nsGkAtoms::legend) {
6975 // Just reframe the parent, since figuring out whether this
6976 // should be the new legend and then handling it is too complex.
6977 // We could do a little better here --- check if the fieldset already
6978 // has a legend which occurs earlier in its child list than this node,
6979 // and if so, proceed. But we'd have to extend nsFieldSetFrame
6980 // to locate this legend in the inserted frames and extract it.
6981 LAYOUT_PHASE_TEMP_EXIT();
6982 RecreateFramesForContent(insertion.mParentFrame->GetContent(),
6983 InsertionKind::Async);
6984 LAYOUT_PHASE_TEMP_REENTER();
6985 return;
6988 // Don't construct kids of leaves
6989 if (insertion.mParentFrame->IsLeaf()) {
6990 // Clear lazy bits so we don't try to construct again.
6991 ClearLazyBits(aStartChild, aEndChild);
6992 return;
6995 LAYOUT_PHASE_TEMP_EXIT();
6996 if (WipeInsertionParent(insertion.mParentFrame)) {
6997 LAYOUT_PHASE_TEMP_REENTER();
6998 return;
7000 LAYOUT_PHASE_TEMP_REENTER();
7002 nsFrameConstructorState state(
7003 mPresShell, GetAbsoluteContainingBlock(insertion.mParentFrame, FIXED_POS),
7004 GetAbsoluteContainingBlock(insertion.mParentFrame, ABS_POS),
7005 GetFloatContainingBlock(insertion.mParentFrame),
7006 do_AddRef(mFrameTreeState));
7008 // Recover state for the containing block - we need to know if
7009 // it has :first-letter or :first-line style applied to it. The
7010 // reason we care is that the internal structure in these cases
7011 // is not the normal structure and requires custom updating
7012 // logic.
7013 nsContainerFrame* containingBlock = state.mFloatedList.mContainingBlock;
7014 bool haveFirstLetterStyle = false;
7015 bool haveFirstLineStyle = false;
7017 // In order to shave off some cycles, we only dig up the
7018 // containing block haveFirst* flags if the parent frame where
7019 // the insertion/append is occurring is an inline or block
7020 // container. For other types of containers this isn't relevant.
7021 StyleDisplayInside parentDisplayInside =
7022 insertion.mParentFrame->StyleDisplay()->DisplayInside();
7024 // Examine the insertion.mParentFrame where the insertion is taking
7025 // place. If it's a certain kind of container then some special
7026 // processing is done.
7027 if (StyleDisplayInside::Flow == parentDisplayInside) {
7028 // Recover the special style flags for the containing block
7029 if (containingBlock) {
7030 haveFirstLetterStyle = HasFirstLetterStyle(containingBlock);
7031 haveFirstLineStyle = ShouldHaveFirstLineStyle(
7032 containingBlock->GetContent(), containingBlock->Style());
7035 if (haveFirstLetterStyle) {
7036 // If our current insertion.mParentFrame is a Letter frame, use its parent
7037 // as our new parent hint
7038 if (insertion.mParentFrame->IsLetterFrame()) {
7039 // If insertion.mParentFrame is out of flow, then we actually want the
7040 // parent of the placeholder frame.
7041 if (insertion.mParentFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
7042 nsPlaceholderFrame* placeholderFrame =
7043 insertion.mParentFrame->GetPlaceholderFrame();
7044 NS_ASSERTION(placeholderFrame, "No placeholder for out-of-flow?");
7045 insertion.mParentFrame = placeholderFrame->GetParent();
7046 } else {
7047 insertion.mParentFrame = insertion.mParentFrame->GetParent();
7051 // Remove the old letter frames before doing the insertion
7052 RemoveLetterFrames(mPresShell, state.mFloatedList.mContainingBlock);
7054 // Removing the letterframes messes around with the frame tree, removing
7055 // and creating frames. We need to reget our prevsibling, parent frame,
7056 // etc.
7057 prevSibling = GetInsertionPrevSibling(&insertion, aStartChild, &isAppend,
7058 &isRangeInsertSafe);
7060 // Need check whether a range insert is still safe.
7061 if (!isSingleInsert && !isRangeInsertSafe) {
7062 // Need to recover the letter frames first.
7063 RecoverLetterFrames(state.mFloatedList.mContainingBlock);
7065 // must fall back to a single ContertInserted for each child in the
7066 // range
7067 LAYOUT_PHASE_TEMP_EXIT();
7068 IssueSingleInsertNofications(aStartChild, aEndChild,
7069 InsertionKind::Sync);
7070 LAYOUT_PHASE_TEMP_REENTER();
7071 return;
7074 frameType = insertion.mParentFrame->Type();
7078 // This handles fallback to 'list-style-type' when a 'list-style-image' fails
7079 // to load.
7080 if (aStartChild->IsInNativeAnonymousSubtree() &&
7081 aStartChild->IsHTMLElement(nsGkAtoms::mozgeneratedcontentimage)) {
7082 MOZ_ASSERT(isSingleInsert);
7083 MOZ_ASSERT(insertion.mParentFrame->Style()->GetPseudoType() ==
7084 PseudoStyleType::marker,
7085 "we can only handle ::marker fallback for now");
7086 nsIContent* const nextSibling = aStartChild->GetNextSibling();
7087 MOZ_ASSERT(nextSibling && nextSibling->IsText(),
7088 "expected a text node after the list-style-image image");
7089 RemoveFrame(FrameChildListID::Principal, nextSibling->GetPrimaryFrame());
7090 auto* const container = aStartChild->GetParent()->AsElement();
7091 nsIContent* firstNewChild = nullptr;
7092 auto InsertChild = [this, container, nextSibling,
7093 &firstNewChild](RefPtr<nsIContent>&& aChild) {
7094 // We don't strictly have to set NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE
7095 // here; it would get set under AppendChildTo. But AppendChildTo might
7096 // think that we're going from not being anonymous to being anonymous and
7097 // do some extra work; setting the flag here avoids that.
7098 aChild->SetFlags(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE);
7099 container->InsertChildBefore(aChild, nextSibling, false, IgnoreErrors());
7100 if (auto* childElement = Element::FromNode(aChild)) {
7101 // If we created any children elements, Servo needs to traverse them,
7102 // but the root is already set up.
7103 mPresShell->StyleSet()->StyleNewSubtree(childElement);
7105 if (!firstNewChild) {
7106 firstNewChild = aChild;
7109 CreateGeneratedContentFromListStyleType(
7110 state, *insertion.mContainer->AsElement(),
7111 *insertion.mParentFrame->Style(), InsertChild);
7112 if (!firstNewChild) {
7113 // No fallback content - we're done.
7114 return;
7116 aStartChild = firstNewChild;
7117 MOZ_ASSERT(firstNewChild->GetNextSibling() == nextSibling,
7118 "list-style-type should only create one child");
7121 AutoFrameConstructionItemList items(this);
7122 RefPtr<ComputedStyle> parentStyle =
7123 ResolveComputedStyle(insertion.mContainer);
7124 ParentType parentType = GetParentType(frameType);
7125 FlattenedChildIterator iter(insertion.mContainer);
7126 const bool haveNoShadowDOM =
7127 !iter.ShadowDOMInvolved() || !iter.GetNextChild();
7128 if (aStartChild->GetPreviousSibling() && parentType == eTypeBlock &&
7129 haveNoShadowDOM) {
7130 // If there's a text node in the normal content list just before the
7131 // new nodes, and it has no frame, make a frame construction item for
7132 // it, because it might need a frame now. No need to do this if our
7133 // parent type is not block, though, since WipeContainingBlock
7134 // already handles that situation.
7135 AddTextItemIfNeeded(state, *parentStyle, insertion,
7136 aStartChild->GetPreviousSibling(), items);
7139 if (isSingleInsert) {
7140 AddFrameConstructionItems(state, aStartChild,
7141 aStartChild->IsRootOfNativeAnonymousSubtree(),
7142 *parentStyle, insertion, items);
7143 } else {
7144 for (nsIContent* child = aStartChild; child != aEndChild;
7145 child = child->GetNextSibling()) {
7146 AddFrameConstructionItems(state, child, false, *parentStyle, insertion,
7147 items);
7151 if (aEndChild && parentType == eTypeBlock && haveNoShadowDOM) {
7152 // If there's a text node in the normal content list just after the
7153 // new nodes, and it has no frame, make a frame construction item for
7154 // it, because it might need a frame now. No need to do this if our
7155 // parent type is not block, though, since WipeContainingBlock
7156 // already handles that situation.
7157 AddTextItemIfNeeded(state, *parentStyle, insertion, aEndChild, items);
7160 // Perform special check for diddling around with the frames in
7161 // a special inline frame.
7162 // If we're appending before :after content, then we're not really
7163 // appending, so let WipeContainingBlock know that.
7164 LAYOUT_PHASE_TEMP_EXIT();
7165 if (WipeContainingBlock(state, containingBlock, insertion.mParentFrame, items,
7166 isAppend, prevSibling)) {
7167 LAYOUT_PHASE_TEMP_REENTER();
7168 return;
7170 LAYOUT_PHASE_TEMP_REENTER();
7172 nsFrameConstructorSaveState floatSaveState;
7173 state.MaybePushFloatContainingBlock(insertion.mParentFrame, floatSaveState);
7175 if (state.mPresContext->IsPaginated() &&
7176 StaticPrefs::layout_css_named_pages_enabled()) {
7177 // Because this function can be called outside frame construction, we need
7178 // to set state.mAutoPageNameValue based on what the parent frame's auto
7179 // value is.
7180 // Calling this from outside the frame constructor can violate many of the
7181 // expectations in AutoFrameConstructionPageName, and unlike during frame
7182 // construction we already have an auto value from parentFrame, so we do
7183 // not use AutoFrameConstructionPageName here.
7184 state.mAutoPageNameValue = insertion.mParentFrame->GetAutoPageValue();
7185 #ifdef DEBUG
7186 insertion.mParentFrame->mWasVisitedByAutoFrameConstructionPageName = true;
7187 #endif
7190 // If the container is a table and a caption will be appended, it needs to be
7191 // put in the table wrapper frame's additional child list.
7192 // We make no attempt here to set flags to indicate whether the list
7193 // will be at the start or end of a block. It doesn't seem worthwhile.
7194 nsFrameList frameList, captionList;
7195 ConstructFramesFromItemList(state, items, insertion.mParentFrame,
7196 ParentIsWrapperAnonBox(insertion.mParentFrame),
7197 frameList);
7199 if (frameList.NotEmpty()) {
7200 for (nsIContent* child = aStartChild; child != aEndChild;
7201 child = child->GetNextSibling()) {
7202 InvalidateCanvasIfNeeded(mPresShell, child);
7205 if (LayoutFrameType::Table == frameType ||
7206 LayoutFrameType::TableWrapper == frameType) {
7207 PullOutCaptionFrames(frameList, captionList);
7211 if (haveFirstLineStyle && insertion.mParentFrame == containingBlock &&
7212 isAppend) {
7213 // It's possible that the new frame goes into a first-line
7214 // frame. Look at it and see...
7215 AppendFirstLineFrames(state, containingBlock->GetContent(), containingBlock,
7216 frameList);
7217 } else if (insertion.mParentFrame->Style()->HasPseudoElementData()) {
7218 CheckForFirstLineInsertion(insertion.mParentFrame, frameList);
7219 CheckForFirstLineInsertion(insertion.mParentFrame, captionList);
7222 // We might have captions; put them into the caption list of the
7223 // table wrapper frame.
7224 if (captionList.NotEmpty()) {
7225 NS_ASSERTION(LayoutFrameType::Table == frameType ||
7226 LayoutFrameType::TableWrapper == frameType,
7227 "parent for caption is not table?");
7228 // We need to determine where to put the caption items; start with the
7229 // the parent frame that has already been determined and get the insertion
7230 // prevsibling of the first caption item.
7231 bool captionIsAppend;
7232 nsIFrame* captionPrevSibling = nullptr;
7234 // aIsRangeInsertSafe is ignored on purpose because it is irrelevant here.
7235 bool ignored;
7236 InsertionPoint captionInsertion = insertion;
7237 if (isSingleInsert) {
7238 captionPrevSibling = GetInsertionPrevSibling(
7239 &captionInsertion, aStartChild, &captionIsAppend, &ignored);
7240 } else {
7241 nsIContent* firstCaption = captionList.FirstChild()->GetContent();
7242 // It is very important here that we skip the children in
7243 // [aStartChild,aEndChild) when looking for a
7244 // prevsibling.
7245 captionPrevSibling = GetInsertionPrevSibling(
7246 &captionInsertion, firstCaption, &captionIsAppend, &ignored,
7247 aStartChild, aEndChild);
7250 nsContainerFrame* outerTable =
7251 captionInsertion.mParentFrame->IsTableFrame()
7252 ? captionInsertion.mParentFrame->GetParent()
7253 : captionInsertion.mParentFrame;
7255 // If the parent is not a table wrapper frame we will try to add frames
7256 // to a named child list that the parent does not honor and the frames
7257 // will get lost.
7258 MOZ_ASSERT(outerTable->IsTableWrapperFrame(),
7259 "Pseudo frame construction failure; "
7260 "a caption can be only a child of a table wrapper frame");
7262 // If the parent of our current prevSibling is different from the frame
7263 // we'll actually use as the parent, then the calculated insertion
7264 // point is now invalid (bug 341382).
7265 if (captionPrevSibling && captionPrevSibling->GetParent() != outerTable) {
7266 captionPrevSibling = nullptr;
7269 captionList.ApplySetParent(outerTable);
7270 if (captionIsAppend) {
7271 AppendFrames(outerTable, FrameChildListID::Caption,
7272 std::move(captionList));
7273 } else {
7274 InsertFrames(outerTable, FrameChildListID::Caption, captionPrevSibling,
7275 std::move(captionList));
7279 LAYOUT_PHASE_TEMP_EXIT();
7280 if (MaybeRecreateForColumnSpan(state, insertion.mParentFrame, frameList,
7281 prevSibling)) {
7282 LAYOUT_PHASE_TEMP_REENTER();
7283 return;
7285 LAYOUT_PHASE_TEMP_REENTER();
7287 if (frameList.NotEmpty()) {
7288 // Notify the parent frame
7289 if (isAppend) {
7290 AppendFramesToParent(state, insertion.mParentFrame, frameList,
7291 prevSibling);
7292 } else {
7293 InsertFrames(insertion.mParentFrame, FrameChildListID::Principal,
7294 prevSibling, std::move(frameList));
7298 if (haveFirstLetterStyle) {
7299 // Recover the letter frames for the containing block when
7300 // it has first-letter style.
7301 RecoverLetterFrames(state.mFloatedList.mContainingBlock);
7304 #ifdef DEBUG
7305 if (gReallyNoisyContentUpdates && insertion.mParentFrame) {
7306 printf(
7307 "nsCSSFrameConstructor::ContentRangeInserted: resulting frame "
7308 "model:\n");
7309 insertion.mParentFrame->List(stdout);
7311 #endif
7313 #ifdef ACCESSIBILITY
7314 if (nsAccessibilityService* accService = GetAccService()) {
7315 accService->ContentRangeInserted(mPresShell, aStartChild, aEndChild);
7317 #endif
7320 bool nsCSSFrameConstructor::ContentRemoved(nsIContent* aChild,
7321 nsIContent* aOldNextSibling,
7322 RemoveFlags aFlags) {
7323 MOZ_ASSERT(aChild);
7324 MOZ_ASSERT(!aChild->IsRootOfNativeAnonymousSubtree() || !aOldNextSibling,
7325 "Anonymous roots don't have siblings");
7326 AUTO_PROFILER_LABEL("nsCSSFrameConstructor::ContentRemoved",
7327 LAYOUT_FrameConstruction);
7328 AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
7329 nsPresContext* presContext = mPresShell->GetPresContext();
7330 MOZ_ASSERT(presContext, "Our presShell should have a valid presContext");
7332 // We want to detect when the viewport override element stored in the
7333 // prescontext is in the subtree being removed. Except in fullscreen cases
7334 // (which are handled in Element::UnbindFromTree and do not get stored on the
7335 // prescontext), the override element is always either the root element or a
7336 // <body> child of the root element. So we can only be removing the stored
7337 // override element if the thing being removed is either the override element
7338 // itself or the root element (which can be a parent of the override element).
7339 if (aChild == presContext->GetViewportScrollStylesOverrideElement() ||
7340 (aChild->IsElement() && !aChild->GetParent())) {
7341 // We might be removing the element that we propagated viewport scrollbar
7342 // styles from. Recompute those. (This clause covers two of the three
7343 // possible scrollbar-propagation sources: the <body> [as aChild or a
7344 // descendant] and the root node. The other possible scrollbar-propagation
7345 // source is a fullscreen element, and we have code elsewhere to update
7346 // scrollbars after fullscreen elements are removed -- specifically, it's
7347 // part of the fullscreen cleanup code called by Element::UnbindFromTree.
7348 // We don't handle the fullscreen case here, because it doesn't change the
7349 // scrollbar styles override element stored on the prescontext.)
7350 Element* newOverrideElement =
7351 presContext->UpdateViewportScrollStylesOverride();
7353 // If aChild is the root, then we don't need to do any reframing of
7354 // newOverrideElement, because we're about to tear down the whole frame tree
7355 // anyway. And we need to make sure we don't do any such reframing, because
7356 // reframing the <body> can trigger a reframe of the <html> and then reenter
7357 // here.
7359 // But if aChild is not the root, and if newOverrideElement is not
7360 // the root and isn't aChild (which it could be if all we're doing
7361 // here is reframing the current override element), it needs
7362 // reframing. In particular, it used to have a scrollframe
7363 // (because its overflow was not "visible"), but now it will
7364 // propagate its overflow to the viewport, so it should not need a
7365 // scrollframe anymore.
7366 if (aChild->GetParent() && newOverrideElement &&
7367 newOverrideElement->GetParent() && newOverrideElement != aChild) {
7368 LAYOUT_PHASE_TEMP_EXIT();
7369 RecreateFramesForContent(newOverrideElement, InsertionKind::Async);
7370 LAYOUT_PHASE_TEMP_REENTER();
7374 #ifdef DEBUG
7375 if (gNoisyContentUpdates) {
7376 printf(
7377 "nsCSSFrameConstructor::ContentRemoved container=%p child=%p "
7378 "old-next-sibling=%p\n",
7379 aChild->GetParent(), aChild, aOldNextSibling);
7380 if (gReallyNoisyContentUpdates) {
7381 aChild->GetParent()->List(stdout, 0);
7384 #endif
7386 nsIFrame* childFrame = aChild->GetPrimaryFrame();
7387 if (!childFrame || childFrame->GetContent() != aChild) {
7388 // XXXbz the GetContent() != aChild check is needed due to bug 135040.
7389 // Remove it once that's fixed.
7390 childFrame = nullptr;
7393 // If we're removing the root, then make sure to remove things starting at
7394 // the viewport's child instead of the primary frame (which might even be
7395 // null if the root was display:none, even though the frames above it got
7396 // created). Detecting removal of a root is a little exciting; in particular,
7397 // having no parent is necessary but NOT sufficient.
7399 // Due to how we process reframes, the content node might not even be in our
7400 // document by now. So explicitly check whether the viewport's first kid's
7401 // content node is aChild.
7403 // FIXME(emilio): I think the "might not be in our document" bit is impossible
7404 // now.
7405 bool isRoot = false;
7406 if (!aChild->GetParent()) {
7407 if (nsIFrame* viewport = GetRootFrame()) {
7408 nsIFrame* firstChild = viewport->PrincipalChildList().FirstChild();
7409 if (firstChild && firstChild->GetContent() == aChild) {
7410 isRoot = true;
7411 childFrame = firstChild;
7412 NS_ASSERTION(!childFrame->GetNextSibling(), "How did that happen?");
7417 // We need to be conservative about when to determine whether something has
7418 // display: contents or not because at this point our actual display may be
7419 // different.
7421 // Consider the case of:
7423 // <div id="A" style="display: contents"><div id="B"></div></div>
7425 // If we reconstruct A because its display changed to "none", we still need to
7426 // cleanup the frame on B, but A's display is now "none", so we can't poke at
7427 // the style of it.
7429 // FIXME(emilio, bug 1450366): We can make this faster without adding much
7430 // complexity for the display: none -> other case, which right now
7431 // unnecessarily walks the content tree down.
7432 auto CouldHaveBeenDisplayContents = [aFlags](nsIContent* aContent) -> bool {
7433 return aFlags == REMOVE_FOR_RECONSTRUCTION || IsDisplayContents(aContent);
7436 if (!childFrame && CouldHaveBeenDisplayContents(aChild)) {
7437 // NOTE(emilio): We may iterate through ::before and ::after here and they
7438 // may be gone after the respective ContentRemoved call. Right now
7439 // StyleChildrenIterator handles that properly, so it's not an issue.
7440 StyleChildrenIterator iter(aChild);
7441 for (nsIContent* c = iter.GetNextChild(); c; c = iter.GetNextChild()) {
7442 if (c->GetPrimaryFrame() || CouldHaveBeenDisplayContents(c)) {
7443 LAYOUT_PHASE_TEMP_EXIT();
7444 bool didReconstruct = ContentRemoved(c, nullptr, aFlags);
7445 LAYOUT_PHASE_TEMP_REENTER();
7446 if (didReconstruct) {
7447 return true;
7451 return false;
7454 if (childFrame) {
7455 if (aFlags == REMOVE_FOR_RECONSTRUCTION) {
7456 // Before removing the frames associated with the content object,
7457 // ask them to save their state onto our state object.
7458 CaptureStateForFramesOf(aChild, mFrameTreeState);
7461 InvalidateCanvasIfNeeded(mPresShell, aChild);
7463 // See whether we need to remove more than just childFrame
7464 LAYOUT_PHASE_TEMP_EXIT();
7465 if (MaybeRecreateContainerForFrameRemoval(childFrame)) {
7466 LAYOUT_PHASE_TEMP_REENTER();
7467 return true;
7469 LAYOUT_PHASE_TEMP_REENTER();
7471 // Get the childFrame's parent frame
7472 nsIFrame* parentFrame = childFrame->GetParent();
7473 LayoutFrameType parentType = parentFrame->Type();
7475 if (parentType == LayoutFrameType::FrameSet &&
7476 IsSpecialFramesetChild(aChild)) {
7477 // Just reframe the parent, since framesets are weird like that.
7478 LAYOUT_PHASE_TEMP_EXIT();
7479 RecreateFramesForContent(parentFrame->GetContent(), InsertionKind::Async);
7480 LAYOUT_PHASE_TEMP_REENTER();
7481 return true;
7484 // If we're a child of MathML, then we should reframe the MathML content.
7485 // If we're non-MathML, then we would be wrapped in a block so we need to
7486 // check our grandparent in that case.
7487 nsIFrame* possibleMathMLAncestor = parentType == LayoutFrameType::Block
7488 ? parentFrame->GetParent()
7489 : parentFrame;
7490 if (possibleMathMLAncestor->IsFrameOfType(nsIFrame::eMathML)) {
7491 LAYOUT_PHASE_TEMP_EXIT();
7492 RecreateFramesForContent(parentFrame->GetContent(), InsertionKind::Async);
7493 LAYOUT_PHASE_TEMP_REENTER();
7494 return true;
7497 #ifdef ACCESSIBILITY
7498 if (aFlags != REMOVE_FOR_RECONSTRUCTION) {
7499 if (nsAccessibilityService* accService = GetAccService()) {
7500 accService->ContentRemoved(mPresShell, aChild);
7503 #endif
7505 // Examine the containing-block for the removed content and see if
7506 // :first-letter style applies.
7507 nsIFrame* inflowChild = childFrame;
7508 if (childFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
7509 inflowChild = childFrame->GetPlaceholderFrame();
7510 NS_ASSERTION(inflowChild, "No placeholder for out-of-flow?");
7512 nsContainerFrame* containingBlock =
7513 GetFloatContainingBlock(inflowChild->GetParent());
7514 bool haveFLS = containingBlock && HasFirstLetterStyle(containingBlock);
7515 if (haveFLS) {
7516 // Trap out to special routine that handles adjusting a blocks
7517 // frame tree when first-letter style is present.
7518 #ifdef NOISY_FIRST_LETTER
7519 printf("ContentRemoved: containingBlock=");
7520 containingBlock->ListTag(stdout);
7521 printf(" parentFrame=");
7522 parentFrame->ListTag(stdout);
7523 printf(" childFrame=");
7524 childFrame->ListTag(stdout);
7525 printf("\n");
7526 #endif
7528 // First update the containing blocks structure by removing the
7529 // existing letter frames. This makes the subsequent logic
7530 // simpler.
7531 RemoveLetterFrames(mPresShell, containingBlock);
7533 // Recover childFrame and parentFrame
7534 childFrame = aChild->GetPrimaryFrame();
7535 if (!childFrame || childFrame->GetContent() != aChild) {
7536 // XXXbz the GetContent() != aChild check is needed due to bug 135040.
7537 // Remove it once that's fixed.
7538 return false;
7540 parentFrame = childFrame->GetParent();
7541 parentType = parentFrame->Type();
7543 #ifdef NOISY_FIRST_LETTER
7544 printf(" ==> revised parentFrame=");
7545 parentFrame->ListTag(stdout);
7546 printf(" childFrame=");
7547 childFrame->ListTag(stdout);
7548 printf("\n");
7549 #endif
7552 #ifdef DEBUG
7553 if (gReallyNoisyContentUpdates) {
7554 printf("nsCSSFrameConstructor::ContentRemoved: childFrame=");
7555 childFrame->ListTag(stdout);
7556 putchar('\n');
7557 parentFrame->List(stdout);
7559 #endif
7561 // Notify the parent frame that it should delete the frame
7562 if (childFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
7563 childFrame = childFrame->GetPlaceholderFrame();
7564 NS_ASSERTION(childFrame, "Missing placeholder frame for out of flow.");
7565 parentFrame = childFrame->GetParent();
7568 RemoveFrame(nsLayoutUtils::GetChildListNameFor(childFrame), childFrame);
7570 // NOTE(emilio): aChild could be dead here already if it is a ::before or
7571 // ::after pseudo-element (since in that case it was owned by childFrame,
7572 // which we just destroyed).
7574 if (isRoot) {
7575 mRootElementFrame = nullptr;
7576 mRootElementStyleFrame = nullptr;
7577 mDocElementContainingBlock = nullptr;
7578 mPageSequenceFrame = nullptr;
7581 if (haveFLS && mRootElementFrame) {
7582 RecoverLetterFrames(containingBlock);
7585 // If we're just reconstructing frames for the element, then the
7586 // following ContentInserted notification on the element will
7587 // take care of fixing up any adjacent text nodes. We don't need
7588 // to do this if the table parent type of our parent type is not
7589 // eTypeBlock, though, because in that case the whitespace isn't
7590 // being suppressed due to us anyway.
7591 if (aOldNextSibling && aFlags == REMOVE_CONTENT &&
7592 GetParentType(parentType) == eTypeBlock) {
7593 MOZ_ASSERT(aChild->GetParentNode(),
7594 "How did we have a sibling without a parent?");
7595 // Adjacent whitespace-only text nodes might have been suppressed if
7596 // this node does not have inline ends. Create frames for them now
7597 // if necessary.
7598 // Reframe any text node just before the node being removed, if there is
7599 // one, and if it's not the last child or the first child. If a whitespace
7600 // textframe was being suppressed and it's now the last child or first
7601 // child then it can stay suppressed since the parent must be a block
7602 // and hence it's adjacent to a block end.
7603 // If aOldNextSibling is null, then the text node before the node being
7604 // removed is the last node, and we don't need to worry about it.
7606 // FIXME(emilio): This should probably use the lazy frame construction
7607 // bits if possible instead of reframing it in place.
7608 nsIContent* prevSibling = aOldNextSibling->GetPreviousSibling();
7609 if (prevSibling && prevSibling->GetPreviousSibling()) {
7610 LAYOUT_PHASE_TEMP_EXIT();
7611 ReframeTextIfNeeded(prevSibling);
7612 LAYOUT_PHASE_TEMP_REENTER();
7614 // Reframe any text node just after the node being removed, if there is
7615 // one, and if it's not the last child or the first child.
7616 if (aOldNextSibling->GetNextSibling() &&
7617 aOldNextSibling->GetPreviousSibling()) {
7618 LAYOUT_PHASE_TEMP_EXIT();
7619 ReframeTextIfNeeded(aOldNextSibling);
7620 LAYOUT_PHASE_TEMP_REENTER();
7624 #ifdef DEBUG
7625 if (gReallyNoisyContentUpdates && parentFrame) {
7626 printf("nsCSSFrameConstructor::ContentRemoved: resulting frame model:\n");
7627 parentFrame->List(stdout);
7629 #endif
7632 return false;
7636 * This method invalidates the canvas when frames are removed or added for a
7637 * node that might have its background propagated to the canvas, i.e., a
7638 * document root node or an HTML BODY which is a child of the root node.
7640 * @param aFrame a frame for a content node about to be removed or a frame that
7641 * was just created for a content node that was inserted.
7643 static void InvalidateCanvasIfNeeded(PresShell* aPresShell, nsIContent* aNode) {
7644 MOZ_ASSERT(aPresShell->GetRootFrame(), "What happened here?");
7645 MOZ_ASSERT(aPresShell->GetPresContext(), "Say what?");
7647 // Note that both in ContentRemoved and ContentInserted the content node
7648 // will still have the right parent pointer, so looking at that is ok.
7650 nsIContent* parent = aNode->GetParent();
7651 if (parent) {
7652 // Has a parent; might not be what we want
7653 nsIContent* grandParent = parent->GetParent();
7654 if (grandParent) {
7655 // Has a grandparent, so not what we want
7656 return;
7659 // Check whether it's an HTML body
7660 if (!aNode->IsHTMLElement(nsGkAtoms::body)) {
7661 return;
7665 // At this point the node has no parent or it's an HTML <body> child of the
7666 // root. We might not need to invalidate in this case (eg we might be in
7667 // XHTML or something), but chances are we want to. Play it safe.
7668 // Invalidate the viewport.
7670 nsIFrame* rootFrame = aPresShell->GetRootFrame();
7671 rootFrame->InvalidateFrameSubtree();
7674 bool nsCSSFrameConstructor::EnsureFrameForTextNodeIsCreatedAfterFlush(
7675 CharacterData* aContent) {
7676 if (!aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE)) {
7677 return false;
7680 if (mAlwaysCreateFramesForIgnorableWhitespace) {
7681 return false;
7684 // Text frame may have been suppressed. Disable suppression and signal that a
7685 // flush should be performed. We do this on a document-wide basis so that
7686 // pages that repeatedly query metrics for collapsed-whitespace text nodes
7687 // don't trigger pathological behavior.
7688 mAlwaysCreateFramesForIgnorableWhitespace = true;
7689 Element* root = mDocument->GetRootElement();
7690 if (!root) {
7691 return false;
7694 RestyleManager()->PostRestyleEvent(root, RestyleHint{0},
7695 nsChangeHint_ReconstructFrame);
7696 return true;
7699 void nsCSSFrameConstructor::CharacterDataChanged(
7700 nsIContent* aContent, const CharacterDataChangeInfo& aInfo) {
7701 AUTO_PROFILER_LABEL("nsCSSFrameConstructor::CharacterDataChanged",
7702 LAYOUT_FrameConstruction);
7703 AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
7705 if ((aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE) &&
7706 !aContent->TextIsOnlyWhitespace()) ||
7707 (aContent->HasFlag(NS_REFRAME_IF_WHITESPACE) &&
7708 aContent->TextIsOnlyWhitespace())) {
7709 #ifdef DEBUG
7710 nsIFrame* frame = aContent->GetPrimaryFrame();
7711 NS_ASSERTION(!frame || !frame->IsGeneratedContentFrame(),
7712 "Bit should never be set on generated content");
7713 #endif
7714 LAYOUT_PHASE_TEMP_EXIT();
7715 RecreateFramesForContent(aContent, InsertionKind::Async);
7716 LAYOUT_PHASE_TEMP_REENTER();
7717 return;
7720 // It's possible the frame whose content changed isn't inserted into the
7721 // frame hierarchy yet, or that there is no frame that maps the content
7722 if (nsIFrame* frame = aContent->GetPrimaryFrame()) {
7723 #if 0
7724 NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
7725 ("nsCSSFrameConstructor::CharacterDataChanged: content=%p[%s] subcontent=%p frame=%p",
7726 aContent, ContentTag(aContent, 0),
7727 aSubContent, frame));
7728 #endif
7730 // Special check for text content that is a child of a letter frame. If
7731 // this happens, we should remove the letter frame, do whatever we're
7732 // planning to do with this notification, then put the letter frame back.
7733 // Note that this is basically what RecreateFramesForContent ends up doing;
7734 // the reason we dont' want to call that here is that our text content
7735 // could be native anonymous, in which case RecreateFramesForContent would
7736 // completely barf on it. And recreating the non-anonymous ancestor would
7737 // just lead us to come back into this notification (e.g. if quotes or
7738 // counters are involved), leading to a loop.
7739 nsContainerFrame* block = GetFloatContainingBlock(frame);
7740 bool haveFirstLetterStyle = false;
7741 if (block) {
7742 // See if the block has first-letter style applied to it.
7743 haveFirstLetterStyle = HasFirstLetterStyle(block);
7744 if (haveFirstLetterStyle) {
7745 RemoveLetterFrames(mPresShell, block);
7746 // Reget |frame|, since we might have killed it.
7747 // Do we really need to call CharacterDataChanged in this case, though?
7748 frame = aContent->GetPrimaryFrame();
7749 NS_ASSERTION(frame, "Should have frame here!");
7753 // Notify the first frame that maps the content. It will generate a reflow
7754 // command
7755 frame->CharacterDataChanged(aInfo);
7757 if (haveFirstLetterStyle) {
7758 RecoverLetterFrames(block);
7763 void nsCSSFrameConstructor::RecalcQuotesAndCounters() {
7764 nsAutoScriptBlocker scriptBlocker;
7766 if (mQuotesDirty) {
7767 mQuotesDirty = false;
7768 mContainStyleScopeManager.RecalcAllQuotes();
7771 if (mCountersDirty) {
7772 mCountersDirty = false;
7773 mContainStyleScopeManager.RecalcAllCounters();
7776 NS_ASSERTION(!mQuotesDirty, "Quotes updates will be lost");
7777 NS_ASSERTION(!mCountersDirty, "Counter updates will be lost");
7780 void nsCSSFrameConstructor::NotifyCounterStylesAreDirty() {
7781 mContainStyleScopeManager.SetAllCountersDirty();
7782 CountersDirty();
7785 void nsCSSFrameConstructor::WillDestroyFrameTree() {
7786 #if defined(DEBUG_dbaron_off)
7787 mContainStyleScopeManager.DumpCounters();
7788 #endif
7790 // Prevent frame tree destruction from being O(N^2)
7791 mContainStyleScopeManager.Clear();
7792 nsFrameManager::Destroy();
7795 // STATIC
7797 // XXXbz I'd really like this method to go away. Once we have inline-block and
7798 // I can just use that for sized broken images, that can happen, maybe.
7800 // NOTE(emilio): This needs to match MozAltContent handling.
7801 void nsCSSFrameConstructor::GetAlternateTextFor(const Element& aElement,
7802 nsAString& aAltText) {
7803 // The "alt" attribute specifies alternate text that is rendered
7804 // when the image can not be displayed.
7805 if (aElement.GetAttr(nsGkAtoms::alt, aAltText)) {
7806 return;
7809 if (aElement.IsHTMLElement(nsGkAtoms::input)) {
7810 // If there's no "alt" attribute, and aElement is an input element, then use
7811 // the value of the "value" attribute.
7812 if (aElement.GetAttr(nsGkAtoms::value, aAltText)) {
7813 return;
7816 // If there's no "value" attribute either, then use the localized string for
7817 // "Submit" as the alternate text.
7818 nsContentUtils::GetMaybeLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
7819 "Submit", aElement.OwnerDoc(),
7820 aAltText);
7824 nsIFrame* nsCSSFrameConstructor::CreateContinuingOuterTableFrame(
7825 nsIFrame* aFrame, nsContainerFrame* aParentFrame, nsIContent* aContent,
7826 ComputedStyle* aComputedStyle) {
7827 nsTableWrapperFrame* newFrame =
7828 NS_NewTableWrapperFrame(mPresShell, aComputedStyle);
7830 newFrame->Init(aContent, aParentFrame, aFrame);
7832 // Create a continuing inner table frame, and if there's a caption then
7833 // replicate the caption
7834 nsFrameList newChildFrames;
7836 nsIFrame* childFrame = aFrame->PrincipalChildList().FirstChild();
7837 if (childFrame) {
7838 nsIFrame* continuingTableFrame =
7839 CreateContinuingFrame(childFrame, newFrame);
7840 newChildFrames.AppendFrame(nullptr, continuingTableFrame);
7842 NS_ASSERTION(!childFrame->GetNextSibling(),
7843 "there can be only one inner table frame");
7846 // Set the table wrapper's initial child list
7847 newFrame->SetInitialChildList(FrameChildListID::Principal,
7848 std::move(newChildFrames));
7850 return newFrame;
7853 nsIFrame* nsCSSFrameConstructor::CreateContinuingTableFrame(
7854 nsIFrame* aFrame, nsContainerFrame* aParentFrame, nsIContent* aContent,
7855 ComputedStyle* aComputedStyle) {
7856 nsTableFrame* newFrame = NS_NewTableFrame(mPresShell, aComputedStyle);
7858 newFrame->Init(aContent, aParentFrame, aFrame);
7860 // Replicate any header/footer frames
7861 nsFrameList childFrames;
7862 for (nsIFrame* childFrame : aFrame->PrincipalChildList()) {
7863 // See if it's a header/footer, possibly wrapped in a scroll frame.
7864 nsTableRowGroupFrame* rowGroupFrame =
7865 static_cast<nsTableRowGroupFrame*>(childFrame);
7866 // If the row group was continued, then don't replicate it.
7867 nsIFrame* rgNextInFlow = rowGroupFrame->GetNextInFlow();
7868 if (rgNextInFlow) {
7869 rowGroupFrame->SetRepeatable(false);
7870 } else if (rowGroupFrame->IsRepeatable()) {
7871 // Replicate the header/footer frame.
7872 nsTableRowGroupFrame* headerFooterFrame;
7873 nsFrameList childList;
7875 nsFrameConstructorState state(
7876 mPresShell, GetAbsoluteContainingBlock(newFrame, FIXED_POS),
7877 GetAbsoluteContainingBlock(newFrame, ABS_POS), nullptr);
7878 state.mCreatingExtraFrames = true;
7880 ComputedStyle* const headerFooterComputedStyle = rowGroupFrame->Style();
7881 headerFooterFrame = static_cast<nsTableRowGroupFrame*>(
7882 NS_NewTableRowGroupFrame(mPresShell, headerFooterComputedStyle));
7884 nsIContent* headerFooter = rowGroupFrame->GetContent();
7885 headerFooterFrame->Init(headerFooter, newFrame, nullptr);
7887 nsFrameConstructorSaveState absoluteSaveState;
7888 MakeTablePartAbsoluteContainingBlock(state, absoluteSaveState,
7889 headerFooterFrame);
7891 nsFrameConstructorSaveState floatSaveState;
7892 state.MaybePushFloatContainingBlock(headerFooterFrame, floatSaveState);
7894 ProcessChildren(state, headerFooter, rowGroupFrame->Style(),
7895 headerFooterFrame, true, childList, false, nullptr);
7896 NS_ASSERTION(state.mFloatedList.IsEmpty(), "unexpected floated element");
7897 headerFooterFrame->SetInitialChildList(FrameChildListID::Principal,
7898 std::move(childList));
7899 headerFooterFrame->SetRepeatable(true);
7901 // Table specific initialization
7902 headerFooterFrame->InitRepeatedFrame(rowGroupFrame);
7904 // XXX Deal with absolute and fixed frames...
7905 childFrames.AppendFrame(nullptr, headerFooterFrame);
7909 // Set the table frame's initial child list
7910 newFrame->SetInitialChildList(FrameChildListID::Principal,
7911 std::move(childFrames));
7913 return newFrame;
7916 nsIFrame* nsCSSFrameConstructor::CreateContinuingFrame(
7917 nsIFrame* aFrame, nsContainerFrame* aParentFrame, bool aIsFluid) {
7918 ComputedStyle* computedStyle = aFrame->Style();
7919 nsIFrame* newFrame = nullptr;
7920 nsIFrame* nextContinuation = aFrame->GetNextContinuation();
7921 nsIFrame* nextInFlow = aFrame->GetNextInFlow();
7923 // Use the frame type to determine what type of frame to create
7924 LayoutFrameType frameType = aFrame->Type();
7925 nsIContent* content = aFrame->GetContent();
7927 if (LayoutFrameType::Text == frameType) {
7928 newFrame = NS_NewContinuingTextFrame(mPresShell, computedStyle);
7929 newFrame->Init(content, aParentFrame, aFrame);
7930 } else if (LayoutFrameType::Inline == frameType) {
7931 newFrame = NS_NewInlineFrame(mPresShell, computedStyle);
7932 newFrame->Init(content, aParentFrame, aFrame);
7933 } else if (LayoutFrameType::Block == frameType) {
7934 MOZ_ASSERT(!aFrame->IsTableCaption(),
7935 "no support for fragmenting table captions yet");
7936 newFrame = NS_NewBlockFrame(mPresShell, computedStyle);
7937 newFrame->Init(content, aParentFrame, aFrame);
7938 } else if (LayoutFrameType::ColumnSetWrapper == frameType) {
7939 newFrame =
7940 NS_NewColumnSetWrapperFrame(mPresShell, computedStyle, nsFrameState(0));
7941 newFrame->Init(content, aParentFrame, aFrame);
7942 } else if (LayoutFrameType::ColumnSet == frameType) {
7943 MOZ_ASSERT(!aFrame->IsTableCaption(),
7944 "no support for fragmenting table captions yet");
7945 newFrame = NS_NewColumnSetFrame(mPresShell, computedStyle, nsFrameState(0));
7946 newFrame->Init(content, aParentFrame, aFrame);
7947 } else if (LayoutFrameType::PrintedSheet == frameType) {
7948 newFrame = ConstructPrintedSheetFrame(mPresShell, aParentFrame, aFrame);
7949 } else if (LayoutFrameType::Page == frameType) {
7950 nsContainerFrame* canvasFrame; // (unused outparam for ConstructPageFrame)
7951 newFrame =
7952 ConstructPageFrame(mPresShell, aParentFrame, aFrame, canvasFrame);
7953 } else if (LayoutFrameType::TableWrapper == frameType) {
7954 newFrame = CreateContinuingOuterTableFrame(aFrame, aParentFrame, content,
7955 computedStyle);
7956 } else if (LayoutFrameType::Table == frameType) {
7957 newFrame = CreateContinuingTableFrame(aFrame, aParentFrame, content,
7958 computedStyle);
7959 } else if (LayoutFrameType::TableRowGroup == frameType) {
7960 newFrame = NS_NewTableRowGroupFrame(mPresShell, computedStyle);
7961 newFrame->Init(content, aParentFrame, aFrame);
7962 } else if (LayoutFrameType::TableRow == frameType) {
7963 nsTableRowFrame* rowFrame = NS_NewTableRowFrame(mPresShell, computedStyle);
7965 rowFrame->Init(content, aParentFrame, aFrame);
7967 // Create a continuing frame for each table cell frame
7968 nsFrameList newChildList;
7969 nsIFrame* cellFrame = aFrame->PrincipalChildList().FirstChild();
7970 while (cellFrame) {
7971 // See if it's a table cell frame
7972 if (cellFrame->IsTableCellFrame()) {
7973 nsIFrame* continuingCellFrame =
7974 CreateContinuingFrame(cellFrame, rowFrame);
7975 newChildList.AppendFrame(nullptr, continuingCellFrame);
7977 cellFrame = cellFrame->GetNextSibling();
7980 rowFrame->SetInitialChildList(FrameChildListID::Principal,
7981 std::move(newChildList));
7982 newFrame = rowFrame;
7984 } else if (LayoutFrameType::TableCell == frameType) {
7985 // Warning: If you change this and add a wrapper frame around table cell
7986 // frames, make sure Bug 368554 doesn't regress!
7987 // See IsInAutoWidthTableCellForQuirk() in nsImageFrame.cpp.
7988 nsTableFrame* tableFrame =
7989 static_cast<nsTableRowFrame*>(aParentFrame)->GetTableFrame();
7990 nsTableCellFrame* cellFrame =
7991 NS_NewTableCellFrame(mPresShell, computedStyle, tableFrame);
7993 cellFrame->Init(content, aParentFrame, aFrame);
7995 // Create a continuing area frame
7996 nsIFrame* blockFrame = aFrame->PrincipalChildList().FirstChild();
7997 nsIFrame* continuingBlockFrame =
7998 CreateContinuingFrame(blockFrame, cellFrame);
8000 SetInitialSingleChild(cellFrame, continuingBlockFrame);
8001 newFrame = cellFrame;
8002 } else if (LayoutFrameType::Line == frameType) {
8003 newFrame = NS_NewFirstLineFrame(mPresShell, computedStyle);
8004 newFrame->Init(content, aParentFrame, aFrame);
8005 } else if (LayoutFrameType::Letter == frameType) {
8006 newFrame = NS_NewFirstLetterFrame(mPresShell, computedStyle);
8007 newFrame->Init(content, aParentFrame, aFrame);
8008 } else if (LayoutFrameType::Image == frameType) {
8009 auto* imageFrame = static_cast<nsImageFrame*>(aFrame);
8010 newFrame = imageFrame->CreateContinuingFrame(mPresShell, computedStyle);
8011 newFrame->Init(content, aParentFrame, aFrame);
8012 } else if (LayoutFrameType::ImageControl == frameType) {
8013 newFrame = NS_NewImageControlFrame(mPresShell, computedStyle);
8014 newFrame->Init(content, aParentFrame, aFrame);
8015 } else if (LayoutFrameType::FieldSet == frameType) {
8016 newFrame = NS_NewFieldSetFrame(mPresShell, computedStyle);
8017 newFrame->Init(content, aParentFrame, aFrame);
8018 } else if (LayoutFrameType::FlexContainer == frameType) {
8019 newFrame = NS_NewFlexContainerFrame(mPresShell, computedStyle);
8020 newFrame->Init(content, aParentFrame, aFrame);
8021 } else if (LayoutFrameType::GridContainer == frameType) {
8022 newFrame = NS_NewGridContainerFrame(mPresShell, computedStyle);
8023 newFrame->Init(content, aParentFrame, aFrame);
8024 } else if (LayoutFrameType::Ruby == frameType) {
8025 newFrame = NS_NewRubyFrame(mPresShell, computedStyle);
8026 newFrame->Init(content, aParentFrame, aFrame);
8027 } else if (LayoutFrameType::RubyBaseContainer == frameType) {
8028 newFrame = NS_NewRubyBaseContainerFrame(mPresShell, computedStyle);
8029 newFrame->Init(content, aParentFrame, aFrame);
8030 } else if (LayoutFrameType::RubyTextContainer == frameType) {
8031 newFrame = NS_NewRubyTextContainerFrame(mPresShell, computedStyle);
8032 newFrame->Init(content, aParentFrame, aFrame);
8033 } else {
8034 MOZ_CRASH("unexpected frame type");
8037 // Init() set newFrame to be a fluid continuation of aFrame.
8038 // If we want a non-fluid continuation, we need to call SetPrevContinuation()
8039 // to reset NS_FRAME_IS_FLUID_CONTINUATION.
8040 if (!aIsFluid) {
8041 newFrame->SetPrevContinuation(aFrame);
8044 // If a continuing frame needs to carry frame state bits from its previous
8045 // continuation or parent, set them in nsIFrame::Init(), or in any derived
8046 // frame class's Init() if the bits are belong to specific group.
8048 if (nextInFlow) {
8049 nextInFlow->SetPrevInFlow(newFrame);
8050 newFrame->SetNextInFlow(nextInFlow);
8051 } else if (nextContinuation) {
8052 nextContinuation->SetPrevContinuation(newFrame);
8053 newFrame->SetNextContinuation(nextContinuation);
8056 // aFrame cannot be a dynamic reflow root because it has a continuation now.
8057 aFrame->RemoveStateBits(NS_FRAME_DYNAMIC_REFLOW_ROOT);
8059 // XXXalaskanemily: This avoids linear-time FirstContinuation lookups during
8060 // paginated reflow, but there are a lot of smarter ways to manage this. We
8061 // might also want to share the struct (refcount or some guarantee the struct
8062 // will remain valid during reflow).
8063 if (nsIFrame::PageValues* pageValues =
8064 aFrame->GetProperty(nsIFrame::PageValuesProperty())) {
8065 // It is possible that both values of a PageValues struct can be
8066 // overwritten with null. If that's the case, then as a minor optimization
8067 // we don't need to create a copy of the struct since this property being
8068 // missing is equivalent to having null start/end values.
8069 if (pageValues->mStartPageValue || pageValues->mEndPageValue) {
8070 nsIFrame::PageValues* const newPageValues =
8071 new nsIFrame::PageValues(*pageValues);
8072 newFrame->SetProperty(nsIFrame::PageValuesProperty(), newPageValues);
8076 MOZ_ASSERT(!newFrame->GetNextSibling(), "unexpected sibling");
8077 return newFrame;
8080 nsresult nsCSSFrameConstructor::ReplicateFixedFrames(
8081 nsPageContentFrame* aParentFrame) {
8082 // Now deal with fixed-pos things.... They should appear on all pages,
8083 // so we want to move over the placeholders when processing the child
8084 // of the pageContentFrame.
8086 nsIFrame* prevPageContentFrame = aParentFrame->GetPrevInFlow();
8087 if (!prevPageContentFrame) {
8088 return NS_OK;
8090 nsContainerFrame* canvasFrame =
8091 do_QueryFrame(aParentFrame->PrincipalChildList().FirstChild());
8092 nsIFrame* prevCanvasFrame =
8093 prevPageContentFrame->PrincipalChildList().FirstChild();
8094 if (!canvasFrame || !prevCanvasFrame) {
8095 // document's root element frame missing
8096 return NS_ERROR_UNEXPECTED;
8099 nsFrameList fixedPlaceholders;
8100 nsIFrame* firstFixed =
8101 prevPageContentFrame->GetChildList(FrameChildListID::Fixed).FirstChild();
8102 if (!firstFixed) {
8103 return NS_OK;
8106 // Don't allow abs-pos descendants of the fixed content to escape the content.
8107 // This should not normally be possible (because fixed-pos elements should
8108 // be absolute containers) but fixed-pos tables currently aren't abs-pos
8109 // containers.
8110 nsFrameConstructorState state(mPresShell, aParentFrame, nullptr,
8111 mRootElementFrame);
8112 state.mCreatingExtraFrames = true;
8114 // We can't use an ancestor filter here, because we're not going to
8115 // be usefully recurring down the tree. This means that other
8116 // places in frame construction can't assume a filter is
8117 // initialized!
8119 // Iterate across fixed frames and replicate each whose placeholder is a
8120 // descendant of aFrame. (We don't want to explicitly copy placeholders that
8121 // are within fixed frames, because that would cause duplicates on the new
8122 // page - bug 389619)
8123 for (nsIFrame* fixed = firstFixed; fixed; fixed = fixed->GetNextSibling()) {
8124 nsIFrame* prevPlaceholder = fixed->GetPlaceholderFrame();
8125 if (prevPlaceholder && nsLayoutUtils::IsProperAncestorFrame(
8126 prevCanvasFrame, prevPlaceholder)) {
8127 // We want to use the same style as the primary style frame for
8128 // our content
8129 nsIContent* content = fixed->GetContent();
8130 ComputedStyle* computedStyle =
8131 nsLayoutUtils::GetStyleFrame(content)->Style();
8132 AutoFrameConstructionItemList items(this);
8133 AddFrameConstructionItemsInternal(state, content, canvasFrame, true,
8134 computedStyle,
8135 {ItemFlag::AllowPageBreak}, items);
8136 ConstructFramesFromItemList(state, items, canvasFrame,
8137 /* aParentIsWrapperAnonBox = */ false,
8138 fixedPlaceholders);
8142 // Add the placeholders to our primary child list.
8143 // XXXbz this is a little screwed up, since the fixed frames will have
8144 // broken auto-positioning. Oh, well.
8145 NS_ASSERTION(!canvasFrame->PrincipalChildList().FirstChild(),
8146 "leaking frames; doc root continuation must be empty");
8147 canvasFrame->SetInitialChildList(FrameChildListID::Principal,
8148 std::move(fixedPlaceholders));
8149 return NS_OK;
8152 nsCSSFrameConstructor::InsertionPoint nsCSSFrameConstructor::GetInsertionPoint(
8153 nsIContent* aChild) {
8154 MOZ_ASSERT(aChild);
8155 nsIContent* insertionElement = aChild->GetFlattenedTreeParent();
8156 if (!insertionElement) {
8157 // The element doesn't belong in the flattened tree, and thus we don't want
8158 // to render it.
8159 return {};
8162 return {GetContentInsertionFrameFor(insertionElement), insertionElement};
8165 // Capture state for the frame tree rooted at the frame associated with the
8166 // content object, aContent
8167 void nsCSSFrameConstructor::CaptureStateForFramesOf(
8168 nsIContent* aContent, nsILayoutHistoryState* aHistoryState) {
8169 if (!aHistoryState) {
8170 return;
8172 nsIFrame* frame = aContent->GetPrimaryFrame();
8173 if (frame == mRootElementFrame) {
8174 frame = mRootElementFrame
8175 ? GetAbsoluteContainingBlock(mRootElementFrame, FIXED_POS)
8176 : GetRootFrame();
8178 for (; frame;
8179 frame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(frame)) {
8180 CaptureFrameState(frame, aHistoryState);
8184 static bool IsWhitespaceFrame(nsIFrame* aFrame) {
8185 MOZ_ASSERT(aFrame, "invalid argument");
8186 return aFrame->IsTextFrame() && aFrame->GetContent()->TextIsOnlyWhitespace();
8189 static nsIFrame* FindFirstNonWhitespaceChild(nsIFrame* aParentFrame) {
8190 nsIFrame* f = aParentFrame->PrincipalChildList().FirstChild();
8191 while (f && IsWhitespaceFrame(f)) {
8192 f = f->GetNextSibling();
8194 return f;
8197 static nsIFrame* FindNextNonWhitespaceSibling(nsIFrame* aFrame) {
8198 nsIFrame* f = aFrame;
8199 do {
8200 f = f->GetNextSibling();
8201 } while (f && IsWhitespaceFrame(f));
8202 return f;
8205 static nsIFrame* FindPreviousNonWhitespaceSibling(nsIFrame* aFrame) {
8206 nsIFrame* f = aFrame;
8207 do {
8208 f = f->GetPrevSibling();
8209 } while (f && IsWhitespaceFrame(f));
8210 return f;
8213 bool nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(
8214 nsIFrame* aFrame) {
8215 #define TRACE(reason) \
8216 PROFILER_MARKER("MaybeRecreateContainerForFrameRemoval: " reason, LAYOUT, \
8217 {}, Tracing, "Layout")
8218 MOZ_ASSERT(aFrame, "Must have a frame");
8219 MOZ_ASSERT(aFrame->GetParent(), "Frame shouldn't be root");
8220 MOZ_ASSERT(aFrame == aFrame->FirstContinuation(),
8221 "aFrame not the result of GetPrimaryFrame()?");
8223 nsIFrame* inFlowFrame = aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)
8224 ? aFrame->GetPlaceholderFrame()
8225 : aFrame;
8226 MOZ_ASSERT(inFlowFrame, "How did that happen?");
8227 MOZ_ASSERT(inFlowFrame == inFlowFrame->FirstContinuation(),
8228 "placeholder for primary frame has previous continuations?");
8229 nsIFrame* parent = inFlowFrame->GetParent();
8231 if (inFlowFrame->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR)) {
8232 nsIFrame* grandparent = parent->GetParent();
8233 MOZ_ASSERT(grandparent);
8235 bool needsReframe =
8236 // 1. Removing a column-span may lead to an empty
8237 // ::-moz-column-span-wrapper.
8238 inFlowFrame->IsColumnSpan() ||
8239 // 2. Removing a frame which has any column-span siblings may also
8240 // lead to an empty ::-moz-column-span-wrapper subtree. The
8241 // column-span siblings were the frame's children, but later become
8242 // the frame's siblings after CreateColumnSpanSiblings().
8243 inFlowFrame->HasColumnSpanSiblings() ||
8244 // 3. Removing the only child of a ::-moz-column-content, whose
8245 // ColumnSet grandparent has a previous column-span sibling, requires
8246 // reframing since we might connect the ColumnSet's next column-span
8247 // sibling (if there's one). Note that this isn't actually needed if
8248 // the ColumnSet is at the end of ColumnSetWrapper since we create
8249 // empty ones at the end anyway, but we're not worried about
8250 // optimizing that case.
8251 (parent->Style()->GetPseudoType() == PseudoStyleType::columnContent &&
8252 // The only child in ::-moz-column-content (might be tall enough to
8253 // split across columns)
8254 !inFlowFrame->GetPrevSibling() && !inFlowFrame->GetNextSibling() &&
8255 // That ::-moz-column-content is the first column.
8256 !parent->GetPrevInFlow() &&
8257 // The ColumnSet grandparent has a previous sibling that is a
8258 // column-span.
8259 grandparent->GetPrevSibling());
8261 if (needsReframe) {
8262 nsContainerFrame* containingBlock =
8263 GetMultiColumnContainingBlockFor(inFlowFrame);
8265 #ifdef DEBUG
8266 if (IsFramePartOfIBSplit(inFlowFrame)) {
8267 nsIFrame* ibContainingBlock = GetIBContainingBlockFor(inFlowFrame);
8268 MOZ_ASSERT(containingBlock == ibContainingBlock ||
8269 nsLayoutUtils::IsProperAncestorFrame(containingBlock,
8270 ibContainingBlock),
8271 "Multi-column containing block should be equal to or be the "
8272 "ancestor of the IB containing block!");
8274 #endif
8276 TRACE("Multi-column");
8277 RecreateFramesForContent(containingBlock->GetContent(),
8278 InsertionKind::Async);
8279 return true;
8283 if (IsFramePartOfIBSplit(aFrame)) {
8284 // The removal functions can't handle removal of an {ib} split directly; we
8285 // need to rebuild the containing block.
8286 TRACE("IB split removal");
8287 ReframeContainingBlock(aFrame);
8288 return true;
8291 if (inFlowFrame->IsRenderedLegend()) {
8292 TRACE("Fieldset / Legend");
8293 RecreateFramesForContent(parent->GetContent(), InsertionKind::Async);
8294 return true;
8297 // Now check for possibly needing to reconstruct due to a pseudo parent
8298 // For the case of ruby pseudo parent, effectively, only pseudo rb/rt frame
8299 // need to be checked here, since all other types of parent will be catched
8300 // by "Check ruby containers" section below.
8301 if (IsTableOrRubyPseudo(parent)) {
8302 if (FindFirstNonWhitespaceChild(parent) == inFlowFrame ||
8303 !FindNextNonWhitespaceSibling(inFlowFrame->LastContinuation()) ||
8304 // If it is a whitespace, and is the only child of the parent, the
8305 // pseudo parent was created for the space, and should now be removed.
8306 (IsWhitespaceFrame(aFrame) &&
8307 parent->PrincipalChildList().OnlyChild()) ||
8308 // If we're a table-column-group, then the OnlyChild check above is
8309 // not going to catch cases when we're the first child.
8310 (inFlowFrame->IsTableColGroupFrame() &&
8311 parent->GetChildList(FrameChildListID::ColGroup).FirstChild() ==
8312 inFlowFrame) ||
8313 // Similar if we're a table-caption.
8314 (inFlowFrame->IsTableCaption() &&
8315 parent->GetChildList(FrameChildListID::Caption).FirstChild() ==
8316 inFlowFrame)) {
8317 TRACE("Table or ruby pseudo parent");
8318 RecreateFramesForContent(parent->GetContent(), InsertionKind::Async);
8319 return true;
8323 // Might need to reconstruct things if this frame's nextSibling is a table
8324 // or ruby pseudo, since removal of this frame might mean that this pseudo
8325 // needs to get merged with the frame's prevSibling if that's also a table
8326 // or ruby pseudo.
8327 nsIFrame* nextSibling =
8328 FindNextNonWhitespaceSibling(inFlowFrame->LastContinuation());
8329 NS_ASSERTION(!IsTableOrRubyPseudo(inFlowFrame), "Shouldn't happen here");
8330 // Effectively, for the ruby pseudo sibling case, only pseudo <ruby> frame
8331 // need to be checked here, since all other types of such frames will have
8332 // a ruby container parent, and be catched by "Check ruby containers" below.
8333 if (nextSibling && IsTableOrRubyPseudo(nextSibling)) {
8334 nsIFrame* prevSibling = FindPreviousNonWhitespaceSibling(inFlowFrame);
8335 if (prevSibling && IsTableOrRubyPseudo(prevSibling)) {
8336 TRACE("Table or ruby pseudo sibling");
8337 // Good enough to recreate frames for aFrame's parent's content; even if
8338 // aFrame's parent is a pseudo, that'll be the right content node.
8339 RecreateFramesForContent(parent->GetContent(), InsertionKind::Async);
8340 return true;
8344 // Check ruby containers
8345 LayoutFrameType parentType = parent->Type();
8346 if (parentType == LayoutFrameType::Ruby ||
8347 RubyUtils::IsRubyContainerBox(parentType)) {
8348 // In ruby containers, pseudo frames may be created from
8349 // whitespaces or even nothing. There are two cases we actually
8350 // need to handle here, but hard to check exactly:
8351 // 1. Status of spaces beside the frame may vary, and related
8352 // frames may be constructed or destroyed accordingly.
8353 // 2. The type of the first child of a ruby frame determines
8354 // whether a pseudo ruby base container should exist.
8355 TRACE("Ruby container");
8356 RecreateFramesForContent(parent->GetContent(), InsertionKind::Async);
8357 return true;
8360 // Might need to reconstruct things if the removed frame's nextSibling is an
8361 // anonymous flex item. The removed frame might've been what divided two
8362 // runs of inline content into two anonymous flex items, which would now
8363 // need to be merged.
8364 // NOTE: It's fine that we've advanced nextSibling past whitespace (up above);
8365 // we're only interested in anonymous flex items here, and those can never
8366 // be adjacent to whitespace, since they absorb contiguous runs of inline
8367 // non-replaced content (including whitespace).
8368 if (nextSibling && IsAnonymousItem(nextSibling)) {
8369 AssertAnonymousFlexOrGridItemParent(nextSibling, parent);
8370 TRACE("Anon flex or grid item next sibling");
8371 // Recreate frames for the flex container (the removed frame's parent)
8372 RecreateFramesForContent(parent->GetContent(), InsertionKind::Async);
8373 return true;
8376 // Might need to reconstruct things if the removed frame's nextSibling is
8377 // null and its parent is an anonymous flex item. (This might be the last
8378 // remaining child of that anonymous flex item, which can then go away.)
8379 if (!nextSibling && IsAnonymousItem(parent)) {
8380 AssertAnonymousFlexOrGridItemParent(parent, parent->GetParent());
8381 TRACE("Anon flex or grid item parent");
8382 // Recreate frames for the flex container (the removed frame's grandparent)
8383 RecreateFramesForContent(parent->GetParent()->GetContent(),
8384 InsertionKind::Async);
8385 return true;
8388 // Reconstruct if inflowFrame is parent's only child, and parent is, or has,
8389 // a non-fluid continuation, i.e. it was split by bidi resolution
8390 if (!inFlowFrame->GetPrevSibling() && !inFlowFrame->GetNextSibling() &&
8391 ((parent->GetPrevContinuation() && !parent->GetPrevInFlow()) ||
8392 (parent->GetNextContinuation() && !parent->GetNextInFlow()))) {
8393 TRACE("Removing last child of non-fluid split parent");
8394 RecreateFramesForContent(parent->GetContent(), InsertionKind::Async);
8395 return true;
8398 // We might still need to reconstruct things if the parent of inFlowFrame is
8399 // ib-split, since in that case the removal of aFrame might affect the
8400 // splitting of its parent.
8401 if (!IsFramePartOfIBSplit(parent)) {
8402 return false;
8405 // If inFlowFrame is not the only in-flow child of |parent|, then removing
8406 // it will change nothing about the {ib} split.
8407 if (inFlowFrame != parent->PrincipalChildList().FirstChild() ||
8408 inFlowFrame->LastContinuation()->GetNextSibling()) {
8409 return false;
8412 // If the parent is the first or last part of the {ib} split, then
8413 // removing one of its kids will have no effect on the splitting.
8414 // Get the first continuation up front so we don't have to do it twice.
8415 nsIFrame* parentFirstContinuation = parent->FirstContinuation();
8416 if (!GetIBSplitSibling(parentFirstContinuation) ||
8417 !GetIBSplitPrevSibling(parentFirstContinuation)) {
8418 return false;
8421 TRACE("IB split parent");
8422 ReframeContainingBlock(parent);
8423 return true;
8424 #undef TRACE
8427 void nsCSSFrameConstructor::UpdateTableCellSpans(nsIContent* aContent) {
8428 nsTableCellFrame* cellFrame = do_QueryFrame(aContent->GetPrimaryFrame());
8430 // It's possible that this warning could fire if some other style change
8431 // simultaneously changes the 'display' of the element and makes it no
8432 // longer be a table cell.
8433 NS_WARNING_ASSERTION(cellFrame, "Hint should only be posted on table cells!");
8435 if (cellFrame) {
8436 cellFrame->GetTableFrame()->RowOrColSpanChanged(cellFrame);
8440 static nsIContent* GetTopmostMathMLElement(nsIContent* aMathMLContent) {
8441 MOZ_ASSERT(aMathMLContent->IsMathMLElement());
8442 MOZ_ASSERT(aMathMLContent->GetPrimaryFrame());
8443 MOZ_ASSERT(
8444 aMathMLContent->GetPrimaryFrame()->IsFrameOfType(nsIFrame::eMathML));
8445 nsIContent* root = aMathMLContent;
8447 for (nsIContent* parent = aMathMLContent->GetFlattenedTreeParent(); parent;
8448 parent = parent->GetFlattenedTreeParent()) {
8449 nsIFrame* frame = parent->GetPrimaryFrame();
8450 if (!frame || !frame->IsFrameOfType(nsIFrame::eMathML)) {
8451 break;
8453 root = parent;
8456 return root;
8459 // We don't know how to re-insert an anonymous subtree root, so recreate the
8460 // closest non-generated ancestor instead, except for a few special cases...
8461 static bool ShouldRecreateContainerForNativeAnonymousContentRoot(
8462 nsIContent* aContent) {
8463 if (!aContent->IsRootOfNativeAnonymousSubtree()) {
8464 return false;
8466 if (ManualNACPtr::IsManualNAC(aContent)) {
8467 // Editor NAC, would enter an infinite loop, and we sorta get away with it
8468 // because it's all abspos.
8469 return false;
8471 if (auto* el = Element::FromNode(aContent)) {
8472 if (auto* classes = el->GetClasses()) {
8473 if (classes->Contains(nsGkAtoms::mozCustomContentContainer,
8474 eCaseMatters)) {
8475 // Canvas anonymous content (like the custom content container) is also
8476 // fine, because its only sibling is a tooltip which is also abspos, so
8477 // relative insertion order doesn't really matter.
8479 // This is important because the inspector uses it, and we don't want
8480 // inspecting the page to change behavior heavily (and reframing
8481 // unfortunately has side-effects sometimes, even though they're bugs).
8482 return false;
8487 return true;
8490 void nsCSSFrameConstructor::RecreateFramesForContent(
8491 nsIContent* aContent, InsertionKind aInsertionKind) {
8492 MOZ_ASSERT(aContent);
8494 // If there is no document, we don't want to recreate frames for it. (You
8495 // shouldn't generally be giving this method content without a document
8496 // anyway).
8497 // Rebuilding the frame tree can have bad effects, especially if it's the
8498 // frame tree for chrome (see bug 157322).
8499 if (NS_WARN_IF(!aContent->GetComposedDoc())) {
8500 return;
8503 // TODO(emilio): We technically can find the right insertion point nowadays
8504 // using StyleChildrenIterator rather than FlattenedTreeIterator. But we'd
8505 // need to tweak the setup to insert into replaced elements to filter which
8506 // anonymous roots can be allowed, and which can't.
8508 // TODO(emilio, 2022): Is this true? If we have a replaced element we wouldn't
8509 // have generated e.g., a ::before/::after pseudo-element to begin with (which
8510 // is what this code is about, so maybe we can just remove this piece of code
8511 // altogether).
8512 if (ShouldRecreateContainerForNativeAnonymousContentRoot(aContent)) {
8513 do {
8514 aContent = aContent->GetParent();
8515 } while (ShouldRecreateContainerForNativeAnonymousContentRoot(aContent));
8516 return RecreateFramesForContent(aContent, InsertionKind::Async);
8519 nsIFrame* frame = aContent->GetPrimaryFrame();
8520 if (frame && frame->IsFrameOfType(nsIFrame::eMathML)) {
8521 // Reframe the topmost MathML element to prevent exponential blowup
8522 // (see bug 397518).
8523 aContent = GetTopmostMathMLElement(aContent);
8524 frame = aContent->GetPrimaryFrame();
8527 if (frame) {
8528 nsIFrame* parent = frame->GetParent();
8529 nsIContent* parentContent = parent ? parent->GetContent() : nullptr;
8530 // If the parent frame is a leaf then the subsequent insert will fail to
8531 // create a frame, so we need to recreate the parent content. This happens
8532 // with native anonymous content from the editor.
8533 if (parent && parent->IsLeaf() && parentContent &&
8534 parentContent != aContent) {
8535 return RecreateFramesForContent(parentContent, InsertionKind::Async);
8539 if (frame && MaybeRecreateContainerForFrameRemoval(frame)) {
8540 return;
8543 MOZ_ASSERT(aContent->GetParentNode());
8545 // Remove the frames associated with the content object.
8546 nsIContent* nextSibling = aContent->IsRootOfNativeAnonymousSubtree()
8547 ? nullptr
8548 : aContent->GetNextSibling();
8549 bool didReconstruct =
8550 ContentRemoved(aContent, nextSibling, REMOVE_FOR_RECONSTRUCTION);
8552 if (!didReconstruct) {
8553 if (aInsertionKind == InsertionKind::Async && aContent->IsElement()) {
8554 // FIXME(emilio, bug 1397239): There's nothing removing the frame state
8555 // for elements that go away before we come back to the frame
8556 // constructor.
8558 // Also, it'd be nice to just use the `ContentRangeInserted` path for
8559 // both elements and non-elements, but we need to make lazy frame
8560 // construction to apply to all elements first.
8561 RestyleManager()->PostRestyleEvent(aContent->AsElement(), RestyleHint{0},
8562 nsChangeHint_ReconstructFrame);
8563 } else {
8564 // Now, recreate the frames associated with this content object. If
8565 // ContentRemoved triggered reconstruction, then we don't need to do this
8566 // because the frames will already have been built.
8567 ContentRangeInserted(aContent, aContent->GetNextSibling(),
8568 aInsertionKind);
8573 bool nsCSSFrameConstructor::DestroyFramesFor(nsIContent* aContent) {
8574 MOZ_ASSERT(aContent && aContent->GetParentNode());
8576 nsIContent* nextSibling = aContent->IsRootOfNativeAnonymousSubtree()
8577 ? nullptr
8578 : aContent->GetNextSibling();
8580 return ContentRemoved(aContent, nextSibling, REMOVE_FOR_RECONSTRUCTION);
8583 //////////////////////////////////////////////////////////////////////
8585 // Block frame construction code
8587 already_AddRefed<ComputedStyle> nsCSSFrameConstructor::GetFirstLetterStyle(
8588 nsIContent* aContent, ComputedStyle* aComputedStyle) {
8589 if (aContent) {
8590 return mPresShell->StyleSet()->ResolvePseudoElementStyle(
8591 *aContent->AsElement(), PseudoStyleType::firstLetter, aComputedStyle);
8593 return nullptr;
8596 already_AddRefed<ComputedStyle> nsCSSFrameConstructor::GetFirstLineStyle(
8597 nsIContent* aContent, ComputedStyle* aComputedStyle) {
8598 if (aContent) {
8599 return mPresShell->StyleSet()->ResolvePseudoElementStyle(
8600 *aContent->AsElement(), PseudoStyleType::firstLine, aComputedStyle);
8602 return nullptr;
8605 // Predicate to see if a given content (block element) has
8606 // first-letter style applied to it.
8607 bool nsCSSFrameConstructor::ShouldHaveFirstLetterStyle(
8608 nsIContent* aContent, ComputedStyle* aComputedStyle) {
8609 return nsLayoutUtils::HasPseudoStyle(aContent, aComputedStyle,
8610 PseudoStyleType::firstLetter,
8611 mPresShell->GetPresContext());
8614 bool nsCSSFrameConstructor::HasFirstLetterStyle(nsIFrame* aBlockFrame) {
8615 MOZ_ASSERT(aBlockFrame, "Need a frame");
8616 NS_ASSERTION(aBlockFrame->IsBlockFrameOrSubclass(), "Not a block frame?");
8618 return aBlockFrame->HasAnyStateBits(NS_BLOCK_HAS_FIRST_LETTER_STYLE);
8621 bool nsCSSFrameConstructor::ShouldHaveFirstLineStyle(
8622 nsIContent* aContent, ComputedStyle* aComputedStyle) {
8623 bool hasFirstLine = nsLayoutUtils::HasPseudoStyle(
8624 aContent, aComputedStyle, PseudoStyleType::firstLine,
8625 mPresShell->GetPresContext());
8626 return hasFirstLine && !aContent->IsHTMLElement(nsGkAtoms::fieldset);
8629 void nsCSSFrameConstructor::ShouldHaveSpecialBlockStyle(
8630 nsIContent* aContent, ComputedStyle* aComputedStyle,
8631 bool* aHaveFirstLetterStyle, bool* aHaveFirstLineStyle) {
8632 *aHaveFirstLetterStyle = ShouldHaveFirstLetterStyle(aContent, aComputedStyle);
8633 *aHaveFirstLineStyle = ShouldHaveFirstLineStyle(aContent, aComputedStyle);
8636 /* static */
8637 const nsCSSFrameConstructor::PseudoParentData
8638 nsCSSFrameConstructor::sPseudoParentData[eParentTypeCount] = {
8639 // Cell
8640 {{&nsCSSFrameConstructor::ConstructTableCell,
8641 FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET | FCDATA_USE_CHILD_ITEMS |
8642 FCDATA_IS_WRAPPER_ANON_BOX |
8643 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRow)},
8644 PseudoStyleType::tableCell},
8645 // Row
8646 {{&nsCSSFrameConstructor::ConstructTableRowOrRowGroup,
8647 FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET | FCDATA_USE_CHILD_ITEMS |
8648 FCDATA_IS_WRAPPER_ANON_BOX |
8649 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRowGroup)},
8650 PseudoStyleType::tableRow},
8651 // Row group
8652 {{&nsCSSFrameConstructor::ConstructTableRowOrRowGroup,
8653 FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET | FCDATA_USE_CHILD_ITEMS |
8654 FCDATA_IS_WRAPPER_ANON_BOX |
8655 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable)},
8656 PseudoStyleType::tableRowGroup},
8657 // Column group
8658 {{ToCreationFunc(NS_NewTableColGroupFrame),
8659 FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET |
8660 FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_USE_CHILD_ITEMS |
8661 FCDATA_SKIP_ABSPOS_PUSH |
8662 // Not FCDATA_IS_WRAPPER_ANON_BOX, because we don't need to
8663 // restyle these: they have non-inheriting styles.
8664 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable)},
8665 PseudoStyleType::tableColGroup},
8666 // Table
8667 {{&nsCSSFrameConstructor::ConstructTable,
8668 FCDATA_SKIP_FRAMESET | FCDATA_USE_CHILD_ITEMS |
8669 FCDATA_IS_WRAPPER_ANON_BOX},
8670 PseudoStyleType::table},
8671 // Ruby
8672 {{ToCreationFunc(NS_NewRubyFrame),
8673 FCDATA_IS_LINE_PARTICIPANT | FCDATA_USE_CHILD_ITEMS |
8674 FCDATA_IS_WRAPPER_ANON_BOX | FCDATA_SKIP_FRAMESET},
8675 PseudoStyleType::ruby},
8676 // Ruby Base
8677 {{ToCreationFunc(NS_NewRubyBaseFrame),
8678 FCDATA_USE_CHILD_ITEMS | FCDATA_IS_LINE_PARTICIPANT |
8679 FCDATA_IS_WRAPPER_ANON_BOX |
8680 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRubyBaseContainer) |
8681 FCDATA_SKIP_FRAMESET},
8682 PseudoStyleType::rubyBase},
8683 // Ruby Base Container
8684 {{ToCreationFunc(NS_NewRubyBaseContainerFrame),
8685 FCDATA_USE_CHILD_ITEMS | FCDATA_IS_LINE_PARTICIPANT |
8686 FCDATA_IS_WRAPPER_ANON_BOX |
8687 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRuby) |
8688 FCDATA_SKIP_FRAMESET},
8689 PseudoStyleType::rubyBaseContainer},
8690 // Ruby Text
8691 {{ToCreationFunc(NS_NewRubyTextFrame),
8692 FCDATA_USE_CHILD_ITEMS | FCDATA_IS_LINE_PARTICIPANT |
8693 FCDATA_IS_WRAPPER_ANON_BOX |
8694 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRubyTextContainer) |
8695 FCDATA_SKIP_FRAMESET},
8696 PseudoStyleType::rubyText},
8697 // Ruby Text Container
8698 {{ToCreationFunc(NS_NewRubyTextContainerFrame),
8699 FCDATA_USE_CHILD_ITEMS | FCDATA_IS_WRAPPER_ANON_BOX |
8700 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRuby) |
8701 FCDATA_SKIP_FRAMESET},
8702 PseudoStyleType::rubyTextContainer}};
8704 void nsCSSFrameConstructor::CreateNeededAnonFlexOrGridItems(
8705 nsFrameConstructorState& aState, FrameConstructionItemList& aItems,
8706 nsIFrame* aParentFrame) {
8707 if (aItems.IsEmpty()) {
8708 return;
8711 if (!aParentFrame->IsFlexOrGridContainer() &&
8712 !aParentFrame->IsXULBoxFrame()) {
8713 return;
8716 const bool isLegacyWebKitBox =
8717 IsFlexContainerForLegacyWebKitBox(aParentFrame);
8718 FCItemIterator iter(aItems);
8719 do {
8720 // Advance iter past children that don't want to be wrapped
8721 if (iter.SkipItemsThatDontNeedAnonFlexOrGridItem(aState,
8722 isLegacyWebKitBox)) {
8723 // Hit the end of the items without finding any remaining children that
8724 // need to be wrapped. We're finished!
8725 return;
8728 // If our next potentially-wrappable child is whitespace, then see if
8729 // there's anything wrappable immediately after it. If not, we just drop
8730 // the whitespace and move on. (We're not supposed to create any anonymous
8731 // flex/grid items that _only_ contain whitespace).
8732 // (BUT if this is generated content, then we don't give whitespace nodes
8733 // any special treatment, because they're probably not really whitespace --
8734 // they're just temporarily empty, waiting for their generated text.)
8735 // XXXdholbert If this node's generated text will *actually end up being
8736 // entirely whitespace*, then we technically should still skip over it, per
8737 // the CSS grid & flexbox specs. I'm not bothering with that at this point,
8738 // since it's a pretty extreme edge case.
8739 if (!aParentFrame->IsGeneratedContentFrame() &&
8740 iter.item().IsWhitespace(aState)) {
8741 FCItemIterator afterWhitespaceIter(iter);
8742 bool hitEnd = afterWhitespaceIter.SkipWhitespace(aState);
8743 bool nextChildNeedsAnonItem =
8744 !hitEnd && afterWhitespaceIter.item().NeedsAnonFlexOrGridItem(
8745 aState, isLegacyWebKitBox);
8747 if (!nextChildNeedsAnonItem) {
8748 // There's nothing after the whitespace that we need to wrap, so we
8749 // just drop this run of whitespace.
8750 iter.DeleteItemsTo(this, afterWhitespaceIter);
8751 if (hitEnd) {
8752 // Nothing left to do -- we're finished!
8753 return;
8755 // else, we have a next child and it does not want to be wrapped. So,
8756 // we jump back to the beginning of the loop to skip over that child
8757 // (and anything else non-wrappable after it)
8758 MOZ_ASSERT(!iter.IsDone() && !iter.item().NeedsAnonFlexOrGridItem(
8759 aState, isLegacyWebKitBox),
8760 "hitEnd and/or nextChildNeedsAnonItem lied");
8761 continue;
8765 // Now |iter| points to the first child that needs to be wrapped in an
8766 // anonymous flex/grid item. Now we see how many children after it also want
8767 // to be wrapped in an anonymous flex/grid item.
8768 FCItemIterator endIter(iter); // iterator to find the end of the group
8769 endIter.SkipItemsThatNeedAnonFlexOrGridItem(aState, isLegacyWebKitBox);
8771 NS_ASSERTION(iter != endIter,
8772 "Should've had at least one wrappable child to seek past");
8774 // Now, we create the anonymous flex or grid item to contain the children
8775 // between |iter| and |endIter|.
8776 nsIContent* parentContent = aParentFrame->GetContent();
8777 RefPtr<ComputedStyle> wrapperStyle =
8778 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
8779 PseudoStyleType::anonymousItem, aParentFrame->Style());
8781 static constexpr FrameConstructionData sBlockFormattingContextFCData(
8782 ToCreationFunc(NS_NewBlockFormattingContext),
8783 FCDATA_SKIP_FRAMESET | FCDATA_USE_CHILD_ITEMS |
8784 FCDATA_IS_WRAPPER_ANON_BOX);
8786 FrameConstructionItem* newItem = new (this)
8787 FrameConstructionItem(&sBlockFormattingContextFCData,
8788 // Use the content of our parent frame
8789 parentContent, wrapperStyle.forget(), true);
8791 newItem->mIsAllInline =
8792 newItem->mComputedStyle->StyleDisplay()->IsInlineOutsideStyle();
8793 newItem->mIsBlock = !newItem->mIsAllInline;
8795 MOZ_ASSERT(!newItem->mIsAllInline && newItem->mIsBlock,
8796 "expecting anonymous flex/grid items to be block-level "
8797 "(this will make a difference when we encounter "
8798 "'align-items: baseline')");
8800 // Anonymous flex and grid items induce line boundaries around their
8801 // contents.
8802 newItem->mChildItems.SetLineBoundaryAtStart(true);
8803 newItem->mChildItems.SetLineBoundaryAtEnd(true);
8804 // The parent of the items in aItems is also the parent of the items
8805 // in mChildItems
8806 newItem->mChildItems.SetParentHasNoShadowDOM(aItems.ParentHasNoShadowDOM());
8808 // Eat up all items between |iter| and |endIter| and put them in our
8809 // wrapper. This advances |iter| to point to |endIter|.
8810 iter.AppendItemsToList(this, endIter, newItem->mChildItems);
8812 iter.InsertItem(newItem);
8813 } while (!iter.IsDone());
8816 /* static */ nsCSSFrameConstructor::RubyWhitespaceType
8817 nsCSSFrameConstructor::ComputeRubyWhitespaceType(StyleDisplay aPrevDisplay,
8818 StyleDisplay aNextDisplay) {
8819 MOZ_ASSERT(nsStyleDisplay::IsRubyDisplayType(aPrevDisplay) &&
8820 nsStyleDisplay::IsRubyDisplayType(aNextDisplay));
8821 if (aPrevDisplay == aNextDisplay &&
8822 (aPrevDisplay == StyleDisplay::RubyBase ||
8823 aPrevDisplay == StyleDisplay::RubyText)) {
8824 return eRubyInterLeafWhitespace;
8826 if (aNextDisplay == StyleDisplay::RubyText ||
8827 aNextDisplay == StyleDisplay::RubyTextContainer) {
8828 return eRubyInterLevelWhitespace;
8830 return eRubyInterSegmentWhitespace;
8834 * This function checks the content from |aStartIter| to |aEndIter|,
8835 * determines whether it contains only whitespace, and if yes,
8836 * interprets the type of whitespace. This method does not change
8837 * any of the iters.
8839 /* static */ nsCSSFrameConstructor::RubyWhitespaceType
8840 nsCSSFrameConstructor::InterpretRubyWhitespace(nsFrameConstructorState& aState,
8841 const FCItemIterator& aStartIter,
8842 const FCItemIterator& aEndIter) {
8843 if (!aStartIter.item().IsWhitespace(aState)) {
8844 return eRubyNotWhitespace;
8847 FCItemIterator spaceEndIter(aStartIter);
8848 spaceEndIter.SkipWhitespace(aState);
8849 if (spaceEndIter != aEndIter) {
8850 return eRubyNotWhitespace;
8853 // Any leading or trailing whitespace in non-pseudo ruby box
8854 // should have been trimmed, hence there should not be any
8855 // whitespace at the start or the end.
8856 MOZ_ASSERT(!aStartIter.AtStart() && !aEndIter.IsDone());
8857 FCItemIterator prevIter(aStartIter);
8858 prevIter.Prev();
8859 return ComputeRubyWhitespaceType(
8860 prevIter.item().mComputedStyle->StyleDisplay()->mDisplay,
8861 aEndIter.item().mComputedStyle->StyleDisplay()->mDisplay);
8865 * This function eats up consecutive items which do not want the current
8866 * parent into either a ruby base box or a ruby text box. When it
8867 * returns, |aIter| points to the first item it doesn't wrap.
8869 void nsCSSFrameConstructor::WrapItemsInPseudoRubyLeafBox(
8870 FCItemIterator& aIter, ComputedStyle* aParentStyle,
8871 nsIContent* aParentContent) {
8872 StyleDisplay parentDisplay = aParentStyle->StyleDisplay()->mDisplay;
8873 ParentType parentType, wrapperType;
8874 if (parentDisplay == StyleDisplay::RubyTextContainer) {
8875 parentType = eTypeRubyTextContainer;
8876 wrapperType = eTypeRubyText;
8877 } else {
8878 MOZ_ASSERT(parentDisplay == StyleDisplay::RubyBaseContainer);
8879 parentType = eTypeRubyBaseContainer;
8880 wrapperType = eTypeRubyBase;
8883 MOZ_ASSERT(aIter.item().DesiredParentType() != parentType,
8884 "Should point to something needs to be wrapped.");
8886 FCItemIterator endIter(aIter);
8887 endIter.SkipItemsNotWantingParentType(parentType);
8889 WrapItemsInPseudoParent(aParentContent, aParentStyle, wrapperType, aIter,
8890 endIter);
8894 * This function eats up consecutive items into a ruby level container.
8895 * It may create zero or one level container. When it returns, |aIter|
8896 * points to the first item it doesn't wrap.
8898 void nsCSSFrameConstructor::WrapItemsInPseudoRubyLevelContainer(
8899 nsFrameConstructorState& aState, FCItemIterator& aIter,
8900 ComputedStyle* aParentStyle, nsIContent* aParentContent) {
8901 MOZ_ASSERT(aIter.item().DesiredParentType() != eTypeRuby,
8902 "Pointing to a level container?");
8904 FrameConstructionItem& firstItem = aIter.item();
8905 ParentType wrapperType = firstItem.DesiredParentType();
8906 if (wrapperType != eTypeRubyTextContainer) {
8907 // If the first item is not ruby text,
8908 // it should be in a base container.
8909 wrapperType = eTypeRubyBaseContainer;
8912 FCItemIterator endIter(aIter);
8913 do {
8914 if (endIter.SkipItemsWantingParentType(wrapperType) ||
8915 // If the skipping above stops at some item which wants a
8916 // different ruby parent, then we have finished.
8917 IsRubyParentType(endIter.item().DesiredParentType())) {
8918 // No more items need to be wrapped in this level container.
8919 break;
8922 FCItemIterator contentEndIter(endIter);
8923 contentEndIter.SkipItemsNotWantingRubyParent();
8924 // endIter must be on something doesn't want a ruby parent.
8925 MOZ_ASSERT(contentEndIter != endIter);
8927 // InterpretRubyWhitespace depends on the fact that any leading or
8928 // trailing whitespace described in the spec have been trimmed at
8929 // this point. With this precondition, it is safe not to check
8930 // whether contentEndIter has been done.
8931 RubyWhitespaceType whitespaceType =
8932 InterpretRubyWhitespace(aState, endIter, contentEndIter);
8933 if (whitespaceType == eRubyInterLevelWhitespace) {
8934 // Remove inter-level whitespace.
8935 bool atStart = (aIter == endIter);
8936 endIter.DeleteItemsTo(this, contentEndIter);
8937 if (atStart) {
8938 aIter = endIter;
8940 } else if (whitespaceType == eRubyInterSegmentWhitespace) {
8941 // If this level container starts with inter-segment whitespaces,
8942 // wrap them. Break at contentEndIter. Otherwise, leave it here.
8943 // Break at endIter. They will be wrapped when we are here again.
8944 if (aIter == endIter) {
8945 MOZ_ASSERT(wrapperType == eTypeRubyBaseContainer,
8946 "Inter-segment whitespace should be wrapped in rbc");
8947 endIter = contentEndIter;
8949 break;
8950 } else if (wrapperType == eTypeRubyTextContainer &&
8951 whitespaceType != eRubyInterLeafWhitespace) {
8952 // Misparented inline content that's not inter-annotation
8953 // whitespace doesn't belong in a pseudo ruby text container.
8954 // Break at endIter.
8955 break;
8956 } else {
8957 endIter = contentEndIter;
8959 } while (!endIter.IsDone());
8961 // It is possible that everything our parent wants us to wrap is
8962 // simply an inter-level whitespace, which has been trimmed, or
8963 // an inter-segment whitespace, which will be wrapped later.
8964 // In those cases, don't create anything.
8965 if (aIter != endIter) {
8966 WrapItemsInPseudoParent(aParentContent, aParentStyle, wrapperType, aIter,
8967 endIter);
8972 * This function trims leading and trailing whitespaces
8973 * in the given item list.
8975 void nsCSSFrameConstructor::TrimLeadingAndTrailingWhitespaces(
8976 nsFrameConstructorState& aState, FrameConstructionItemList& aItems) {
8977 FCItemIterator iter(aItems);
8978 if (!iter.IsDone() && iter.item().IsWhitespace(aState)) {
8979 FCItemIterator spaceEndIter(iter);
8980 spaceEndIter.SkipWhitespace(aState);
8981 iter.DeleteItemsTo(this, spaceEndIter);
8984 iter.SetToEnd();
8985 if (!iter.AtStart()) {
8986 FCItemIterator spaceEndIter(iter);
8987 do {
8988 iter.Prev();
8989 if (iter.AtStart()) {
8990 // It's fine to not check the first item, because we
8991 // should have trimmed leading whitespaces above.
8992 break;
8994 } while (iter.item().IsWhitespace(aState));
8995 iter.Next();
8996 if (iter != spaceEndIter) {
8997 iter.DeleteItemsTo(this, spaceEndIter);
9003 * This function walks through the child list (aItems) and creates
9004 * needed pseudo ruby boxes to wrap misparented children.
9006 void nsCSSFrameConstructor::CreateNeededPseudoInternalRubyBoxes(
9007 nsFrameConstructorState& aState, FrameConstructionItemList& aItems,
9008 nsIFrame* aParentFrame) {
9009 const ParentType ourParentType = GetParentType(aParentFrame);
9010 if (!IsRubyParentType(ourParentType) ||
9011 aItems.AllWantParentType(ourParentType)) {
9012 return;
9015 if (!IsRubyPseudo(aParentFrame) ||
9016 ourParentType == eTypeRuby /* for 'display:block ruby' */) {
9017 // Normally, ruby pseudo frames start from and end at some elements,
9018 // which means they don't have leading and trailing whitespaces at
9019 // all. But there are two cases where they do actually have leading
9020 // or trailing whitespaces:
9021 // 1. It is an inter-segment whitespace which in an individual ruby
9022 // base container.
9023 // 2. The pseudo frame starts from or ends at consecutive inline
9024 // content, which is not pure whitespace, but includes some.
9025 // In either case, the whitespaces are not the leading or trailing
9026 // whitespaces defined in the spec, and thus should not be trimmed.
9027 TrimLeadingAndTrailingWhitespaces(aState, aItems);
9030 FCItemIterator iter(aItems);
9031 nsIContent* parentContent = aParentFrame->GetContent();
9032 ComputedStyle* parentStyle = aParentFrame->Style();
9033 while (!iter.IsDone()) {
9034 if (!iter.SkipItemsWantingParentType(ourParentType)) {
9035 if (ourParentType == eTypeRuby) {
9036 WrapItemsInPseudoRubyLevelContainer(aState, iter, parentStyle,
9037 parentContent);
9038 } else {
9039 WrapItemsInPseudoRubyLeafBox(iter, parentStyle, parentContent);
9046 * This function works as follows: we walk through the child list (aItems) and
9047 * find items that cannot have aParentFrame as their parent. We wrap
9048 * continuous runs of such items into a FrameConstructionItem for a frame that
9049 * gets them closer to their desired parents. For example, a run of non-row
9050 * children of a row-group will get wrapped in a row. When we later construct
9051 * the frame for this wrapper (in this case for the row), it'll be the correct
9052 * parent for the cells in the set of items we wrapped or we'll wrap cells
9053 * around everything else. At the end of this method, aItems is guaranteed to
9054 * contain only items for frames that can be direct kids of aParentFrame.
9056 void nsCSSFrameConstructor::CreateNeededPseudoContainers(
9057 nsFrameConstructorState& aState, FrameConstructionItemList& aItems,
9058 nsIFrame* aParentFrame) {
9059 ParentType ourParentType = GetParentType(aParentFrame);
9060 if (IsRubyParentType(ourParentType) ||
9061 aItems.AllWantParentType(ourParentType)) {
9062 // Nothing to do here
9063 return;
9066 FCItemIterator iter(aItems);
9067 do {
9068 if (iter.SkipItemsWantingParentType(ourParentType)) {
9069 // Nothing else to do here; we're finished
9070 return;
9073 // Now we're pointing to the first child that wants a different parent
9074 // type.
9076 // Now try to figure out what kids we can group together. We can generally
9077 // group everything that has a different desired parent type from us. Two
9078 // exceptions to this:
9079 // 1) If our parent type is table, we can't group columns with anything
9080 // else other than whitespace.
9081 // 2) Whitespace that lies between two things we can group which both want
9082 // a non-block parent should be dropped, even if we can't group them
9083 // with each other and even if the whitespace wants a parent of
9084 // ourParentType. Ends of the list count as things that don't want a
9085 // block parent (so that for example we'll drop a whitespace-only list).
9087 FCItemIterator endIter(iter); /* iterator to find the end of the group */
9088 ParentType groupingParentType = endIter.item().DesiredParentType();
9089 if (aItems.AllWantParentType(groupingParentType) &&
9090 groupingParentType != eTypeBlock) {
9091 // Just group them all and be done with it. We need the check for
9092 // eTypeBlock here to catch the "all the items are whitespace" case
9093 // described above.
9094 endIter.SetToEnd();
9095 } else {
9096 // Locate the end of the group.
9098 // Keep track of the type the previous item wanted, in case we have to
9099 // deal with whitespace. Start it off with ourParentType, since that's
9100 // the last thing |iter| would have skipped over.
9101 ParentType prevParentType = ourParentType;
9102 do {
9103 // Walk an iterator past any whitespace that we might be able to drop
9104 // from the list
9105 FCItemIterator spaceEndIter(endIter);
9106 if (prevParentType != eTypeBlock &&
9107 !aParentFrame->IsGeneratedContentFrame() &&
9108 spaceEndIter.item().IsWhitespace(aState)) {
9109 bool trailingSpaces = spaceEndIter.SkipWhitespace(aState);
9111 // We drop the whitespace in the following cases:
9112 // 1) If these are not trailing spaces and the next item wants a table
9113 // or table-part parent
9114 // 2) If these are trailing spaces and aParentFrame is a
9115 // tabular container according to rule 1.3 of CSS 2.1 Sec 17.2.1.
9116 // (Being a tabular container pretty much means ourParentType is
9117 // not eTypeBlock besides the eTypeColGroup case, which won't
9118 // reach here.)
9119 if ((!trailingSpaces &&
9120 IsTableParentType(spaceEndIter.item().DesiredParentType())) ||
9121 (trailingSpaces && ourParentType != eTypeBlock)) {
9122 bool updateStart = (iter == endIter);
9123 endIter.DeleteItemsTo(this, spaceEndIter);
9124 NS_ASSERTION(trailingSpaces == endIter.IsDone(),
9125 "These should match");
9127 if (updateStart) {
9128 iter = endIter;
9131 if (trailingSpaces) {
9132 break; /* Found group end */
9135 if (updateStart) {
9136 // Update groupingParentType, since it might have been eTypeBlock
9137 // just because of the whitespace.
9138 groupingParentType = iter.item().DesiredParentType();
9143 // Now endIter points to a non-whitespace item or a non-droppable
9144 // whitespace item. In the latter case, if this is the end of the group
9145 // we'll traverse this whitespace again. But it'll all just be quick
9146 // DesiredParentType() checks which will match ourParentType (that's
9147 // what it means that this is the group end), so it's OK.
9148 // However, when we are grouping a ruby parent, and endIter points to
9149 // a non-droppable whitespace, if the next non-whitespace item also
9150 // wants a ruby parent, the whitespace should also be included into
9151 // the current ruby container.
9152 prevParentType = endIter.item().DesiredParentType();
9153 if (prevParentType == ourParentType &&
9154 (endIter == spaceEndIter || spaceEndIter.IsDone() ||
9155 !IsRubyParentType(groupingParentType) ||
9156 !IsRubyParentType(spaceEndIter.item().DesiredParentType()))) {
9157 // End the group at endIter.
9158 break;
9161 if (ourParentType == eTypeTable &&
9162 (prevParentType == eTypeColGroup) !=
9163 (groupingParentType == eTypeColGroup)) {
9164 // Either we started with columns and now found something else, or
9165 // vice versa. In any case, end the grouping.
9166 break;
9169 // If we have some whitespace that we were not able to drop and there is
9170 // an item after the whitespace that is already properly parented, then
9171 // make sure to include the spaces in our group but stop the group after
9172 // that.
9173 if (spaceEndIter != endIter && !spaceEndIter.IsDone() &&
9174 ourParentType == spaceEndIter.item().DesiredParentType()) {
9175 endIter = spaceEndIter;
9176 break;
9179 // Include the whitespace we didn't drop (if any) in the group.
9180 endIter = spaceEndIter;
9181 prevParentType = endIter.item().DesiredParentType();
9183 endIter.Next();
9184 } while (!endIter.IsDone());
9187 if (iter == endIter) {
9188 // Nothing to wrap here; just skipped some whitespace
9189 continue;
9192 // Now group together all the items between iter and endIter. The right
9193 // parent type to use depends on ourParentType.
9194 ParentType wrapperType;
9195 switch (ourParentType) {
9196 case eTypeRow:
9197 // The parent type for a cell is eTypeBlock, since that's what a cell
9198 // looks like to its kids.
9199 wrapperType = eTypeBlock;
9200 break;
9201 case eTypeRowGroup:
9202 wrapperType = eTypeRow;
9203 break;
9204 case eTypeTable:
9205 // Either colgroup or rowgroup, depending on what we're grouping.
9206 wrapperType =
9207 groupingParentType == eTypeColGroup ? eTypeColGroup : eTypeRowGroup;
9208 break;
9209 case eTypeColGroup:
9210 MOZ_CRASH("Colgroups should be suppresing non-col child items");
9211 default:
9212 NS_ASSERTION(ourParentType == eTypeBlock, "Unrecognized parent type");
9213 if (IsRubyParentType(groupingParentType)) {
9214 wrapperType = eTypeRuby;
9215 } else {
9216 NS_ASSERTION(IsTableParentType(groupingParentType),
9217 "groupingParentType should be either Ruby or table");
9218 wrapperType = eTypeTable;
9222 ComputedStyle* parentStyle = aParentFrame->Style();
9223 WrapItemsInPseudoParent(aParentFrame->GetContent(), parentStyle,
9224 wrapperType, iter, endIter);
9226 // Now |iter| points to the item that was the first one we didn't wrap;
9227 // loop and see whether we need to skip it or wrap it in something
9228 // different.
9229 } while (!iter.IsDone());
9233 * This method wraps frame construction item from |aIter| to
9234 * |aEndIter|. After it returns, aIter points to the first item
9235 * after the wrapper.
9237 void nsCSSFrameConstructor::WrapItemsInPseudoParent(
9238 nsIContent* aParentContent, ComputedStyle* aParentStyle,
9239 ParentType aWrapperType, FCItemIterator& aIter,
9240 const FCItemIterator& aEndIter) {
9241 const PseudoParentData& pseudoData = sPseudoParentData[aWrapperType];
9242 PseudoStyleType pseudoType = pseudoData.mPseudoType;
9243 auto& parentDisplay = *aParentStyle->StyleDisplay();
9244 auto parentDisplayInside = parentDisplay.DisplayInside();
9246 // XXXmats should we use IsInlineInsideStyle() here instead? seems odd to
9247 // exclude RubyBaseContainer/RubyTextContainer...
9248 if (pseudoType == PseudoStyleType::table &&
9249 (parentDisplay.IsInlineFlow() ||
9250 parentDisplayInside == StyleDisplayInside::RubyBase ||
9251 parentDisplayInside == StyleDisplayInside::RubyText)) {
9252 pseudoType = PseudoStyleType::inlineTable;
9255 RefPtr<ComputedStyle> wrapperStyle;
9256 if (pseudoData.mFCData.mBits & FCDATA_IS_WRAPPER_ANON_BOX) {
9257 wrapperStyle = mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
9258 pseudoType, aParentStyle);
9259 } else {
9260 wrapperStyle =
9261 mPresShell->StyleSet()->ResolveNonInheritingAnonymousBoxStyle(
9262 pseudoType);
9265 FrameConstructionItem* newItem = new (this)
9266 FrameConstructionItem(&pseudoData.mFCData,
9267 // Use the content of our parent frame
9268 aParentContent, wrapperStyle.forget(), true);
9270 const nsStyleDisplay* disp = newItem->mComputedStyle->StyleDisplay();
9271 // Here we're cheating a tad... technically, table-internal items should be
9272 // inline if aParentFrame is inline, but they'll get wrapped in an
9273 // inline-table in the end, so it'll all work out. In any case, arguably
9274 // we don't need to maintain this state at this point... but it's better
9275 // to, I guess.
9276 newItem->mIsAllInline = disp->IsInlineOutsideStyle();
9278 bool isRuby = disp->IsRubyDisplayType();
9279 if (!isRuby) {
9280 // Table pseudo frames always induce line boundaries around their
9281 // contents.
9282 newItem->mChildItems.SetLineBoundaryAtStart(true);
9283 newItem->mChildItems.SetLineBoundaryAtEnd(true);
9285 // The parent of the items in aItems is also the parent of the items
9286 // in mChildItems
9287 newItem->mChildItems.SetParentHasNoShadowDOM(
9288 aIter.List()->ParentHasNoShadowDOM());
9290 // Eat up all items between |aIter| and |aEndIter| and put them in our
9291 // wrapper Advances |aIter| to point to |aEndIter|.
9292 aIter.AppendItemsToList(this, aEndIter, newItem->mChildItems);
9294 aIter.InsertItem(newItem);
9297 void nsCSSFrameConstructor::CreateNeededPseudoSiblings(
9298 nsFrameConstructorState& aState, FrameConstructionItemList& aItems,
9299 nsIFrame* aParentFrame) {
9300 if (aItems.IsEmpty() || GetParentType(aParentFrame) != eTypeRuby) {
9301 return;
9304 FCItemIterator iter(aItems);
9305 StyleDisplay firstDisplay =
9306 iter.item().mComputedStyle->StyleDisplay()->mDisplay;
9307 if (firstDisplay == StyleDisplay::RubyBaseContainer) {
9308 return;
9310 NS_ASSERTION(firstDisplay == StyleDisplay::RubyTextContainer,
9311 "Child of ruby frame should either a rbc or a rtc");
9313 const PseudoParentData& pseudoData =
9314 sPseudoParentData[eTypeRubyBaseContainer];
9315 RefPtr<ComputedStyle> pseudoStyle =
9316 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
9317 pseudoData.mPseudoType, aParentFrame->Style());
9318 FrameConstructionItem* newItem = new (this) FrameConstructionItem(
9319 &pseudoData.mFCData,
9320 // Use the content of the parent frame
9321 aParentFrame->GetContent(), pseudoStyle.forget(), true);
9322 newItem->mIsAllInline = true;
9323 newItem->mChildItems.SetParentHasNoShadowDOM(true);
9324 iter.InsertItem(newItem);
9327 #ifdef DEBUG
9329 * Returns true iff aFrame should be wrapped in an anonymous flex/grid item,
9330 * rather than being a direct child of aContainerFrame.
9332 * NOTE: aContainerFrame must be a flex or grid container - this function is
9333 * purely for sanity-checking the children of these container types.
9334 * NOTE: See also NeedsAnonFlexOrGridItem(), for the non-debug version of this
9335 * logic (which operates a bit earlier, on FCData instead of frames).
9337 static bool FrameWantsToBeInAnonymousItem(const nsIFrame* aContainerFrame,
9338 const nsIFrame* aFrame) {
9339 MOZ_ASSERT(aContainerFrame->IsFlexOrGridContainer() ||
9340 aContainerFrame->IsXULBoxFrame());
9342 // Any line-participant frames (e.g. text) definitely want to be wrapped in
9343 // an anonymous flex/grid item.
9344 if (aFrame->IsFrameOfType(nsIFrame::eLineParticipant)) {
9345 return true;
9348 // If the container is a -webkit-{inline-}box container, then placeholders
9349 // also need to be wrapped, for compatibility.
9350 if (IsFlexContainerForLegacyWebKitBox(aContainerFrame) &&
9351 aFrame->IsPlaceholderFrame()) {
9352 return true;
9355 return false;
9357 #endif
9359 static void VerifyGridFlexContainerChildren(nsIFrame* aParentFrame,
9360 const nsFrameList& aChildren) {
9361 #ifdef DEBUG
9362 if (!aParentFrame->IsFlexOrGridContainer() &&
9363 !aParentFrame->IsXULBoxFrame()) {
9364 return;
9367 bool prevChildWasAnonItem = false;
9368 for (const nsIFrame* child : aChildren) {
9369 MOZ_ASSERT(!FrameWantsToBeInAnonymousItem(aParentFrame, child),
9370 "frame wants to be inside an anonymous item, but it isn't");
9371 if (IsAnonymousItem(child)) {
9372 AssertAnonymousFlexOrGridItemParent(child, aParentFrame);
9373 MOZ_ASSERT(!prevChildWasAnonItem, "two anon items in a row");
9374 nsIFrame* firstWrappedChild = child->PrincipalChildList().FirstChild();
9375 MOZ_ASSERT(firstWrappedChild, "anonymous item shouldn't be empty");
9376 prevChildWasAnonItem = true;
9377 } else {
9378 prevChildWasAnonItem = false;
9381 #endif
9384 static bool FrameHasOnlyPlaceholderPrevSiblings(const nsIFrame* aFrame) {
9385 // Check for prev siblings, ignoring placeholder frames.
9386 MOZ_ASSERT(aFrame, "frame must not be null");
9387 const nsIFrame* prevSibling = aFrame;
9388 do {
9389 prevSibling = prevSibling->GetPrevSibling();
9390 } while (prevSibling && prevSibling->IsPlaceholderFrame());
9391 return !prevSibling;
9394 static bool FrameHasOnlyPlaceholderNextSiblings(const nsIFrame* aFrame) {
9395 // Check for next siblings, ignoring placeholder frames.
9396 MOZ_ASSERT(aFrame, "frame must not be null");
9397 const nsIFrame* nextSibling = aFrame;
9398 do {
9399 nextSibling = nextSibling->GetNextSibling();
9400 } while (nextSibling && nextSibling->IsPlaceholderFrame());
9401 return !nextSibling;
9404 static void SetPageValues(nsIFrame* const aFrame,
9405 const nsAtom* const aAutoValue,
9406 const nsAtom* const aStartValue,
9407 const nsAtom* const aEndValue) {
9408 MOZ_ASSERT(aAutoValue, "Auto page value should never be null");
9409 MOZ_ASSERT(aStartValue || aEndValue, "Should not have called with no values");
9410 nsIFrame::PageValues* pageValues =
9411 aFrame->GetProperty(nsIFrame::PageValuesProperty());
9413 if (aStartValue) {
9414 if (aStartValue == aAutoValue) {
9415 // If the page value struct already exists, set the start value to null
9416 // to indicate the auto value.
9417 if (pageValues) {
9418 pageValues->mStartPageValue = nullptr;
9420 } else {
9421 // The start value is not auto, so we need to store it, creating the
9422 // page values struct if it does not already exist.
9423 if (!pageValues) {
9424 pageValues = new nsIFrame::PageValues();
9425 aFrame->SetProperty(nsIFrame::PageValuesProperty(), pageValues);
9427 pageValues->mStartPageValue = aStartValue;
9430 if (aEndValue) {
9431 if (aEndValue == aAutoValue) {
9432 // If the page value struct already exists, set the end value to null
9433 // to indicate the auto value.
9434 if (pageValues) {
9435 pageValues->mEndPageValue = nullptr;
9437 } else {
9438 // The end value is not auto, so we need to store it, creating the
9439 // page values struct if it does not already exist.
9440 if (!pageValues) {
9441 pageValues = new nsIFrame::PageValues();
9442 aFrame->SetProperty(nsIFrame::PageValuesProperty(), pageValues);
9444 pageValues->mEndPageValue = aEndValue;
9449 inline void nsCSSFrameConstructor::ConstructFramesFromItemList(
9450 nsFrameConstructorState& aState, FrameConstructionItemList& aItems,
9451 nsContainerFrame* aParentFrame, bool aParentIsWrapperAnonBox,
9452 nsFrameList& aFrameList) {
9453 #ifdef DEBUG
9454 if (aParentFrame->StyleContent()->mContent.IsNone() &&
9455 StaticPrefs::layout_css_element_content_none_enabled()) {
9456 for (FCItemIterator iter(aItems); !iter.IsDone(); iter.Next()) {
9457 MOZ_ASSERT(iter.item().mContent->IsInNativeAnonymousSubtree() ||
9458 iter.item().mComputedStyle->IsPseudoOrAnonBox());
9462 // The assertion condition should match the logic in
9463 // MaybePushFloatContainingBlock().
9464 MOZ_ASSERT(!(ShouldSuppressFloatingOfDescendants(aParentFrame) ||
9465 aParentFrame->IsFloatContainingBlock()) ||
9466 aState.mFloatCBCandidate == aParentFrame,
9467 "Our caller or ProcessChildren()'s caller should call "
9468 "MaybePushFloatContainingBlock() to handle the float containing "
9469 "block candidate!");
9470 aState.mFloatCBCandidate = nullptr;
9471 #endif
9473 // Ensure aParentIsWrapperAnonBox is correct. We _could_ compute it directly,
9474 // but it would be a bit slow, which is why we pass it from callers, who have
9475 // that information offhand in many cases.
9476 MOZ_ASSERT(ParentIsWrapperAnonBox(aParentFrame) == aParentIsWrapperAnonBox);
9478 // Note: we explicitly exclude TableColGroupFrame because it doesn't
9479 // have the FCDATA_IS_WRAPPER_ANON_BOX on pseudos so aParentIsWrapperAnonBox
9480 // is false for such pseudos (see sPseudoParentData below).
9481 if (!aParentIsWrapperAnonBox && aState.mHasRenderedLegend &&
9482 aParentFrame->GetContent()->IsHTMLElement(nsGkAtoms::fieldset) &&
9483 !aParentFrame->IsTableColGroupFrame()) {
9484 DebugOnly<bool> found = false;
9485 for (FCItemIterator iter(aItems); !iter.IsDone(); iter.Next()) {
9486 if (iter.item().mIsRenderedLegend) {
9487 // This makes the rendered legend the first frame in the fieldset child
9488 // list which makes keyboard traversal follow the visual order.
9489 nsFieldSetFrame* fieldSetFrame = GetFieldSetFrameFor(aParentFrame);
9490 nsFrameList renderedLegend;
9491 ConstructFramesFromItem(aState, iter, fieldSetFrame, renderedLegend);
9492 MOZ_ASSERT(renderedLegend.OnlyChild(),
9493 "a rendered legend should have exactly one frame");
9494 fieldSetFrame->InsertFrames(FrameChildListID::Principal, nullptr,
9495 nullptr, std::move(renderedLegend));
9496 FCItemIterator next = iter;
9497 next.Next();
9498 iter.DeleteItemsTo(this, next);
9499 found = true;
9500 break;
9503 MOZ_ASSERT(found, "should have found our rendered legend");
9506 CreateNeededPseudoContainers(aState, aItems, aParentFrame);
9507 CreateNeededAnonFlexOrGridItems(aState, aItems, aParentFrame);
9508 CreateNeededPseudoInternalRubyBoxes(aState, aItems, aParentFrame);
9509 CreateNeededPseudoSiblings(aState, aItems, aParentFrame);
9511 for (FCItemIterator iter(aItems); !iter.IsDone(); iter.Next()) {
9512 MOZ_ASSERT(!iter.item().mIsRenderedLegend,
9513 "Only one item can be the rendered legend, "
9514 "and it should've been handled above");
9515 NS_ASSERTION(iter.item().DesiredParentType() == GetParentType(aParentFrame),
9516 "Needed pseudos didn't get created; expect bad things");
9517 ConstructFramesFromItem(aState, iter, aParentFrame, aFrameList);
9520 VerifyGridFlexContainerChildren(aParentFrame, aFrameList);
9522 // Calculate and propagate page-name values for each frame in the frame list.
9523 // This will be affected by https://bugzilla.mozilla.org/1782597
9524 if (aState.mPresContext->IsPaginated() &&
9525 StaticPrefs::layout_css_named_pages_enabled() &&
9526 aParentFrame->IsBlockFrameOrSubclass()) {
9527 // Set the start/end page values while iterating the frame list, to walk
9528 // up the frame tree only once after iterating the frame list.
9529 // This also avoids extra property lookups on these frames.
9530 MOZ_ASSERT(aState.mAutoPageNameValue == aParentFrame->GetAutoPageValue(),
9531 "aState.mAutoPageNameValue should have been equivalent to "
9532 "the auto value stored on our parent frame.");
9533 // Even though we store null for page values that equal the "auto" resolved
9534 // value on frames, we always want startPageValue/endPageValue to be the
9535 // actual atoms reflecting the start/end values. This is because when we
9536 // propagate the values up the frame tree, we will need to compare them to
9537 // the auto value for each ancestor. This value might be different than the
9538 // auto value for this frame.
9539 const nsAtom* startPageValue = nullptr;
9540 const nsAtom* endPageValue = nullptr;
9541 for (nsIFrame* f : aFrameList) {
9542 if (f->IsPlaceholderFrame()) {
9543 continue;
9545 // Resolve auto against the parent frame's used page name, which has been
9546 // determined and set on aState.mAutoPageNameValue. If this item is not
9547 // block-level then we use the value that auto resolves to.
9549 // This is to achieve the propagation behavior described in the spec:
9551 // "A start page value and end page value is determined for each box as
9552 // the value (if any) propagated from its first or last child box
9553 // (respectively), else the used value on the box itself."
9555 // "A child propagates its own start or end page value if and only if the
9556 // page property applies to it."
9558 // The page property only applies to "boxes that create class A break
9559 // points". When taken together, this means that non block-level children
9560 // do not propagate start/end page values, and instead we use "the used
9561 // value on the box itself", the "box itself" being aParentFrame. This
9562 // value has been determined and saved as aState.mAutoPageNameValue
9564 // https://www.w3.org/TR/css-page-3/#using-named-pages
9565 // https://www.w3.org/TR/css-break-3/#btw-blocks
9566 const StylePageName& pageName = f->StylePage()->mPage;
9567 const nsAtom* const pageNameAtom =
9568 (pageName.IsPageName() && f->IsBlockOutside())
9569 ? pageName.AsPageName().AsAtom()
9570 : aState.mAutoPageNameValue;
9571 nsIFrame::PageValues* pageValues =
9572 f->GetProperty(nsIFrame::PageValuesProperty());
9573 // If this frame has any children, it will already have had its page
9574 // values set at this point. However, if no page values have been set,
9575 // we must ensure that the appropriate PageValuesProperty value has been
9576 // set.
9577 // If the page name is equal to the auto value, then PageValuesProperty
9578 // should remain null to indicate that the start/end values are both
9579 // equal to the auto value.
9580 if (pageNameAtom != aState.mAutoPageNameValue && !pageValues) {
9581 pageValues = new nsIFrame::PageValues{pageNameAtom, pageNameAtom};
9582 f->SetProperty(nsIFrame::PageValuesProperty(), pageValues);
9584 // We don't want to use GetStartPageValue() or GetEndPageValue(), as each
9585 // requires a property lookup which we can avoid here.
9586 if (!startPageValue) {
9587 startPageValue = (pageValues && pageValues->mStartPageValue)
9588 ? pageValues->mStartPageValue.get()
9589 : aState.mAutoPageNameValue;
9591 endPageValue = (pageValues && pageValues->mEndPageValue)
9592 ? pageValues->mEndPageValue.get()
9593 : aState.mAutoPageNameValue;
9594 MOZ_ASSERT(startPageValue && endPageValue,
9595 "Should have found start/end page value");
9597 MOZ_ASSERT(!startPageValue == !endPageValue,
9598 "Should have set both or neither page values");
9599 if (startPageValue) {
9600 // Walk up the frame tree from our parent frame, propagating start and
9601 // end page values.
9602 // As we go, if we find that, for a frame, we are not contributing one of
9603 // the start/end page values, then our subtree will not contribute this
9604 // value from that frame onward. startPageValue/endPageValue are set to
9605 // null to indicate this.
9606 // Stop iterating when we are not contributing either start or end
9607 // values, when we hit the root frame (no parent), or when we find a
9608 // frame that is not a block frame.
9609 for (nsContainerFrame* ancestorFrame = aParentFrame;
9610 (startPageValue || endPageValue) && ancestorFrame &&
9611 ancestorFrame->IsBlockFrameOrSubclass();
9612 ancestorFrame = ancestorFrame->GetParent()) {
9613 MOZ_ASSERT(!ancestorFrame->GetPrevInFlow(),
9614 "Should not have fragmentation yet");
9615 MOZ_ASSERT(ancestorFrame->mWasVisitedByAutoFrameConstructionPageName,
9616 "Frame should have been visited by "
9617 "AutoFrameConstructionPageName");
9619 // Get what the auto value is, based on this frame's parent.
9620 // For the root frame, `auto` resolves to the empty atom.
9621 const nsContainerFrame* const parent = ancestorFrame->GetParent();
9622 const nsAtom* const parentAuto = MOZ_LIKELY(parent)
9623 ? parent->GetAutoPageValue()
9624 : nsGkAtoms::_empty;
9625 SetPageValues(ancestorFrame, parentAuto, startPageValue,
9626 endPageValue);
9628 // Once we stop contributing start/end values, we know there is a
9629 // sibling subtree that contributed that value to our shared parent
9630 // instead of our starting frame's subtree. This means once
9631 // startPageValue/endPageValue becomes null, indicating that we are no
9632 // longer contributing that page value, it should stay null and we no
9633 // longer need to check for siblings in that direction.
9634 if (startPageValue &&
9635 !FrameHasOnlyPlaceholderPrevSiblings(ancestorFrame)) {
9636 startPageValue = nullptr;
9638 if (endPageValue &&
9639 !FrameHasOnlyPlaceholderNextSiblings(ancestorFrame)) {
9640 endPageValue = nullptr;
9646 if (aParentIsWrapperAnonBox) {
9647 for (nsIFrame* f : aFrameList) {
9648 f->SetParentIsWrapperAnonBox();
9653 void nsCSSFrameConstructor::AddFCItemsForAnonymousContent(
9654 nsFrameConstructorState& aState, nsContainerFrame* aFrame,
9655 const nsTArray<nsIAnonymousContentCreator::ContentInfo>& aAnonymousItems,
9656 FrameConstructionItemList& aItemsToConstruct,
9657 const AutoFrameConstructionPageName&) {
9658 for (const auto& info : aAnonymousItems) {
9659 nsIContent* content = info.mContent;
9660 // Gecko-styled nodes should have no pending restyle flags.
9661 // Assert some things about this content
9662 MOZ_ASSERT(!(content->GetFlags() &
9663 (NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME)),
9664 "Should not be marked as needing frames");
9665 MOZ_ASSERT(!content->GetPrimaryFrame(), "Should have no existing frame");
9666 MOZ_ASSERT(!content->IsComment() && !content->IsProcessingInstruction(),
9667 "Why is someone creating garbage anonymous content");
9669 // Make sure we eagerly performed the servo cascade when the anonymous
9670 // nodes were created.
9671 MOZ_ASSERT(!content->IsElement() || content->AsElement()->HasServoData());
9673 RefPtr<ComputedStyle> computedStyle = ResolveComputedStyle(content);
9675 AddFrameConstructionItemsInternal(aState, content, aFrame, true,
9676 computedStyle, {ItemFlag::AllowPageBreak},
9677 aItemsToConstruct);
9681 void nsCSSFrameConstructor::ProcessChildren(
9682 nsFrameConstructorState& aState, nsIContent* aContent,
9683 ComputedStyle* aComputedStyle, nsContainerFrame* aFrame,
9684 const bool aCanHaveGeneratedContent, nsFrameList& aFrameList,
9685 const bool aAllowBlockStyles, nsIFrame* aPossiblyLeafFrame) {
9686 MOZ_ASSERT(aFrame, "Must have parent frame here");
9687 MOZ_ASSERT(aFrame->GetContentInsertionFrame() == aFrame,
9688 "Parent frame in ProcessChildren should be its own "
9689 "content insertion frame");
9691 const uint32_t kMaxDepth = 2 * MAX_REFLOW_DEPTH;
9692 static_assert(kMaxDepth <= UINT16_MAX, "mCurrentDepth type is too narrow");
9693 AutoRestore<uint16_t> savedDepth(mCurrentDepth);
9694 if (mCurrentDepth != UINT16_MAX) {
9695 ++mCurrentDepth;
9698 if (!aPossiblyLeafFrame) {
9699 aPossiblyLeafFrame = aFrame;
9702 // XXXbz ideally, this would do all the pushing of various
9703 // containing blocks as needed, so callers don't have to do it...
9705 // Check that our parent frame is a block before allowing ::first-letter/line.
9706 // E.g. <button style="display:grid"> should not allow it.
9707 const bool allowFirstPseudos =
9708 aAllowBlockStyles && aFrame->IsBlockFrameOrSubclass();
9709 bool haveFirstLetterStyle = false, haveFirstLineStyle = false;
9710 if (allowFirstPseudos) {
9711 ShouldHaveSpecialBlockStyle(aContent, aComputedStyle, &haveFirstLetterStyle,
9712 &haveFirstLineStyle);
9715 AutoFrameConstructionItemList itemsToConstruct(this);
9716 AutoFrameConstructionPageName pageNameTracker(aState, aFrame);
9718 // If we have first-letter or first-line style then frames can get
9719 // moved around so don't set these flags.
9720 if (allowFirstPseudos && !haveFirstLetterStyle && !haveFirstLineStyle) {
9721 itemsToConstruct.SetLineBoundaryAtStart(true);
9722 itemsToConstruct.SetLineBoundaryAtEnd(true);
9725 // Create any anonymous frames we need here.
9726 AutoTArray<nsIAnonymousContentCreator::ContentInfo, 4> anonymousItems;
9727 GetAnonymousContent(aContent, aPossiblyLeafFrame, anonymousItems);
9728 #ifdef DEBUG
9729 for (uint32_t i = 0; i < anonymousItems.Length(); ++i) {
9730 MOZ_ASSERT(anonymousItems[i].mContent->IsRootOfNativeAnonymousSubtree(),
9731 "Content should know it's an anonymous subtree");
9733 #endif
9734 AddFCItemsForAnonymousContent(aState, aFrame, anonymousItems,
9735 itemsToConstruct, pageNameTracker);
9737 nsBlockFrame* listItem = nullptr;
9738 bool isOutsideMarker = false;
9739 if (!aPossiblyLeafFrame->IsLeaf()) {
9740 // :before/:after content should have the same style parent as normal kids.
9742 // Note that we don't use this style for looking up things like special
9743 // block styles because in some cases involving table pseudo-frames it has
9744 // nothing to do with the parent frame's desired behavior.
9745 auto* styleParentFrame =
9746 nsIFrame::CorrectStyleParentFrame(aFrame, PseudoStyleType::NotPseudo);
9747 ComputedStyle* computedStyle = styleParentFrame->Style();
9749 if (aCanHaveGeneratedContent) {
9750 if (computedStyle->StyleDisplay()->IsListItem() &&
9751 (listItem = do_QueryFrame(aFrame)) &&
9752 !styleParentFrame->IsFieldSetFrame()) {
9753 isOutsideMarker = computedStyle->StyleList()->mListStylePosition ==
9754 StyleListStylePosition::Outside;
9755 ItemFlags extraFlags;
9756 if (isOutsideMarker) {
9757 extraFlags += ItemFlag::IsForOutsideMarker;
9759 CreateGeneratedContentItem(aState, aFrame, *aContent->AsElement(),
9760 *computedStyle, PseudoStyleType::marker,
9761 itemsToConstruct, extraFlags);
9763 // Probe for generated content before
9764 CreateGeneratedContentItem(aState, aFrame, *aContent->AsElement(),
9765 *computedStyle, PseudoStyleType::before,
9766 itemsToConstruct);
9769 const bool addChildItems = MOZ_LIKELY(mCurrentDepth < kMaxDepth);
9770 if (!addChildItems) {
9771 NS_WARNING("ProcessChildren max depth exceeded");
9774 FlattenedChildIterator iter(aContent);
9775 const InsertionPoint insertion(aFrame, aContent);
9776 for (nsIContent* child = iter.GetNextChild(); child;
9777 child = iter.GetNextChild()) {
9778 MOZ_ASSERT(insertion.mContainer == GetInsertionPoint(child).mContainer,
9779 "GetInsertionPoint should agree with us");
9780 if (addChildItems) {
9781 AddFrameConstructionItems(aState, child, iter.ShadowDOMInvolved(),
9782 *computedStyle, insertion, itemsToConstruct);
9783 } else {
9784 ClearLazyBits(child, child->GetNextSibling());
9787 itemsToConstruct.SetParentHasNoShadowDOM(!iter.ShadowDOMInvolved());
9789 if (aCanHaveGeneratedContent) {
9790 // Probe for generated content after
9791 CreateGeneratedContentItem(aState, aFrame, *aContent->AsElement(),
9792 *computedStyle, PseudoStyleType::after,
9793 itemsToConstruct);
9795 } else {
9796 ClearLazyBits(aContent->GetFirstChild(), nullptr);
9799 ConstructFramesFromItemList(aState, itemsToConstruct, aFrame,
9800 /* aParentIsWrapperAnonBox = */ false,
9801 aFrameList);
9803 NS_ASSERTION(!allowFirstPseudos || !aFrame->IsXULBoxFrame(),
9804 "can't be both block and box");
9806 if (listItem) {
9807 if (auto* markerFrame = nsLayoutUtils::GetMarkerFrame(aContent)) {
9808 for (auto* childFrame : aFrameList) {
9809 if (markerFrame == childFrame) {
9810 if (isOutsideMarker) {
9811 // SetMarkerFrameForListItem will add childFrame to the
9812 // FrameChildListID::Bullet
9813 aFrameList.RemoveFrame(childFrame);
9814 auto* grandParent = listItem->GetParent()->GetParent();
9815 if (listItem->Style()->GetPseudoType() ==
9816 PseudoStyleType::columnContent &&
9817 grandParent && grandParent->IsColumnSetWrapperFrame()) {
9818 listItem = do_QueryFrame(grandParent);
9819 MOZ_ASSERT(listItem,
9820 "ColumnSetWrapperFrame is expected to be "
9821 "a nsBlockFrame subclass");
9822 childFrame->SetParent(listItem);
9825 listItem->SetMarkerFrameForListItem(childFrame);
9826 MOZ_ASSERT(listItem->HasAnyStateBits(
9827 NS_BLOCK_FRAME_HAS_OUTSIDE_MARKER) == isOutsideMarker);
9828 #ifdef ACCESSIBILITY
9829 if (nsAccessibilityService* accService = GetAccService()) {
9830 auto* marker = markerFrame->GetContent();
9831 accService->ContentRangeInserted(mPresShell, marker, nullptr);
9833 #endif
9834 break;
9840 if (haveFirstLetterStyle) {
9841 WrapFramesInFirstLetterFrame(aFrame, aFrameList);
9843 if (haveFirstLineStyle) {
9844 WrapFramesInFirstLineFrame(aState, aContent, aFrame, nullptr, aFrameList);
9848 //----------------------------------------------------------------------
9850 // Support for :first-line style
9852 // Special routine to handle placing a list of frames into a block
9853 // frame that has first-line style. The routine ensures that the first
9854 // collection of inline frames end up in a first-line frame.
9855 // NOTE: aState may have containing block information related to a
9856 // different part of the frame tree than where the first line occurs.
9857 // In particular aState may be set up for where ContentInserted or
9858 // ContentAppended is inserting content, which may be some
9859 // non-first-in-flow continuation of the block to which the first-line
9860 // belongs. So this function needs to be careful about how it uses
9861 // aState.
9862 void nsCSSFrameConstructor::WrapFramesInFirstLineFrame(
9863 nsFrameConstructorState& aState, nsIContent* aBlockContent,
9864 nsContainerFrame* aBlockFrame, nsFirstLineFrame* aLineFrame,
9865 nsFrameList& aFrameList) {
9866 // Extract any initial inline frames from aFrameList so we can put them
9867 // in the first-line.
9868 nsFrameList firstLineChildren =
9869 aFrameList.Split([](nsIFrame* f) { return !f->IsInlineOutside(); });
9871 if (firstLineChildren.IsEmpty()) {
9872 // Nothing is supposed to go into the first-line; nothing to do
9873 return;
9876 if (!aLineFrame) {
9877 // Create line frame
9878 ComputedStyle* parentStyle = nsIFrame::CorrectStyleParentFrame(
9879 aBlockFrame, PseudoStyleType::firstLine)
9880 ->Style();
9881 RefPtr<ComputedStyle> firstLineStyle =
9882 GetFirstLineStyle(aBlockContent, parentStyle);
9884 aLineFrame = NS_NewFirstLineFrame(mPresShell, firstLineStyle);
9886 // Initialize the line frame
9887 InitAndRestoreFrame(aState, aBlockContent, aBlockFrame, aLineFrame);
9889 // The lineFrame will be the block's first child; the rest of the
9890 // frame list (after lastInlineFrame) will be the second and
9891 // subsequent children; insert lineFrame into aFrameList.
9892 aFrameList.InsertFrame(nullptr, nullptr, aLineFrame);
9894 NS_ASSERTION(aLineFrame->Style() == firstLineStyle,
9895 "Bogus style on line frame");
9898 // Give the inline frames to the lineFrame <b>after</b> reparenting them
9899 ReparentFrames(this, aLineFrame, firstLineChildren, true);
9900 if (aLineFrame->PrincipalChildList().IsEmpty() &&
9901 aLineFrame->HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
9902 aLineFrame->SetInitialChildList(FrameChildListID::Principal,
9903 std::move(firstLineChildren));
9904 } else {
9905 AppendFrames(aLineFrame, FrameChildListID::Principal,
9906 std::move(firstLineChildren));
9910 // Special routine to handle appending a new frame to a block frame's
9911 // child list. Takes care of placing the new frame into the right
9912 // place when first-line style is present.
9913 void nsCSSFrameConstructor::AppendFirstLineFrames(
9914 nsFrameConstructorState& aState, nsIContent* aBlockContent,
9915 nsContainerFrame* aBlockFrame, nsFrameList& aFrameList) {
9916 // It's possible that aBlockFrame needs to have a first-line frame
9917 // created because it doesn't currently have any children.
9918 const nsFrameList& blockKids = aBlockFrame->PrincipalChildList();
9919 if (blockKids.IsEmpty()) {
9920 WrapFramesInFirstLineFrame(aState, aBlockContent, aBlockFrame, nullptr,
9921 aFrameList);
9922 return;
9925 // Examine the last block child - if it's a first-line frame then
9926 // appended frames need special treatment.
9927 nsIFrame* lastBlockKid = blockKids.LastChild();
9928 if (!lastBlockKid->IsLineFrame()) {
9929 // No first-line frame at the end of the list, therefore there is
9930 // an intervening block between any first-line frame the frames
9931 // we are appending. Therefore, we don't need any special
9932 // treatment of the appended frames.
9933 return;
9936 nsFirstLineFrame* lineFrame = static_cast<nsFirstLineFrame*>(lastBlockKid);
9937 WrapFramesInFirstLineFrame(aState, aBlockContent, aBlockFrame, lineFrame,
9938 aFrameList);
9941 void nsCSSFrameConstructor::CheckForFirstLineInsertion(
9942 nsIFrame* aParentFrame, nsFrameList& aFrameList) {
9943 MOZ_ASSERT(aParentFrame->Style()->HasPseudoElementData(),
9944 "Why were we called?");
9946 if (aFrameList.IsEmpty()) {
9947 // Happens often enough, with the caption stuff. No need to do the ancestor
9948 // walk here.
9949 return;
9952 class RestyleManager* restyleManager = RestyleManager();
9954 // Check whether there's a ::first-line on the path up from aParentFrame.
9955 // Note that we can't stop until we've run out of ancestors with
9956 // pseudo-element data, because the first-letter might be somewhere way up the
9957 // tree; in particular it might be past our containing block.
9958 nsIFrame* ancestor = aParentFrame;
9959 while (ancestor) {
9960 if (!ancestor->Style()->HasPseudoElementData()) {
9961 // We know we won't find a ::first-line now.
9962 return;
9965 if (!ancestor->IsLineFrame()) {
9966 ancestor = ancestor->GetParent();
9967 continue;
9970 if (!ancestor->Style()->IsPseudoElement()) {
9971 // This is a continuation lineframe, not the first line; no need to do
9972 // anything to the styles.
9973 return;
9976 // Fix up the styles of aFrameList for ::first-line.
9977 for (nsIFrame* f : aFrameList) {
9978 restyleManager->ReparentComputedStyleForFirstLine(f);
9980 return;
9984 //----------------------------------------------------------------------
9986 // First-letter support
9988 // Determine how many characters in the text fragment apply to the
9989 // first letter
9990 static int32_t FirstLetterCount(const nsTextFragment* aFragment) {
9991 int32_t count = 0;
9992 int32_t firstLetterLength = 0;
9994 const uint32_t n = aFragment->GetLength();
9995 for (uint32_t i = 0; i < n; i++) {
9996 const char16_t ch = aFragment->CharAt(i);
9997 // FIXME: take content language into account when deciding whitespace.
9998 if (dom::IsSpaceCharacter(ch)) {
9999 if (firstLetterLength) {
10000 break;
10002 count++;
10003 continue;
10005 // XXX I18n
10006 if ((ch == '\'') || (ch == '\"')) {
10007 if (firstLetterLength) {
10008 break;
10010 // keep looping
10011 firstLetterLength = 1;
10012 } else {
10013 count++;
10014 break;
10018 return count;
10021 static bool NeedFirstLetterContinuation(Text* aText) {
10022 MOZ_ASSERT(aText, "null ptr");
10023 int32_t flc = FirstLetterCount(&aText->TextFragment());
10024 int32_t tl = aText->TextDataLength();
10025 return flc < tl;
10028 static bool IsFirstLetterContent(Text* aText) {
10029 return aText->TextDataLength() && !aText->TextIsOnlyWhitespace();
10033 * Create a letter frame, only make it a floating frame.
10035 nsFirstLetterFrame* nsCSSFrameConstructor::CreateFloatingLetterFrame(
10036 nsFrameConstructorState& aState, Text* aTextContent, nsIFrame* aTextFrame,
10037 nsContainerFrame* aParentFrame, ComputedStyle* aParentStyle,
10038 ComputedStyle* aComputedStyle, nsFrameList& aResult) {
10039 MOZ_ASSERT(aParentStyle);
10041 nsFirstLetterFrame* letterFrame =
10042 NS_NewFirstLetterFrame(mPresShell, aComputedStyle);
10043 // We don't want to use a text content for a non-text frame (because we want
10044 // its primary frame to be a text frame).
10045 nsIContent* letterContent = aParentFrame->GetContent();
10046 nsContainerFrame* containingBlock =
10047 aState.GetGeometricParent(*aComputedStyle->StyleDisplay(), aParentFrame);
10048 InitAndRestoreFrame(aState, letterContent, containingBlock, letterFrame);
10050 // Init the text frame to refer to the letter frame.
10052 // Make sure we get a proper style for it (the one passed in is for the letter
10053 // frame and will have the float property set on it; the text frame shouldn't
10054 // have that set).
10055 ServoStyleSet* styleSet = mPresShell->StyleSet();
10056 RefPtr<ComputedStyle> textSC =
10057 styleSet->ResolveStyleForText(aTextContent, aComputedStyle);
10058 aTextFrame->SetComputedStyleWithoutNotification(textSC);
10059 InitAndRestoreFrame(aState, aTextContent, letterFrame, aTextFrame);
10061 // And then give the text frame to the letter frame
10062 SetInitialSingleChild(letterFrame, aTextFrame);
10064 // See if we will need to continue the text frame (does it contain
10065 // more than just the first-letter text or not?) If it does, then we
10066 // create (in advance) a continuation frame for it.
10067 nsIFrame* nextTextFrame = nullptr;
10068 if (NeedFirstLetterContinuation(aTextContent)) {
10069 // Create continuation
10070 nextTextFrame = CreateContinuingFrame(aTextFrame, aParentFrame);
10071 RefPtr<ComputedStyle> newSC =
10072 styleSet->ResolveStyleForText(aTextContent, aParentStyle);
10073 nextTextFrame->SetComputedStyle(newSC);
10076 NS_ASSERTION(aResult.IsEmpty(), "aResult should be an empty nsFrameList!");
10077 // Put the new float before any of the floats in the block we're doing
10078 // first-letter for, that is, before any floats whose parent is
10079 // containingBlock.
10080 nsIFrame* prevSibling = nullptr;
10081 for (nsIFrame* f : aState.mFloatedList) {
10082 if (f->GetParent() == containingBlock) {
10083 break;
10085 prevSibling = f;
10088 aState.AddChild(letterFrame, aResult, letterContent, aParentFrame, false,
10089 true, true, prevSibling);
10091 if (nextTextFrame) {
10092 aResult.AppendFrame(nullptr, nextTextFrame);
10095 return letterFrame;
10099 * Create a new letter frame for aTextFrame. The letter frame will be
10100 * a child of aParentFrame.
10102 void nsCSSFrameConstructor::CreateLetterFrame(
10103 nsContainerFrame* aBlockFrame, nsContainerFrame* aBlockContinuation,
10104 Text* aTextContent, nsContainerFrame* aParentFrame, nsFrameList& aResult) {
10105 NS_ASSERTION(aBlockFrame->IsBlockFrameOrSubclass(), "Not a block frame?");
10107 // Get a ComputedStyle for the first-letter-frame.
10109 // Keep this in sync with nsBlockFrame::UpdatePseudoElementStyles.
10110 nsIFrame* parentFrame = nsIFrame::CorrectStyleParentFrame(
10111 aParentFrame, PseudoStyleType::firstLetter);
10113 ComputedStyle* parentComputedStyle = parentFrame->Style();
10115 // Use content from containing block so that we can actually
10116 // find a matching style rule.
10117 nsIContent* blockContent = aBlockFrame->GetContent();
10119 // Create first-letter style rule
10120 RefPtr<ComputedStyle> sc =
10121 GetFirstLetterStyle(blockContent, parentComputedStyle);
10123 if (sc) {
10124 if (parentFrame->IsLineFrame()) {
10125 nsIFrame* parentIgnoringFirstLine = nsIFrame::CorrectStyleParentFrame(
10126 aBlockFrame, PseudoStyleType::firstLetter);
10128 sc = mPresShell->StyleSet()->ReparentComputedStyle(
10129 sc, parentComputedStyle, parentIgnoringFirstLine->Style(),
10130 parentComputedStyle, blockContent->AsElement());
10133 RefPtr<ComputedStyle> textSC =
10134 mPresShell->StyleSet()->ResolveStyleForText(aTextContent, sc);
10136 // Create a new text frame (the original one will be discarded)
10137 // pass a temporary stylecontext, the correct one will be set
10138 // later. Start off by unsetting the primary frame for
10139 // aTextContent, so it's no longer pointing to the to-be-destroyed
10140 // frame.
10141 // XXXbz it would be really nice to destroy the old frame _first_,
10142 // then create the new one, so we could avoid this hack.
10143 aTextContent->SetPrimaryFrame(nullptr);
10144 nsIFrame* textFrame = NS_NewTextFrame(mPresShell, textSC);
10146 NS_ASSERTION(aBlockContinuation == GetFloatContainingBlock(aParentFrame),
10147 "Containing block is confused");
10148 nsFrameConstructorState state(
10149 mPresShell, GetAbsoluteContainingBlock(aParentFrame, FIXED_POS),
10150 GetAbsoluteContainingBlock(aParentFrame, ABS_POS), aBlockContinuation);
10152 // Create the right type of first-letter frame
10153 const nsStyleDisplay* display = sc->StyleDisplay();
10154 nsFirstLetterFrame* letterFrame;
10155 if (display->IsFloatingStyle() &&
10156 !SVGUtils::IsInSVGTextSubtree(aParentFrame)) {
10157 // Make a floating first-letter frame
10158 letterFrame = CreateFloatingLetterFrame(state, aTextContent, textFrame,
10159 aParentFrame, parentComputedStyle,
10160 sc, aResult);
10161 } else {
10162 // Make an inflow first-letter frame
10163 letterFrame = NS_NewFirstLetterFrame(mPresShell, sc);
10165 // Initialize the first-letter-frame. We don't want to use a text
10166 // content for a non-text frame (because we want its primary frame to
10167 // be a text frame).
10168 nsIContent* letterContent = aParentFrame->GetContent();
10169 letterFrame->Init(letterContent, aParentFrame, nullptr);
10171 InitAndRestoreFrame(state, aTextContent, letterFrame, textFrame);
10173 SetInitialSingleChild(letterFrame, textFrame);
10174 aResult.Clear();
10175 aResult.AppendFrame(nullptr, letterFrame);
10176 NS_ASSERTION(!aBlockFrame->GetPrevContinuation(),
10177 "should have the first continuation here");
10178 aBlockFrame->AddStateBits(NS_BLOCK_HAS_FIRST_LETTER_CHILD);
10180 MOZ_ASSERT(
10181 !aBlockFrame->GetPrevContinuation(),
10182 "Setting up a first-letter frame on a non-first block continuation?");
10183 auto parent =
10184 static_cast<nsContainerFrame*>(aParentFrame->FirstContinuation());
10185 if (MOZ_UNLIKELY(parent->IsLineFrame())) {
10186 parent = static_cast<nsContainerFrame*>(
10187 parent->GetParent()->FirstContinuation());
10189 parent->SetHasFirstLetterChild();
10190 aBlockFrame->SetProperty(nsContainerFrame::FirstLetterProperty(),
10191 letterFrame);
10192 aTextContent->SetPrimaryFrame(textFrame);
10196 void nsCSSFrameConstructor::WrapFramesInFirstLetterFrame(
10197 nsContainerFrame* aBlockFrame, nsFrameList& aBlockFrames) {
10198 aBlockFrame->AddStateBits(NS_BLOCK_HAS_FIRST_LETTER_STYLE);
10200 nsContainerFrame* parentFrame = nullptr;
10201 nsIFrame* textFrame = nullptr;
10202 nsIFrame* prevFrame = nullptr;
10203 nsFrameList letterFrames;
10204 bool stopLooking = false;
10205 WrapFramesInFirstLetterFrame(
10206 aBlockFrame, aBlockFrame, aBlockFrame, aBlockFrames.FirstChild(),
10207 &parentFrame, &textFrame, &prevFrame, letterFrames, &stopLooking);
10208 if (parentFrame) {
10209 if (parentFrame == aBlockFrame) {
10210 // Take textFrame out of the block's frame list and substitute the
10211 // letter frame(s) instead.
10212 aBlockFrames.DestroyFrame(textFrame);
10213 aBlockFrames.InsertFrames(nullptr, prevFrame, std::move(letterFrames));
10214 } else {
10215 // Take the old textFrame out of the inline parent's child list
10216 RemoveFrame(FrameChildListID::Principal, textFrame);
10218 // Insert in the letter frame(s)
10219 parentFrame->InsertFrames(FrameChildListID::Principal, prevFrame, nullptr,
10220 std::move(letterFrames));
10225 void nsCSSFrameConstructor::WrapFramesInFirstLetterFrame(
10226 nsContainerFrame* aBlockFrame, nsContainerFrame* aBlockContinuation,
10227 nsContainerFrame* aParentFrame, nsIFrame* aParentFrameList,
10228 nsContainerFrame** aModifiedParent, nsIFrame** aTextFrame,
10229 nsIFrame** aPrevFrame, nsFrameList& aLetterFrames, bool* aStopLooking) {
10230 nsIFrame* prevFrame = nullptr;
10231 nsIFrame* frame = aParentFrameList;
10233 // This loop attempts to implement "Finding the First Letter":
10234 // https://drafts.csswg.org/css-pseudo-4/#application-in-css
10235 // FIXME: we don't handle nested blocks correctly yet though (bug 214004)
10236 while (frame) {
10237 nsIFrame* nextFrame = frame->GetNextSibling();
10239 // Skip all ::markers and placeholders.
10240 if (frame->Style()->GetPseudoType() == PseudoStyleType::marker ||
10241 frame->IsPlaceholderFrame()) {
10242 prevFrame = frame;
10243 frame = nextFrame;
10244 continue;
10246 LayoutFrameType frameType = frame->Type();
10247 if (LayoutFrameType::Text == frameType) {
10248 // Wrap up first-letter content in a letter frame
10249 Text* textContent = frame->GetContent()->AsText();
10250 if (IsFirstLetterContent(textContent)) {
10251 // Create letter frame to wrap up the text
10252 CreateLetterFrame(aBlockFrame, aBlockContinuation, textContent,
10253 aParentFrame, aLetterFrames);
10255 // Provide adjustment information for parent
10256 *aModifiedParent = aParentFrame;
10257 *aTextFrame = frame;
10258 *aPrevFrame = prevFrame;
10259 *aStopLooking = true;
10260 return;
10262 } else if (IsInlineFrame(frame) && frameType != LayoutFrameType::Br) {
10263 nsIFrame* kids = frame->PrincipalChildList().FirstChild();
10264 WrapFramesInFirstLetterFrame(aBlockFrame, aBlockContinuation,
10265 static_cast<nsContainerFrame*>(frame), kids,
10266 aModifiedParent, aTextFrame, aPrevFrame,
10267 aLetterFrames, aStopLooking);
10268 if (*aStopLooking) {
10269 return;
10271 } else {
10272 // This will stop us looking to create more letter frames. For
10273 // example, maybe the frame-type is "letterFrame" or
10274 // "placeholderFrame". This keeps us from creating extra letter
10275 // frames, and also prevents us from creating letter frames when
10276 // the first real content child of a block is not text (e.g. an
10277 // image, hr, etc.)
10278 *aStopLooking = true;
10279 break;
10282 prevFrame = frame;
10283 frame = nextFrame;
10287 static nsIFrame* FindFirstLetterFrame(nsIFrame* aFrame,
10288 FrameChildListID aListID) {
10289 for (nsIFrame* f : aFrame->GetChildList(aListID)) {
10290 if (f->IsLetterFrame()) {
10291 return f;
10294 return nullptr;
10297 static void ClearHasFirstLetterChildFrom(nsContainerFrame* aParentFrame) {
10298 MOZ_ASSERT(aParentFrame);
10299 auto* parent =
10300 static_cast<nsContainerFrame*>(aParentFrame->FirstContinuation());
10301 if (MOZ_UNLIKELY(parent->IsLineFrame())) {
10302 MOZ_ASSERT(!parent->HasFirstLetterChild());
10303 parent = static_cast<nsContainerFrame*>(
10304 parent->GetParent()->FirstContinuation());
10306 MOZ_ASSERT(parent->HasFirstLetterChild());
10307 parent->ClearHasFirstLetterChild();
10310 void nsCSSFrameConstructor::RemoveFloatingFirstLetterFrames(
10311 PresShell* aPresShell, nsIFrame* aBlockFrame) {
10312 // Look for the first letter frame on the FrameChildListID::Float, then
10313 // FrameChildListID::PushedFloats.
10314 nsIFrame* floatFrame =
10315 ::FindFirstLetterFrame(aBlockFrame, FrameChildListID::Float);
10316 if (!floatFrame) {
10317 floatFrame =
10318 ::FindFirstLetterFrame(aBlockFrame, FrameChildListID::PushedFloats);
10319 if (!floatFrame) {
10320 return;
10324 // Take the text frame away from the letter frame (so it isn't
10325 // destroyed when we destroy the letter frame).
10326 nsIFrame* textFrame = floatFrame->PrincipalChildList().FirstChild();
10327 if (!textFrame) {
10328 return;
10331 // Discover the placeholder frame for the letter frame
10332 nsPlaceholderFrame* placeholderFrame = floatFrame->GetPlaceholderFrame();
10333 if (!placeholderFrame) {
10334 // Somethings really wrong
10335 return;
10337 nsContainerFrame* parentFrame = placeholderFrame->GetParent();
10338 if (!parentFrame) {
10339 // Somethings really wrong
10340 return;
10343 ClearHasFirstLetterChildFrom(parentFrame);
10345 // Create a new text frame with the right style that maps all of the content
10346 // that was previously part of the letter frame (and probably continued
10347 // elsewhere).
10348 ComputedStyle* parentSC = parentFrame->Style();
10349 nsIContent* textContent = textFrame->GetContent();
10350 if (!textContent) {
10351 return;
10353 RefPtr<ComputedStyle> newSC =
10354 aPresShell->StyleSet()->ResolveStyleForText(textContent, parentSC);
10355 nsIFrame* newTextFrame = NS_NewTextFrame(aPresShell, newSC);
10356 newTextFrame->Init(textContent, parentFrame, nullptr);
10358 // Destroy the old text frame's continuations (the old text frame
10359 // will be destroyed when its letter frame is destroyed).
10360 nsIFrame* frameToDelete = textFrame->LastContinuation();
10361 while (frameToDelete != textFrame) {
10362 nsIFrame* nextFrameToDelete = frameToDelete->GetPrevContinuation();
10363 RemoveFrame(FrameChildListID::Principal, frameToDelete);
10364 frameToDelete = nextFrameToDelete;
10367 nsIFrame* prevSibling = placeholderFrame->GetPrevSibling();
10369 // Now that everything is set...
10370 #ifdef NOISY_FIRST_LETTER
10371 printf(
10372 "RemoveFloatingFirstLetterFrames: textContent=%p oldTextFrame=%p "
10373 "newTextFrame=%p\n",
10374 textContent.get(), textFrame, newTextFrame);
10375 #endif
10377 // Remove placeholder frame and the float
10378 RemoveFrame(FrameChildListID::Principal, placeholderFrame);
10380 // Now that the old frames are gone, we can start pointing to our
10381 // new primary frame.
10382 textContent->SetPrimaryFrame(newTextFrame);
10384 // Wallpaper bug 822910.
10385 bool offsetsNeedFixing = prevSibling && prevSibling->IsTextFrame();
10386 if (offsetsNeedFixing) {
10387 prevSibling->AddStateBits(TEXT_OFFSETS_NEED_FIXING);
10390 // Insert text frame in its place
10391 InsertFrames(parentFrame, FrameChildListID::Principal, prevSibling,
10392 nsFrameList(newTextFrame, newTextFrame));
10394 if (offsetsNeedFixing) {
10395 prevSibling->RemoveStateBits(TEXT_OFFSETS_NEED_FIXING);
10399 void nsCSSFrameConstructor::RemoveFirstLetterFrames(
10400 PresShell* aPresShell, nsContainerFrame* aFrame,
10401 nsContainerFrame* aBlockFrame, bool* aStopLooking) {
10402 nsIFrame* prevSibling = nullptr;
10403 nsIFrame* kid = aFrame->PrincipalChildList().FirstChild();
10405 while (kid) {
10406 if (kid->IsLetterFrame()) {
10407 ClearHasFirstLetterChildFrom(aFrame);
10408 nsIFrame* textFrame = kid->PrincipalChildList().FirstChild();
10409 if (!textFrame) {
10410 break;
10413 // Create a new textframe
10414 ComputedStyle* parentSC = aFrame->Style();
10415 if (!parentSC) {
10416 break;
10418 nsIContent* textContent = textFrame->GetContent();
10419 if (!textContent) {
10420 break;
10422 RefPtr<ComputedStyle> newSC =
10423 aPresShell->StyleSet()->ResolveStyleForText(textContent, parentSC);
10424 textFrame = NS_NewTextFrame(aPresShell, newSC);
10425 textFrame->Init(textContent, aFrame, nullptr);
10427 // Next rip out the kid and replace it with the text frame
10428 RemoveFrame(FrameChildListID::Principal, kid);
10430 // Now that the old frames are gone, we can start pointing to our
10431 // new primary frame.
10432 textContent->SetPrimaryFrame(textFrame);
10434 // Wallpaper bug 822910.
10435 bool offsetsNeedFixing = prevSibling && prevSibling->IsTextFrame();
10436 if (offsetsNeedFixing) {
10437 prevSibling->AddStateBits(TEXT_OFFSETS_NEED_FIXING);
10440 // Insert text frame in its place
10441 InsertFrames(aFrame, FrameChildListID::Principal, prevSibling,
10442 nsFrameList(textFrame, textFrame));
10444 if (offsetsNeedFixing) {
10445 prevSibling->RemoveStateBits(TEXT_OFFSETS_NEED_FIXING);
10448 *aStopLooking = true;
10449 NS_ASSERTION(!aBlockFrame->GetPrevContinuation(),
10450 "should have the first continuation here");
10451 aBlockFrame->RemoveStateBits(NS_BLOCK_HAS_FIRST_LETTER_CHILD);
10452 break;
10453 } else if (IsInlineFrame(kid)) {
10454 nsContainerFrame* kidAsContainerFrame = do_QueryFrame(kid);
10455 if (kidAsContainerFrame) {
10456 // Look inside child inline frame for the letter frame.
10457 RemoveFirstLetterFrames(aPresShell, kidAsContainerFrame, aBlockFrame,
10458 aStopLooking);
10459 if (*aStopLooking) {
10460 break;
10464 prevSibling = kid;
10465 kid = kid->GetNextSibling();
10469 void nsCSSFrameConstructor::RemoveLetterFrames(PresShell* aPresShell,
10470 nsContainerFrame* aBlockFrame) {
10471 aBlockFrame =
10472 static_cast<nsContainerFrame*>(aBlockFrame->FirstContinuation());
10473 aBlockFrame->RemoveProperty(nsContainerFrame::FirstLetterProperty());
10474 nsContainerFrame* continuation = aBlockFrame;
10476 bool stopLooking = false;
10477 do {
10478 RemoveFloatingFirstLetterFrames(aPresShell, continuation);
10479 RemoveFirstLetterFrames(aPresShell, continuation, aBlockFrame,
10480 &stopLooking);
10481 if (stopLooking) {
10482 break;
10484 continuation =
10485 static_cast<nsContainerFrame*>(continuation->GetNextContinuation());
10486 } while (continuation);
10489 // Fixup the letter frame situation for the given block
10490 void nsCSSFrameConstructor::RecoverLetterFrames(nsContainerFrame* aBlockFrame) {
10491 aBlockFrame =
10492 static_cast<nsContainerFrame*>(aBlockFrame->FirstContinuation());
10493 nsContainerFrame* continuation = aBlockFrame;
10495 nsContainerFrame* parentFrame = nullptr;
10496 nsIFrame* textFrame = nullptr;
10497 nsIFrame* prevFrame = nullptr;
10498 nsFrameList letterFrames;
10499 bool stopLooking = false;
10500 do {
10501 // XXX shouldn't this bit be set already (bug 408493), assert instead?
10502 continuation->AddStateBits(NS_BLOCK_HAS_FIRST_LETTER_STYLE);
10503 WrapFramesInFirstLetterFrame(
10504 aBlockFrame, continuation, continuation,
10505 continuation->PrincipalChildList().FirstChild(), &parentFrame,
10506 &textFrame, &prevFrame, letterFrames, &stopLooking);
10507 if (stopLooking) {
10508 break;
10510 continuation =
10511 static_cast<nsContainerFrame*>(continuation->GetNextContinuation());
10512 } while (continuation);
10514 if (parentFrame) {
10515 // Take the old textFrame out of the parent's child list
10516 RemoveFrame(FrameChildListID::Principal, textFrame);
10518 // Insert in the letter frame(s)
10519 parentFrame->InsertFrames(FrameChildListID::Principal, prevFrame, nullptr,
10520 std::move(letterFrames));
10524 //----------------------------------------------------------------------
10526 void nsCSSFrameConstructor::ConstructBlock(
10527 nsFrameConstructorState& aState, nsIContent* aContent,
10528 nsContainerFrame* aParentFrame, nsContainerFrame* aContentParentFrame,
10529 ComputedStyle* aComputedStyle, nsContainerFrame** aNewFrame,
10530 nsFrameList& aFrameList, nsIFrame* aPositionedFrameForAbsPosContainer) {
10531 // clang-format off
10533 // If a block frame is in a multi-column subtree, its children may need to
10534 // be chopped into runs of blocks containing column-spans and runs of
10535 // blocks containing no column-spans. Each run containing column-spans
10536 // will be wrapped by an anonymous block. See CreateColumnSpanSiblings() for
10537 // the implementation.
10539 // If a block frame is a multi-column container, its children will need to
10540 // be processed as above. Moreover, it creates a ColumnSetWrapperFrame as
10541 // its outermost frame, and its children which have no
10542 // -moz-column-span-wrapper pseudo will be wrapped in ColumnSetFrames. See
10543 // FinishBuildingColumns() for the implementation.
10545 // The multi-column subtree maintains the following invariants:
10547 // 1) All the frames have the frame state bit
10548 // NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR set, except for top-level
10549 // ColumnSetWrapperFrame and those children in the column-span subtrees.
10551 // 2) The first and last frame under ColumnSetWrapperFrame are always
10552 // ColumnSetFrame.
10554 // 3) ColumnSetFrames are linked together as continuations.
10556 // 4) Those column-span wrappers are *not* linked together with themselves nor
10557 // with the original block frame. The continuation chain consists of the
10558 // original block frame and the original block's continuations wrapping
10559 // non-column-spans.
10561 // For example, this HTML
10562 // <div id="x" style="column-count: 2;">
10563 // <div style="column-span: all">a</div>
10564 // <div id="y">
10565 // b
10566 // <div style="column-span: all">c</div>
10567 // <div style="column-span: all">d</div>
10568 // e
10569 // </div>
10570 // </div>
10571 // <div style="column-span: all">f</div>
10573 // yields the following frame tree.
10575 // A) ColumnSetWrapper (original style)
10576 // B) ColumnSet (-moz-column-set) <-- always created by BeginBuildingColumns
10577 // C) Block (-moz-column-content)
10578 // D) Block (-moz-column-span-wrapper, created by x)
10579 // E) Block (div)
10580 // F) Text ("a")
10581 // G) ColumnSet (-moz-column-set)
10582 // H) Block (-moz-column-content, created by x)
10583 // I) Block (div, y)
10584 // J) Text ("b")
10585 // K) Block (-moz-column-span-wrapper, created by x)
10586 // L) Block (-moz-column-span-wrapper, created by y)
10587 // M) Block (div, new BFC)
10588 // N) Text ("c")
10589 // O) Block (div, new BFC)
10590 // P) Text ("d")
10591 // Q) ColumnSet (-moz-column-set)
10592 // R) Block (-moz-column-content, created by x)
10593 // S) Block (div, y)
10594 // T) Text ("e")
10595 // U) Block (div, new BFC) <-- not in multi-column hierarchy
10596 // V) Text ("f")
10598 // ColumnSet linkage described in 3): B -> G -> Q
10600 // Block linkage described in 4): C -> H -> R and I -> S
10602 // clang-format on
10604 nsBlockFrame* blockFrame = do_QueryFrame(*aNewFrame);
10605 MOZ_ASSERT(blockFrame && blockFrame->IsBlockFrame(), "not a block frame?");
10607 // Create column hierarchy if necessary.
10608 const bool needsColumn =
10609 aComputedStyle->StyleColumn()->IsColumnContainerStyle();
10610 if (needsColumn) {
10611 *aNewFrame = BeginBuildingColumns(aState, aContent, aParentFrame,
10612 blockFrame, aComputedStyle);
10614 if (aPositionedFrameForAbsPosContainer == blockFrame) {
10615 aPositionedFrameForAbsPosContainer = *aNewFrame;
10617 } else {
10618 // No need to create column hierarchy. Initialize block frame.
10619 blockFrame->SetComputedStyleWithoutNotification(aComputedStyle);
10620 InitAndRestoreFrame(aState, aContent, aParentFrame, blockFrame);
10623 aState.AddChild(*aNewFrame, aFrameList, aContent,
10624 aContentParentFrame ? aContentParentFrame : aParentFrame);
10625 if (!mRootElementFrame) {
10626 // The frame we're constructing will be the root element frame.
10627 SetRootElementFrameAndConstructCanvasAnonContent(*aNewFrame, aState,
10628 aFrameList);
10631 // We should make the outer frame be the absolute containing block,
10632 // if one is required. We have to do this because absolute
10633 // positioning must be computed with respect to the CSS dimensions
10634 // of the element, which are the dimensions of the outer block. But
10635 // we can't really do that because only blocks can have absolute
10636 // children. So use the block and try to compensate with hacks
10637 // in nsBlockFrame::CalculateContainingBlockSizeForAbsolutes.
10638 nsFrameConstructorSaveState absoluteSaveState;
10639 (*aNewFrame)->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
10640 if (aPositionedFrameForAbsPosContainer) {
10641 aState.PushAbsoluteContainingBlock(
10642 *aNewFrame, aPositionedFrameForAbsPosContainer, absoluteSaveState);
10645 nsFrameConstructorSaveState floatSaveState;
10646 aState.MaybePushFloatContainingBlock(blockFrame, floatSaveState);
10648 if (aParentFrame->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR) &&
10649 !ShouldSuppressColumnSpanDescendants(aParentFrame)) {
10650 blockFrame->AddStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR);
10653 // Process the child content
10654 nsFrameList childList;
10655 ProcessChildren(aState, aContent, aComputedStyle, blockFrame, true, childList,
10656 true);
10658 if (!MayNeedToCreateColumnSpanSiblings(blockFrame, childList)) {
10659 // No need to create column-span siblings.
10660 blockFrame->SetInitialChildList(FrameChildListID::Principal,
10661 std::move(childList));
10662 return;
10665 // Extract any initial non-column-span kids, and put them in block frame's
10666 // child list.
10667 nsFrameList initialNonColumnSpanKids =
10668 childList.Split([](nsIFrame* f) { return f->IsColumnSpan(); });
10669 blockFrame->SetInitialChildList(FrameChildListID::Principal,
10670 std::move(initialNonColumnSpanKids));
10672 if (childList.IsEmpty()) {
10673 // No more kids to process (there weren't any column-span kids).
10674 return;
10677 nsFrameList columnSpanSiblings = CreateColumnSpanSiblings(
10678 aState, blockFrame, childList,
10679 // If we're constructing a column container, pass nullptr as
10680 // aPositionedFrame to forbid reparenting absolute/fixed positioned frames
10681 // to column contents or column-span wrappers.
10682 needsColumn ? nullptr : aPositionedFrameForAbsPosContainer);
10684 if (needsColumn) {
10685 // We're constructing a column container; need to finish building it.
10686 FinishBuildingColumns(aState, *aNewFrame, blockFrame, columnSpanSiblings);
10687 } else {
10688 // We're constructing a normal block which has column-span children in a
10689 // column hierarchy such as "x" in the following example.
10691 // <div style="column-count: 2">
10692 // <div id="x">
10693 // <div>normal child</div>
10694 // <div style="column-span">spanner</div>
10695 // </div>
10696 // </div>
10697 aFrameList.AppendFrames(nullptr, std::move(columnSpanSiblings));
10700 MOZ_ASSERT(columnSpanSiblings.IsEmpty(),
10701 "The column-span siblings should be moved to the proper place!");
10704 nsBlockFrame* nsCSSFrameConstructor::BeginBuildingColumns(
10705 nsFrameConstructorState& aState, nsIContent* aContent,
10706 nsContainerFrame* aParentFrame, nsContainerFrame* aColumnContent,
10707 ComputedStyle* aComputedStyle) {
10708 MOZ_ASSERT(aColumnContent->IsBlockFrame(),
10709 "aColumnContent should be a block frame.");
10710 MOZ_ASSERT(aComputedStyle->StyleColumn()->IsColumnContainerStyle(),
10711 "No need to build a column hierarchy!");
10713 // The initial column hierarchy looks like this:
10715 // ColumnSetWrapper (original style)
10716 // ColumnSet (-moz-column-set)
10717 // Block (-moz-column-content)
10719 nsBlockFrame* columnSetWrapper = NS_NewColumnSetWrapperFrame(
10720 mPresShell, aComputedStyle, nsFrameState(NS_FRAME_OWNS_ANON_BOXES));
10721 InitAndRestoreFrame(aState, aContent, aParentFrame, columnSetWrapper);
10722 if (aParentFrame->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR) &&
10723 !ShouldSuppressColumnSpanDescendants(aParentFrame)) {
10724 columnSetWrapper->AddStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR);
10727 AutoFrameConstructionPageName pageNameTracker(aState, columnSetWrapper);
10728 RefPtr<ComputedStyle> columnSetStyle =
10729 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
10730 PseudoStyleType::columnSet, aComputedStyle);
10731 nsContainerFrame* columnSet = NS_NewColumnSetFrame(
10732 mPresShell, columnSetStyle, nsFrameState(NS_FRAME_OWNS_ANON_BOXES));
10733 InitAndRestoreFrame(aState, aContent, columnSetWrapper, columnSet);
10734 columnSet->AddStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR);
10736 RefPtr<ComputedStyle> blockStyle =
10737 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
10738 PseudoStyleType::columnContent, columnSetStyle);
10739 aColumnContent->SetComputedStyleWithoutNotification(blockStyle);
10740 InitAndRestoreFrame(aState, aContent, columnSet, aColumnContent);
10741 aColumnContent->AddStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR |
10742 NS_BLOCK_FORMATTING_CONTEXT_STATE_BITS);
10744 // Set up the parent-child chain.
10745 SetInitialSingleChild(columnSetWrapper, columnSet);
10746 SetInitialSingleChild(columnSet, aColumnContent);
10748 return columnSetWrapper;
10751 void nsCSSFrameConstructor::FinishBuildingColumns(
10752 nsFrameConstructorState& aState, nsContainerFrame* aColumnSetWrapper,
10753 nsContainerFrame* aColumnContent, nsFrameList& aColumnContentSiblings) {
10754 nsContainerFrame* prevColumnSet = aColumnContent->GetParent();
10756 MOZ_ASSERT(prevColumnSet->IsColumnSetFrame() &&
10757 prevColumnSet->GetParent() == aColumnSetWrapper,
10758 "Should have established column hierarchy!");
10760 // Tag the first ColumnSet to have column-span siblings so that the bit can
10761 // propagate to all the continuations. We don't want the last ColumnSet to
10762 // have this bit, so we will unset the bit for it at the end of this function.
10763 prevColumnSet->SetHasColumnSpanSiblings(true);
10765 nsFrameList finalList;
10766 while (aColumnContentSiblings.NotEmpty()) {
10767 nsIFrame* f = aColumnContentSiblings.RemoveFirstChild();
10768 if (f->IsColumnSpan()) {
10769 // Do nothing for column-span wrappers. Just move it to the final
10770 // items.
10771 finalList.AppendFrame(aColumnSetWrapper, f);
10772 } else {
10773 auto* continuingColumnSet = static_cast<nsContainerFrame*>(
10774 CreateContinuingFrame(prevColumnSet, aColumnSetWrapper, false));
10775 MOZ_ASSERT(continuingColumnSet->HasColumnSpanSiblings(),
10776 "The bit should propagate to the next continuation!");
10778 f->SetParent(continuingColumnSet);
10779 SetInitialSingleChild(continuingColumnSet, f);
10780 finalList.AppendFrame(aColumnSetWrapper, continuingColumnSet);
10781 prevColumnSet = continuingColumnSet;
10785 // Unset the bit because the last ColumnSet has no column-span siblings.
10786 prevColumnSet->SetHasColumnSpanSiblings(false);
10788 aColumnSetWrapper->AppendFrames(FrameChildListID::Principal,
10789 std::move(finalList));
10792 bool nsCSSFrameConstructor::MayNeedToCreateColumnSpanSiblings(
10793 nsContainerFrame* aBlockFrame, const nsFrameList& aChildList) {
10794 if (!aBlockFrame->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR)) {
10795 // The block frame isn't in a multi-column block formatting context.
10796 return false;
10799 if (ShouldSuppressColumnSpanDescendants(aBlockFrame)) {
10800 // No need to create column-span siblings for a frame that suppresses them.
10801 return false;
10804 if (aChildList.IsEmpty()) {
10805 // No child needs to be processed.
10806 return false;
10809 // Need to actually look into the child list.
10810 return true;
10813 nsFrameList nsCSSFrameConstructor::CreateColumnSpanSiblings(
10814 nsFrameConstructorState& aState, nsContainerFrame* aInitialBlock,
10815 nsFrameList& aChildList, nsIFrame* aPositionedFrame) {
10816 MOZ_ASSERT(aInitialBlock->IsBlockFrameOrSubclass());
10817 MOZ_ASSERT(!aPositionedFrame || aPositionedFrame->IsAbsPosContainingBlock());
10819 nsIContent* const content = aInitialBlock->GetContent();
10820 nsContainerFrame* const parentFrame = aInitialBlock->GetParent();
10821 const bool isInitialBlockFloatCB = aInitialBlock->IsFloatContainingBlock();
10823 nsFrameList siblings;
10824 nsContainerFrame* lastNonColumnSpanWrapper = aInitialBlock;
10826 // Tag the first non-column-span wrapper to have column-span siblings so that
10827 // the bit can propagate to all the continuations. We don't want the last
10828 // wrapper to have this bit, so we will unset the bit for it at the end of
10829 // this function.
10830 lastNonColumnSpanWrapper->SetHasColumnSpanSiblings(true);
10831 do {
10832 MOZ_ASSERT(aChildList.NotEmpty(), "Why call this if child list is empty?");
10833 MOZ_ASSERT(aChildList.FirstChild()->IsColumnSpan(),
10834 "Must have the child starting with column-span!");
10836 // Grab the consecutive column-span kids, and reparent them into a
10837 // block frame.
10838 RefPtr<ComputedStyle> columnSpanWrapperStyle =
10839 mPresShell->StyleSet()->ResolveNonInheritingAnonymousBoxStyle(
10840 PseudoStyleType::columnSpanWrapper);
10841 nsBlockFrame* columnSpanWrapper =
10842 NS_NewBlockFrame(mPresShell, columnSpanWrapperStyle);
10843 InitAndRestoreFrame(aState, content, parentFrame, columnSpanWrapper, false);
10844 columnSpanWrapper->AddStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR |
10845 NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
10847 nsFrameList columnSpanKids =
10848 aChildList.Split([](nsIFrame* f) { return !f->IsColumnSpan(); });
10849 columnSpanKids.ApplySetParent(columnSpanWrapper);
10850 columnSpanWrapper->SetInitialChildList(FrameChildListID::Principal,
10851 std::move(columnSpanKids));
10852 if (aPositionedFrame) {
10853 aState.ReparentAbsoluteItems(columnSpanWrapper);
10856 siblings.AppendFrame(nullptr, columnSpanWrapper);
10858 // Grab the consecutive non-column-span kids, and reparent them into a new
10859 // continuation of the last non-column-span wrapper frame.
10860 auto* nonColumnSpanWrapper = static_cast<nsContainerFrame*>(
10861 CreateContinuingFrame(lastNonColumnSpanWrapper, parentFrame, false));
10862 nonColumnSpanWrapper->AddStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR |
10863 NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
10864 MOZ_ASSERT(nonColumnSpanWrapper->HasColumnSpanSiblings(),
10865 "The bit should propagate to the next continuation!");
10867 if (aChildList.NotEmpty()) {
10868 nsFrameList nonColumnSpanKids =
10869 aChildList.Split([](nsIFrame* f) { return f->IsColumnSpan(); });
10871 nonColumnSpanKids.ApplySetParent(nonColumnSpanWrapper);
10872 nonColumnSpanWrapper->SetInitialChildList(FrameChildListID::Principal,
10873 std::move(nonColumnSpanKids));
10874 if (aPositionedFrame) {
10875 aState.ReparentAbsoluteItems(nonColumnSpanWrapper);
10877 if (isInitialBlockFloatCB) {
10878 aState.ReparentFloats(nonColumnSpanWrapper);
10882 siblings.AppendFrame(nullptr, nonColumnSpanWrapper);
10884 lastNonColumnSpanWrapper = nonColumnSpanWrapper;
10885 } while (aChildList.NotEmpty());
10887 // Unset the bit because the last non-column-span wrapper has no column-span
10888 // siblings.
10889 lastNonColumnSpanWrapper->SetHasColumnSpanSiblings(false);
10891 return siblings;
10894 bool nsCSSFrameConstructor::MaybeRecreateForColumnSpan(
10895 nsFrameConstructorState& aState, nsContainerFrame* aParentFrame,
10896 nsFrameList& aFrameList, nsIFrame* aPrevSibling) {
10897 if (!aParentFrame->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR)) {
10898 return false;
10901 if (aFrameList.IsEmpty()) {
10902 return false;
10905 MOZ_ASSERT(!IsFramePartOfIBSplit(aParentFrame),
10906 "We should have wiped aParentFrame in WipeContainingBlock if it's "
10907 "part of IB split!");
10909 nsIFrame* nextSibling = ::GetInsertNextSibling(aParentFrame, aPrevSibling);
10910 if (!nextSibling && IsLastContinuationForColumnContent(aParentFrame)) {
10911 // We are appending a list of frames to the last continuation of a
10912 // ::-moz-column-content. This is the case where we can fix the frame tree
10913 // instead of reframing the containing block. Return false and let
10914 // AppendFramesToParent() deal with this.
10915 return false;
10918 auto HasColumnSpan = [](const nsFrameList& aList) {
10919 for (nsIFrame* f : aList) {
10920 if (f->IsColumnSpan()) {
10921 return true;
10924 return false;
10927 if (HasColumnSpan(aFrameList)) {
10928 // If any frame in the frame list has "column-span:all" style, i.e. a
10929 // -moz-column-span-wrapper frame, we need to reframe the multi-column
10930 // containing block.
10932 // We can only be here if none of the new inserted nsIContent* nodes (via
10933 // ContentAppended or ContentRangeInserted) have column-span:all style, yet
10934 // some of them have column-span:all descendants. Sadly, there's no way to
10935 // detect this by checking FrameConstructionItems in WipeContainingBlock().
10936 // Otherwise, we would have already wiped the multi-column containing block.
10937 PROFILER_MARKER("Reframe multi-column after constructing frame list",
10938 LAYOUT, {}, Tracing, "Layout");
10940 // aFrameList can contain placeholder frames. In order to destroy their
10941 // associated out-of-flow frames properly, we need to manually flush all the
10942 // out-of-flow frames in aState to their container frames.
10943 aState.ProcessFrameInsertionsForAllLists();
10944 aFrameList.DestroyFrames();
10945 RecreateFramesForContent(
10946 GetMultiColumnContainingBlockFor(aParentFrame)->GetContent(),
10947 InsertionKind::Async);
10948 return true;
10951 return false;
10954 nsIFrame* nsCSSFrameConstructor::ConstructInline(
10955 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
10956 nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay,
10957 nsFrameList& aFrameList) {
10958 // If an inline frame has non-inline kids, then we chop up the child list
10959 // into runs of blocks and runs of inlines, create anonymous block frames to
10960 // contain the runs of blocks, inline frames with our style for the runs of
10961 // inlines, and put all these frames, in order, into aFrameList.
10963 // When there are column-span blocks in a run of blocks, instead of creating
10964 // an anonymous block to wrap them, we create multiple anonymous blocks,
10965 // wrapping runs of non-column-spans and runs of column-spans.
10967 // We return the the first one. The whole setup is called an {ib}
10968 // split; in what follows "frames in the split" refers to the anonymous blocks
10969 // and inlines that contain our children.
10971 // {ib} splits maintain the following invariants:
10972 // 1) All frames in the split have the NS_FRAME_PART_OF_IBSPLIT bit
10973 // set.
10975 // 2) Each frame in the split has the nsIFrame::IBSplitSibling
10976 // property pointing to the next frame in the split, except for the last
10977 // one, which does not have it set.
10979 // 3) Each frame in the split has the nsIFrame::IBSplitPrevSibling
10980 // property pointing to the previous frame in the split, except for the
10981 // first one, which does not have it set.
10983 // 4) The first and last frame in the split are always inlines.
10985 // 5) The frames wrapping runs of non-column-spans are linked together as
10986 // continuations. The frames wrapping runs of column-spans are *not*
10987 // linked with each other nor with other non-column-span wrappers.
10989 // 6) The first and last frame in the chains of blocks are always wrapping
10990 // non-column-spans. Both of them are created even if they're empty.
10992 // An invariant that is NOT maintained is that the wrappers are actually
10993 // linked via GetNextSibling linkage. A simple example is an inline
10994 // containing an inline that contains a block. The three parts of the inner
10995 // inline end up with three different parents.
10997 // For example, this HTML:
10998 // <span>
10999 // <div>a</div>
11000 // <span>
11001 // b
11002 // <div>c</div>
11003 // </span>
11004 // d
11005 // <div>e</div>
11006 // f
11007 // </span>
11008 // Gives the following frame tree:
11010 // Inline (outer span)
11011 // Block (anonymous, outer span)
11012 // Block (div)
11013 // Text("a")
11014 // Inline (outer span)
11015 // Inline (inner span)
11016 // Text("b")
11017 // Block (anonymous, outer span)
11018 // Block (anonymous, inner span)
11019 // Block (div)
11020 // Text("c")
11021 // Inline (outer span)
11022 // Inline (inner span)
11023 // Text("d")
11024 // Block (anonymous, outer span)
11025 // Block (div)
11026 // Text("e")
11027 // Inline (outer span)
11028 // Text("f")
11030 nsIContent* const content = aItem.mContent;
11031 ComputedStyle* const computedStyle = aItem.mComputedStyle;
11033 nsInlineFrame* newFrame = NS_NewInlineFrame(mPresShell, computedStyle);
11035 // Initialize the frame
11036 InitAndRestoreFrame(aState, content, aParentFrame, newFrame);
11038 // definition cannot be inside next block because the object's destructor is
11039 // significant. this is part of the fix for bug 42372
11040 nsFrameConstructorSaveState absoluteSaveState;
11042 bool isAbsPosCB = newFrame->IsAbsPosContainingBlock();
11043 newFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
11044 if (isAbsPosCB) {
11045 // Relatively positioned frames becomes a container for child
11046 // frames that are positioned
11047 aState.PushAbsoluteContainingBlock(newFrame, newFrame, absoluteSaveState);
11050 if (aParentFrame->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR) &&
11051 !ShouldSuppressColumnSpanDescendants(aParentFrame)) {
11052 newFrame->AddStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR);
11055 // Process the child content
11056 nsFrameList childList;
11057 ConstructFramesFromItemList(aState, aItem.mChildItems, newFrame,
11058 /* aParentIsWrapperAnonBox = */ false, childList);
11060 nsIFrame* firstBlock = nullptr;
11061 if (!aItem.mIsAllInline) {
11062 for (nsIFrame* f : childList) {
11063 if (f->IsBlockOutside()) {
11064 firstBlock = f;
11065 break;
11070 if (aItem.mIsAllInline || !firstBlock) {
11071 // This part is easy. We either already know we have no non-inline kids,
11072 // or haven't found any when constructing actual frames (the latter can
11073 // happen only if out-of-flows that we thought had no containing block
11074 // acquired one when ancestor inline frames and {ib} splits got
11075 // constructed). Just put all the kids into the single inline frame and
11076 // bail.
11077 newFrame->SetInitialChildList(FrameChildListID::Principal,
11078 std::move(childList));
11079 aState.AddChild(newFrame, aFrameList, content, aParentFrame);
11080 return newFrame;
11083 // This inline frame contains several types of children. Therefore this frame
11084 // has to be chopped into several pieces, as described above.
11086 // Grab the first inline's kids
11087 nsFrameList firstInlineKids = childList.TakeFramesBefore(firstBlock);
11088 newFrame->SetInitialChildList(FrameChildListID::Principal,
11089 std::move(firstInlineKids));
11091 aFrameList.AppendFrame(nullptr, newFrame);
11093 newFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
11094 CreateIBSiblings(aState, newFrame, isAbsPosCB, childList, aFrameList);
11096 return newFrame;
11099 void nsCSSFrameConstructor::CreateIBSiblings(nsFrameConstructorState& aState,
11100 nsContainerFrame* aInitialInline,
11101 bool aIsAbsPosCB,
11102 nsFrameList& aChildList,
11103 nsFrameList& aSiblings) {
11104 MOZ_ASSERT(aIsAbsPosCB == aInitialInline->IsAbsPosContainingBlock());
11106 nsIContent* content = aInitialInline->GetContent();
11107 ComputedStyle* computedStyle = aInitialInline->Style();
11108 nsContainerFrame* parentFrame = aInitialInline->GetParent();
11110 // Resolve the right style for our anonymous blocks.
11112 // The distinction in styles is needed because of CSS 2.1, section
11113 // 9.2.1.1, which says:
11115 // When such an inline box is affected by relative positioning, any
11116 // resulting translation also affects the block-level box contained
11117 // in the inline box.
11118 RefPtr<ComputedStyle> blockSC =
11119 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
11120 PseudoStyleType::mozBlockInsideInlineWrapper, computedStyle);
11122 nsContainerFrame* lastNewInline =
11123 static_cast<nsContainerFrame*>(aInitialInline->FirstContinuation());
11124 do {
11125 // On entry to this loop aChildList is not empty and the first frame in it
11126 // is block-level.
11127 MOZ_ASSERT(aChildList.NotEmpty(), "Should have child items");
11128 MOZ_ASSERT(aChildList.FirstChild()->IsBlockOutside(),
11129 "Must have list starting with block");
11131 // The initial run of blocks belongs to an anonymous block that we create
11132 // right now. The anonymous block will be the parent of these block
11133 // children of the inline.
11134 nsBlockFrame* blockFrame = NS_NewBlockFrame(mPresShell, blockSC);
11135 InitAndRestoreFrame(aState, content, parentFrame, blockFrame, false);
11136 if (aInitialInline->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR)) {
11137 blockFrame->AddStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR);
11140 // Find the first non-block child which defines the end of our block kids
11141 // and the start of our next inline's kids
11142 nsFrameList blockKids =
11143 aChildList.Split([](nsIFrame* f) { return !f->IsBlockOutside(); });
11145 if (!aInitialInline->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR)) {
11146 MoveChildrenTo(aInitialInline, blockFrame, blockKids);
11148 SetFrameIsIBSplit(lastNewInline, blockFrame);
11149 aSiblings.AppendFrame(nullptr, blockFrame);
11150 } else {
11151 // Extract any initial non-column-span frames, and put them in
11152 // blockFrame's child list.
11153 nsFrameList initialNonColumnSpanKids =
11154 blockKids.Split([](nsIFrame* f) { return f->IsColumnSpan(); });
11155 MoveChildrenTo(aInitialInline, blockFrame, initialNonColumnSpanKids);
11157 SetFrameIsIBSplit(lastNewInline, blockFrame);
11158 aSiblings.AppendFrame(nullptr, blockFrame);
11160 if (blockKids.NotEmpty()) {
11161 // Although SetFrameIsIBSplit() will add NS_FRAME_PART_OF_IBSPLIT for
11162 // blockFrame later, we manually add the bit earlier here to make all
11163 // the continuations of blockFrame created in
11164 // CreateColumnSpanSiblings(), i.e. non-column-span wrappers, have the
11165 // bit via nsIFrame::Init().
11166 blockFrame->AddStateBits(NS_FRAME_PART_OF_IBSPLIT);
11168 nsFrameList columnSpanSiblings =
11169 CreateColumnSpanSiblings(aState, blockFrame, blockKids,
11170 aIsAbsPosCB ? aInitialInline : nullptr);
11171 aSiblings.AppendFrames(nullptr, std::move(columnSpanSiblings));
11175 // Now grab the initial inlines in aChildList and put them into an inline
11176 // frame.
11177 nsInlineFrame* inlineFrame = NS_NewInlineFrame(mPresShell, computedStyle);
11178 InitAndRestoreFrame(aState, content, parentFrame, inlineFrame, false);
11179 inlineFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
11180 if (aInitialInline->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR)) {
11181 inlineFrame->AddStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR);
11184 if (aIsAbsPosCB) {
11185 inlineFrame->MarkAsAbsoluteContainingBlock();
11188 if (aChildList.NotEmpty()) {
11189 nsFrameList inlineKids =
11190 aChildList.Split([](nsIFrame* f) { return f->IsBlockOutside(); });
11191 MoveChildrenTo(aInitialInline, inlineFrame, inlineKids);
11194 SetFrameIsIBSplit(blockFrame, inlineFrame);
11195 aSiblings.AppendFrame(nullptr, inlineFrame);
11196 lastNewInline = inlineFrame;
11197 } while (aChildList.NotEmpty());
11199 SetFrameIsIBSplit(lastNewInline, nullptr);
11202 void nsCSSFrameConstructor::BuildInlineChildItems(
11203 nsFrameConstructorState& aState, FrameConstructionItem& aParentItem,
11204 bool aItemIsWithinSVGText, bool aItemAllowsTextPathChild) {
11205 ComputedStyle* const parentComputedStyle = aParentItem.mComputedStyle;
11206 nsIContent* const parentContent = aParentItem.mContent;
11208 if (!aItemIsWithinSVGText) {
11209 if (parentComputedStyle->StyleDisplay()->IsListItem()) {
11210 CreateGeneratedContentItem(aState, nullptr, *parentContent->AsElement(),
11211 *parentComputedStyle, PseudoStyleType::marker,
11212 aParentItem.mChildItems);
11214 // Probe for generated content before
11215 CreateGeneratedContentItem(aState, nullptr, *parentContent->AsElement(),
11216 *parentComputedStyle, PseudoStyleType::before,
11217 aParentItem.mChildItems);
11220 ItemFlags flags;
11221 if (aItemIsWithinSVGText) {
11222 flags += ItemFlag::IsWithinSVGText;
11224 if (aItemAllowsTextPathChild &&
11225 aParentItem.mContent->IsSVGElement(nsGkAtoms::a)) {
11226 flags += ItemFlag::AllowTextPathChild;
11229 FlattenedChildIterator iter(parentContent);
11230 for (nsIContent* content = iter.GetNextChild(); content;
11231 content = iter.GetNextChild()) {
11232 AddFrameConstructionItems(aState, content, iter.ShadowDOMInvolved(),
11233 *parentComputedStyle, InsertionPoint(),
11234 aParentItem.mChildItems, flags);
11237 if (!aItemIsWithinSVGText) {
11238 // Probe for generated content after
11239 CreateGeneratedContentItem(aState, nullptr, *parentContent->AsElement(),
11240 *parentComputedStyle, PseudoStyleType::after,
11241 aParentItem.mChildItems);
11244 aParentItem.mIsAllInline = aParentItem.mChildItems.AreAllItemsInline();
11247 // return whether it's ok to append (in the AppendFrames sense) to
11248 // aParentFrame if our nextSibling is aNextSibling. aParentFrame must
11249 // be an ib-split inline.
11250 static bool IsSafeToAppendToIBSplitInline(nsIFrame* aParentFrame,
11251 nsIFrame* aNextSibling) {
11252 MOZ_ASSERT(IsInlineFrame(aParentFrame), "Must have an inline parent here");
11254 do {
11255 NS_ASSERTION(IsFramePartOfIBSplit(aParentFrame),
11256 "How is this not part of an ib-split?");
11257 if (aNextSibling || aParentFrame->GetNextContinuation() ||
11258 GetIBSplitSibling(aParentFrame)) {
11259 return false;
11262 aNextSibling = aParentFrame->GetNextSibling();
11263 aParentFrame = aParentFrame->GetParent();
11264 } while (IsInlineFrame(aParentFrame));
11266 return true;
11269 bool nsCSSFrameConstructor::WipeInsertionParent(nsContainerFrame* aFrame) {
11270 #define TRACE(reason) \
11271 PROFILER_MARKER("WipeInsertionParent: " reason, LAYOUT, {}, Tracing, \
11272 "Layout");
11274 const LayoutFrameType frameType = aFrame->Type();
11276 // FIXME(emilio): This looks terribly inefficient if you insert elements deep
11277 // in a MathML subtree.
11278 if (aFrame->IsFrameOfType(nsIFrame::eMathML)) {
11279 TRACE("MathML");
11280 RecreateFramesForContent(aFrame->GetContent(), InsertionKind::Async);
11281 return true;
11284 // A ruby-related frame that's getting new children.
11285 // The situation for ruby is complex, especially when interacting with
11286 // spaces. It contains these two special cases apart from tables:
11287 // 1) There are effectively three types of white spaces in ruby frames
11288 // we handle differently: leading/tailing/inter-level space,
11289 // inter-base/inter-annotation space, and inter-segment space.
11290 // These three types of spaces can be converted to each other when
11291 // their sibling changes.
11292 // 2) The first effective child of a ruby frame must always be a ruby
11293 // base container. It should be created or destroyed accordingly.
11294 if (IsRubyPseudo(aFrame) || frameType == LayoutFrameType::Ruby ||
11295 RubyUtils::IsRubyContainerBox(frameType)) {
11296 // We want to optimize it better, and avoid reframing as much as
11297 // possible. But given the cases above, and the fact that a ruby
11298 // usually won't be very large, it should be fine to reframe it.
11299 TRACE("Ruby");
11300 RecreateFramesForContent(aFrame->GetContent(), InsertionKind::Async);
11301 return true;
11304 // Reframe the multi-column container whenever elements insert/append
11305 // into it because we need to reconstruct column-span split.
11306 if (aFrame->IsColumnSetWrapperFrame()) {
11307 TRACE("Multi-column");
11308 RecreateFramesForContent(aFrame->GetContent(), InsertionKind::Async);
11309 return true;
11312 return false;
11314 #undef TRACE
11317 bool nsCSSFrameConstructor::WipeContainingBlock(
11318 nsFrameConstructorState& aState, nsIFrame* aContainingBlock,
11319 nsIFrame* aFrame, FrameConstructionItemList& aItems, bool aIsAppend,
11320 nsIFrame* aPrevSibling) {
11321 #define TRACE(reason) \
11322 PROFILER_MARKER("WipeContainingBlock: " reason, LAYOUT, {}, Tracing, \
11323 "Layout");
11325 if (aItems.IsEmpty()) {
11326 return false;
11329 // Before we go and append the frames, we must check for several
11330 // special situations.
11332 if (aFrame->GetContent() == mDocument->GetRootElement()) {
11333 // Situation #1 is when we insert content that becomes the canonical body
11334 // element, and its used WritingMode is different from the root element's
11335 // used WritingMode.
11336 // We need to reframe the root element so that the root element's frames has
11337 // the correct writing-mode propagated from body element. (See
11338 // nsCSSFrameConstructor::ConstructDocElementFrame.)
11340 // Bug 1594297: When inserting a new <body>, we may need to reframe the old
11341 // <body> which has a "overflow" value other than simple "visible". But it's
11342 // tricky, see bug 1593752.
11343 nsIContent* bodyElement = mDocument->GetBodyElement();
11344 for (FCItemIterator iter(aItems); !iter.IsDone(); iter.Next()) {
11345 const WritingMode bodyWM(iter.item().mComputedStyle);
11346 if (iter.item().mContent == bodyElement &&
11347 bodyWM != aFrame->GetWritingMode()) {
11348 TRACE("Root");
11349 RecreateFramesForContent(mDocument->GetRootElement(),
11350 InsertionKind::Async);
11351 return true;
11356 nsIFrame* nextSibling = ::GetInsertNextSibling(aFrame, aPrevSibling);
11358 // Situation #2 is a flex / grid / XUL box container frame into which we're
11359 // inserting new inline non-replaced children, adjacent to an existing
11360 // anonymous flex or grid item.
11361 if (aFrame->IsFlexOrGridContainer() || aFrame->IsXULBoxFrame()) {
11362 FCItemIterator iter(aItems);
11364 // Check if we're adding to-be-wrapped content right *after* an existing
11365 // anonymous flex or grid item (which would need to absorb this content).
11366 const bool isLegacyWebKitBox = IsFlexContainerForLegacyWebKitBox(aFrame);
11367 if (aPrevSibling && IsAnonymousItem(aPrevSibling) &&
11368 iter.item().NeedsAnonFlexOrGridItem(aState, isLegacyWebKitBox)) {
11369 TRACE("Inserting inline after anon flex or grid item");
11370 RecreateFramesForContent(aFrame->GetContent(), InsertionKind::Async);
11371 return true;
11374 // Check if we're adding to-be-wrapped content right *before* an existing
11375 // anonymous flex or grid item (which would need to absorb this content).
11376 if (nextSibling && IsAnonymousItem(nextSibling)) {
11377 // Jump to the last entry in the list
11378 iter.SetToEnd();
11379 iter.Prev();
11380 if (iter.item().NeedsAnonFlexOrGridItem(aState, isLegacyWebKitBox)) {
11381 TRACE("Inserting inline before anon flex or grid item");
11382 RecreateFramesForContent(aFrame->GetContent(), InsertionKind::Async);
11383 return true;
11388 // Situation #3 is an anonymous flex or grid item that's getting new children
11389 // who don't want to be wrapped.
11390 if (IsAnonymousItem(aFrame)) {
11391 AssertAnonymousFlexOrGridItemParent(aFrame, aFrame->GetParent());
11393 // We need to push a null float containing block to be sure that
11394 // "NeedsAnonFlexOrGridItem" will know we're not honoring floats for this
11395 // inserted content. (In particular, this is necessary in order for
11396 // its "GetGeometricParent" call to return the correct result.)
11397 // We're not honoring floats on this content because it has the
11398 // _flex/grid container_ as its parent in the content tree.
11399 nsFrameConstructorSaveState floatSaveState;
11400 aState.PushFloatContainingBlock(nullptr, floatSaveState);
11402 FCItemIterator iter(aItems);
11403 // Skip over things that _do_ need an anonymous flex item, because
11404 // they're perfectly happy to go here -- they won't cause a reframe.
11405 nsIFrame* containerFrame = aFrame->GetParent();
11406 const bool isLegacyWebKitBox =
11407 IsFlexContainerForLegacyWebKitBox(containerFrame);
11408 if (!iter.SkipItemsThatNeedAnonFlexOrGridItem(aState, isLegacyWebKitBox)) {
11409 // We hit something that _doesn't_ need an anonymous flex item!
11410 // Rebuild the flex container to bust it out.
11411 TRACE("Inserting non-inlines inside anon flex or grid item");
11412 RecreateFramesForContent(containerFrame->GetContent(),
11413 InsertionKind::Async);
11414 return true;
11417 // If we get here, then everything in |aItems| needs to be wrapped in
11418 // an anonymous flex or grid item. That's where it's already going - good!
11421 // Situation #4 is a case when table pseudo-frames don't work out right
11422 ParentType parentType = GetParentType(aFrame);
11423 // If all the kids want a parent of the type that aFrame is, then we're all
11424 // set to go. Indeed, there won't be any table pseudo-frames created between
11425 // aFrame and the kids, so those won't need to be merged with any table
11426 // pseudo-frames that might already be kids of aFrame. If aFrame itself is a
11427 // table pseudo-frame, then all the kids in this list would have wanted a
11428 // frame of that type wrapping them anyway, so putting them inside it is ok.
11429 if (!aItems.AllWantParentType(parentType)) {
11430 // Don't give up yet. If parentType is not eTypeBlock and the parent is
11431 // not a generated content frame, then try filtering whitespace out of the
11432 // list.
11433 if (parentType != eTypeBlock && !aFrame->IsGeneratedContentFrame()) {
11434 // For leading whitespace followed by a kid that wants our parent type,
11435 // there are four cases:
11436 // 1) We have a previous sibling which is not a table pseudo. That means
11437 // that previous sibling wanted a (non-block) parent of the type we're
11438 // looking at. Then the whitespace comes between two table-internal
11439 // elements, so should be collapsed out.
11440 // 2) We have a previous sibling which is a table pseudo. It might have
11441 // kids who want this whitespace, so we need to reframe.
11442 // 3) We have no previous sibling and our parent frame is not a table
11443 // pseudo. That means that we'll be at the beginning of our actual
11444 // non-block-type parent, and the whitespace is OK to collapse out.
11445 // If something is ever inserted before us, it'll find our own parent
11446 // as its parent and if it's something that would care about the
11447 // whitespace it'll want a block parent, so it'll trigger a reframe at
11448 // that point.
11449 // 4) We have no previous sibling and our parent frame is a table pseudo.
11450 // Need to reframe.
11451 // All that is predicated on finding the correct previous sibling. We
11452 // might have to walk backwards along continuations from aFrame to do so.
11454 // It's always OK to drop whitespace between any two items that want a
11455 // parent of type parentType.
11457 // For trailing whitespace preceded by a kid that wants our parent type,
11458 // there are four cases:
11459 // 1) We have a next sibling which is not a table pseudo. That means
11460 // that next sibling wanted a (non-block) parent of the type we're
11461 // looking at. Then the whitespace comes between two table-internal
11462 // elements, so should be collapsed out.
11463 // 2) We have a next sibling which is a table pseudo. It might have
11464 // kids who want this whitespace, so we need to reframe.
11465 // 3) We have no next sibling and our parent frame is not a table
11466 // pseudo. That means that we'll be at the end of our actual
11467 // non-block-type parent, and the whitespace is OK to collapse out.
11468 // If something is ever inserted after us, it'll find our own parent
11469 // as its parent and if it's something that would care about the
11470 // whitespace it'll want a block parent, so it'll trigger a reframe at
11471 // that point.
11472 // 4) We have no next sibling and our parent frame is a table pseudo.
11473 // Need to reframe.
11474 // All that is predicated on finding the correct next sibling. We might
11475 // have to walk forward along continuations from aFrame to do so. That
11476 // said, in the case when nextSibling is null at this point and aIsAppend
11477 // is true, we know we're in case 3. Furthermore, in that case we don't
11478 // even have to worry about the table pseudo situation; we know our
11479 // parent is not a table pseudo there.
11480 FCItemIterator iter(aItems);
11481 FCItemIterator start(iter);
11482 do {
11483 if (iter.SkipItemsWantingParentType(parentType)) {
11484 break;
11487 // iter points to an item that wants a different parent. If it's not
11488 // whitespace, we're done; no more point scanning the list.
11489 if (!iter.item().IsWhitespace(aState)) {
11490 break;
11493 if (iter == start) {
11494 // Leading whitespace. How to handle this depends on our
11495 // previous sibling and aFrame. See the long comment above.
11496 nsIFrame* prevSibling = aPrevSibling;
11497 if (!prevSibling) {
11498 // Try to find one after all
11499 nsIFrame* parentPrevCont = aFrame->GetPrevContinuation();
11500 while (parentPrevCont) {
11501 prevSibling = parentPrevCont->PrincipalChildList().LastChild();
11502 if (prevSibling) {
11503 break;
11505 parentPrevCont = parentPrevCont->GetPrevContinuation();
11508 if (prevSibling) {
11509 if (IsTablePseudo(prevSibling)) {
11510 // need to reframe
11511 break;
11513 } else if (IsTablePseudo(aFrame)) {
11514 // need to reframe
11515 break;
11519 FCItemIterator spaceEndIter(iter);
11520 // Advance spaceEndIter past any whitespace
11521 bool trailingSpaces = spaceEndIter.SkipWhitespace(aState);
11523 bool okToDrop;
11524 if (trailingSpaces) {
11525 // Trailing whitespace. How to handle this depeds on aIsAppend, our
11526 // next sibling and aFrame. See the long comment above.
11527 okToDrop = aIsAppend && !nextSibling;
11528 if (!okToDrop) {
11529 if (!nextSibling) {
11530 // Try to find one after all
11531 nsIFrame* parentNextCont = aFrame->GetNextContinuation();
11532 while (parentNextCont) {
11533 nextSibling = parentNextCont->PrincipalChildList().FirstChild();
11534 if (nextSibling) {
11535 break;
11537 parentNextCont = parentNextCont->GetNextContinuation();
11541 okToDrop = (nextSibling && !IsTablePseudo(nextSibling)) ||
11542 (!nextSibling && !IsTablePseudo(aFrame));
11544 #ifdef DEBUG
11545 else {
11546 NS_ASSERTION(!IsTablePseudo(aFrame), "How did that happen?");
11548 #endif
11549 } else {
11550 okToDrop = (spaceEndIter.item().DesiredParentType() == parentType);
11553 if (okToDrop) {
11554 iter.DeleteItemsTo(this, spaceEndIter);
11555 } else {
11556 // We're done: we don't want to drop the whitespace, and it has the
11557 // wrong parent type.
11558 break;
11561 // Now loop, since |iter| points to item right after the whitespace we
11562 // removed.
11563 } while (!iter.IsDone());
11566 // We might be able to figure out some sort of optimizations here, but they
11567 // would have to depend on having a correct aPrevSibling and a correct next
11568 // sibling. For example, we can probably avoid reframing if none of
11569 // aFrame, aPrevSibling, and next sibling are table pseudo-frames. But it
11570 // doesn't seem worth it to worry about that for now, especially since we
11571 // in fact do not have a reliable aPrevSibling, nor any next sibling, in
11572 // this method.
11574 // aItems might have changed, so recheck the parent type thing. In fact,
11575 // it might be empty, so recheck that too.
11576 if (aItems.IsEmpty()) {
11577 return false;
11580 if (!aItems.AllWantParentType(parentType)) {
11581 // Reframing aFrame->GetContent() is good enough, since the content of
11582 // table pseudo-frames is the ancestor content.
11583 TRACE("Pseudo-frames going wrong");
11584 RecreateFramesForContent(aFrame->GetContent(), InsertionKind::Async);
11585 return true;
11589 // Situation #5 is a frame in multicol subtree that's getting new children.
11590 if (aFrame->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR)) {
11591 bool anyColumnSpanItems = false;
11592 for (FCItemIterator iter(aItems); !iter.IsDone(); iter.Next()) {
11593 if (iter.item().mComputedStyle->StyleColumn()->IsColumnSpanStyle()) {
11594 anyColumnSpanItems = true;
11595 break;
11599 bool needsReframe =
11600 // 1. Insert / append any column-span children.
11601 anyColumnSpanItems ||
11602 // 2. GetInsertionPrevSibling() modifies insertion parent. If the prev
11603 // sibling is a column-span, aFrame ends up being the
11604 // column-span-wrapper.
11605 aFrame->Style()->GetPseudoType() ==
11606 PseudoStyleType::columnSpanWrapper ||
11607 // 3. Append into {ib} split container. There might be room for
11608 // optimization, but let's reframe for correctness...
11609 IsFramePartOfIBSplit(aFrame);
11611 if (needsReframe) {
11612 TRACE("Multi-column");
11613 RecreateFramesForContent(
11614 GetMultiColumnContainingBlockFor(aFrame)->GetContent(),
11615 InsertionKind::Async);
11616 return true;
11619 // If we get here, then we need further check for {ib} split to decide
11620 // whether to reframe. For example, appending a block into an empty inline
11621 // that is not part of an {ib} split, but should become an {ib} split.
11624 // A <fieldset> may need to pick up a new rendered legend from aItems.
11625 // We currently can't handle this case without recreating frames for
11626 // the fieldset.
11627 // XXXmats we should be able to optimize this when the fieldset doesn't
11628 // currently have a rendered legend. ContentRangeInserted needs to be fixed
11629 // to use the inner frame as the content insertion frame in that case.
11630 if (const auto* fieldset = GetFieldSetFrameFor(aFrame)) {
11631 // Check if any item is eligible to be a rendered legend.
11632 for (FCItemIterator iter(aItems); !iter.IsDone(); iter.Next()) {
11633 const auto& item = iter.item();
11634 if (!item.mContent->IsHTMLElement(nsGkAtoms::legend)) {
11635 continue;
11637 const auto* display = item.mComputedStyle->StyleDisplay();
11638 if (display->IsFloatingStyle() ||
11639 display->IsAbsolutelyPositionedStyle()) {
11640 continue;
11642 TRACE("Fieldset with rendered legend");
11643 RecreateFramesForContent(fieldset->GetContent(), InsertionKind::Async);
11644 return true;
11648 // Now we have several cases involving {ib} splits. Put them all in a
11649 // do/while with breaks to take us to the "go and reconstruct" code.
11650 do {
11651 if (IsInlineFrame(aFrame)) {
11652 if (aItems.AreAllItemsInline()) {
11653 // We can just put the kids in.
11654 return false;
11657 if (!IsFramePartOfIBSplit(aFrame)) {
11658 // Need to go ahead and reconstruct.
11659 break;
11662 // Now we're adding kids including some blocks to an inline part of an
11663 // {ib} split. If we plan to call AppendFrames, and don't have a next
11664 // sibling for the new frames, and our parent is the last continuation of
11665 // the last part of the {ib} split, and the same is true of all our
11666 // ancestor inlines (they have no following continuations and they're the
11667 // last part of their {ib} splits and we'd be adding to the end for all
11668 // of them), then AppendFrames will handle things for us. Bail out in
11669 // that case.
11670 if (aIsAppend && IsSafeToAppendToIBSplitInline(aFrame, nextSibling)) {
11671 return false;
11674 // Need to reconstruct.
11675 break;
11678 // Now we know we have a block parent. If it's not part of an
11679 // ib-split, we're all set.
11680 if (!IsFramePartOfIBSplit(aFrame)) {
11681 return false;
11684 // We're adding some kids to a block part of an {ib} split. If all the
11685 // kids are blocks, we don't need to reconstruct.
11686 if (aItems.AreAllItemsBlock()) {
11687 return false;
11690 // We might have some inline kids for this block. Just fall out of the
11691 // loop and reconstruct.
11692 } while (0);
11694 // If we don't have a containing block, start with aFrame and look for one.
11695 if (!aContainingBlock) {
11696 aContainingBlock = aFrame;
11699 // To find the right block to reframe, just walk up the tree until we find a
11700 // frame that is:
11701 // 1) Not part of an IB split
11702 // 2) Not a pseudo-frame
11703 // 3) Not an inline frame
11704 // We're guaranteed to find one, since ComputedStyle::ApplyStyleFixups
11705 // enforces that the root is display:none, display:table, or display:block.
11706 // Note that walking up "too far" is OK in terms of correctness, even if it
11707 // might be a little inefficient. This is why we walk out of all
11708 // pseudo-frames -- telling which ones are or are not OK to walk out of is
11709 // too hard (and I suspect that we do in fact need to walk out of all of
11710 // them).
11711 while (IsFramePartOfIBSplit(aContainingBlock) ||
11712 aContainingBlock->IsInlineOutside() ||
11713 aContainingBlock->Style()->IsPseudoOrAnonBox()) {
11714 aContainingBlock = aContainingBlock->GetParent();
11715 NS_ASSERTION(aContainingBlock,
11716 "Must have non-inline, non-ib-split, non-pseudo frame as "
11717 "root (or child of root, for a table root)!");
11720 // Tell parent of the containing block to reformulate the
11721 // entire block. This is painful and definitely not optimal
11722 // but it will *always* get the right answer.
11724 nsIContent* blockContent = aContainingBlock->GetContent();
11725 TRACE("IB splits");
11726 RecreateFramesForContent(blockContent, InsertionKind::Async);
11727 return true;
11728 #undef TRACE
11731 void nsCSSFrameConstructor::ReframeContainingBlock(nsIFrame* aFrame) {
11732 // XXXbz how exactly would we get here while isReflowing anyway? Should this
11733 // whole test be ifdef DEBUG?
11734 if (mPresShell->IsReflowLocked()) {
11735 // don't ReframeContainingBlock, this will result in a crash
11736 // if we remove a tree that's in reflow - see bug 121368 for testcase
11737 NS_ERROR(
11738 "Atemptted to nsCSSFrameConstructor::ReframeContainingBlock during a "
11739 "Reflow!!!");
11740 return;
11743 // Get the first "normal" ancestor of the target frame.
11744 nsIFrame* containingBlock = GetIBContainingBlockFor(aFrame);
11745 if (containingBlock) {
11746 // From here we look for the containing block in case the target
11747 // frame is already a block (which can happen when an inline frame
11748 // wraps some of its content in an anonymous block; see
11749 // ConstructInline)
11751 // NOTE: We used to get the FloatContainingBlock here, but it was often
11752 // wrong. GetIBContainingBlock works much better and provides the correct
11753 // container in all cases so GetFloatContainingBlock(aFrame) has been
11754 // removed
11756 // And get the containingBlock's content
11757 if (nsIContent* blockContent = containingBlock->GetContent()) {
11758 #ifdef DEBUG
11759 if (gNoisyContentUpdates) {
11760 printf(" ==> blockContent=%p\n", blockContent);
11762 #endif
11763 RecreateFramesForContent(blockContent, InsertionKind::Async);
11764 return;
11768 // If we get here, we're screwed!
11769 RecreateFramesForContent(mPresShell->GetDocument()->GetRootElement(),
11770 InsertionKind::Async);
11773 //////////////////////////////////////////////////////////
11774 // nsCSSFrameConstructor::FrameConstructionItem methods //
11775 //////////////////////////////////////////////////////////
11776 bool nsCSSFrameConstructor::FrameConstructionItem::IsWhitespace(
11777 nsFrameConstructorState& aState) const {
11778 MOZ_ASSERT(aState.mCreatingExtraFrames || !mContent->GetPrimaryFrame(),
11779 "How did that happen?");
11780 if (!mIsText) {
11781 return false;
11783 mContent->SetFlags(NS_CREATE_FRAME_IF_NON_WHITESPACE |
11784 NS_REFRAME_IF_WHITESPACE);
11785 return mContent->TextIsOnlyWhitespace();
11788 //////////////////////////////////////////////////////////////
11789 // nsCSSFrameConstructor::FrameConstructionItemList methods //
11790 //////////////////////////////////////////////////////////////
11791 void nsCSSFrameConstructor::FrameConstructionItemList::AdjustCountsForItem(
11792 FrameConstructionItem* aItem, int32_t aDelta) {
11793 MOZ_ASSERT(aDelta == 1 || aDelta == -1, "Unexpected delta");
11794 mItemCount += aDelta;
11795 if (aItem->mIsAllInline) {
11796 mInlineCount += aDelta;
11798 if (aItem->mIsBlock) {
11799 mBlockCount += aDelta;
11801 mDesiredParentCounts[aItem->DesiredParentType()] += aDelta;
11804 ////////////////////////////////////////////////////////////////////////
11805 // nsCSSFrameConstructor::FrameConstructionItemList::Iterator methods //
11806 ////////////////////////////////////////////////////////////////////////
11807 inline bool nsCSSFrameConstructor::FrameConstructionItemList::Iterator::
11808 SkipItemsWantingParentType(ParentType aParentType) {
11809 MOZ_ASSERT(!IsDone(), "Shouldn't be done yet");
11810 while (item().DesiredParentType() == aParentType) {
11811 Next();
11812 if (IsDone()) {
11813 return true;
11816 return false;
11819 inline bool nsCSSFrameConstructor::FrameConstructionItemList::Iterator::
11820 SkipItemsNotWantingParentType(ParentType aParentType) {
11821 MOZ_ASSERT(!IsDone(), "Shouldn't be done yet");
11822 while (item().DesiredParentType() != aParentType) {
11823 Next();
11824 if (IsDone()) {
11825 return true;
11828 return false;
11831 // Note: we implement -webkit-{inline-}box using nsFlexContainerFrame, but we
11832 // use different rules for what gets wrapped in an anonymous flex item.
11833 bool nsCSSFrameConstructor::FrameConstructionItem::NeedsAnonFlexOrGridItem(
11834 const nsFrameConstructorState& aState, bool aIsLegacyWebKitBox) {
11835 if (mFCData->mBits & FCDATA_IS_LINE_PARTICIPANT) {
11836 // This will be an inline non-replaced box.
11837 return true;
11840 if (aIsLegacyWebKitBox) {
11841 if (mComputedStyle->StyleDisplay()->IsInlineOutsideStyle()) {
11842 // In an emulated legacy box, all inline-level content gets wrapped in an
11843 // anonymous flex item.
11844 return true;
11846 if (mIsPopup ||
11847 (!(mFCData->mBits & FCDATA_DISALLOW_OUT_OF_FLOW) &&
11848 aState.GetGeometricParent(*mComputedStyle->StyleDisplay(), nullptr))) {
11849 // We're abspos or fixedpos (or a XUL popup), which means we'll spawn a
11850 // placeholder which (because our container is an emulated legacy box)
11851 // we'll need to wrap in an anonymous flex item. So, we just treat
11852 // _this_ frame as if _it_ needs to be wrapped in an anonymous flex item,
11853 // and then when we spawn the placeholder, it'll end up in the right
11854 // spot.
11855 return true;
11859 return false;
11862 inline bool nsCSSFrameConstructor::FrameConstructionItemList::Iterator::
11863 SkipItemsThatNeedAnonFlexOrGridItem(const nsFrameConstructorState& aState,
11864 bool aIsLegacyWebKitBox) {
11865 MOZ_ASSERT(!IsDone(), "Shouldn't be done yet");
11866 while (item().NeedsAnonFlexOrGridItem(aState, aIsLegacyWebKitBox)) {
11867 Next();
11868 if (IsDone()) {
11869 return true;
11872 return false;
11875 inline bool nsCSSFrameConstructor::FrameConstructionItemList::Iterator::
11876 SkipItemsThatDontNeedAnonFlexOrGridItem(
11877 const nsFrameConstructorState& aState, bool aIsLegacyWebKitBox) {
11878 MOZ_ASSERT(!IsDone(), "Shouldn't be done yet");
11879 while (!(item().NeedsAnonFlexOrGridItem(aState, aIsLegacyWebKitBox))) {
11880 Next();
11881 if (IsDone()) {
11882 return true;
11885 return false;
11888 inline bool nsCSSFrameConstructor::FrameConstructionItemList::Iterator::
11889 SkipItemsNotWantingRubyParent() {
11890 MOZ_ASSERT(!IsDone(), "Shouldn't be done yet");
11891 while (!IsRubyParentType(item().DesiredParentType())) {
11892 Next();
11893 if (IsDone()) {
11894 return true;
11897 return false;
11900 inline bool
11901 nsCSSFrameConstructor::FrameConstructionItemList::Iterator::SkipWhitespace(
11902 nsFrameConstructorState& aState) {
11903 MOZ_ASSERT(!IsDone(), "Shouldn't be done yet");
11904 MOZ_ASSERT(item().IsWhitespace(aState), "Not pointing to whitespace?");
11905 do {
11906 Next();
11907 if (IsDone()) {
11908 return true;
11910 } while (item().IsWhitespace(aState));
11912 return false;
11915 void nsCSSFrameConstructor::FrameConstructionItemList::Iterator::
11916 AppendItemToList(FrameConstructionItemList& aTargetList) {
11917 NS_ASSERTION(&aTargetList != &mList, "Unexpected call");
11918 MOZ_ASSERT(!IsDone(), "should not be done");
11920 FrameConstructionItem* item = mCurrent;
11921 Next();
11922 item->remove();
11923 aTargetList.mItems.insertBack(item);
11925 mList.AdjustCountsForItem(item, -1);
11926 aTargetList.AdjustCountsForItem(item, 1);
11929 void nsCSSFrameConstructor::FrameConstructionItemList::Iterator::
11930 AppendItemsToList(nsCSSFrameConstructor* aFCtor, const Iterator& aEnd,
11931 FrameConstructionItemList& aTargetList) {
11932 NS_ASSERTION(&aTargetList != &mList, "Unexpected call");
11933 MOZ_ASSERT(&mList == &aEnd.mList, "End iterator for some other list?");
11935 // We can't just move our guts to the other list if it already has
11936 // some information or if we're not moving our entire list.
11937 if (!AtStart() || !aEnd.IsDone() || !aTargetList.IsEmpty()) {
11938 do {
11939 AppendItemToList(aTargetList);
11940 } while (*this != aEnd);
11941 return;
11944 // Move our entire list of items into the empty target list.
11945 aTargetList.mItems = std::move(mList.mItems);
11947 // Copy over the various counters
11948 aTargetList.mInlineCount = mList.mInlineCount;
11949 aTargetList.mBlockCount = mList.mBlockCount;
11950 aTargetList.mItemCount = mList.mItemCount;
11951 memcpy(aTargetList.mDesiredParentCounts, mList.mDesiredParentCounts,
11952 sizeof(aTargetList.mDesiredParentCounts));
11954 // reset mList
11955 mList.Reset(aFCtor);
11957 // Point ourselves to aEnd, as advertised
11958 SetToEnd();
11959 MOZ_ASSERT(*this == aEnd, "How did that happen?");
11962 void nsCSSFrameConstructor::FrameConstructionItemList::Iterator::InsertItem(
11963 FrameConstructionItem* aItem) {
11964 if (IsDone()) {
11965 mList.mItems.insertBack(aItem);
11966 } else {
11967 // Just insert the item before us. There's no magic here.
11968 mCurrent->setPrevious(aItem);
11970 mList.AdjustCountsForItem(aItem, 1);
11972 MOZ_ASSERT(aItem->getNext() == mCurrent, "How did that happen?");
11975 void nsCSSFrameConstructor::FrameConstructionItemList::Iterator::DeleteItemsTo(
11976 nsCSSFrameConstructor* aFCtor, const Iterator& aEnd) {
11977 MOZ_ASSERT(&mList == &aEnd.mList, "End iterator for some other list?");
11978 MOZ_ASSERT(*this != aEnd, "Shouldn't be at aEnd yet");
11980 do {
11981 NS_ASSERTION(!IsDone(), "Ran off end of list?");
11982 FrameConstructionItem* item = mCurrent;
11983 Next();
11984 item->remove();
11985 mList.AdjustCountsForItem(item, -1);
11986 item->Delete(aFCtor);
11987 } while (*this != aEnd);
11990 void nsCSSFrameConstructor::QuotesDirty() {
11991 mQuotesDirty = true;
11992 mPresShell->SetNeedLayoutFlush();
11995 void nsCSSFrameConstructor::CountersDirty() {
11996 mCountersDirty = true;
11997 mPresShell->SetNeedLayoutFlush();
12000 void* nsCSSFrameConstructor::AllocateFCItem() {
12001 void* item;
12002 if (mFirstFreeFCItem) {
12003 item = mFirstFreeFCItem;
12004 mFirstFreeFCItem = mFirstFreeFCItem->mNext;
12005 } else {
12006 item = mFCItemPool.Allocate(sizeof(FrameConstructionItem));
12008 ++mFCItemsInUse;
12009 return item;
12012 void nsCSSFrameConstructor::FreeFCItem(FrameConstructionItem* aItem) {
12013 MOZ_ASSERT(mFCItemsInUse != 0);
12014 if (--mFCItemsInUse == 0) {
12015 // The arena is now unused - clear it but retain one chunk.
12016 mFirstFreeFCItem = nullptr;
12017 mFCItemPool.Clear();
12018 } else {
12019 // Prepend it to the list of free items.
12020 FreeFCItemLink* item = reinterpret_cast<FreeFCItemLink*>(aItem);
12021 item->mNext = mFirstFreeFCItem;
12022 mFirstFreeFCItem = item;
12026 void nsCSSFrameConstructor::AddSizeOfIncludingThis(
12027 nsWindowSizes& aSizes) const {
12028 if (nsIFrame* rootFrame = GetRootFrame()) {
12029 rootFrame->AddSizeOfExcludingThisForTree(aSizes);
12030 if (RetainedDisplayListBuilder* builder =
12031 rootFrame->GetProperty(RetainedDisplayListBuilder::Cached())) {
12032 builder->AddSizeOfIncludingThis(aSizes);
12036 // This must be done after measuring from the frame tree, since frame
12037 // manager will measure sizes of staled computed values and style
12038 // structs, which only make sense after we know what are being used.
12039 nsFrameManager::AddSizeOfIncludingThis(aSizes);
12041 // Measurement of the following members may be added later if DMD finds it
12042 // is worthwhile:
12043 // - mFCItemPool
12044 // - mContainStyleScopeManager