Bug 1877662 - expose mozconfig as an artifact from build-fat-aar. r=glandium,geckovie...
[gecko.git] / layout / base / nsCSSFrameConstructor.cpp
blob149f2f24bf9ce201761bea8d2b0bee2a3caac030
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /*
8 * construction of a frame tree that is nearly isomorphic to the content
9 * tree and updating of that tree in response to dynamic changes
12 #include "nsCSSFrameConstructor.h"
14 #include "mozilla/AutoRestore.h"
15 #include "mozilla/ComputedStyleInlines.h"
16 #include "mozilla/DebugOnly.h"
17 #include "mozilla/ErrorResult.h"
18 #include "mozilla/ManualNAC.h"
19 #include "mozilla/dom/BindContext.h"
20 #include "mozilla/dom/BrowsingContext.h"
21 #include "mozilla/dom/GeneratedImageContent.h"
22 #include "mozilla/dom/HTMLSelectElement.h"
23 #include "mozilla/dom/HTMLSharedListElement.h"
24 #include "mozilla/dom/HTMLSummaryElement.h"
25 #include "mozilla/Likely.h"
26 #include "mozilla/LinkedList.h"
27 #include "mozilla/MemoryReporting.h"
28 #include "mozilla/PresShell.h"
29 #include "mozilla/PresShellInlines.h"
30 #include "mozilla/PrintedSheetFrame.h"
31 #include "mozilla/ProfilerLabels.h"
32 #include "mozilla/ProfilerMarkers.h"
33 #include "mozilla/ScopeExit.h"
34 #include "mozilla/ServoBindings.h"
35 #include "mozilla/ServoStyleSetInlines.h"
36 #include "mozilla/StaticPrefs_browser.h"
37 #include "mozilla/StaticPrefs_layout.h"
38 #include "mozilla/StaticPrefs_mathml.h"
39 #include "mozilla/Unused.h"
40 #include "RetainedDisplayListBuilder.h"
41 #include "nsAbsoluteContainingBlock.h"
42 #include "nsCSSPseudoElements.h"
43 #include "nsCheckboxRadioFrame.h"
44 #include "nsCRT.h"
45 #include "nsAtom.h"
46 #include "nsIFrameInlines.h"
47 #include "nsGkAtoms.h"
48 #include "nsPresContext.h"
49 #include "mozilla/dom/Document.h"
50 #include "mozilla/dom/DocumentInlines.h"
51 #include "nsTableFrame.h"
52 #include "nsTableColFrame.h"
53 #include "nsTableRowFrame.h"
54 #include "nsTableCellFrame.h"
55 #include "nsFileControlFrame.h"
56 #include "nsHTMLParts.h"
57 #include "nsUnicharUtils.h"
58 #include "nsViewManager.h"
59 #include "nsStyleConsts.h"
60 #include "nsXULElement.h"
61 #include "nsContainerFrame.h"
62 #include "nsNameSpaceManager.h"
63 #include "nsComboboxControlFrame.h"
64 #include "nsListControlFrame.h"
65 #include "nsPlaceholderFrame.h"
66 #include "nsTableRowGroupFrame.h"
67 #include "nsIFormControl.h"
68 #include "nsCSSAnonBoxes.h"
69 #include "nsTextFragment.h"
70 #include "nsIAnonymousContentCreator.h"
71 #include "nsContentUtils.h"
72 #include "nsIScriptError.h"
73 #ifdef XP_MACOSX
74 # include "nsIDocShell.h"
75 #endif
76 #include "ChildIterator.h"
77 #include "nsError.h"
78 #include "nsLayoutUtils.h"
79 #include "nsFlexContainerFrame.h"
80 #include "nsGridContainerFrame.h"
81 #include "RubyUtils.h"
82 #include "nsRubyFrame.h"
83 #include "nsRubyBaseFrame.h"
84 #include "nsRubyBaseContainerFrame.h"
85 #include "nsRubyTextFrame.h"
86 #include "nsRubyTextContainerFrame.h"
87 #include "nsImageFrame.h"
88 #include "nsIObjectLoadingContent.h"
89 #include "nsTArray.h"
90 #include "mozilla/dom/CharacterData.h"
91 #include "mozilla/dom/Element.h"
92 #include "mozilla/dom/ElementInlines.h"
93 #include "mozilla/dom/HTMLInputElement.h"
94 #include "nsAutoLayoutPhase.h"
95 #include "nsStyleStructInlines.h"
96 #include "nsPageContentFrame.h"
97 #include "mozilla/RestyleManager.h"
98 #include "StickyScrollContainer.h"
99 #include "nsFieldSetFrame.h"
100 #include "nsInlineFrame.h"
101 #include "nsBlockFrame.h"
102 #include "nsCanvasFrame.h"
103 #include "nsFirstLetterFrame.h"
104 #include "nsGfxScrollFrame.h"
105 #include "nsPageFrame.h"
106 #include "nsPageSequenceFrame.h"
107 #include "nsTableWrapperFrame.h"
108 #include "nsIScrollableFrame.h"
109 #include "nsBackdropFrame.h"
110 #include "nsTransitionManager.h"
112 #include "nsIPopupContainer.h"
113 #ifdef ACCESSIBILITY
114 # include "nsAccessibilityService.h"
115 #endif
117 #undef NOISY_FIRST_LETTER
119 #include "nsMathMLParts.h"
120 #include "mozilla/SVGGradientFrame.h"
122 #include "nsRefreshDriver.h"
123 #include "nsTextNode.h"
124 #include "ActiveLayerTracker.h"
126 using namespace mozilla;
127 using namespace mozilla::dom;
129 nsIFrame* NS_NewHTMLCanvasFrame(PresShell* aPresShell, ComputedStyle* aStyle);
131 nsIFrame* NS_NewHTMLVideoFrame(PresShell* aPresShell, ComputedStyle* aStyle);
132 nsIFrame* NS_NewHTMLAudioFrame(PresShell* aPresShell, ComputedStyle* aStyle);
134 nsContainerFrame* NS_NewSVGOuterSVGFrame(PresShell* aPresShell,
135 ComputedStyle* aStyle);
136 nsContainerFrame* NS_NewSVGOuterSVGAnonChildFrame(PresShell* aPresShell,
137 ComputedStyle* aStyle);
138 nsIFrame* NS_NewSVGInnerSVGFrame(PresShell* aPresShell, ComputedStyle* aStyle);
139 nsIFrame* NS_NewSVGGeometryFrame(PresShell* aPresShell, ComputedStyle* aStyle);
140 nsIFrame* NS_NewSVGGFrame(PresShell* aPresShell, ComputedStyle* aStyle);
141 nsContainerFrame* NS_NewSVGForeignObjectFrame(PresShell* aPresShell,
142 ComputedStyle* aStyle);
143 nsIFrame* NS_NewSVGAFrame(PresShell* aPresShell, ComputedStyle* aStyle);
144 nsIFrame* NS_NewSVGSwitchFrame(PresShell* aPresShell, ComputedStyle* aStyle);
145 nsIFrame* NS_NewSVGSymbolFrame(PresShell* aPresShell, ComputedStyle* aStyle);
146 nsIFrame* NS_NewSVGTextFrame(PresShell* aPresShell, ComputedStyle* aStyle);
147 nsIFrame* NS_NewSVGContainerFrame(PresShell* aPresShell, ComputedStyle* aStyle);
148 nsIFrame* NS_NewSVGUseFrame(PresShell* aPresShell, ComputedStyle* aStyle);
149 nsIFrame* NS_NewSVGViewFrame(PresShell* aPresShell, ComputedStyle* aStyle);
150 extern nsIFrame* NS_NewSVGLinearGradientFrame(PresShell* aPresShell,
151 ComputedStyle* aStyle);
152 extern nsIFrame* NS_NewSVGRadialGradientFrame(PresShell* aPresShell,
153 ComputedStyle* aStyle);
154 extern nsIFrame* NS_NewSVGStopFrame(PresShell* aPresShell,
155 ComputedStyle* aStyle);
156 nsContainerFrame* NS_NewSVGMarkerFrame(PresShell* aPresShell,
157 ComputedStyle* aStyle);
158 nsContainerFrame* NS_NewSVGMarkerAnonChildFrame(PresShell* aPresShell,
159 ComputedStyle* aStyle);
160 extern nsIFrame* NS_NewSVGImageFrame(PresShell* aPresShell,
161 ComputedStyle* aStyle);
162 nsIFrame* NS_NewSVGClipPathFrame(PresShell* aPresShell, ComputedStyle* aStyle);
163 nsIFrame* NS_NewSVGFilterFrame(PresShell* aPresShell, ComputedStyle* aStyle);
164 nsIFrame* NS_NewSVGPatternFrame(PresShell* aPresShell, ComputedStyle* aStyle);
165 nsIFrame* NS_NewSVGMaskFrame(PresShell* aPresShell, ComputedStyle* aStyle);
166 nsIFrame* NS_NewSVGFEContainerFrame(PresShell* aPresShell,
167 ComputedStyle* aStyle);
168 nsIFrame* NS_NewSVGFELeafFrame(PresShell* aPresShell, ComputedStyle* aStyle);
169 nsIFrame* NS_NewSVGFEImageFrame(PresShell* aPresShell, ComputedStyle* aStyle);
170 nsIFrame* NS_NewSVGFEUnstyledLeafFrame(PresShell* aPresShell,
171 ComputedStyle* aStyle);
172 nsIFrame* NS_NewFileControlLabelFrame(PresShell*, ComputedStyle*);
173 nsIFrame* NS_NewComboboxLabelFrame(PresShell*, ComputedStyle*);
174 nsIFrame* NS_NewMiddleCroppingLabelFrame(PresShell*, ComputedStyle*);
176 #include "mozilla/dom/NodeInfo.h"
177 #include "prenv.h"
178 #include "nsNodeInfoManager.h"
179 #include "nsContentCreatorFunctions.h"
181 #ifdef DEBUG
182 // Set the environment variable GECKO_FRAMECTOR_DEBUG_FLAGS to one or
183 // more of the following flags (comma separated) for handy debug
184 // output.
185 static bool gNoisyContentUpdates = false;
186 static bool gReallyNoisyContentUpdates = false;
187 static bool gNoisyInlineConstruction = false;
189 struct FrameCtorDebugFlags {
190 const char* name;
191 bool* on;
194 static FrameCtorDebugFlags gFlags[] = {
195 {"content-updates", &gNoisyContentUpdates},
196 {"really-noisy-content-updates", &gReallyNoisyContentUpdates},
197 {"noisy-inline", &gNoisyInlineConstruction}};
199 # define NUM_DEBUG_FLAGS (sizeof(gFlags) / sizeof(gFlags[0]))
200 #endif
202 //------------------------------------------------------------------
204 nsIFrame* NS_NewLeafBoxFrame(PresShell* aPresShell, ComputedStyle* aStyle);
206 nsIFrame* NS_NewRangeFrame(PresShell* aPresShell, ComputedStyle* aStyle);
208 nsIFrame* NS_NewTextBoxFrame(PresShell* aPresShell, ComputedStyle* aStyle);
210 nsIFrame* NS_NewSplitterFrame(PresShell* aPresShell, ComputedStyle* aStyle);
212 nsIFrame* NS_NewMenuPopupFrame(PresShell* aPresShell, ComputedStyle* aStyle);
214 nsIFrame* NS_NewTreeBodyFrame(PresShell* aPresShell, ComputedStyle* aStyle);
216 nsHTMLScrollFrame* NS_NewHTMLScrollFrame(PresShell* aPresShell,
217 ComputedStyle* aStyle, bool aIsRoot);
219 nsIFrame* NS_NewSliderFrame(PresShell* aPresShell, ComputedStyle* aStyle);
221 nsIFrame* NS_NewScrollbarFrame(PresShell* aPresShell, ComputedStyle* aStyle);
223 nsIFrame* NS_NewScrollbarButtonFrame(PresShell*, ComputedStyle*);
224 nsIFrame* NS_NewSimpleXULLeafFrame(PresShell*, ComputedStyle*);
226 nsIFrame* NS_NewXULImageFrame(PresShell*, ComputedStyle*);
227 nsIFrame* NS_NewImageFrameForContentProperty(PresShell*, ComputedStyle*);
228 nsIFrame* NS_NewImageFrameForGeneratedContentIndex(PresShell*, ComputedStyle*);
229 nsIFrame* NS_NewImageFrameForListStyleImage(PresShell*, ComputedStyle*);
231 // Returns true if aFrame is an anonymous flex/grid item.
232 static inline bool IsAnonymousItem(const nsIFrame* aFrame) {
233 return aFrame->Style()->GetPseudoType() == PseudoStyleType::anonymousItem;
236 // Returns true IFF the given nsIFrame is a nsFlexContainerFrame and represents
237 // a -webkit-{inline-}box container.
238 static inline bool IsFlexContainerForLegacyWebKitBox(const nsIFrame* aFrame) {
239 return aFrame->IsFlexContainerFrame() &&
240 aFrame->HasAnyStateBits(NS_STATE_FLEX_IS_EMULATING_LEGACY_WEBKIT_BOX);
243 #if DEBUG
244 static void AssertAnonymousFlexOrGridItemParent(const nsIFrame* aChild,
245 const nsIFrame* aParent) {
246 MOZ_ASSERT(IsAnonymousItem(aChild), "expected an anonymous item child frame");
247 MOZ_ASSERT(aParent, "expected a parent frame");
248 MOZ_ASSERT(aParent->IsFlexOrGridContainer(),
249 "anonymous items should only exist as children of flex/grid "
250 "container frames");
252 #else
253 # define AssertAnonymousFlexOrGridItemParent(x, y) PR_BEGIN_MACRO PR_END_MACRO
254 #endif
256 #define ToCreationFunc(_func) \
257 [](PresShell* aPs, ComputedStyle* aStyle) -> nsIFrame* { \
258 return _func(aPs, aStyle); \
262 * True if aFrame is an actual inline frame in the sense of non-replaced
263 * display:inline CSS boxes. In other words, it can be affected by {ib}
264 * splitting and can contain first-letter frames. Basically, this is either an
265 * inline frame (positioned or otherwise) or an line frame (this last because
266 * it can contain first-letter and because inserting blocks in the middle of it
267 * needs to terminate it).
269 static bool IsInlineFrame(const nsIFrame* aFrame) {
270 return aFrame->IsLineParticipant();
274 * True for display: contents elements.
276 static inline bool IsDisplayContents(const Element* aElement) {
277 return aElement->IsDisplayContents();
280 static inline bool IsDisplayContents(const nsIContent* aContent) {
281 return aContent->IsElement() && IsDisplayContents(aContent->AsElement());
285 * True if aFrame is an instance of an SVG frame class or is an inline/block
286 * frame being used for SVG text.
288 static bool IsFrameForSVG(const nsIFrame* aFrame) {
289 return aFrame->IsSVGFrame() || aFrame->IsInSVGTextSubtree();
292 static bool IsLastContinuationForColumnContent(const nsIFrame* aFrame) {
293 MOZ_ASSERT(aFrame);
294 return aFrame->Style()->GetPseudoType() == PseudoStyleType::columnContent &&
295 !aFrame->GetNextContinuation();
299 * Returns true iff aFrame explicitly prevents its descendants from floating
300 * (at least, down to the level of descendants which themselves are
301 * float-containing blocks -- those will manage the floating status of any
302 * lower-level descendents inside them, of course).
304 static bool ShouldSuppressFloatingOfDescendants(nsIFrame* aFrame) {
305 return aFrame->IsFlexOrGridContainer() || aFrame->IsMathMLFrame();
308 // Return true if column-span descendants should be suppressed under aFrame's
309 // subtree (until a multi-column container re-establishing a block formatting
310 // context). Basically, this is testing whether aFrame establishes a new block
311 // formatting context or not.
312 static bool ShouldSuppressColumnSpanDescendants(nsIFrame* aFrame) {
313 if (aFrame->Style()->GetPseudoType() == PseudoStyleType::columnContent) {
314 // Never suppress column-span under ::-moz-column-content frames.
315 return false;
318 if (aFrame->IsInlineFrame()) {
319 // Allow inline frames to have column-span block children.
320 return false;
323 if (!aFrame->IsBlockFrameOrSubclass() ||
324 aFrame->HasAnyStateBits(NS_BLOCK_BFC | NS_FRAME_OUT_OF_FLOW) ||
325 aFrame->IsFixedPosContainingBlock()) {
326 // Need to suppress column-span if we:
327 // - Are a different block formatting context,
328 // - Are an out-of-flow frame, OR
329 // - Establish a containing block for fixed-position descendants
331 // For example, the children of a column-span never need to be further
332 // processed even if there is a nested column-span child. Because a
333 // column-span always creates its own block formatting context, a nested
334 // column-span child won't be in the same block formatting context with the
335 // nearest multi-column ancestor. This is the same case as if the
336 // column-span is outside of a multi-column hierarchy.
337 return true;
340 return false;
343 // Reparent a frame into a wrapper frame that is a child of its old parent.
344 static void ReparentFrame(RestyleManager* aRestyleManager,
345 nsContainerFrame* aNewParentFrame, nsIFrame* aFrame,
346 bool aForceStyleReparent) {
347 aFrame->SetParent(aNewParentFrame);
348 // We reparent frames for two reasons: to put them inside ::first-line, and to
349 // put them inside some wrapper anonymous boxes.
350 if (aForceStyleReparent) {
351 aRestyleManager->ReparentComputedStyleForFirstLine(aFrame);
355 static void ReparentFrames(nsCSSFrameConstructor* aFrameConstructor,
356 nsContainerFrame* aNewParentFrame,
357 const nsFrameList& aFrameList,
358 bool aForceStyleReparent) {
359 RestyleManager* restyleManager = aFrameConstructor->RestyleManager();
360 for (nsIFrame* f : aFrameList) {
361 ReparentFrame(restyleManager, aNewParentFrame, f, aForceStyleReparent);
365 //----------------------------------------------------------------------
367 // When inline frames get weird and have block frames in them, we
368 // annotate them to help us respond to incremental content changes
369 // more easily.
371 static inline bool IsFramePartOfIBSplit(nsIFrame* aFrame) {
372 bool result = aFrame->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT);
373 MOZ_ASSERT(!result || static_cast<nsBlockFrame*>(do_QueryFrame(aFrame)) ||
374 static_cast<nsInlineFrame*>(do_QueryFrame(aFrame)),
375 "only block/inline frames can have NS_FRAME_PART_OF_IBSPLIT");
376 return result;
379 static nsContainerFrame* GetIBSplitSibling(nsIFrame* aFrame) {
380 MOZ_ASSERT(IsFramePartOfIBSplit(aFrame), "Shouldn't call this");
382 // We only store the "ib-split sibling" annotation with the first
383 // frame in the continuation chain. Walk back to find that frame now.
384 return aFrame->FirstContinuation()->GetProperty(nsIFrame::IBSplitSibling());
387 static nsContainerFrame* GetIBSplitPrevSibling(nsIFrame* aFrame) {
388 MOZ_ASSERT(IsFramePartOfIBSplit(aFrame), "Shouldn't call this");
390 // We only store the ib-split sibling annotation with the first
391 // frame in the continuation chain. Walk back to find that frame now.
392 return aFrame->FirstContinuation()->GetProperty(
393 nsIFrame::IBSplitPrevSibling());
396 static nsContainerFrame* GetLastIBSplitSibling(nsIFrame* aFrame) {
397 for (nsIFrame *frame = aFrame, *next;; frame = next) {
398 next = GetIBSplitSibling(frame);
399 if (!next) {
400 return static_cast<nsContainerFrame*>(frame);
403 MOZ_ASSERT_UNREACHABLE("unreachable code");
404 return nullptr;
407 static void SetFrameIsIBSplit(nsContainerFrame* aFrame,
408 nsContainerFrame* aIBSplitSibling) {
409 MOZ_ASSERT(aFrame, "bad args!");
411 // We should be the only continuation
412 NS_ASSERTION(!aFrame->GetPrevContinuation(),
413 "assigning ib-split sibling to other than first continuation!");
414 NS_ASSERTION(!aFrame->GetNextContinuation() ||
415 IsFramePartOfIBSplit(aFrame->GetNextContinuation()),
416 "should have no non-ib-split continuations here");
418 // Mark the frame as ib-split.
419 aFrame->AddStateBits(NS_FRAME_PART_OF_IBSPLIT);
421 if (aIBSplitSibling) {
422 NS_ASSERTION(!aIBSplitSibling->GetPrevContinuation(),
423 "assigning something other than the first continuation as the "
424 "ib-split sibling");
426 // Store the ib-split sibling (if we were given one) with the
427 // first frame in the flow.
428 aFrame->SetProperty(nsIFrame::IBSplitSibling(), aIBSplitSibling);
429 aIBSplitSibling->SetProperty(nsIFrame::IBSplitPrevSibling(), aFrame);
433 static nsIFrame* GetIBContainingBlockFor(nsIFrame* aFrame) {
434 MOZ_ASSERT(
435 IsFramePartOfIBSplit(aFrame),
436 "GetIBContainingBlockFor() should only be called on known IB frames");
438 // Get the first "normal" ancestor of the target frame.
439 nsIFrame* parentFrame;
440 do {
441 parentFrame = aFrame->GetParent();
443 if (!parentFrame) {
444 NS_ERROR("no unsplit block frame in IB hierarchy");
445 return aFrame;
448 // Note that we ignore non-ib-split frames which have a pseudo on their
449 // ComputedStyle -- they're not the frames we're looking for! In
450 // particular, they may be hiding a real parent that _is_ in an ib-split.
451 if (!IsFramePartOfIBSplit(parentFrame) &&
452 !parentFrame->Style()->IsPseudoOrAnonBox())
453 break;
455 aFrame = parentFrame;
456 } while (1);
458 // post-conditions
459 NS_ASSERTION(parentFrame,
460 "no normal ancestor found for ib-split frame "
461 "in GetIBContainingBlockFor");
462 NS_ASSERTION(parentFrame != aFrame,
463 "parentFrame is actually the child frame - bogus reslt");
465 return parentFrame;
468 // Find the multicol containing block suitable for reframing.
470 // Note: this function may not return a ColumnSetWrapperFrame. For example, if
471 // the multicol containing block has "overflow:scroll" style, HTMLScrollFrame is
472 // returned because ColumnSetWrapperFrame is the scrolled frame which has the
473 // -moz-scrolled-content pseudo style. We may walk up "too far", but in terms of
474 // correctness of reframing, it's OK.
475 static nsContainerFrame* GetMultiColumnContainingBlockFor(nsIFrame* aFrame) {
476 MOZ_ASSERT(aFrame->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR),
477 "Should only be called if the frame has a multi-column ancestor!");
479 nsContainerFrame* current = aFrame->GetParent();
480 while (current &&
481 (current->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR) ||
482 current->Style()->IsPseudoOrAnonBox())) {
483 current = current->GetParent();
486 MOZ_ASSERT(current,
487 "No multicol containing block in a valid column hierarchy?");
489 return current;
492 // This is a bit slow, but sometimes we need it.
493 static bool ParentIsWrapperAnonBox(nsIFrame* aParent) {
494 nsIFrame* maybeAnonBox = aParent;
495 if (maybeAnonBox->Style()->GetPseudoType() == PseudoStyleType::cellContent) {
496 // The thing that would maybe be a wrapper anon box is the cell.
497 maybeAnonBox = maybeAnonBox->GetParent();
499 return maybeAnonBox->Style()->IsWrapperAnonBox();
502 static bool InsertSeparatorBeforeAccessKey() {
503 static bool sInitialized = false;
504 static bool sValue = false;
505 if (!sInitialized) {
506 sInitialized = true;
508 const char* prefName = "intl.menuitems.insertseparatorbeforeaccesskeys";
509 nsAutoString val;
510 Preferences::GetLocalizedString(prefName, val);
511 sValue = val.EqualsLiteral("true");
513 return sValue;
516 static bool AlwaysAppendAccessKey() {
517 static bool sInitialized = false;
518 static bool sValue = false;
519 if (!sInitialized) {
520 sInitialized = true;
521 const char* prefName = "intl.menuitems.alwaysappendaccesskeys";
522 nsAutoString val;
523 Preferences::GetLocalizedString(prefName, val);
524 sValue = val.EqualsLiteral("true");
526 return sValue;
529 //----------------------------------------------------------------------
531 // Block/inline frame construction logic. We maintain a few invariants here:
533 // 1. Block frames contain block and inline frames.
535 // 2. Inline frames only contain inline frames. If an inline parent has a block
536 // child then the block child is migrated upward until it lands in a block
537 // parent (the inline frames containing block is where it will end up).
539 inline void SetInitialSingleChild(nsContainerFrame* aParent, nsIFrame* aFrame) {
540 MOZ_ASSERT(!aFrame->GetNextSibling(), "Should be using a frame list");
541 aParent->SetInitialChildList(FrameChildListID::Principal,
542 nsFrameList(aFrame, aFrame));
545 // -----------------------------------------------------------
547 // Structure used when constructing formatting object trees. Contains
548 // state information needed for absolutely positioned elements
549 namespace mozilla {
550 struct AbsoluteFrameList final : public nsFrameList {
551 // Containing block for absolutely positioned elements.
552 nsContainerFrame* mContainingBlock;
554 explicit AbsoluteFrameList(nsContainerFrame* aContainingBlock = nullptr)
555 : mContainingBlock(aContainingBlock) {}
557 // Transfer frames in aOther to this list. aOther becomes empty after this
558 // operation.
559 AbsoluteFrameList(AbsoluteFrameList&& aOther) = default;
560 AbsoluteFrameList& operator=(AbsoluteFrameList&& aOther) = default;
562 #ifdef DEBUG
563 // XXXbz Does this need a debug-only assignment operator that nulls out the
564 // childList in the AbsoluteFrameList we're copying? Introducing a difference
565 // between debug and non-debug behavior seems bad, so I guess not...
566 ~AbsoluteFrameList() {
567 NS_ASSERTION(!FirstChild(),
568 "Dangling child list. Someone forgot to insert it?");
570 #endif
572 } // namespace mozilla
574 // -----------------------------------------------------------
576 // Structure for saving the existing state when pushing/poping containing
577 // blocks. The destructor restores the state to its previous state
578 class MOZ_STACK_CLASS nsFrameConstructorSaveState {
579 public:
580 ~nsFrameConstructorSaveState();
582 private:
583 // Pointer to struct whose data we save/restore.
584 AbsoluteFrameList* mList = nullptr;
586 // The saved pointer to the fixed list.
587 AbsoluteFrameList* mSavedFixedList = nullptr;
589 // Copy of original frame list. This can be the original absolute list or a
590 // float list.
591 AbsoluteFrameList mSavedList;
593 // The name of the child list in which our frames would belong.
594 mozilla::FrameChildListID mChildListID = FrameChildListID::Principal;
595 nsFrameConstructorState* mState = nullptr;
597 friend class nsFrameConstructorState;
600 // Structure used for maintaining state information during the
601 // frame construction process
602 class MOZ_STACK_CLASS nsFrameConstructorState {
603 public:
604 nsPresContext* mPresContext;
605 PresShell* mPresShell;
606 nsCSSFrameConstructor* mFrameConstructor;
608 // Containing block information for out-of-flow frames.
610 // Floats are easy. Whatever is our float CB.
612 // Regular abspos elements are easy too. Its containing block can be the
613 // nearest abspos element, or the ICB (the canvas frame).
615 // Top layer abspos elements are always children of the ICB, but we can get
616 // away with having two different lists (mAbsoluteList and
617 // mTopLayerAbsoluteList), because because top layer frames cause
618 // non-top-layer frames to be contained inside (so any descendants of a top
619 // layer abspos can never share containing block with it, unless they're also
620 // in the top layer).
622 // Regular fixed elements however are trickier. Fixed elements can be
623 // contained in one of three lists:
625 // * mAbsoluteList, if our abspos cb is also a fixpos cb (e.g., is
626 // transformed or has a filter).
628 // * mAncestorFixedList, if the fixpos cb is an ancestor element other than
629 // the viewport frame, (so, a transformed / filtered
630 // ancestor).
632 // * mRealFixedList, which is also the fixed list used for the top layer
633 // fixed items, which is the fixed list of the viewport
634 // frame.
636 // It is important that mRealFixedList is shared between regular and top layer
637 // fixpos elements, since no-top-layer descendants of top layer fixed elements
638 // could share ICB and vice versa, so without that there would be no guarantee
639 // of layout ordering between them.
640 AbsoluteFrameList mFloatedList;
641 AbsoluteFrameList mAbsoluteList;
642 AbsoluteFrameList mTopLayerAbsoluteList;
643 AbsoluteFrameList mAncestorFixedList;
644 AbsoluteFrameList mRealFixedList;
646 // Never null, always pointing to one of the lists documented above.
647 AbsoluteFrameList* mFixedList;
649 // What `page: auto` resolves to. This is the used page-name of the parent
650 // frame. Updated by AutoFrameConstructionPageName.
651 const nsAtom* mAutoPageNameValue = nullptr;
653 nsCOMPtr<nsILayoutHistoryState> mFrameState;
654 // These bits will be added to the state bits of any frame we construct
655 // using this state.
656 nsFrameState mAdditionalStateBits{0};
658 // If false (which is the default) then call SetPrimaryFrame() as needed
659 // during frame construction. If true, don't make any SetPrimaryFrame()
660 // calls, except for generated content which doesn't have a primary frame
661 // yet. The mCreatingExtraFrames == true mode is meant to be used for
662 // construction of random "extra" frames for elements via normal frame
663 // construction APIs (e.g. replication of things across pages in paginated
664 // mode).
665 bool mCreatingExtraFrames;
667 // This keeps track of whether we have found a "rendered legend" for
668 // the current FieldSetFrame.
669 bool mHasRenderedLegend;
671 nsTArray<RefPtr<nsIContent>> mGeneratedContentWithInitializer;
673 #ifdef DEBUG
674 // Record the float containing block candidate passed into
675 // MaybePushFloatContainingBlock() to keep track that we've call the method to
676 // handle the float CB scope before processing the CB's children. It is reset
677 // in ConstructFramesFromItemList().
678 nsContainerFrame* mFloatCBCandidate = nullptr;
679 #endif
681 // Constructor
682 // Use the passed-in history state.
683 nsFrameConstructorState(
684 PresShell* aPresShell, nsContainerFrame* aFixedContainingBlock,
685 nsContainerFrame* aAbsoluteContainingBlock,
686 nsContainerFrame* aFloatContainingBlock,
687 already_AddRefed<nsILayoutHistoryState> aHistoryState);
688 // Get the history state from the pres context's pres shell.
689 nsFrameConstructorState(PresShell* aPresShell,
690 nsContainerFrame* aFixedContainingBlock,
691 nsContainerFrame* aAbsoluteContainingBlock,
692 nsContainerFrame* aFloatContainingBlock);
694 ~nsFrameConstructorState();
696 // Process the frame insertions for all the out-of-flow nsAbsoluteItems.
697 void ProcessFrameInsertionsForAllLists();
699 // Function to push the existing absolute containing block state and
700 // create a new scope. Code that uses this function should get matching
701 // logic in GetAbsoluteContainingBlock.
702 // Also makes aNewAbsoluteContainingBlock the containing block for
703 // fixed-pos elements if necessary.
704 // aPositionedFrame is the frame whose style actually makes
705 // aNewAbsoluteContainingBlock a containing block. E.g. for a scrollable
706 // element aPositionedFrame is the element's primary frame and
707 // aNewAbsoluteContainingBlock is the scrolled frame.
708 void PushAbsoluteContainingBlock(
709 nsContainerFrame* aNewAbsoluteContainingBlock, nsIFrame* aPositionedFrame,
710 nsFrameConstructorSaveState& aSaveState);
712 // Function to forbid floats descendants under aFloatCBCandidate, or open a
713 // new float containing block scope for aFloatCBCandidate. The current
714 // state is saved in aSaveState if a new scope is pushed.
715 void MaybePushFloatContainingBlock(nsContainerFrame* aFloatCBCandidate,
716 nsFrameConstructorSaveState& aSaveState);
718 // Helper function for MaybePushFloatContainingBlock().
719 void PushFloatContainingBlock(nsContainerFrame* aNewFloatContainingBlock,
720 nsFrameConstructorSaveState& aSaveState);
722 // Function to return the proper geometric parent for a frame with display
723 // struct given by aStyleDisplay and parent's frame given by
724 // aContentParentFrame.
725 nsContainerFrame* GetGeometricParent(
726 const nsStyleDisplay& aStyleDisplay,
727 nsContainerFrame* aContentParentFrame) const;
729 // Collect absolute frames in mAbsoluteList which are proper descendants
730 // of aNewParent, and reparent them to aNewParent.
732 // Note: This function does something unusual that moves absolute items
733 // after their frames are constructed under a column hierarchy which has
734 // column-span elements. Do not use this if you're not dealing with
735 // columns.
736 void ReparentAbsoluteItems(nsContainerFrame* aNewParent);
738 // Collect floats in mFloatedList which are proper descendants of aNewParent,
739 // and reparent them to aNewParent.
741 // Note: This function does something unusual that moves floats after their
742 // frames are constructed under a column hierarchy which has column-span
743 // elements. Do not use this if you're not dealing with columns.
744 void ReparentFloats(nsContainerFrame* aNewParent);
747 * Function to add a new frame to the right frame list. This MUST be called
748 * on frames before their children have been processed if the frames might
749 * conceivably be out-of-flow; otherwise cleanup in error cases won't work
750 * right. Also, this MUST be called on frames after they have been
751 * initialized.
752 * @param aNewFrame the frame to add
753 * @param aFrameList the list to add in-flow frames to
754 * @param aContent the content pointer for aNewFrame
755 * @param aParentFrame the parent frame for the content if it were in-flow
756 * @param aCanBePositioned pass false if the frame isn't allowed to be
757 * positioned
758 * @param aCanBeFloated pass false if the frame isn't allowed to be
759 * floated
761 void AddChild(nsIFrame* aNewFrame, nsFrameList& aFrameList,
762 nsIContent* aContent, nsContainerFrame* aParentFrame,
763 bool aCanBePositioned = true, bool aCanBeFloated = true,
764 bool aInsertAfter = false,
765 nsIFrame* aInsertAfterFrame = nullptr);
768 * Function to return the fixed-pos element list. Normally this will just
769 * hand back the fixed-pos element list, but in case we're dealing with a
770 * transformed element that's acting as an abs-pos and fixed-pos container,
771 * we'll hand back the abs-pos list. Callers should use this function if they
772 * want to get the list acting as the fixed-pos item parent.
774 AbsoluteFrameList& GetFixedList() { return *mFixedList; }
775 const AbsoluteFrameList& GetFixedList() const { return *mFixedList; }
777 protected:
778 friend class nsFrameConstructorSaveState;
781 * ProcessFrameInsertions takes the frames in aFrameList and adds them as
782 * kids to the aChildListID child list of |aFrameList.containingBlock|.
784 void ProcessFrameInsertions(AbsoluteFrameList& aFrameList,
785 mozilla::FrameChildListID aChildListID);
788 * GetOutOfFlowFrameList selects the out-of-flow frame list the new
789 * frame should be added to. If the frame shouldn't be added to any
790 * out-of-flow list, it returns nullptr. The corresponding type of
791 * placeholder is also returned via the aPlaceholderType parameter
792 * if this method doesn't return nullptr. The caller should check
793 * whether the returned list really has a containing block.
795 AbsoluteFrameList* GetOutOfFlowFrameList(nsIFrame* aNewFrame,
796 bool aCanBePositioned,
797 bool aCanBeFloated,
798 nsFrameState* aPlaceholderType);
800 void ConstructBackdropFrameFor(nsIContent* aContent, nsIFrame* aFrame);
803 nsFrameConstructorState::nsFrameConstructorState(
804 PresShell* aPresShell, nsContainerFrame* aFixedContainingBlock,
805 nsContainerFrame* aAbsoluteContainingBlock,
806 nsContainerFrame* aFloatContainingBlock,
807 already_AddRefed<nsILayoutHistoryState> aHistoryState)
808 : mPresContext(aPresShell->GetPresContext()),
809 mPresShell(aPresShell),
810 mFrameConstructor(aPresShell->FrameConstructor()),
811 mFloatedList(aFloatContainingBlock),
812 mAbsoluteList(aAbsoluteContainingBlock),
813 mTopLayerAbsoluteList(mFrameConstructor->GetCanvasFrame()),
814 mAncestorFixedList(aFixedContainingBlock),
815 mRealFixedList(
816 static_cast<nsContainerFrame*>(mFrameConstructor->GetRootFrame())),
817 // See PushAbsoluteContaningBlock below
818 mFrameState(aHistoryState),
819 mCreatingExtraFrames(false),
820 mHasRenderedLegend(false) {
821 MOZ_COUNT_CTOR(nsFrameConstructorState);
822 mFixedList = [&] {
823 if (aFixedContainingBlock == aAbsoluteContainingBlock) {
824 return &mAbsoluteList;
826 if (aAbsoluteContainingBlock == mRealFixedList.mContainingBlock) {
827 return &mRealFixedList;
829 return &mAncestorFixedList;
830 }();
833 nsFrameConstructorState::nsFrameConstructorState(
834 PresShell* aPresShell, nsContainerFrame* aFixedContainingBlock,
835 nsContainerFrame* aAbsoluteContainingBlock,
836 nsContainerFrame* aFloatContainingBlock)
837 : nsFrameConstructorState(
838 aPresShell, aFixedContainingBlock, aAbsoluteContainingBlock,
839 aFloatContainingBlock,
840 aPresShell->GetDocument()->GetLayoutHistoryState()) {}
842 nsFrameConstructorState::~nsFrameConstructorState() {
843 MOZ_COUNT_DTOR(nsFrameConstructorState);
844 ProcessFrameInsertionsForAllLists();
845 for (auto& content : Reversed(mGeneratedContentWithInitializer)) {
846 content->RemoveProperty(nsGkAtoms::genConInitializerProperty);
850 void nsFrameConstructorState::ProcessFrameInsertionsForAllLists() {
851 ProcessFrameInsertions(mFloatedList, FrameChildListID::Float);
852 ProcessFrameInsertions(mAbsoluteList, FrameChildListID::Absolute);
853 ProcessFrameInsertions(mTopLayerAbsoluteList, FrameChildListID::Absolute);
854 ProcessFrameInsertions(*mFixedList, FrameChildListID::Fixed);
855 ProcessFrameInsertions(mRealFixedList, FrameChildListID::Fixed);
858 void nsFrameConstructorState::PushAbsoluteContainingBlock(
859 nsContainerFrame* aNewAbsoluteContainingBlock, nsIFrame* aPositionedFrame,
860 nsFrameConstructorSaveState& aSaveState) {
861 MOZ_ASSERT(!!aNewAbsoluteContainingBlock == !!aPositionedFrame,
862 "We should have both or none");
863 aSaveState.mList = &mAbsoluteList;
864 aSaveState.mChildListID = FrameChildListID::Absolute;
865 aSaveState.mState = this;
866 aSaveState.mSavedList = std::move(mAbsoluteList);
867 aSaveState.mSavedFixedList = mFixedList;
868 mAbsoluteList = AbsoluteFrameList(aNewAbsoluteContainingBlock);
869 mFixedList = [&] {
870 if (!aPositionedFrame || aPositionedFrame->IsFixedPosContainingBlock()) {
871 // See if we need to treat abspos and fixedpos the same. This happens if
872 // we're a transformed/filtered/etc element, or if we force a null abspos
873 // containing block (for mathml for example).
874 return &mAbsoluteList;
876 if (aPositionedFrame->StyleDisplay()->mTopLayer == StyleTopLayer::Top) {
877 // If our new CB is in the top layer, and isn't a fixed CB itself, we also
878 // escape the usual containment.
879 return &mRealFixedList;
881 if (mFixedList == &mAbsoluteList) {
882 // If we were pointing to our old absolute list, keep pointing to it.
883 return &aSaveState.mSavedList;
885 // Otherwise keep pointing to the current thing (another ancestor's
886 // absolute list, or the real fixed list, doesn't matter).
887 return mFixedList;
888 }();
890 if (aNewAbsoluteContainingBlock) {
891 aNewAbsoluteContainingBlock->MarkAsAbsoluteContainingBlock();
895 void nsFrameConstructorState::MaybePushFloatContainingBlock(
896 nsContainerFrame* aFloatCBCandidate,
897 nsFrameConstructorSaveState& aSaveState) {
898 // The logic here needs to match the logic in GetFloatContainingBlock().
899 if (ShouldSuppressFloatingOfDescendants(aFloatCBCandidate)) {
900 // Pushing a null float containing block forbids any frames from being
901 // floated until a new float containing block is pushed. See implementation
902 // of nsFrameConstructorState::AddChild().
904 // XXX we should get rid of null float containing blocks and teach the
905 // various frame classes to deal with floats instead.
906 PushFloatContainingBlock(nullptr, aSaveState);
907 } else if (aFloatCBCandidate->IsFloatContainingBlock()) {
908 PushFloatContainingBlock(aFloatCBCandidate, aSaveState);
911 #ifdef DEBUG
912 mFloatCBCandidate = aFloatCBCandidate;
913 #endif
916 void nsFrameConstructorState::PushFloatContainingBlock(
917 nsContainerFrame* aNewFloatContainingBlock,
918 nsFrameConstructorSaveState& aSaveState) {
919 MOZ_ASSERT(!aNewFloatContainingBlock ||
920 aNewFloatContainingBlock->IsFloatContainingBlock(),
921 "Please push a real float containing block!");
922 NS_ASSERTION(
923 !aNewFloatContainingBlock ||
924 !ShouldSuppressFloatingOfDescendants(aNewFloatContainingBlock),
925 "We should not push a frame that is supposed to _suppress_ "
926 "floats as a float containing block!");
927 aSaveState.mList = &mFloatedList;
928 aSaveState.mSavedList = std::move(mFloatedList);
929 aSaveState.mChildListID = FrameChildListID::Float;
930 aSaveState.mState = this;
931 mFloatedList = AbsoluteFrameList(aNewFloatContainingBlock);
934 nsContainerFrame* nsFrameConstructorState::GetGeometricParent(
935 const nsStyleDisplay& aStyleDisplay,
936 nsContainerFrame* aContentParentFrame) const {
937 // If there is no container for a fixed, absolute, or floating root
938 // frame, we will ignore the positioning. This hack is originally
939 // brought to you by the letter T: tables, since other roots don't
940 // even call into this code. See bug 178855.
942 // XXX Disabling positioning in this case is a hack. If one was so inclined,
943 // one could support this either by (1) inserting a dummy block between the
944 // table and the canvas or (2) teaching the canvas how to reflow positioned
945 // elements. (1) has the usual problems when multiple frames share the same
946 // content (notice all the special cases in this file dealing with inner
947 // tables and table wrappers which share the same content). (2) requires some
948 // work and possible factoring.
950 // XXXbz couldn't we just force position to "static" on roots and
951 // float to "none"? That's OK per CSS 2.1, as far as I can tell.
953 if (aContentParentFrame && aContentParentFrame->IsInSVGTextSubtree()) {
954 return aContentParentFrame;
957 if (aStyleDisplay.IsFloatingStyle() && mFloatedList.mContainingBlock) {
958 NS_ASSERTION(!aStyleDisplay.IsAbsolutelyPositionedStyle(),
959 "Absolutely positioned _and_ floating?");
960 return mFloatedList.mContainingBlock;
963 if (aStyleDisplay.mTopLayer != StyleTopLayer::None) {
964 MOZ_ASSERT(aStyleDisplay.mTopLayer == StyleTopLayer::Top,
965 "-moz-top-layer should be either none or top");
966 MOZ_ASSERT(aStyleDisplay.IsAbsolutelyPositionedStyle(),
967 "Top layer items should always be absolutely positioned");
968 if (aStyleDisplay.mPosition == StylePositionProperty::Fixed) {
969 MOZ_ASSERT(mRealFixedList.mContainingBlock, "No root frame?");
970 return mRealFixedList.mContainingBlock;
972 MOZ_ASSERT(aStyleDisplay.mPosition == StylePositionProperty::Absolute);
973 MOZ_ASSERT(mTopLayerAbsoluteList.mContainingBlock);
974 return mTopLayerAbsoluteList.mContainingBlock;
977 if (aStyleDisplay.mPosition == StylePositionProperty::Absolute &&
978 mAbsoluteList.mContainingBlock) {
979 return mAbsoluteList.mContainingBlock;
982 if (aStyleDisplay.mPosition == StylePositionProperty::Fixed &&
983 mFixedList->mContainingBlock) {
984 return mFixedList->mContainingBlock;
987 return aContentParentFrame;
990 void nsFrameConstructorState::ReparentAbsoluteItems(
991 nsContainerFrame* aNewParent) {
992 // Bug 1491727: This function might not conform to the spec. See
993 // https://github.com/w3c/csswg-drafts/issues/1894.
995 MOZ_ASSERT(aNewParent->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR),
996 "Restrict the usage under column hierarchy.");
998 AbsoluteFrameList newAbsoluteItems(aNewParent);
1000 nsIFrame* current = mAbsoluteList.FirstChild();
1001 while (current) {
1002 nsIFrame* placeholder = current->GetPlaceholderFrame();
1004 if (nsLayoutUtils::IsProperAncestorFrame(aNewParent, placeholder)) {
1005 nsIFrame* next = current->GetNextSibling();
1006 mAbsoluteList.RemoveFrame(current);
1007 newAbsoluteItems.AppendFrame(aNewParent, current);
1008 current = next;
1009 } else {
1010 current = current->GetNextSibling();
1014 if (newAbsoluteItems.NotEmpty()) {
1015 // ~nsFrameConstructorSaveState() will move newAbsoluteItems to
1016 // aNewParent's absolute child list.
1017 nsFrameConstructorSaveState absoluteSaveState;
1019 // It doesn't matter whether aNewParent has position style or not. Caller
1020 // won't call us if we can't have absolute children.
1021 PushAbsoluteContainingBlock(aNewParent, aNewParent, absoluteSaveState);
1022 mAbsoluteList = std::move(newAbsoluteItems);
1026 void nsFrameConstructorState::ReparentFloats(nsContainerFrame* aNewParent) {
1027 MOZ_ASSERT(aNewParent->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR),
1028 "Restrict the usage under column hierarchy.");
1029 MOZ_ASSERT(
1030 aNewParent->IsFloatContainingBlock(),
1031 "Why calling this method if aNewParent is not a float containing block?");
1033 // Gather floats that should reparent under aNewParent.
1034 AbsoluteFrameList floats(aNewParent);
1035 nsIFrame* current = mFloatedList.FirstChild();
1036 while (current) {
1037 nsIFrame* placeholder = current->GetPlaceholderFrame();
1038 nsIFrame* next = current->GetNextSibling();
1039 if (nsLayoutUtils::IsProperAncestorFrame(aNewParent, placeholder)) {
1040 mFloatedList.RemoveFrame(current);
1041 floats.AppendFrame(aNewParent, current);
1043 current = next;
1046 if (floats.NotEmpty()) {
1047 // Make floats move into aNewParent's float child list in
1048 // ~nsFrameConstructorSaveState() when destructing floatSaveState.
1049 nsFrameConstructorSaveState floatSaveState;
1050 PushFloatContainingBlock(aNewParent, floatSaveState);
1051 mFloatedList = std::move(floats);
1055 AbsoluteFrameList* nsFrameConstructorState::GetOutOfFlowFrameList(
1056 nsIFrame* aNewFrame, bool aCanBePositioned, bool aCanBeFloated,
1057 nsFrameState* aPlaceholderType) {
1058 const nsStyleDisplay* disp = aNewFrame->StyleDisplay();
1059 if (aCanBeFloated && disp->IsFloatingStyle()) {
1060 *aPlaceholderType = PLACEHOLDER_FOR_FLOAT;
1061 return &mFloatedList;
1064 if (aCanBePositioned) {
1065 if (disp->mTopLayer != StyleTopLayer::None) {
1066 *aPlaceholderType = PLACEHOLDER_FOR_TOPLAYER;
1067 if (disp->mPosition == StylePositionProperty::Fixed) {
1068 *aPlaceholderType |= PLACEHOLDER_FOR_FIXEDPOS;
1069 return &mRealFixedList;
1071 *aPlaceholderType |= PLACEHOLDER_FOR_ABSPOS;
1072 return &mTopLayerAbsoluteList;
1074 if (disp->mPosition == StylePositionProperty::Absolute) {
1075 *aPlaceholderType = PLACEHOLDER_FOR_ABSPOS;
1076 return &mAbsoluteList;
1078 if (disp->mPosition == StylePositionProperty::Fixed) {
1079 *aPlaceholderType = PLACEHOLDER_FOR_FIXEDPOS;
1080 return mFixedList;
1083 return nullptr;
1086 void nsFrameConstructorState::ConstructBackdropFrameFor(nsIContent* aContent,
1087 nsIFrame* aFrame) {
1088 MOZ_ASSERT(aFrame->StyleDisplay()->mTopLayer == StyleTopLayer::Top);
1089 nsContainerFrame* frame = do_QueryFrame(aFrame);
1090 if (!frame) {
1091 NS_WARNING("Cannot create backdrop frame for non-container frame");
1092 return;
1095 ComputedStyle* parentStyle = nsLayoutUtils::GetStyleFrame(aFrame)->Style();
1096 RefPtr<ComputedStyle> style =
1097 mPresShell->StyleSet()->ResolvePseudoElementStyle(
1098 *aContent->AsElement(), PseudoStyleType::backdrop, nullptr,
1099 parentStyle);
1100 MOZ_ASSERT(style->StyleDisplay()->mTopLayer == StyleTopLayer::Top);
1101 nsContainerFrame* parentFrame =
1102 GetGeometricParent(*style->StyleDisplay(), nullptr);
1104 nsBackdropFrame* backdropFrame =
1105 new (mPresShell) nsBackdropFrame(style, mPresShell->GetPresContext());
1106 backdropFrame->Init(aContent, parentFrame, nullptr);
1108 nsFrameState placeholderType;
1109 AbsoluteFrameList* frameList =
1110 GetOutOfFlowFrameList(backdropFrame, true, true, &placeholderType);
1111 MOZ_ASSERT(placeholderType & PLACEHOLDER_FOR_TOPLAYER);
1113 nsIFrame* placeholder = nsCSSFrameConstructor::CreatePlaceholderFrameFor(
1114 mPresShell, aContent, backdropFrame, frame, nullptr, placeholderType);
1115 frame->SetInitialChildList(FrameChildListID::Backdrop,
1116 nsFrameList(placeholder, placeholder));
1118 frameList->AppendFrame(nullptr, backdropFrame);
1121 void nsFrameConstructorState::AddChild(
1122 nsIFrame* aNewFrame, nsFrameList& aFrameList, nsIContent* aContent,
1123 nsContainerFrame* aParentFrame, bool aCanBePositioned, bool aCanBeFloated,
1124 bool aInsertAfter, nsIFrame* aInsertAfterFrame) {
1125 MOZ_ASSERT(!aNewFrame->GetNextSibling(), "Shouldn't happen");
1127 nsFrameState placeholderType;
1128 AbsoluteFrameList* outOfFlowFrameList = GetOutOfFlowFrameList(
1129 aNewFrame, aCanBePositioned, aCanBeFloated, &placeholderType);
1131 // The comments in GetGeometricParent regarding root table frames
1132 // all apply here, unfortunately. Thus, we need to check whether
1133 // the returned frame items really has containing block.
1134 nsFrameList* frameList;
1135 if (outOfFlowFrameList && outOfFlowFrameList->mContainingBlock) {
1136 MOZ_ASSERT(aNewFrame->GetParent() == outOfFlowFrameList->mContainingBlock,
1137 "Parent of the frame is not the containing block?");
1138 frameList = outOfFlowFrameList;
1139 } else {
1140 frameList = &aFrameList;
1141 placeholderType = nsFrameState(0);
1144 if (placeholderType) {
1145 NS_ASSERTION(frameList != &aFrameList,
1146 "Putting frame in-flow _and_ want a placeholder?");
1147 nsIFrame* placeholderFrame =
1148 nsCSSFrameConstructor::CreatePlaceholderFrameFor(
1149 mPresShell, aContent, aNewFrame, aParentFrame, nullptr,
1150 placeholderType);
1152 placeholderFrame->AddStateBits(mAdditionalStateBits);
1153 // Add the placeholder frame to the flow
1154 aFrameList.AppendFrame(nullptr, placeholderFrame);
1156 if (placeholderType & PLACEHOLDER_FOR_TOPLAYER) {
1157 ConstructBackdropFrameFor(aContent, aNewFrame);
1160 #ifdef DEBUG
1161 else {
1162 NS_ASSERTION(aNewFrame->GetParent() == aParentFrame,
1163 "In-flow frame has wrong parent");
1165 #endif
1167 if (aInsertAfter) {
1168 frameList->InsertFrame(nullptr, aInsertAfterFrame, aNewFrame);
1169 } else {
1170 frameList->AppendFrame(nullptr, aNewFrame);
1174 // Some of this function's callers recurse 1000 levels deep in crashtests. On
1175 // platforms where stack limits are low, we can't afford to incorporate this
1176 // function's `AutoTArray`s into its callers' stack frames, so disable inlining.
1177 MOZ_NEVER_INLINE void nsFrameConstructorState::ProcessFrameInsertions(
1178 AbsoluteFrameList& aFrameList, FrameChildListID aChildListID) {
1179 MOZ_ASSERT(&aFrameList == &mFloatedList || &aFrameList == &mAbsoluteList ||
1180 &aFrameList == &mTopLayerAbsoluteList ||
1181 &aFrameList == &mAncestorFixedList || &aFrameList == mFixedList ||
1182 &aFrameList == &mRealFixedList);
1183 MOZ_ASSERT_IF(&aFrameList == &mFloatedList,
1184 aChildListID == FrameChildListID::Float);
1185 MOZ_ASSERT_IF(&aFrameList == &mAbsoluteList || &aFrameList == mFixedList,
1186 aChildListID == FrameChildListID::Absolute ||
1187 aChildListID == FrameChildListID::Fixed);
1188 MOZ_ASSERT_IF(&aFrameList == &mTopLayerAbsoluteList,
1189 aChildListID == FrameChildListID::Absolute);
1190 MOZ_ASSERT_IF(&aFrameList == mFixedList && &aFrameList != &mAbsoluteList,
1191 aChildListID == FrameChildListID::Fixed);
1192 MOZ_ASSERT_IF(&aFrameList == &mAncestorFixedList,
1193 aChildListID == FrameChildListID::Fixed);
1194 MOZ_ASSERT_IF(&aFrameList == &mRealFixedList,
1195 aChildListID == FrameChildListID::Fixed);
1197 if (aFrameList.IsEmpty()) {
1198 return;
1201 nsContainerFrame* containingBlock = aFrameList.mContainingBlock;
1203 NS_ASSERTION(containingBlock, "Child list without containing block?");
1205 if (aChildListID == FrameChildListID::Fixed) {
1206 // Put this frame on the transformed-frame's abs-pos list instead, if
1207 // it has abs-pos children instead of fixed-pos children.
1208 aChildListID = containingBlock->GetAbsoluteListID();
1211 // Insert the frames hanging out in aItems. We can use SetInitialChildList()
1212 // if the containing block hasn't been reflowed yet (so NS_FRAME_FIRST_REFLOW
1213 // is set) and doesn't have any frames in the aChildListID child list yet.
1214 const nsFrameList& childList = containingBlock->GetChildList(aChildListID);
1215 if (childList.IsEmpty() &&
1216 containingBlock->HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
1217 // If we're injecting absolutely positioned frames, inject them on the
1218 // absolute containing block
1219 if (aChildListID == containingBlock->GetAbsoluteListID()) {
1220 containingBlock->GetAbsoluteContainingBlock()->SetInitialChildList(
1221 containingBlock, aChildListID, std::move(aFrameList));
1222 } else {
1223 containingBlock->SetInitialChildList(aChildListID, std::move(aFrameList));
1225 } else if (aChildListID == FrameChildListID::Fixed ||
1226 aChildListID == FrameChildListID::Absolute) {
1227 // The order is not important for abs-pos/fixed-pos frame list, just
1228 // append the frame items to the list directly.
1229 mFrameConstructor->AppendFrames(containingBlock, aChildListID,
1230 std::move(aFrameList));
1231 } else {
1232 // Note that whether the frame construction context is doing an append or
1233 // not is not helpful here, since it could be appending to some frame in
1234 // the middle of the document, which means we're not necessarily
1235 // appending to the children of the containing block.
1237 // We need to make sure the 'append to the end of document' case is fast.
1238 // So first test the last child of the containing block
1239 nsIFrame* lastChild = childList.LastChild();
1241 // CompareTreePosition uses placeholder hierarchy for out of flow frames,
1242 // so this will make out-of-flows respect the ordering of placeholders,
1243 // which is great because it takes care of anonymous content.
1244 nsIFrame* firstNewFrame = aFrameList.FirstChild();
1246 // Cache the ancestor chain so that we can reuse it if needed.
1247 AutoTArray<nsIFrame*, 20> firstNewFrameAncestors;
1248 nsIFrame* notCommonAncestor = nullptr;
1249 if (lastChild) {
1250 notCommonAncestor = nsLayoutUtils::FillAncestors(
1251 firstNewFrame, containingBlock, &firstNewFrameAncestors);
1254 if (!lastChild || nsLayoutUtils::CompareTreePosition(
1255 lastChild, firstNewFrame, firstNewFrameAncestors,
1256 notCommonAncestor ? containingBlock : nullptr) < 0) {
1257 // no lastChild, or lastChild comes before the new children, so just
1258 // append
1259 mFrameConstructor->AppendFrames(containingBlock, aChildListID,
1260 std::move(aFrameList));
1261 } else {
1262 // Try the other children. First collect them to an array so that a
1263 // reasonable fast binary search can be used to find the insertion point.
1264 AutoTArray<nsIFrame*, 128> children;
1265 for (nsIFrame* f = childList.FirstChild(); f != lastChild;
1266 f = f->GetNextSibling()) {
1267 children.AppendElement(f);
1270 nsIFrame* insertionPoint = nullptr;
1271 int32_t imin = 0;
1272 int32_t max = children.Length();
1273 while (max > imin) {
1274 int32_t imid = imin + ((max - imin) / 2);
1275 nsIFrame* f = children[imid];
1276 int32_t compare = nsLayoutUtils::CompareTreePosition(
1277 f, firstNewFrame, firstNewFrameAncestors,
1278 notCommonAncestor ? containingBlock : nullptr);
1279 if (compare > 0) {
1280 // f is after the new frame.
1281 max = imid;
1282 insertionPoint = imid > 0 ? children[imid - 1] : nullptr;
1283 } else if (compare < 0) {
1284 // f is before the new frame.
1285 imin = imid + 1;
1286 insertionPoint = f;
1287 } else {
1288 // This is for the old behavior. Should be removed once it is
1289 // guaranteed that CompareTreePosition can't return 0!
1290 // See bug 928645.
1291 NS_WARNING("Something odd happening???");
1292 insertionPoint = nullptr;
1293 for (uint32_t i = 0; i < children.Length(); ++i) {
1294 nsIFrame* f = children[i];
1295 if (nsLayoutUtils::CompareTreePosition(
1296 f, firstNewFrame, firstNewFrameAncestors,
1297 notCommonAncestor ? containingBlock : nullptr) > 0) {
1298 break;
1300 insertionPoint = f;
1302 break;
1305 mFrameConstructor->InsertFrames(containingBlock, aChildListID,
1306 insertionPoint, std::move(aFrameList));
1310 MOZ_ASSERT(aFrameList.IsEmpty(), "How did that happen?");
1313 nsFrameConstructorSaveState::~nsFrameConstructorSaveState() {
1314 // Restore the state
1315 if (mList) {
1316 MOZ_ASSERT(mState, "Can't have mList set without having a state!");
1317 mState->ProcessFrameInsertions(*mList, mChildListID);
1319 if (mList == &mState->mAbsoluteList) {
1320 mState->mAbsoluteList = std::move(mSavedList);
1321 mState->mFixedList = mSavedFixedList;
1322 } else {
1323 mState->mFloatedList = std::move(mSavedList);
1326 MOZ_ASSERT(mSavedList.IsEmpty(),
1327 "Frames in mSavedList should've moved back into mState!");
1328 MOZ_ASSERT(!mList->LastChild() || !mList->LastChild()->GetNextSibling(),
1329 "Something corrupted our list!");
1334 * Moves aFrameList from aOldParent to aNewParent. This updates the parent
1335 * pointer of the frames in the list, and reparents their views as needed.
1336 * nsIFrame::SetParent sets the NS_FRAME_HAS_VIEW bit on aNewParent and its
1337 * ancestors as needed. Then it sets the list as the initial child list
1338 * on aNewParent, unless aNewParent either already has kids or has been
1339 * reflowed; in that case it appends the new frames. Note that this
1340 * method differs from ReparentFrames in that it doesn't change the kids'
1341 * style.
1343 // XXXbz Since this is only used for {ib} splits, could we just copy the view
1344 // bits from aOldParent to aNewParent and then use the
1345 // nsFrameList::ApplySetParent? That would still leave us doing two passes
1346 // over the list, of course; if we really wanted to we could factor out the
1347 // relevant part of ReparentFrameViewList, I suppose... Or just get rid of
1348 // views, which would make most of this function go away.
1349 static void MoveChildrenTo(nsIFrame* aOldParent, nsContainerFrame* aNewParent,
1350 nsFrameList& aFrameList) {
1351 #ifdef DEBUG
1352 bool sameGrandParent = aOldParent->GetParent() == aNewParent->GetParent();
1354 if (aNewParent->HasView() || aOldParent->HasView() || !sameGrandParent) {
1355 // Move the frames into the new view
1356 nsContainerFrame::ReparentFrameViewList(aFrameList, aOldParent, aNewParent);
1358 #endif
1360 aFrameList.ApplySetParent(aNewParent);
1362 if (aNewParent->PrincipalChildList().IsEmpty() &&
1363 aNewParent->HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
1364 aNewParent->SetInitialChildList(FrameChildListID::Principal,
1365 std::move(aFrameList));
1366 } else {
1367 aNewParent->AppendFrames(FrameChildListID::Principal,
1368 std::move(aFrameList));
1372 static void EnsureAutoPageName(nsFrameConstructorState& aState,
1373 const nsContainerFrame* const aFrame) {
1374 // Check if we need to figure out our used page name.
1375 // When building the entire document, this should only happen for the
1376 // root, which will mean the loop will immediately end. Either way, this will
1377 // only happen once for each time the frame constructor is run.
1378 if (aState.mAutoPageNameValue) {
1379 return;
1382 for (const nsContainerFrame* frame = aFrame; frame;
1383 frame = frame->GetParent()) {
1384 if (const nsAtom* maybePageName = frame->GetStylePageName()) {
1385 aState.mAutoPageNameValue = maybePageName;
1386 return;
1389 // Ensure that a root with `page: auto` gets an empty page name
1390 // https://drafts.csswg.org/css-page-3/#using-named-pages
1391 aState.mAutoPageNameValue = nsGkAtoms::_empty;
1394 nsCSSFrameConstructor::AutoFrameConstructionPageName::
1395 AutoFrameConstructionPageName(nsFrameConstructorState& aState,
1396 nsIFrame* const aFrame)
1397 : mState(aState), mNameToRestore(nullptr) {
1398 if (!aState.mPresContext->IsPaginated()) {
1399 MOZ_ASSERT(!aState.mAutoPageNameValue,
1400 "Page name should not have been set");
1401 return;
1403 #ifdef DEBUG
1404 MOZ_ASSERT(!aFrame->mWasVisitedByAutoFrameConstructionPageName,
1405 "Frame should only have been visited once");
1406 aFrame->mWasVisitedByAutoFrameConstructionPageName = true;
1407 #endif
1409 EnsureAutoPageName(aState, aFrame->GetParent());
1410 mNameToRestore = aState.mAutoPageNameValue;
1412 MOZ_ASSERT(mNameToRestore,
1413 "Page name should have been found by EnsureAutoPageName");
1414 if (const nsAtom* maybePageName = aFrame->GetStylePageName()) {
1415 aState.mAutoPageNameValue = maybePageName;
1417 aFrame->SetAutoPageValue(aState.mAutoPageNameValue);
1420 nsCSSFrameConstructor::AutoFrameConstructionPageName::
1421 ~AutoFrameConstructionPageName() {
1422 // This isn't actually useful when not in paginated layout, but it's very
1423 // likely cheaper to unconditionally write this pointer than to test for
1424 // paginated layout and then branch on the result.
1425 mState.mAutoPageNameValue = mNameToRestore;
1428 //----------------------------------------------------------------------
1430 nsCSSFrameConstructor::nsCSSFrameConstructor(Document* aDocument,
1431 PresShell* aPresShell)
1432 : nsFrameManager(aPresShell),
1433 mDocument(aDocument),
1434 mFirstFreeFCItem(nullptr),
1435 mFCItemsInUse(0),
1436 mCurrentDepth(0),
1437 mQuotesDirty(false),
1438 mCountersDirty(false),
1439 mAlwaysCreateFramesForIgnorableWhitespace(false) {
1440 #ifdef DEBUG
1441 static bool gFirstTime = true;
1442 if (gFirstTime) {
1443 gFirstTime = false;
1444 char* flags = PR_GetEnv("GECKO_FRAMECTOR_DEBUG_FLAGS");
1445 if (flags) {
1446 bool error = false;
1447 for (;;) {
1448 char* comma = strchr(flags, ',');
1449 if (comma) *comma = '\0';
1451 bool found = false;
1452 FrameCtorDebugFlags* flag = gFlags;
1453 FrameCtorDebugFlags* limit = gFlags + NUM_DEBUG_FLAGS;
1454 while (flag < limit) {
1455 if (nsCRT::strcasecmp(flag->name, flags) == 0) {
1456 *(flag->on) = true;
1457 printf("nsCSSFrameConstructor: setting %s debug flag on\n",
1458 flag->name);
1459 found = true;
1460 break;
1462 ++flag;
1465 if (!found) error = true;
1467 if (!comma) break;
1469 *comma = ',';
1470 flags = comma + 1;
1473 if (error) {
1474 printf("Here are the available GECKO_FRAMECTOR_DEBUG_FLAGS:\n");
1475 FrameCtorDebugFlags* flag = gFlags;
1476 FrameCtorDebugFlags* limit = gFlags + NUM_DEBUG_FLAGS;
1477 while (flag < limit) {
1478 printf(" %s\n", flag->name);
1479 ++flag;
1481 printf(
1482 "Note: GECKO_FRAMECTOR_DEBUG_FLAGS is a comma separated list of "
1483 "flag\n");
1484 printf("names (no whitespace)\n");
1488 #endif
1491 void nsCSSFrameConstructor::NotifyDestroyingFrame(nsIFrame* aFrame) {
1492 if (aFrame->StyleDisplay()->IsContainStyle()) {
1493 mContainStyleScopeManager.DestroyScopesFor(aFrame);
1496 if (aFrame->HasAnyStateBits(NS_FRAME_GENERATED_CONTENT) &&
1497 mContainStyleScopeManager.DestroyQuoteNodesFor(aFrame)) {
1498 QuotesDirty();
1501 if (aFrame->HasAnyStateBits(NS_FRAME_HAS_CSS_COUNTER_STYLE) &&
1502 mContainStyleScopeManager.DestroyCounterNodesFor(aFrame)) {
1503 // Technically we don't need to update anything if we destroyed only
1504 // USE nodes. However, this is unlikely to happen in the real world
1505 // since USE nodes generally go along with INCREMENT nodes.
1506 CountersDirty();
1509 RestyleManager()->NotifyDestroyingFrame(aFrame);
1512 struct nsGenConInitializer {
1513 UniquePtr<nsGenConNode> mNode;
1514 nsGenConList* mList;
1515 void (nsCSSFrameConstructor::*mDirtyAll)();
1517 nsGenConInitializer(UniquePtr<nsGenConNode> aNode, nsGenConList* aList,
1518 void (nsCSSFrameConstructor::*aDirtyAll)())
1519 : mNode(std::move(aNode)), mList(aList), mDirtyAll(aDirtyAll) {}
1522 already_AddRefed<nsIContent> nsCSSFrameConstructor::CreateGenConTextNode(
1523 nsFrameConstructorState& aState, const nsAString& aString,
1524 UniquePtr<nsGenConInitializer> aInitializer) {
1525 RefPtr<nsTextNode> content = new (mDocument->NodeInfoManager())
1526 nsTextNode(mDocument->NodeInfoManager());
1527 content->SetText(aString, false);
1528 if (aInitializer) {
1529 aInitializer->mNode->mText = content;
1530 content->SetProperty(nsGkAtoms::genConInitializerProperty,
1531 aInitializer.release(),
1532 nsINode::DeleteProperty<nsGenConInitializer>);
1533 aState.mGeneratedContentWithInitializer.AppendElement(content);
1535 return content.forget();
1538 void nsCSSFrameConstructor::CreateGeneratedContent(
1539 nsFrameConstructorState& aState, Element& aOriginatingElement,
1540 ComputedStyle& aPseudoStyle, uint32_t aContentIndex,
1541 const FunctionRef<void(nsIContent*)> aAddChild) {
1542 using Type = StyleContentItem::Tag;
1543 // Get the content value
1544 const auto& item = aPseudoStyle.StyleContent()->ContentAt(aContentIndex);
1545 const Type type = item.tag;
1547 switch (type) {
1548 case Type::Image: {
1549 RefPtr c = GeneratedImageContent::Create(*mDocument, aContentIndex);
1550 aAddChild(c);
1551 return;
1554 case Type::String: {
1555 const auto string = item.AsString().AsString();
1556 if (string.IsEmpty()) {
1557 return;
1559 RefPtr text =
1560 CreateGenConTextNode(aState, NS_ConvertUTF8toUTF16(string), nullptr);
1561 aAddChild(text);
1562 return;
1565 case Type::Attr: {
1566 const auto& attr = item.AsAttr();
1567 RefPtr<nsAtom> attrName = attr.attribute.AsAtom();
1568 int32_t attrNameSpace = kNameSpaceID_None;
1569 RefPtr<nsAtom> ns = attr.namespace_url.AsAtom();
1570 if (!ns->IsEmpty()) {
1571 nsresult rv = nsNameSpaceManager::GetInstance()->RegisterNameSpace(
1572 ns.forget(), attrNameSpace);
1573 NS_ENSURE_SUCCESS_VOID(rv);
1576 if (mDocument->IsHTMLDocument() && aOriginatingElement.IsHTMLElement()) {
1577 ToLowerCaseASCII(attrName);
1580 RefPtr<nsAtom> fallback = attr.fallback.AsAtom();
1582 nsCOMPtr<nsIContent> content;
1583 NS_NewAttributeContent(mDocument->NodeInfoManager(), attrNameSpace,
1584 attrName, fallback, getter_AddRefs(content));
1585 aAddChild(content);
1586 return;
1589 case Type::Counter:
1590 case Type::Counters: {
1591 RefPtr<nsAtom> name;
1592 CounterStylePtr ptr;
1593 nsString separator;
1594 if (type == Type::Counter) {
1595 auto& counter = item.AsCounter();
1596 name = counter._0.AsAtom();
1597 ptr = CounterStylePtr::FromStyle(counter._1);
1598 } else {
1599 auto& counters = item.AsCounters();
1600 name = counters._0.AsAtom();
1601 CopyUTF8toUTF16(counters._1.AsString(), separator);
1602 ptr = CounterStylePtr::FromStyle(counters._2);
1605 auto* counterList = mContainStyleScopeManager.GetOrCreateCounterList(
1606 aOriginatingElement, name);
1607 auto node = MakeUnique<nsCounterUseNode>(
1608 std::move(ptr), std::move(separator), aContentIndex,
1609 /* aAllCounters = */ type == Type::Counters);
1611 auto initializer = MakeUnique<nsGenConInitializer>(
1612 std::move(node), counterList, &nsCSSFrameConstructor::CountersDirty);
1613 RefPtr c = CreateGenConTextNode(aState, u""_ns, std::move(initializer));
1614 aAddChild(c);
1615 return;
1617 case Type::OpenQuote:
1618 case Type::CloseQuote:
1619 case Type::NoOpenQuote:
1620 case Type::NoCloseQuote: {
1621 auto node = MakeUnique<nsQuoteNode>(type, aContentIndex);
1622 auto* quoteList =
1623 mContainStyleScopeManager.QuoteListFor(aOriginatingElement);
1624 auto initializer = MakeUnique<nsGenConInitializer>(
1625 std::move(node), quoteList, &nsCSSFrameConstructor::QuotesDirty);
1626 RefPtr c = CreateGenConTextNode(aState, u""_ns, std::move(initializer));
1627 aAddChild(c);
1628 return;
1631 case Type::MozLabelContent: {
1632 nsAutoString accesskey;
1633 if (!aOriginatingElement.GetAttr(nsGkAtoms::accesskey, accesskey) ||
1634 accesskey.IsEmpty() || !LookAndFeel::GetMenuAccessKey()) {
1635 // Easy path: just return a regular value attribute content.
1636 nsCOMPtr<nsIContent> content;
1637 NS_NewAttributeContent(mDocument->NodeInfoManager(), kNameSpaceID_None,
1638 nsGkAtoms::value, nsGkAtoms::_empty,
1639 getter_AddRefs(content));
1640 aAddChild(content);
1641 return;
1644 nsAutoString value;
1645 aOriginatingElement.GetAttr(nsGkAtoms::value, value);
1647 auto AppendAccessKeyLabel = [&] {
1648 // Always append accesskey text in uppercase, see bug 1806167.
1649 ToUpperCase(accesskey);
1650 nsAutoString accessKeyLabel = u"("_ns + accesskey + u")"_ns;
1651 if (!StringEndsWith(value, accessKeyLabel)) {
1652 if (InsertSeparatorBeforeAccessKey() && !value.IsEmpty() &&
1653 !NS_IS_SPACE(value.Last())) {
1654 value.Append(' ');
1656 value.Append(accessKeyLabel);
1659 if (AlwaysAppendAccessKey()) {
1660 AppendAccessKeyLabel();
1661 RefPtr c = CreateGenConTextNode(aState, value, nullptr);
1662 aAddChild(c);
1663 return;
1666 const auto accessKeyStart = [&]() -> Maybe<size_t> {
1667 nsAString::const_iterator start, end;
1668 value.BeginReading(start);
1669 value.EndReading(end);
1671 const auto originalStart = start;
1672 // not appending access key - do case-sensitive search
1673 // first
1674 bool found = true;
1675 if (!FindInReadable(accesskey, start, end)) {
1676 start = originalStart;
1677 // didn't find it - perform a case-insensitive search
1678 found = FindInReadable(accesskey, start, end,
1679 nsCaseInsensitiveStringComparator);
1681 if (!found) {
1682 return Nothing();
1684 return Some(Distance(originalStart, start));
1685 }();
1687 if (accessKeyStart.isNothing()) {
1688 AppendAccessKeyLabel();
1689 RefPtr c = CreateGenConTextNode(aState, value, nullptr);
1690 aAddChild(c);
1691 return;
1694 if (*accessKeyStart != 0) {
1695 RefPtr beginning = CreateGenConTextNode(
1696 aState, Substring(value, 0, *accessKeyStart), nullptr);
1697 aAddChild(beginning);
1701 RefPtr accessKeyText = CreateGenConTextNode(
1702 aState, Substring(value, *accessKeyStart, accesskey.Length()),
1703 nullptr);
1704 RefPtr<nsIContent> underline =
1705 mDocument->CreateHTMLElement(nsGkAtoms::u);
1706 underline->AppendChildTo(accessKeyText, /* aNotify = */ false,
1707 IgnoreErrors());
1708 aAddChild(underline);
1711 size_t accessKeyEnd = *accessKeyStart + accesskey.Length();
1712 if (accessKeyEnd != value.Length()) {
1713 RefPtr valueEnd = CreateGenConTextNode(
1714 aState, Substring(value, *accessKeyStart + accesskey.Length()),
1715 nullptr);
1716 aAddChild(valueEnd);
1718 break;
1720 case Type::MozAltContent: {
1721 // Use the "alt" attribute; if that fails and the node is an HTML
1722 // <input>, try the value attribute and then fall back to some default
1723 // localized text we have.
1724 // XXX what if the 'alt' attribute is added later, how will we
1725 // detect that and do the right thing here?
1726 if (aOriginatingElement.HasAttr(nsGkAtoms::alt)) {
1727 nsCOMPtr<nsIContent> content;
1728 NS_NewAttributeContent(mDocument->NodeInfoManager(), kNameSpaceID_None,
1729 nsGkAtoms::alt, nsGkAtoms::_empty,
1730 getter_AddRefs(content));
1731 aAddChild(content);
1732 return;
1735 if (aOriginatingElement.IsHTMLElement(nsGkAtoms::input)) {
1736 if (aOriginatingElement.HasAttr(nsGkAtoms::value)) {
1737 nsCOMPtr<nsIContent> content;
1738 NS_NewAttributeContent(mDocument->NodeInfoManager(),
1739 kNameSpaceID_None, nsGkAtoms::value,
1740 nsGkAtoms::_empty, getter_AddRefs(content));
1741 aAddChild(content);
1742 return;
1745 nsAutoString temp;
1746 nsContentUtils::GetMaybeLocalizedString(
1747 nsContentUtils::eFORMS_PROPERTIES, "Submit", mDocument, temp);
1748 RefPtr c = CreateGenConTextNode(aState, temp, nullptr);
1749 aAddChild(c);
1750 return;
1752 break;
1756 return;
1759 void nsCSSFrameConstructor::CreateGeneratedContentFromListStyle(
1760 nsFrameConstructorState& aState, Element& aOriginatingElement,
1761 const ComputedStyle& aPseudoStyle,
1762 const FunctionRef<void(nsIContent*)> aAddChild) {
1763 const nsStyleList* styleList = aPseudoStyle.StyleList();
1764 if (!styleList->mListStyleImage.IsNone()) {
1765 RefPtr<nsIContent> child =
1766 GeneratedImageContent::CreateForListStyleImage(*mDocument);
1767 aAddChild(child);
1768 child = CreateGenConTextNode(aState, u" "_ns, nullptr);
1769 aAddChild(child);
1770 return;
1772 CreateGeneratedContentFromListStyleType(aState, aOriginatingElement,
1773 aPseudoStyle, aAddChild);
1776 void nsCSSFrameConstructor::CreateGeneratedContentFromListStyleType(
1777 nsFrameConstructorState& aState, Element& aOriginatingElement,
1778 const ComputedStyle& aPseudoStyle,
1779 const FunctionRef<void(nsIContent*)> aAddChild) {
1780 const nsStyleList* styleList = aPseudoStyle.StyleList();
1781 CounterStyle* counterStyle =
1782 mPresShell->GetPresContext()->CounterStyleManager()->ResolveCounterStyle(
1783 styleList->mCounterStyle);
1784 bool needUseNode = false;
1785 switch (counterStyle->GetStyle()) {
1786 case ListStyle::None:
1787 return;
1788 case ListStyle::Disc:
1789 case ListStyle::Circle:
1790 case ListStyle::Square:
1791 case ListStyle::DisclosureClosed:
1792 case ListStyle::DisclosureOpen:
1793 break;
1794 default:
1795 const auto* anonStyle = counterStyle->AsAnonymous();
1796 if (!anonStyle || !anonStyle->IsSingleString()) {
1797 needUseNode = true;
1801 auto node = MakeUnique<nsCounterUseNode>(nsCounterUseNode::ForLegacyBullet,
1802 styleList->mCounterStyle);
1803 if (!needUseNode) {
1804 nsAutoString text;
1805 node->GetText(WritingMode(&aPseudoStyle), counterStyle, text);
1806 // Note that we're done with 'node' in this case. It's not inserted into
1807 // any list so it's deleted when we return.
1808 RefPtr<nsIContent> child = CreateGenConTextNode(aState, text, nullptr);
1809 aAddChild(child);
1810 return;
1813 auto* counterList = mContainStyleScopeManager.GetOrCreateCounterList(
1814 aOriginatingElement, nsGkAtoms::list_item);
1815 auto initializer = MakeUnique<nsGenConInitializer>(
1816 std::move(node), counterList, &nsCSSFrameConstructor::CountersDirty);
1817 RefPtr<nsIContent> child =
1818 CreateGenConTextNode(aState, EmptyString(), std::move(initializer));
1819 aAddChild(child);
1822 // Frames for these may not be leaves in the proper sense, but we still don't
1823 // want to expose generated content on them. For the purposes of the page they
1824 // should be leaves.
1825 static bool HasUAWidget(const Element& aOriginatingElement) {
1826 const ShadowRoot* sr = aOriginatingElement.GetShadowRoot();
1827 return sr && sr->IsUAWidget();
1831 * aParentFrame - the frame that should be the parent of the generated
1832 * content. This is the frame for the corresponding content node,
1833 * which must not be a leaf frame.
1835 * Any items created are added to aItems.
1837 * We create an XML element (tag _moz_generated_content_before/after/marker)
1838 * representing the pseudoelement. We create a DOM node for each 'content'
1839 * item and make those nodes the children of the XML element. Then we create
1840 * a frame subtree for the XML element as if it were a regular child of
1841 * aParentFrame/aParentContent, giving the XML element the ::before, ::after
1842 * or ::marker style.
1844 void nsCSSFrameConstructor::CreateGeneratedContentItem(
1845 nsFrameConstructorState& aState, nsContainerFrame* aParentFrame,
1846 Element& aOriginatingElement, ComputedStyle& aStyle,
1847 PseudoStyleType aPseudoElement, FrameConstructionItemList& aItems,
1848 ItemFlags aExtraFlags) {
1849 MOZ_ASSERT(aPseudoElement == PseudoStyleType::before ||
1850 aPseudoElement == PseudoStyleType::after ||
1851 aPseudoElement == PseudoStyleType::marker,
1852 "unexpected aPseudoElement");
1854 if (HasUAWidget(aOriginatingElement) &&
1855 !aOriginatingElement.IsHTMLElement(nsGkAtoms::details)) {
1856 return;
1859 ServoStyleSet* styleSet = mPresShell->StyleSet();
1861 // Probe for the existence of the pseudo-element.
1862 // |ProbePseudoElementStyle| checks the relevant properties for the pseudo.
1863 // It only returns a non-null value if the pseudo should exist.
1864 RefPtr<ComputedStyle> pseudoStyle = styleSet->ProbePseudoElementStyle(
1865 aOriginatingElement, aPseudoElement, nullptr, &aStyle);
1866 if (!pseudoStyle) {
1867 return;
1870 nsAtom* elemName = nullptr;
1871 nsAtom* property = nullptr;
1872 switch (aPseudoElement) {
1873 case PseudoStyleType::before:
1874 elemName = nsGkAtoms::mozgeneratedcontentbefore;
1875 property = nsGkAtoms::beforePseudoProperty;
1876 break;
1877 case PseudoStyleType::after:
1878 elemName = nsGkAtoms::mozgeneratedcontentafter;
1879 property = nsGkAtoms::afterPseudoProperty;
1880 break;
1881 case PseudoStyleType::marker:
1882 // We want to get a marker style even if we match no rules, but we still
1883 // want to check the result of GeneratedContentPseudoExists.
1884 elemName = nsGkAtoms::mozgeneratedcontentmarker;
1885 property = nsGkAtoms::markerPseudoProperty;
1886 break;
1887 default:
1888 MOZ_ASSERT_UNREACHABLE("unexpected aPseudoElement");
1891 RefPtr<NodeInfo> nodeInfo = mDocument->NodeInfoManager()->GetNodeInfo(
1892 elemName, nullptr, kNameSpaceID_None, nsINode::ELEMENT_NODE);
1893 RefPtr<Element> container;
1894 nsresult rv = NS_NewXMLElement(getter_AddRefs(container), nodeInfo.forget());
1895 if (NS_FAILED(rv)) {
1896 return;
1899 // Cleared when the pseudo is unbound from the tree, so no need to store a
1900 // strong reference, nor a destructor.
1901 aOriginatingElement.SetProperty(property, container.get());
1903 container->SetIsNativeAnonymousRoot();
1904 container->SetPseudoElementType(aPseudoElement);
1906 BindContext context(aOriginatingElement, BindContext::ForNativeAnonymous);
1907 rv = container->BindToTree(context, aOriginatingElement);
1908 if (NS_FAILED(rv)) {
1909 container->UnbindFromTree();
1910 return;
1913 if (mDocument->DevToolsAnonymousAndShadowEventsEnabled()) {
1914 container->QueueDevtoolsAnonymousEvent(/* aIsRemove = */ false);
1917 // Servo has already eagerly computed the style for the container, so we can
1918 // just stick the style on the element and avoid an additional traversal.
1920 // We don't do this for pseudos that may trigger animations or transitions,
1921 // since those need to be kicked off by the traversal machinery.
1923 // Note that when a pseudo-element animates, we flag the originating element,
1924 // so we check that flag, but we could also a more expensive (but exhaustive)
1925 // check using EffectSet::GetEffectSet, for example.
1926 if (!Servo_ComputedValues_SpecifiesAnimationsOrTransitions(pseudoStyle) &&
1927 !aOriginatingElement.MayHaveAnimations()) {
1928 Servo_SetExplicitStyle(container, pseudoStyle);
1929 } else {
1930 // If animations are involved, we avoid the SetExplicitStyle optimization
1931 // above. We need to grab style with animations from the pseudo element and
1932 // replace old one.
1933 mPresShell->StyleSet()->StyleNewSubtree(container);
1934 pseudoStyle = ServoStyleSet::ResolveServoStyle(*container);
1937 auto AppendChild = [&container, this](nsIContent* aChild) {
1938 // We don't strictly have to set NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE
1939 // here; it would get set under AppendChildTo. But AppendChildTo might
1940 // think that we're going from not being anonymous to being anonymous and
1941 // do some extra work; setting the flag here avoids that.
1942 aChild->SetFlags(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE);
1943 container->AppendChildTo(aChild, false, IgnoreErrors());
1944 if (auto* childElement = Element::FromNode(aChild)) {
1945 // If we created any children elements, Servo needs to traverse them, but
1946 // the root is already set up.
1947 mPresShell->StyleSet()->StyleNewSubtree(childElement);
1950 const uint32_t contentCount = pseudoStyle->StyleContent()->ContentCount();
1951 for (uint32_t contentIndex = 0; contentIndex < contentCount; contentIndex++) {
1952 CreateGeneratedContent(aState, aOriginatingElement, *pseudoStyle,
1953 contentIndex, AppendChild);
1955 // If a ::marker has no 'content' then generate it from its 'list-style-*'.
1956 if (contentCount == 0 && aPseudoElement == PseudoStyleType::marker) {
1957 CreateGeneratedContentFromListStyle(aState, aOriginatingElement,
1958 *pseudoStyle, AppendChild);
1960 auto flags = ItemFlags{ItemFlag::IsGeneratedContent} + aExtraFlags;
1961 AddFrameConstructionItemsInternal(aState, container, aParentFrame, true,
1962 pseudoStyle, flags, aItems);
1965 /****************************************************
1966 ** BEGIN TABLE SECTION
1967 ****************************************************/
1969 // The term pseudo frame is being used instead of anonymous frame, since
1970 // anonymous frame has been used elsewhere to refer to frames that have
1971 // generated content
1973 // Return whether the given frame is a table pseudo-frame. Note that
1974 // cell-content and table-outer frames have pseudo-types, but are always
1975 // created, even for non-anonymous cells and tables respectively. So for those
1976 // we have to examine the cell or table frame to see whether it's a pseudo
1977 // frame. In particular, a lone table caption will have a table wrapper as its
1978 // parent, but will also trigger construction of an empty inner table, which
1979 // will be the one we can examine to see whether the wrapper was a pseudo-frame.
1980 static bool IsTablePseudo(nsIFrame* aFrame) {
1981 auto pseudoType = aFrame->Style()->GetPseudoType();
1982 return pseudoType != PseudoStyleType::NotPseudo &&
1983 (pseudoType == PseudoStyleType::table ||
1984 pseudoType == PseudoStyleType::inlineTable ||
1985 pseudoType == PseudoStyleType::tableColGroup ||
1986 pseudoType == PseudoStyleType::tableRowGroup ||
1987 pseudoType == PseudoStyleType::tableRow ||
1988 pseudoType == PseudoStyleType::tableCell ||
1989 (pseudoType == PseudoStyleType::cellContent &&
1990 aFrame->GetParent()->Style()->GetPseudoType() ==
1991 PseudoStyleType::tableCell) ||
1992 (pseudoType == PseudoStyleType::tableWrapper &&
1993 (aFrame->PrincipalChildList()
1994 .FirstChild()
1995 ->Style()
1996 ->GetPseudoType() == PseudoStyleType::table ||
1997 aFrame->PrincipalChildList()
1998 .FirstChild()
1999 ->Style()
2000 ->GetPseudoType() == PseudoStyleType::inlineTable)));
2003 static bool IsRubyPseudo(nsIFrame* aFrame) {
2004 return RubyUtils::IsRubyPseudo(aFrame->Style()->GetPseudoType());
2007 static bool IsTableOrRubyPseudo(nsIFrame* aFrame) {
2008 return IsTablePseudo(aFrame) || IsRubyPseudo(aFrame);
2011 /* static */
2012 nsCSSFrameConstructor::ParentType nsCSSFrameConstructor::GetParentType(
2013 LayoutFrameType aFrameType) {
2014 if (aFrameType == LayoutFrameType::Table) {
2015 return eTypeTable;
2017 if (aFrameType == LayoutFrameType::TableRowGroup) {
2018 return eTypeRowGroup;
2020 if (aFrameType == LayoutFrameType::TableRow) {
2021 return eTypeRow;
2023 if (aFrameType == LayoutFrameType::TableColGroup) {
2024 return eTypeColGroup;
2026 if (aFrameType == LayoutFrameType::RubyBaseContainer) {
2027 return eTypeRubyBaseContainer;
2029 if (aFrameType == LayoutFrameType::RubyTextContainer) {
2030 return eTypeRubyTextContainer;
2032 if (aFrameType == LayoutFrameType::Ruby) {
2033 return eTypeRuby;
2036 return eTypeBlock;
2039 // Pull all the captions present in aItems out into aCaptions.
2040 static void PullOutCaptionFrames(nsFrameList& aList, nsFrameList& aCaptions) {
2041 nsIFrame* child = aList.FirstChild();
2042 while (child) {
2043 nsIFrame* nextSibling = child->GetNextSibling();
2044 if (child->StyleDisplay()->mDisplay == StyleDisplay::TableCaption) {
2045 aList.RemoveFrame(child);
2046 aCaptions.AppendFrame(nullptr, child);
2048 child = nextSibling;
2052 // Construct the outer, inner table frames and the children frames for the
2053 // table.
2054 // XXX Page break frames for pseudo table frames are not constructed to avoid
2055 // the risk associated with revising the pseudo frame mechanism. The long term
2056 // solution of having frames handle page-break-before/after will solve the
2057 // problem.
2058 nsIFrame* nsCSSFrameConstructor::ConstructTable(nsFrameConstructorState& aState,
2059 FrameConstructionItem& aItem,
2060 nsContainerFrame* aParentFrame,
2061 const nsStyleDisplay* aDisplay,
2062 nsFrameList& aFrameList) {
2063 MOZ_ASSERT(aDisplay->mDisplay == StyleDisplay::Table ||
2064 aDisplay->mDisplay == StyleDisplay::InlineTable,
2065 "Unexpected call");
2067 nsIContent* const content = aItem.mContent;
2068 ComputedStyle* const computedStyle = aItem.mComputedStyle;
2069 const bool isMathMLContent = content->IsMathMLElement();
2071 // create the pseudo SC for the table wrapper as a child of the inner SC
2072 RefPtr<ComputedStyle> outerComputedStyle =
2073 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
2074 PseudoStyleType::tableWrapper, computedStyle);
2076 // Create the table wrapper frame which holds the caption and inner table
2077 // frame
2078 nsContainerFrame* newFrame;
2079 if (isMathMLContent)
2080 newFrame = NS_NewMathMLmtableOuterFrame(mPresShell, outerComputedStyle);
2081 else
2082 newFrame = NS_NewTableWrapperFrame(mPresShell, outerComputedStyle);
2084 nsContainerFrame* geometricParent = aState.GetGeometricParent(
2085 *outerComputedStyle->StyleDisplay(), aParentFrame);
2087 // Init the table wrapper frame
2088 InitAndRestoreFrame(aState, content, geometricParent, newFrame);
2090 // Create the inner table frame
2091 nsContainerFrame* innerFrame;
2092 if (isMathMLContent)
2093 innerFrame = NS_NewMathMLmtableFrame(mPresShell, computedStyle);
2094 else
2095 innerFrame = NS_NewTableFrame(mPresShell, computedStyle);
2097 InitAndRestoreFrame(aState, content, newFrame, innerFrame);
2098 innerFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
2100 // Put the newly created frames into the right child list
2101 SetInitialSingleChild(newFrame, innerFrame);
2103 aState.AddChild(newFrame, aFrameList, content, aParentFrame);
2105 if (!mRootElementFrame) {
2106 mRootElementFrame = newFrame;
2109 nsFrameList childList;
2111 // Process children
2112 nsFrameConstructorSaveState absoluteSaveState;
2114 // Mark the table frame as an absolute container if needed
2115 newFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
2116 if (newFrame->IsAbsPosContainingBlock()) {
2117 aState.PushAbsoluteContainingBlock(newFrame, newFrame, absoluteSaveState);
2120 nsFrameConstructorSaveState floatSaveState;
2121 aState.MaybePushFloatContainingBlock(innerFrame, floatSaveState);
2123 if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) {
2124 ConstructFramesFromItemList(
2125 aState, aItem.mChildItems, innerFrame,
2126 aItem.mFCData->mBits & FCDATA_IS_WRAPPER_ANON_BOX, childList);
2127 } else {
2128 ProcessChildren(aState, content, computedStyle, innerFrame, true, childList,
2129 false);
2132 nsFrameList captionList;
2133 PullOutCaptionFrames(childList, captionList);
2135 // Set the inner table frame's initial primary list
2136 innerFrame->SetInitialChildList(FrameChildListID::Principal,
2137 std::move(childList));
2139 // Set the table wrapper frame's secondary childlist lists
2140 if (captionList.NotEmpty()) {
2141 captionList.ApplySetParent(newFrame);
2142 newFrame->SetInitialChildList(FrameChildListID::Caption,
2143 std::move(captionList));
2146 return newFrame;
2149 static void MakeTablePartAbsoluteContainingBlock(
2150 nsFrameConstructorState& aState, nsFrameConstructorSaveState& aAbsSaveState,
2151 nsContainerFrame* aFrame) {
2152 // If we're positioned, then we need to become an absolute containing block
2153 // for any absolutely positioned children.
2154 aFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
2155 if (aFrame->IsAbsPosContainingBlock()) {
2156 aState.PushAbsoluteContainingBlock(aFrame, aFrame, aAbsSaveState);
2160 nsIFrame* nsCSSFrameConstructor::ConstructTableRowOrRowGroup(
2161 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
2162 nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay,
2163 nsFrameList& aFrameList) {
2164 MOZ_ASSERT(aDisplay->mDisplay == StyleDisplay::TableRow ||
2165 aDisplay->mDisplay == StyleDisplay::TableRowGroup ||
2166 aDisplay->mDisplay == StyleDisplay::TableFooterGroup ||
2167 aDisplay->mDisplay == StyleDisplay::TableHeaderGroup,
2168 "Not a row or row group");
2169 MOZ_ASSERT(aItem.mComputedStyle->StyleDisplay() == aDisplay,
2170 "Display style doesn't match style");
2171 nsIContent* const content = aItem.mContent;
2172 ComputedStyle* const computedStyle = aItem.mComputedStyle;
2174 nsContainerFrame* newFrame;
2175 if (aDisplay->mDisplay == StyleDisplay::TableRow) {
2176 if (content->IsMathMLElement())
2177 newFrame = NS_NewMathMLmtrFrame(mPresShell, computedStyle);
2178 else
2179 newFrame = NS_NewTableRowFrame(mPresShell, computedStyle);
2180 } else {
2181 newFrame = NS_NewTableRowGroupFrame(mPresShell, computedStyle);
2184 InitAndRestoreFrame(aState, content, aParentFrame, newFrame);
2186 nsFrameConstructorSaveState absoluteSaveState;
2187 MakeTablePartAbsoluteContainingBlock(aState, absoluteSaveState, newFrame);
2189 nsFrameConstructorSaveState floatSaveState;
2190 aState.MaybePushFloatContainingBlock(newFrame, floatSaveState);
2192 nsFrameList childList;
2193 if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) {
2194 ConstructFramesFromItemList(
2195 aState, aItem.mChildItems, newFrame,
2196 aItem.mFCData->mBits & FCDATA_IS_WRAPPER_ANON_BOX, childList);
2197 } else {
2198 ProcessChildren(aState, content, computedStyle, newFrame, true, childList,
2199 false);
2202 newFrame->SetInitialChildList(FrameChildListID::Principal,
2203 std::move(childList));
2204 aFrameList.AppendFrame(nullptr, newFrame);
2205 return newFrame;
2208 nsIFrame* nsCSSFrameConstructor::ConstructTableCol(
2209 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
2210 nsContainerFrame* aParentFrame, const nsStyleDisplay* aStyleDisplay,
2211 nsFrameList& aFrameList) {
2212 nsIContent* const content = aItem.mContent;
2213 ComputedStyle* const computedStyle = aItem.mComputedStyle;
2215 nsTableColFrame* colFrame = NS_NewTableColFrame(mPresShell, computedStyle);
2216 InitAndRestoreFrame(aState, content, aParentFrame, colFrame);
2218 NS_ASSERTION(colFrame->Style() == computedStyle, "Unexpected style");
2220 aFrameList.AppendFrame(nullptr, colFrame);
2222 // construct additional col frames if the col frame has a span > 1
2223 int32_t span = colFrame->GetSpan();
2224 for (int32_t spanX = 1; spanX < span; spanX++) {
2225 nsTableColFrame* newCol = NS_NewTableColFrame(mPresShell, computedStyle);
2226 InitAndRestoreFrame(aState, content, aParentFrame, newCol, false);
2227 aFrameList.LastChild()->SetNextContinuation(newCol);
2228 newCol->SetPrevContinuation(aFrameList.LastChild());
2229 aFrameList.AppendFrame(nullptr, newCol);
2230 newCol->SetColType(eColAnonymousCol);
2233 return colFrame;
2236 nsIFrame* nsCSSFrameConstructor::ConstructTableCell(
2237 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
2238 nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay,
2239 nsFrameList& aFrameList) {
2240 MOZ_ASSERT(aDisplay->mDisplay == StyleDisplay::TableCell, "Unexpected call");
2242 nsIContent* const content = aItem.mContent;
2243 ComputedStyle* const computedStyle = aItem.mComputedStyle;
2244 const bool isMathMLContent = content->IsMathMLElement();
2246 nsTableFrame* tableFrame =
2247 static_cast<nsTableRowFrame*>(aParentFrame)->GetTableFrame();
2248 nsContainerFrame* cellFrame;
2249 // <mtable> is border separate in mathml.css and the MathML code doesn't
2250 // implement border collapse. For those users who style <mtable> with border
2251 // collapse, give them the default non-MathML table frames that understand
2252 // border collapse. This won't break us because MathML table frames are all
2253 // subclasses of the default table code, and so we can freely mix <mtable>
2254 // with <mtr> or <tr>, <mtd> or <td>. What will happen is just that non-MathML
2255 // frames won't understand MathML attributes and will therefore miss the
2256 // special handling that the MathML code does.
2257 if (isMathMLContent && !tableFrame->IsBorderCollapse()) {
2258 cellFrame = NS_NewMathMLmtdFrame(mPresShell, computedStyle, tableFrame);
2259 } else {
2260 // Warning: If you change this and add a wrapper frame around table cell
2261 // frames, make sure Bug 368554 doesn't regress!
2262 // See IsInAutoWidthTableCellForQuirk() in nsImageFrame.cpp.
2263 cellFrame = NS_NewTableCellFrame(mPresShell, computedStyle, tableFrame);
2266 // Initialize the table cell frame
2267 InitAndRestoreFrame(aState, content, aParentFrame, cellFrame);
2268 cellFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
2270 // Resolve pseudo style and initialize the body cell frame
2271 RefPtr<ComputedStyle> innerPseudoStyle =
2272 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
2273 PseudoStyleType::cellContent, computedStyle);
2275 nsContainerFrame* cellInnerFrame;
2276 nsContainerFrame* scrollFrame = nullptr;
2277 bool isScrollable = false;
2278 // Create a block frame that will format the cell's content
2279 if (isMathMLContent) {
2280 cellInnerFrame = NS_NewMathMLmtdInnerFrame(mPresShell, innerPseudoStyle);
2281 } else {
2282 isScrollable = innerPseudoStyle->StyleDisplay()->IsScrollableOverflow() &&
2283 !aState.mPresContext->IsPaginated() &&
2284 StaticPrefs::layout_tables_scrollable_cells();
2285 if (isScrollable) {
2286 innerPseudoStyle = BeginBuildingScrollFrame(
2287 aState, content, innerPseudoStyle, cellFrame,
2288 PseudoStyleType::scrolledContent, false, scrollFrame);
2290 cellInnerFrame = NS_NewBlockFrame(mPresShell, innerPseudoStyle);
2292 auto* parent = scrollFrame ? scrollFrame : cellFrame;
2293 InitAndRestoreFrame(aState, content, parent, cellInnerFrame);
2295 nsFrameConstructorSaveState absoluteSaveState;
2296 MakeTablePartAbsoluteContainingBlock(aState, absoluteSaveState, cellFrame);
2298 nsFrameConstructorSaveState floatSaveState;
2299 aState.MaybePushFloatContainingBlock(cellInnerFrame, floatSaveState);
2301 nsFrameList childList;
2302 if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) {
2303 AutoFrameConstructionPageName pageNameTracker(aState, cellInnerFrame);
2304 ConstructFramesFromItemList(
2305 aState, aItem.mChildItems, cellInnerFrame,
2306 aItem.mFCData->mBits & FCDATA_IS_WRAPPER_ANON_BOX, childList);
2307 } else {
2308 // Process the child content
2309 ProcessChildren(aState, content, computedStyle, cellInnerFrame, true,
2310 childList, !isMathMLContent);
2313 cellInnerFrame->SetInitialChildList(FrameChildListID::Principal,
2314 std::move(childList));
2316 if (isScrollable) {
2317 FinishBuildingScrollFrame(scrollFrame, cellInnerFrame);
2319 SetInitialSingleChild(cellFrame, scrollFrame ? scrollFrame : cellInnerFrame);
2320 aFrameList.AppendFrame(nullptr, cellFrame);
2321 return cellFrame;
2324 static inline bool NeedFrameFor(const nsFrameConstructorState& aState,
2325 nsContainerFrame* aParentFrame,
2326 nsIContent* aChildContent) {
2327 // XXX the GetContent() != aChildContent check is needed due to bug 135040.
2328 // Remove it once that's fixed.
2329 MOZ_ASSERT(
2330 !aChildContent->GetPrimaryFrame() || aState.mCreatingExtraFrames ||
2331 aChildContent->GetPrimaryFrame()->GetContent() != aChildContent,
2332 "Why did we get called?");
2334 // don't create a whitespace frame if aParentFrame doesn't want it.
2335 // always create frames for children in generated content. counter(),
2336 // quotes, and attr() content can easily change dynamically and we don't
2337 // want to be reconstructing frames. It's not even clear that these
2338 // should be considered ignorable just because they evaluate to
2339 // whitespace.
2341 // We could handle all this in CreateNeededPseudoContainers or some other
2342 // place after we build our frame construction items, but that would involve
2343 // creating frame construction items for whitespace kids that ignores
2344 // white-space, where we know we'll be dropping them all anyway, and involve
2345 // an extra walk down the frame construction item list.
2346 auto excludesIgnorableWhitespace = [](nsIFrame* aParentFrame) {
2347 return aParentFrame->IsMathMLFrame();
2349 if (!aParentFrame || !excludesIgnorableWhitespace(aParentFrame) ||
2350 aParentFrame->IsGeneratedContentFrame() || !aChildContent->IsText()) {
2351 return true;
2354 aChildContent->SetFlags(NS_CREATE_FRAME_IF_NON_WHITESPACE |
2355 NS_REFRAME_IF_WHITESPACE);
2356 return !aChildContent->TextIsOnlyWhitespace();
2359 /***********************************************
2360 * END TABLE SECTION
2361 ***********************************************/
2363 nsIFrame* nsCSSFrameConstructor::ConstructDocElementFrame(
2364 Element* aDocElement) {
2365 MOZ_ASSERT(GetRootFrame(),
2366 "No viewport? Someone forgot to call ConstructRootFrame!");
2367 MOZ_ASSERT(!mDocElementContainingBlock,
2368 "Shouldn't have a doc element containing block here");
2370 // Resolve a new style for the viewport since it may be affected by a new root
2371 // element style (e.g. a propagated 'direction').
2373 // @see ComputedStyle::ApplyStyleFixups
2375 RefPtr<ComputedStyle> sc =
2376 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
2377 PseudoStyleType::viewport, nullptr);
2378 GetRootFrame()->SetComputedStyleWithoutNotification(sc);
2381 // Ensure the document element is styled at this point.
2382 if (!aDocElement->HasServoData()) {
2383 mPresShell->StyleSet()->StyleNewSubtree(aDocElement);
2385 aDocElement->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
2387 // Make sure to call UpdateViewportScrollStylesOverride before
2388 // SetUpDocElementContainingBlock, since it sets up our scrollbar state
2389 // properly.
2390 DebugOnly<nsIContent*> propagatedScrollFrom;
2391 if (nsPresContext* presContext = mPresShell->GetPresContext()) {
2392 propagatedScrollFrom = presContext->UpdateViewportScrollStylesOverride();
2395 SetUpDocElementContainingBlock(aDocElement);
2397 // This has the side-effect of getting `mFrameTreeState` from our docshell.
2399 // FIXME(emilio): There may be a more sensible time to do this.
2400 if (!mFrameTreeState) {
2401 mPresShell->CaptureHistoryState(getter_AddRefs(mFrameTreeState));
2404 NS_ASSERTION(mDocElementContainingBlock, "Should have parent by now");
2405 nsFrameConstructorState state(
2406 mPresShell,
2407 GetAbsoluteContainingBlock(mDocElementContainingBlock, FIXED_POS),
2408 nullptr, nullptr, do_AddRef(mFrameTreeState));
2410 RefPtr<ComputedStyle> computedStyle =
2411 ServoStyleSet::ResolveServoStyle(*aDocElement);
2413 const nsStyleDisplay* display = computedStyle->StyleDisplay();
2415 // --------- IF SCROLLABLE WRAP IN SCROLLFRAME --------
2417 NS_ASSERTION(!display->IsScrollableOverflow() ||
2418 state.mPresContext->IsPaginated() ||
2419 propagatedScrollFrom == aDocElement,
2420 "Scrollbars should have been propagated to the viewport");
2422 if (MOZ_UNLIKELY(display->mDisplay == StyleDisplay::None)) {
2423 return nullptr;
2426 // This implements "The Principal Writing Mode".
2427 // https://drafts.csswg.org/css-writing-modes-3/#principal-flow
2429 // If there's a <body> element in an HTML document, its writing-mode,
2430 // direction, and text-orientation override the root element's used value.
2432 // We need to copy <body>'s WritingMode to mDocElementContainingBlock before
2433 // construct mRootElementFrame so that anonymous internal frames such as
2434 // <html> with table style can copy their parent frame's mWritingMode in
2435 // nsIFrame::Init().
2436 MOZ_ASSERT(!mRootElementFrame,
2437 "We need to copy <body>'s principal writing-mode before "
2438 "constructing mRootElementFrame.");
2440 const WritingMode propagatedWM = [&] {
2441 const WritingMode rootWM(computedStyle);
2442 if (computedStyle->StyleDisplay()->IsContainAny()) {
2443 return rootWM;
2445 Element* body = mDocument->GetBodyElement();
2446 if (!body) {
2447 return rootWM;
2449 RefPtr<ComputedStyle> bodyStyle = ResolveComputedStyle(body);
2450 if (bodyStyle->StyleDisplay()->IsContainAny()) {
2451 return rootWM;
2453 const WritingMode bodyWM(bodyStyle);
2454 if (bodyWM != rootWM) {
2455 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "Layout"_ns,
2456 mDocument,
2457 nsContentUtils::eLAYOUT_PROPERTIES,
2458 "PrincipalWritingModePropagationWarning");
2460 return bodyWM;
2461 }();
2463 mDocElementContainingBlock->PropagateWritingModeToSelfAndAncestors(
2464 propagatedWM);
2466 // Push the absolute containing block now so we can absolutely position the
2467 // root element
2468 nsFrameConstructorSaveState canvasCbSaveState;
2469 mCanvasFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
2471 state.PushAbsoluteContainingBlock(mCanvasFrame, mCanvasFrame,
2472 canvasCbSaveState);
2474 nsFrameConstructorSaveState docElementCbSaveState;
2475 if (mCanvasFrame != mDocElementContainingBlock) {
2476 mDocElementContainingBlock->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
2477 state.PushAbsoluteContainingBlock(mDocElementContainingBlock,
2478 mDocElementContainingBlock,
2479 docElementCbSaveState);
2482 // The rules from CSS 2.1, section 9.2.4, have already been applied
2483 // by the style system, so we can assume that display->mDisplay is
2484 // either NONE, BLOCK, or TABLE.
2486 // contentFrame is the primary frame for the root element. frameList contains
2487 // the children of the initial containing block.
2489 // The first of those frames is usually `contentFrame`, but it can be
2490 // different, in particular if the root frame is positioned, in which case
2491 // contentFrame is the out-of-flow frame and frameList.FirstChild() is the
2492 // placeholder.
2494 // The rest of the frames in frameList are the anonymous content of the canvas
2495 // frame.
2496 nsContainerFrame* contentFrame;
2497 nsFrameList frameList;
2498 bool processChildren = false;
2500 nsFrameConstructorSaveState absoluteSaveState;
2502 if (aDocElement->IsSVGElement()) {
2503 if (!aDocElement->IsSVGElement(nsGkAtoms::svg)) {
2504 return nullptr;
2506 // We're going to call the right function ourselves, so no need to give a
2507 // function to this FrameConstructionData.
2509 // XXXbz on the other hand, if we converted this whole function to
2510 // FrameConstructionData/Item, then we'd need the right function
2511 // here... but would probably be able to get away with less code in this
2512 // function in general.
2513 static constexpr FrameConstructionData rootSVGData;
2514 AutoFrameConstructionItem item(this, &rootSVGData, aDocElement,
2515 do_AddRef(computedStyle), true);
2517 contentFrame = static_cast<nsContainerFrame*>(ConstructOuterSVG(
2518 state, item, mDocElementContainingBlock, display, frameList));
2519 } else if (display->mDisplay == StyleDisplay::Flex ||
2520 display->mDisplay == StyleDisplay::WebkitBox ||
2521 display->mDisplay == StyleDisplay::Grid) {
2522 auto func = [&] {
2523 if (display->mDisplay == StyleDisplay::Grid) {
2524 return NS_NewGridContainerFrame;
2526 return NS_NewFlexContainerFrame;
2527 }();
2528 contentFrame = func(mPresShell, computedStyle);
2529 InitAndRestoreFrame(
2530 state, aDocElement,
2531 state.GetGeometricParent(*display, mDocElementContainingBlock),
2532 contentFrame);
2533 state.AddChild(contentFrame, frameList, aDocElement,
2534 mDocElementContainingBlock);
2535 processChildren = true;
2537 contentFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
2538 if (contentFrame->IsAbsPosContainingBlock()) {
2539 state.PushAbsoluteContainingBlock(contentFrame, contentFrame,
2540 absoluteSaveState);
2542 } else if (display->mDisplay == StyleDisplay::Table) {
2543 // We're going to call the right function ourselves, so no need to give a
2544 // function to this FrameConstructionData.
2546 // XXXbz on the other hand, if we converted this whole function to
2547 // FrameConstructionData/Item, then we'd need the right function
2548 // here... but would probably be able to get away with less code in this
2549 // function in general.
2550 static constexpr FrameConstructionData rootTableData;
2551 AutoFrameConstructionItem item(this, &rootTableData, aDocElement,
2552 do_AddRef(computedStyle), true);
2554 // if the document is a table then just populate it.
2555 contentFrame = static_cast<nsContainerFrame*>(ConstructTable(
2556 state, item, mDocElementContainingBlock, display, frameList));
2557 } else if (display->DisplayInside() == StyleDisplayInside::Ruby) {
2558 static constexpr FrameConstructionData data(
2559 &nsCSSFrameConstructor::ConstructBlockRubyFrame);
2560 AutoFrameConstructionItem item(this, &data, aDocElement,
2561 do_AddRef(computedStyle), true);
2562 contentFrame = static_cast<nsContainerFrame*>(ConstructBlockRubyFrame(
2563 state, item,
2564 state.GetGeometricParent(*display, mDocElementContainingBlock), display,
2565 frameList));
2566 } else {
2567 MOZ_ASSERT(display->mDisplay == StyleDisplay::Block ||
2568 display->mDisplay == StyleDisplay::FlowRoot,
2569 "Unhandled display type for root element");
2570 contentFrame = NS_NewBlockFrame(mPresShell, computedStyle);
2571 ConstructBlock(
2572 state, aDocElement,
2573 state.GetGeometricParent(*display, mDocElementContainingBlock),
2574 mDocElementContainingBlock, computedStyle, &contentFrame, frameList,
2575 contentFrame->IsAbsPosContainingBlock() ? contentFrame : nullptr);
2578 MOZ_ASSERT(frameList.FirstChild());
2579 MOZ_ASSERT(frameList.FirstChild()->GetContent() == aDocElement);
2580 MOZ_ASSERT(contentFrame);
2582 MOZ_ASSERT(
2583 processChildren ? !mRootElementFrame : mRootElementFrame == contentFrame,
2584 "unexpected mRootElementFrame");
2585 if (processChildren) {
2586 mRootElementFrame = contentFrame;
2589 // Figure out which frame has the main style for the document element,
2590 // assigning it to mRootElementStyleFrame.
2591 // Backgrounds should be propagated from that frame to the viewport.
2592 contentFrame->GetParentComputedStyle(&mRootElementStyleFrame);
2593 bool isChild = mRootElementStyleFrame &&
2594 mRootElementStyleFrame->GetParent() == contentFrame;
2595 if (!isChild) {
2596 mRootElementStyleFrame = mRootElementFrame;
2599 if (processChildren) {
2600 // Still need to process the child content
2601 nsFrameList childList;
2603 NS_ASSERTION(
2604 !contentFrame->IsBlockFrameOrSubclass() && !contentFrame->IsSVGFrame(),
2605 "Only XUL frames should reach here");
2607 nsFrameConstructorSaveState floatSaveState;
2608 state.MaybePushFloatContainingBlock(contentFrame, floatSaveState);
2610 ProcessChildren(state, aDocElement, computedStyle, contentFrame, true,
2611 childList, false);
2613 // Set the initial child lists
2614 contentFrame->SetInitialChildList(FrameChildListID::Principal,
2615 std::move(childList));
2618 nsIFrame* newFrame = frameList.FirstChild();
2619 // set the primary frame
2620 aDocElement->SetPrimaryFrame(contentFrame);
2621 mDocElementContainingBlock->AppendFrames(FrameChildListID::Principal,
2622 std::move(frameList));
2624 // NOTE(emilio): This is in the reverse order compared to normal anonymous
2625 // children. We usually generate anonymous kids first, then non-anonymous,
2626 // but we generate the doc element frame the other way around. This is fine
2627 // either way, but generating anonymous children in a different order requires
2628 // changing nsCanvasFrame (and a whole lot of other potentially unknown code)
2629 // to look at the last child to find the root frame rather than the first
2630 // child.
2631 ConstructAnonymousContentForCanvas(
2632 state, mCanvasFrame, mRootElementFrame->GetContent(), frameList);
2633 mCanvasFrame->AppendFrames(FrameChildListID::Principal, std::move(frameList));
2635 return newFrame;
2638 nsIFrame* nsCSSFrameConstructor::ConstructRootFrame() {
2639 AUTO_PROFILER_LABEL_HOT("nsCSSFrameConstructor::ConstructRootFrame",
2640 LAYOUT_FrameConstruction);
2641 AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
2643 ServoStyleSet* styleSet = mPresShell->StyleSet();
2645 // --------- BUILD VIEWPORT -----------
2646 RefPtr<ComputedStyle> viewportPseudoStyle =
2647 styleSet->ResolveInheritingAnonymousBoxStyle(PseudoStyleType::viewport,
2648 nullptr);
2649 ViewportFrame* viewportFrame =
2650 NS_NewViewportFrame(mPresShell, viewportPseudoStyle);
2652 // XXXbz do we _have_ to pass a null content pointer to that frame?
2653 // Would it really kill us to pass in the root element or something?
2654 // What would that break?
2655 viewportFrame->Init(nullptr, nullptr, nullptr);
2657 viewportFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
2659 // Bind the viewport frame to the root view
2660 if (nsView* rootView = mPresShell->GetViewManager()->GetRootView()) {
2661 viewportFrame->SetView(rootView);
2662 viewportFrame->SyncFrameViewProperties(rootView);
2663 rootView->SetNeedsWindowPropertiesSync();
2666 // Make it an absolute container for fixed-pos elements
2667 viewportFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
2668 viewportFrame->MarkAsAbsoluteContainingBlock();
2670 return viewportFrame;
2673 void nsCSSFrameConstructor::SetUpDocElementContainingBlock(
2674 nsIContent* aDocElement) {
2675 MOZ_ASSERT(aDocElement, "No element?");
2676 MOZ_ASSERT(!aDocElement->GetParent(), "Not root content?");
2677 MOZ_ASSERT(aDocElement->GetUncomposedDoc(), "Not in a document?");
2678 MOZ_ASSERT(aDocElement->GetUncomposedDoc()->GetRootElement() == aDocElement,
2679 "Not the root of the document?");
2682 how the root frame hierarchy should look
2684 Galley presentation, with scrolling:
2686 ViewportFrame [fixed-cb]
2687 nsHTMLScrollFrame (if needed)
2688 nsCanvasFrame [abs-cb]
2689 root element frame (nsBlockFrame, SVGOuterSVGFrame,
2690 nsTableWrapperFrame, nsPlaceholderFrame)
2692 Print presentation, non-XUL
2694 ViewportFrame
2695 nsCanvasFrame
2696 nsPageSequenceFrame
2697 PrintedSheetFrame
2698 nsPageFrame
2699 nsPageContentFrame [fixed-cb]
2700 nsCanvasFrame [abs-cb]
2701 root element frame (nsBlockFrame, SVGOuterSVGFrame,
2702 nsTableWrapperFrame, nsPlaceholderFrame)
2704 Print-preview presentation, non-XUL
2706 ViewportFrame
2707 nsHTMLScrollFrame
2708 nsCanvasFrame
2709 nsPageSequenceFrame
2710 PrintedSheetFrame
2711 nsPageFrame
2712 nsPageContentFrame [fixed-cb]
2713 nsCanvasFrame [abs-cb]
2714 root element frame (nsBlockFrame, SVGOuterSVGFrame,
2715 nsTableWrapperFrame,
2716 nsPlaceholderFrame)
2718 Print/print preview of XUL is not supported.
2719 [fixed-cb]: the default containing block for fixed-pos content
2720 [abs-cb]: the default containing block for abs-pos content
2722 Meaning of nsCSSFrameConstructor fields:
2723 mRootElementFrame is "root element frame". This is the primary frame for
2724 the root element.
2725 mDocElementContainingBlock is the parent of mRootElementFrame
2726 (i.e. nsCanvasFrame)
2727 mPageSequenceFrame is the nsPageSequenceFrame, or null if there isn't
2731 // --------- CREATE ROOT FRAME -------
2733 // Create the root frame. The document element's frame is a child of the
2734 // root frame.
2736 // The root frame serves two purposes:
2737 // - reserves space for any margins needed for the document element's frame
2738 // - renders the document element's background. This ensures the background
2739 // covers the entire canvas as specified by the CSS2 spec
2741 nsPresContext* presContext = mPresShell->GetPresContext();
2742 const bool isPaginated = presContext->IsRootPaginatedDocument();
2744 const bool isHTML = aDocElement->IsHTMLElement();
2745 const bool isXUL = !isHTML && aDocElement->IsXULElement();
2747 const bool isScrollable = [&] {
2748 if (isPaginated) {
2749 return presContext->HasPaginatedScrolling();
2751 // Never create scrollbars for XUL documents or top level XHTML documents
2752 // that disable scrolling.
2753 if (isXUL) {
2754 return false;
2756 if (aDocElement->OwnerDoc()->ChromeRulesEnabled() &&
2757 aDocElement->AsElement()->AttrValueIs(
2758 kNameSpaceID_None, nsGkAtoms::scrolling, nsGkAtoms::_false,
2759 eCaseMatters)) {
2760 return false;
2762 return true;
2763 }();
2765 nsContainerFrame* viewportFrame =
2766 static_cast<nsContainerFrame*>(GetRootFrame());
2767 ComputedStyle* viewportPseudoStyle = viewportFrame->Style();
2769 nsCanvasFrame* rootCanvasFrame =
2770 NS_NewCanvasFrame(mPresShell, viewportPseudoStyle);
2771 PseudoStyleType rootPseudo = PseudoStyleType::canvas;
2772 mCanvasFrame = rootCanvasFrame;
2773 mDocElementContainingBlock = rootCanvasFrame;
2775 // --------- IF SCROLLABLE WRAP IN SCROLLFRAME --------
2777 // If the device supports scrolling (e.g., in galley mode on the screen and
2778 // for print-preview, but not when printing), then create a scroll frame that
2779 // will act as the scrolling mechanism for the viewport.
2780 // XXX Do we even need a viewport when printing to a printer?
2782 // We no longer need to do overflow propagation here. It's taken care of
2783 // when we construct frames for the element whose overflow might be
2784 // propagated
2785 NS_ASSERTION(!isScrollable || !isXUL,
2786 "XUL documents should never be scrollable - see above");
2788 nsContainerFrame* newFrame = rootCanvasFrame;
2789 RefPtr<ComputedStyle> rootPseudoStyle;
2790 // we must create a state because if the scrollbars are GFX it needs the
2791 // state to build the scrollbar frames.
2792 nsFrameConstructorState state(mPresShell, nullptr, nullptr, nullptr);
2794 // Start off with the viewport as parent; we'll adjust it as needed.
2795 nsContainerFrame* parentFrame = viewportFrame;
2797 ServoStyleSet* styleSet = mPresShell->StyleSet();
2798 // If paginated, make sure we don't put scrollbars in
2799 if (!isScrollable) {
2800 rootPseudoStyle = styleSet->ResolveInheritingAnonymousBoxStyle(
2801 rootPseudo, viewportPseudoStyle);
2802 } else {
2803 rootPseudo = PseudoStyleType::scrolledCanvas;
2805 // Build the frame. We give it the content we are wrapping which is the
2806 // document element, the root frame, the parent view port frame, and we
2807 // should get back the new frame and the scrollable view if one was
2808 // created.
2810 // resolve a context for the scrollframe
2811 RefPtr<ComputedStyle> computedStyle =
2812 styleSet->ResolveInheritingAnonymousBoxStyle(
2813 PseudoStyleType::viewportScroll, viewportPseudoStyle);
2815 // Note that the viewport scrollframe is always built with
2816 // overflow:auto style. This forces the scroll frame to create
2817 // anonymous content for both scrollbars. This is necessary even
2818 // if the HTML or BODY elements are overriding the viewport
2819 // scroll style to 'hidden' --- dynamic style changes might put
2820 // scrollbars back on the viewport and we don't want to have to
2821 // reframe the viewport to create the scrollbar content.
2822 newFrame = nullptr;
2823 rootPseudoStyle =
2824 BeginBuildingScrollFrame(state, aDocElement, computedStyle,
2825 viewportFrame, rootPseudo, true, newFrame);
2826 parentFrame = newFrame;
2829 rootCanvasFrame->SetComputedStyleWithoutNotification(rootPseudoStyle);
2830 rootCanvasFrame->Init(aDocElement, parentFrame, nullptr);
2832 if (isScrollable) {
2833 FinishBuildingScrollFrame(parentFrame, rootCanvasFrame);
2836 if (isPaginated) {
2837 // Create a page sequence frame
2839 RefPtr<ComputedStyle> pageSequenceStyle =
2840 styleSet->ResolveInheritingAnonymousBoxStyle(
2841 PseudoStyleType::pageSequence, viewportPseudoStyle);
2842 mPageSequenceFrame =
2843 NS_NewPageSequenceFrame(mPresShell, pageSequenceStyle);
2844 mPageSequenceFrame->Init(aDocElement, rootCanvasFrame, nullptr);
2845 SetInitialSingleChild(rootCanvasFrame, mPageSequenceFrame);
2848 // Create the first printed sheet frame, as the sole child (for now) of our
2849 // page sequence frame (mPageSequenceFrame).
2850 auto* printedSheetFrame =
2851 ConstructPrintedSheetFrame(mPresShell, mPageSequenceFrame, nullptr);
2852 SetInitialSingleChild(mPageSequenceFrame, printedSheetFrame);
2854 MOZ_ASSERT(!mNextPageContentFramePageName,
2855 "Next page name should not have been set.");
2857 // Create the first page, as the sole child (for now) of the printed sheet
2858 // frame that we just created.
2859 nsCanvasFrame* canvasFrame;
2860 nsContainerFrame* pageFrame =
2861 ConstructPageFrame(mPresShell, printedSheetFrame, nullptr, canvasFrame);
2862 pageFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
2863 SetInitialSingleChild(printedSheetFrame, pageFrame);
2865 // The eventual parent of the document element frame.
2866 // XXX should this be set for every new page (in ConstructPageFrame)?
2867 mDocElementContainingBlock = canvasFrame;
2870 if (viewportFrame->HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
2871 SetInitialSingleChild(viewportFrame, newFrame);
2872 } else {
2873 viewportFrame->AppendFrames(FrameChildListID::Principal,
2874 nsFrameList(newFrame, newFrame));
2878 void nsCSSFrameConstructor::ConstructAnonymousContentForCanvas(
2879 nsFrameConstructorState& aState, nsContainerFrame* aFrame,
2880 nsIContent* aDocElement, nsFrameList& aFrameList) {
2881 NS_ASSERTION(aFrame->IsCanvasFrame(), "aFrame should be canvas frame!");
2882 MOZ_ASSERT(mRootElementFrame->GetContent() == aDocElement);
2884 AutoTArray<nsIAnonymousContentCreator::ContentInfo, 4> anonymousItems;
2885 GetAnonymousContent(aDocElement, aFrame, anonymousItems);
2886 if (anonymousItems.IsEmpty()) {
2887 return;
2890 AutoFrameConstructionItemList itemsToConstruct(this);
2891 AutoFrameConstructionPageName pageNameTracker(aState, aFrame);
2892 AddFCItemsForAnonymousContent(aState, aFrame, anonymousItems,
2893 itemsToConstruct, pageNameTracker);
2894 ConstructFramesFromItemList(aState, itemsToConstruct, aFrame,
2895 /* aParentIsWrapperAnonBox = */ false,
2896 aFrameList);
2899 PrintedSheetFrame* nsCSSFrameConstructor::ConstructPrintedSheetFrame(
2900 PresShell* aPresShell, nsContainerFrame* aParentFrame,
2901 nsIFrame* aPrevSheetFrame) {
2902 RefPtr<ComputedStyle> printedSheetPseudoStyle =
2903 aPresShell->StyleSet()->ResolveNonInheritingAnonymousBoxStyle(
2904 PseudoStyleType::printedSheet);
2906 auto* printedSheetFrame =
2907 NS_NewPrintedSheetFrame(aPresShell, printedSheetPseudoStyle);
2909 printedSheetFrame->Init(nullptr, aParentFrame, aPrevSheetFrame);
2911 return printedSheetFrame;
2914 nsContainerFrame* nsCSSFrameConstructor::ConstructPageFrame(
2915 PresShell* aPresShell, nsContainerFrame* aParentFrame,
2916 nsIFrame* aPrevPageFrame, nsCanvasFrame*& aCanvasFrame) {
2917 ServoStyleSet* styleSet = aPresShell->StyleSet();
2919 RefPtr<ComputedStyle> pagePseudoStyle =
2920 styleSet->ResolveNonInheritingAnonymousBoxStyle(PseudoStyleType::page);
2922 nsContainerFrame* pageFrame = NS_NewPageFrame(aPresShell, pagePseudoStyle);
2924 // Initialize the page frame and force it to have a view. This makes printing
2925 // of the pages easier and faster.
2926 pageFrame->Init(nullptr, aParentFrame, aPrevPageFrame);
2928 RefPtr<const nsAtom> pageName;
2929 if (mNextPageContentFramePageName) {
2930 pageName = mNextPageContentFramePageName.forget();
2931 } else if (aPrevPageFrame) {
2932 pageName = aPrevPageFrame->ComputePageValue();
2933 MOZ_ASSERT(pageName,
2934 "Page name from prev-in-flow should not have been null");
2936 RefPtr<ComputedStyle> pageContentPseudoStyle =
2937 styleSet->ResolvePageContentStyle(pageName,
2938 StylePagePseudoClassFlags::NONE);
2940 nsContainerFrame* pageContentFrame = NS_NewPageContentFrame(
2941 aPresShell, pageContentPseudoStyle, pageName.forget());
2943 nsPageContentFrame* prevPageContentFrame = nullptr;
2944 if (aPrevPageFrame) {
2945 MOZ_ASSERT(aPrevPageFrame->IsPageFrame());
2946 prevPageContentFrame =
2947 static_cast<nsPageFrame*>(aPrevPageFrame)->PageContentFrame();
2949 pageContentFrame->Init(nullptr, pageFrame, prevPageContentFrame);
2950 if (!prevPageContentFrame) {
2951 // The canvas is an inheriting anon box, so needs to be "owned" by the page
2952 // content.
2953 pageContentFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
2955 SetInitialSingleChild(pageFrame, pageContentFrame);
2956 // Make it an absolute container for fixed-pos elements
2957 pageContentFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
2958 pageContentFrame->MarkAsAbsoluteContainingBlock();
2960 RefPtr<ComputedStyle> canvasPseudoStyle =
2961 styleSet->ResolveInheritingAnonymousBoxStyle(PseudoStyleType::canvas,
2962 pageContentPseudoStyle);
2964 aCanvasFrame = NS_NewCanvasFrame(aPresShell, canvasPseudoStyle);
2966 nsIFrame* prevCanvasFrame = nullptr;
2967 if (prevPageContentFrame) {
2968 prevCanvasFrame = prevPageContentFrame->PrincipalChildList().FirstChild();
2969 NS_ASSERTION(prevCanvasFrame, "missing canvas frame");
2971 aCanvasFrame->Init(nullptr, pageContentFrame, prevCanvasFrame);
2972 SetInitialSingleChild(pageContentFrame, aCanvasFrame);
2973 return pageFrame;
2976 /* static */
2977 nsIFrame* nsCSSFrameConstructor::CreatePlaceholderFrameFor(
2978 PresShell* aPresShell, nsIContent* aContent, nsIFrame* aFrame,
2979 nsContainerFrame* aParentFrame, nsIFrame* aPrevInFlow,
2980 nsFrameState aTypeBit) {
2981 RefPtr<ComputedStyle> placeholderStyle =
2982 aPresShell->StyleSet()->ResolveStyleForPlaceholder();
2984 // The placeholder frame gets a pseudo style.
2985 nsPlaceholderFrame* placeholderFrame =
2986 NS_NewPlaceholderFrame(aPresShell, placeholderStyle, aTypeBit);
2988 placeholderFrame->Init(aContent, aParentFrame, aPrevInFlow);
2990 // Associate the placeholder/out-of-flow with each other.
2991 placeholderFrame->SetOutOfFlowFrame(aFrame);
2992 aFrame->SetProperty(nsIFrame::PlaceholderFrameProperty(), placeholderFrame);
2994 aFrame->AddStateBits(NS_FRAME_OUT_OF_FLOW);
2996 return placeholderFrame;
2999 // Clears any lazy bits set in the range [aStartContent, aEndContent). If
3000 // aEndContent is null, that means to clear bits in all siblings starting with
3001 // aStartContent. aStartContent must not be null unless aEndContent is also
3002 // null. We do this so that when new children are inserted under elements whose
3003 // frame is a leaf the new children don't cause us to try to construct frames
3004 // for the existing children again.
3005 static inline void ClearLazyBits(nsIContent* aStartContent,
3006 nsIContent* aEndContent) {
3007 MOZ_ASSERT(aStartContent || !aEndContent,
3008 "Must have start child if we have an end child");
3010 for (nsIContent* cur = aStartContent; cur != aEndContent;
3011 cur = cur->GetNextSibling()) {
3012 cur->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
3016 /* static */
3017 const nsCSSFrameConstructor::FrameConstructionData*
3018 nsCSSFrameConstructor::FindSelectData(const Element& aElement,
3019 ComputedStyle& aStyle) {
3020 // Construct a frame-based listbox or combobox
3021 const auto* sel = dom::HTMLSelectElement::FromNode(aElement);
3022 MOZ_ASSERT(sel);
3023 if (sel->IsCombobox()) {
3024 static constexpr FrameConstructionData sComboboxData{
3025 ToCreationFunc(NS_NewComboboxControlFrame), 0,
3026 PseudoStyleType::buttonContent};
3027 return &sComboboxData;
3029 // FIXME: Can we simplify this to avoid needing ConstructListboxSelectFrame,
3030 // and reuse ConstructScrollableBlock or so?
3031 static constexpr FrameConstructionData sListBoxData{
3032 &nsCSSFrameConstructor::ConstructListBoxSelectFrame};
3033 return &sListBoxData;
3036 nsIFrame* nsCSSFrameConstructor::ConstructListBoxSelectFrame(
3037 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
3038 nsContainerFrame* aParentFrame, const nsStyleDisplay* aStyleDisplay,
3039 nsFrameList& aFrameList) {
3040 nsIContent* const content = aItem.mContent;
3041 ComputedStyle* const computedStyle = aItem.mComputedStyle;
3043 // Listbox, not combobox
3044 nsContainerFrame* listFrame =
3045 NS_NewListControlFrame(mPresShell, computedStyle);
3047 nsContainerFrame* scrolledFrame =
3048 NS_NewSelectsAreaFrame(mPresShell, computedStyle);
3050 // ******* this code stolen from Initialze ScrollFrame ********
3051 // please adjust this code to use BuildScrollFrame.
3053 InitializeListboxSelect(aState, listFrame, scrolledFrame, content,
3054 aParentFrame, computedStyle, aFrameList);
3056 return listFrame;
3059 void nsCSSFrameConstructor::InitializeListboxSelect(
3060 nsFrameConstructorState& aState, nsContainerFrame* scrollFrame,
3061 nsContainerFrame* scrolledFrame, nsIContent* aContent,
3062 nsContainerFrame* aParentFrame, ComputedStyle* aComputedStyle,
3063 nsFrameList& aFrameList) {
3064 // Initialize it
3065 nsContainerFrame* geometricParent =
3066 aState.GetGeometricParent(*aComputedStyle->StyleDisplay(), aParentFrame);
3068 // We don't call InitAndRestoreFrame for scrollFrame because we can only
3069 // restore the frame state after its parts have been created (in particular,
3070 // the scrollable view). So we have to split Init and Restore.
3072 scrollFrame->Init(aContent, geometricParent, nullptr);
3073 aState.AddChild(scrollFrame, aFrameList, aContent, aParentFrame);
3074 BuildScrollFrame(aState, aContent, aComputedStyle, scrolledFrame,
3075 geometricParent, scrollFrame);
3076 if (aState.mFrameState) {
3077 // Restore frame state for the scroll frame
3078 RestoreFrameStateFor(scrollFrame, aState.mFrameState);
3081 nsFrameConstructorSaveState floatSaveState;
3082 aState.MaybePushFloatContainingBlock(scrolledFrame, floatSaveState);
3084 // Process children
3085 nsFrameList childList;
3087 ProcessChildren(aState, aContent, aComputedStyle, scrolledFrame, false,
3088 childList, false);
3090 // Set the scrolled frame's initial child lists
3091 scrolledFrame->SetInitialChildList(FrameChildListID::Principal,
3092 std::move(childList));
3095 nsIFrame* nsCSSFrameConstructor::ConstructFieldSetFrame(
3096 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
3097 nsContainerFrame* aParentFrame, const nsStyleDisplay* aStyleDisplay,
3098 nsFrameList& aFrameList) {
3099 AutoRestore<bool> savedHasRenderedLegend(aState.mHasRenderedLegend);
3100 aState.mHasRenderedLegend = false;
3101 nsIContent* const content = aItem.mContent;
3102 ComputedStyle* const computedStyle = aItem.mComputedStyle;
3104 nsContainerFrame* fieldsetFrame =
3105 NS_NewFieldSetFrame(mPresShell, computedStyle);
3107 // Initialize it
3108 InitAndRestoreFrame(aState, content,
3109 aState.GetGeometricParent(*aStyleDisplay, aParentFrame),
3110 fieldsetFrame);
3112 fieldsetFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
3114 // Resolve style and initialize the frame
3115 RefPtr<ComputedStyle> fieldsetContentStyle =
3116 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
3117 PseudoStyleType::fieldsetContent, computedStyle);
3119 const nsStyleDisplay* fieldsetContentDisplay =
3120 fieldsetContentStyle->StyleDisplay();
3121 const bool isScrollable = fieldsetContentDisplay->IsScrollableOverflow();
3122 nsContainerFrame* scrollFrame = nullptr;
3123 if (isScrollable) {
3124 fieldsetContentStyle = BeginBuildingScrollFrame(
3125 aState, content, fieldsetContentStyle, fieldsetFrame,
3126 PseudoStyleType::scrolledContent, false, scrollFrame);
3129 // Create the inner ::-moz-fieldset-content frame.
3130 nsContainerFrame* contentFrameTop;
3131 nsContainerFrame* contentFrame;
3132 auto* parent = scrollFrame ? scrollFrame : fieldsetFrame;
3133 MOZ_ASSERT(fieldsetContentDisplay->DisplayOutside() ==
3134 StyleDisplayOutside::Block);
3135 switch (fieldsetContentDisplay->DisplayInside()) {
3136 case StyleDisplayInside::Flex:
3137 contentFrame = NS_NewFlexContainerFrame(mPresShell, fieldsetContentStyle);
3138 InitAndRestoreFrame(aState, content, parent, contentFrame);
3139 contentFrameTop = contentFrame;
3140 break;
3141 case StyleDisplayInside::Grid:
3142 contentFrame = NS_NewGridContainerFrame(mPresShell, fieldsetContentStyle);
3143 InitAndRestoreFrame(aState, content, parent, contentFrame);
3144 contentFrameTop = contentFrame;
3145 break;
3146 default: {
3147 MOZ_ASSERT(fieldsetContentDisplay->mDisplay == StyleDisplay::Block,
3148 "bug in StyleAdjuster::adjust_for_fieldset_content?");
3150 contentFrame = NS_NewBlockFrame(mPresShell, fieldsetContentStyle);
3151 if (fieldsetContentStyle->StyleColumn()->IsColumnContainerStyle()) {
3152 contentFrameTop = BeginBuildingColumns(
3153 aState, content, parent, contentFrame, fieldsetContentStyle);
3154 } else {
3155 // No need to create column container. Initialize content frame.
3156 InitAndRestoreFrame(aState, content, parent, contentFrame);
3157 contentFrameTop = contentFrame;
3160 break;
3164 aState.AddChild(fieldsetFrame, aFrameList, content, aParentFrame);
3166 // Process children
3167 nsFrameConstructorSaveState absoluteSaveState;
3168 nsFrameList childList;
3170 contentFrameTop->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
3171 if (fieldsetFrame->IsAbsPosContainingBlock()) {
3172 aState.PushAbsoluteContainingBlock(contentFrameTop, fieldsetFrame,
3173 absoluteSaveState);
3176 nsFrameConstructorSaveState floatSaveState;
3177 aState.MaybePushFloatContainingBlock(contentFrame, floatSaveState);
3179 ProcessChildren(aState, content, computedStyle, contentFrame, true, childList,
3180 true);
3181 nsFrameList fieldsetKids;
3182 fieldsetKids.AppendFrame(nullptr,
3183 scrollFrame ? scrollFrame : contentFrameTop);
3185 if (!MayNeedToCreateColumnSpanSiblings(contentFrame, childList)) {
3186 // Set the inner frame's initial child lists.
3187 contentFrame->SetInitialChildList(FrameChildListID::Principal,
3188 std::move(childList));
3189 } else {
3190 // Extract any initial non-column-span kids, and put them in inner frame's
3191 // child list.
3192 nsFrameList initialNonColumnSpanKids =
3193 childList.Split([](nsIFrame* f) { return f->IsColumnSpan(); });
3194 contentFrame->SetInitialChildList(FrameChildListID::Principal,
3195 std::move(initialNonColumnSpanKids));
3197 if (childList.NotEmpty()) {
3198 nsFrameList columnSpanSiblings = CreateColumnSpanSiblings(
3199 aState, contentFrame, childList,
3200 // Column content should never be a absolute/fixed positioned
3201 // containing block. Pass nullptr as aPositionedFrame.
3202 nullptr);
3203 FinishBuildingColumns(aState, contentFrameTop, contentFrame,
3204 columnSpanSiblings);
3208 if (isScrollable) {
3209 FinishBuildingScrollFrame(scrollFrame, contentFrameTop);
3212 // We use AppendFrames here because the rendered legend will already
3213 // be present in the principal child list if it exists.
3214 fieldsetFrame->AppendFrames(FrameChildListID::NoReflowPrincipal,
3215 std::move(fieldsetKids));
3217 return fieldsetFrame;
3220 const nsCSSFrameConstructor::FrameConstructionData*
3221 nsCSSFrameConstructor::FindDetailsData(const Element& aElement,
3222 ComputedStyle& aStyle) {
3223 if (!StaticPrefs::layout_details_force_block_layout()) {
3224 return nullptr;
3226 static constexpr FrameConstructionData sBlockData[2] = {
3227 {&nsCSSFrameConstructor::ConstructNonScrollableBlock},
3228 {&nsCSSFrameConstructor::ConstructScrollableBlock},
3230 return &sBlockData[aStyle.StyleDisplay()->IsScrollableOverflow()];
3233 nsIFrame* nsCSSFrameConstructor::ConstructBlockRubyFrame(
3234 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
3235 nsContainerFrame* aParentFrame, const nsStyleDisplay* aStyleDisplay,
3236 nsFrameList& aFrameList) {
3237 nsIContent* const content = aItem.mContent;
3238 ComputedStyle* const computedStyle = aItem.mComputedStyle;
3240 nsBlockFrame* blockFrame = NS_NewBlockFrame(mPresShell, computedStyle);
3241 nsContainerFrame* newFrame = blockFrame;
3242 nsContainerFrame* geometricParent =
3243 aState.GetGeometricParent(*aStyleDisplay, aParentFrame);
3244 AutoFrameConstructionPageName pageNameTracker(aState, blockFrame);
3245 if ((aItem.mFCData->mBits & FCDATA_MAY_NEED_SCROLLFRAME) &&
3246 aStyleDisplay->IsScrollableOverflow()) {
3247 nsContainerFrame* scrollframe = nullptr;
3248 BuildScrollFrame(aState, content, computedStyle, blockFrame,
3249 geometricParent, scrollframe);
3250 newFrame = scrollframe;
3251 } else {
3252 InitAndRestoreFrame(aState, content, geometricParent, blockFrame);
3255 RefPtr<ComputedStyle> rubyStyle =
3256 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
3257 PseudoStyleType::blockRubyContent, computedStyle);
3258 nsContainerFrame* rubyFrame = NS_NewRubyFrame(mPresShell, rubyStyle);
3259 InitAndRestoreFrame(aState, content, blockFrame, rubyFrame);
3260 SetInitialSingleChild(blockFrame, rubyFrame);
3261 blockFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
3263 aState.AddChild(newFrame, aFrameList, content, aParentFrame);
3265 if (!mRootElementFrame) {
3266 mRootElementFrame = newFrame;
3269 nsFrameConstructorSaveState absoluteSaveState;
3270 blockFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
3271 if (newFrame->IsAbsPosContainingBlock()) {
3272 aState.PushAbsoluteContainingBlock(blockFrame, blockFrame,
3273 absoluteSaveState);
3275 nsFrameConstructorSaveState floatSaveState;
3276 aState.MaybePushFloatContainingBlock(blockFrame, floatSaveState);
3278 nsFrameList childList;
3279 ProcessChildren(aState, content, rubyStyle, rubyFrame, true, childList, false,
3280 nullptr);
3281 rubyFrame->SetInitialChildList(FrameChildListID::Principal,
3282 std::move(childList));
3284 return newFrame;
3287 static nsIFrame* FindAncestorWithGeneratedContentPseudo(nsIFrame* aFrame) {
3288 for (nsIFrame* f = aFrame->GetParent(); f; f = f->GetParent()) {
3289 NS_ASSERTION(f->IsGeneratedContentFrame(),
3290 "should not have exited generated content");
3291 auto pseudo = f->Style()->GetPseudoType();
3292 if (pseudo == PseudoStyleType::before || pseudo == PseudoStyleType::after ||
3293 pseudo == PseudoStyleType::marker)
3294 return f;
3296 return nullptr;
3299 /* static */
3300 const nsCSSFrameConstructor::FrameConstructionData*
3301 nsCSSFrameConstructor::FindTextData(const Text& aTextContent,
3302 nsIFrame* aParentFrame) {
3303 if (aParentFrame && IsFrameForSVG(aParentFrame)) {
3304 if (!aParentFrame->IsInSVGTextSubtree()) {
3305 return nullptr;
3308 // FIXME(bug 1588477) Don't render stuff in display: contents / Shadow DOM
3309 // subtrees, because TextCorrespondenceRecorder in the SVG text code doesn't
3310 // really know how to deal with it. This kinda sucks. :(
3311 if (aParentFrame->GetContent() != aTextContent.GetParent()) {
3312 return nullptr;
3315 static constexpr FrameConstructionData sSVGTextData(
3316 NS_NewTextFrame, FCDATA_IS_LINE_PARTICIPANT | FCDATA_IS_SVG_TEXT);
3317 return &sSVGTextData;
3320 static constexpr FrameConstructionData sTextData(NS_NewTextFrame,
3321 FCDATA_IS_LINE_PARTICIPANT);
3322 return &sTextData;
3325 void nsCSSFrameConstructor::ConstructTextFrame(
3326 const FrameConstructionData* aData, nsFrameConstructorState& aState,
3327 nsIContent* aContent, nsContainerFrame* aParentFrame,
3328 ComputedStyle* aComputedStyle, nsFrameList& aFrameList) {
3329 MOZ_ASSERT(aData, "Must have frame construction data");
3331 nsIFrame* newFrame =
3332 (*aData->mFunc.mCreationFunc)(mPresShell, aComputedStyle);
3334 InitAndRestoreFrame(aState, aContent, aParentFrame, newFrame);
3336 // We never need to create a view for a text frame.
3338 if (newFrame->IsGeneratedContentFrame()) {
3339 UniquePtr<nsGenConInitializer> initializer(
3340 static_cast<nsGenConInitializer*>(
3341 aContent->TakeProperty(nsGkAtoms::genConInitializerProperty)));
3342 if (initializer) {
3343 if (initializer->mNode.release()->InitTextFrame(
3344 initializer->mList,
3345 FindAncestorWithGeneratedContentPseudo(newFrame), newFrame)) {
3346 (this->*(initializer->mDirtyAll))();
3351 // Add the newly constructed frame to the flow
3352 aFrameList.AppendFrame(nullptr, newFrame);
3354 if (!aState.mCreatingExtraFrames || (aContent->IsInNativeAnonymousSubtree() &&
3355 !aContent->GetPrimaryFrame())) {
3356 aContent->SetPrimaryFrame(newFrame);
3360 /* static */
3361 const nsCSSFrameConstructor::FrameConstructionData*
3362 nsCSSFrameConstructor::FindDataByInt(int32_t aInt, const Element& aElement,
3363 ComputedStyle& aComputedStyle,
3364 const FrameConstructionDataByInt* aDataPtr,
3365 uint32_t aDataLength) {
3366 for (const FrameConstructionDataByInt *curData = aDataPtr,
3367 *endData = aDataPtr + aDataLength;
3368 curData != endData; ++curData) {
3369 if (curData->mInt == aInt) {
3370 const FrameConstructionData* data = &curData->mData;
3371 if (data->mBits & FCDATA_FUNC_IS_DATA_GETTER) {
3372 return data->mFunc.mDataGetter(aElement, aComputedStyle);
3375 return data;
3379 return nullptr;
3382 /* static */
3383 const nsCSSFrameConstructor::FrameConstructionData*
3384 nsCSSFrameConstructor::FindDataByTag(const Element& aElement,
3385 ComputedStyle& aStyle,
3386 const FrameConstructionDataByTag* aDataPtr,
3387 uint32_t aDataLength) {
3388 const nsAtom* tag = aElement.NodeInfo()->NameAtom();
3389 for (const FrameConstructionDataByTag *curData = aDataPtr,
3390 *endData = aDataPtr + aDataLength;
3391 curData != endData; ++curData) {
3392 if (curData->mTag == tag) {
3393 const FrameConstructionData* data = &curData->mData;
3394 if (data->mBits & FCDATA_FUNC_IS_DATA_GETTER) {
3395 return data->mFunc.mDataGetter(aElement, aStyle);
3398 return data;
3402 return nullptr;
3405 #define SUPPRESS_FCDATA() FrameConstructionData(nullptr, FCDATA_SUPPRESS_FRAME)
3406 #define SIMPLE_INT_CREATE(_int, _func) \
3407 { int32_t(_int), FrameConstructionData(_func) }
3408 #define SIMPLE_INT_CHAIN(_int, _func) \
3409 { int32_t(_int), FrameConstructionData(_func) }
3410 #define COMPLEX_INT_CREATE(_int, _func) \
3411 { int32_t(_int), FrameConstructionData(_func) }
3413 #define SIMPLE_TAG_CREATE(_tag, _func) \
3414 { nsGkAtoms::_tag, FrameConstructionData(_func) }
3415 #define SIMPLE_TAG_CHAIN(_tag, _func) \
3416 { nsGkAtoms::_tag, FrameConstructionData(_func) }
3417 #define COMPLEX_TAG_CREATE(_tag, _func) \
3418 { nsGkAtoms::_tag, FrameConstructionData(_func) }
3420 static nsFieldSetFrame* GetFieldSetFrameFor(nsIFrame* aFrame) {
3421 auto pseudo = aFrame->Style()->GetPseudoType();
3422 if (pseudo == PseudoStyleType::fieldsetContent ||
3423 pseudo == PseudoStyleType::scrolledContent ||
3424 pseudo == PseudoStyleType::columnSet ||
3425 pseudo == PseudoStyleType::columnContent) {
3426 return GetFieldSetFrameFor(aFrame->GetParent());
3428 return do_QueryFrame(aFrame);
3431 /* static */
3432 const nsCSSFrameConstructor::FrameConstructionData*
3433 nsCSSFrameConstructor::FindHTMLData(const Element& aElement,
3434 nsIFrame* aParentFrame,
3435 ComputedStyle& aStyle) {
3436 MOZ_ASSERT(aElement.IsHTMLElement());
3437 NS_ASSERTION(!aParentFrame ||
3438 aParentFrame->Style()->GetPseudoType() !=
3439 PseudoStyleType::fieldsetContent ||
3440 aParentFrame->GetParent()->IsFieldSetFrame(),
3441 "Unexpected parent for fieldset content anon box");
3443 if (aElement.IsInNativeAnonymousSubtree() &&
3444 aElement.NodeInfo()->NameAtom() == nsGkAtoms::label && aParentFrame) {
3445 if (static_cast<nsFileControlFrame*>(do_QueryFrame(aParentFrame))) {
3446 static constexpr FrameConstructionData sFileLabelData(
3447 NS_NewFileControlLabelFrame);
3448 return &sFileLabelData;
3450 if (aParentFrame->GetParent() &&
3451 aParentFrame->GetParent()->IsComboboxControlFrame()) {
3452 static constexpr FrameConstructionData sComboboxLabelData(
3453 NS_NewComboboxLabelFrame);
3454 return &sComboboxLabelData;
3458 static constexpr FrameConstructionDataByTag sHTMLData[] = {
3459 SIMPLE_TAG_CHAIN(img, nsCSSFrameConstructor::FindImgData),
3460 SIMPLE_TAG_CHAIN(mozgeneratedcontentimage,
3461 nsCSSFrameConstructor::FindGeneratedImageData),
3462 {nsGkAtoms::br,
3463 {NS_NewBRFrame, FCDATA_IS_LINE_PARTICIPANT | FCDATA_IS_LINE_BREAK}},
3464 SIMPLE_TAG_CREATE(wbr, NS_NewWBRFrame),
3465 SIMPLE_TAG_CHAIN(input, nsCSSFrameConstructor::FindInputData),
3466 SIMPLE_TAG_CREATE(textarea, NS_NewTextControlFrame),
3467 SIMPLE_TAG_CHAIN(select, nsCSSFrameConstructor::FindSelectData),
3468 SIMPLE_TAG_CHAIN(object, nsCSSFrameConstructor::FindObjectData),
3469 SIMPLE_TAG_CHAIN(embed, nsCSSFrameConstructor::FindObjectData),
3470 COMPLEX_TAG_CREATE(fieldset,
3471 &nsCSSFrameConstructor::ConstructFieldSetFrame),
3472 SIMPLE_TAG_CREATE(frameset, NS_NewHTMLFramesetFrame),
3473 SIMPLE_TAG_CREATE(iframe, NS_NewSubDocumentFrame),
3474 {nsGkAtoms::button,
3475 {ToCreationFunc(NS_NewHTMLButtonControlFrame),
3476 FCDATA_ALLOW_BLOCK_STYLES | FCDATA_ALLOW_GRID_FLEX_COLUMN,
3477 PseudoStyleType::buttonContent}},
3478 SIMPLE_TAG_CHAIN(canvas, nsCSSFrameConstructor::FindCanvasData),
3479 SIMPLE_TAG_CREATE(video, NS_NewHTMLVideoFrame),
3480 SIMPLE_TAG_CREATE(audio, NS_NewHTMLAudioFrame),
3481 SIMPLE_TAG_CREATE(progress, NS_NewProgressFrame),
3482 SIMPLE_TAG_CREATE(meter, NS_NewMeterFrame),
3483 SIMPLE_TAG_CHAIN(details, nsCSSFrameConstructor::FindDetailsData),
3486 return FindDataByTag(aElement, aStyle, sHTMLData, ArrayLength(sHTMLData));
3489 /* static */
3490 const nsCSSFrameConstructor::FrameConstructionData*
3491 nsCSSFrameConstructor::FindGeneratedImageData(const Element& aElement,
3492 ComputedStyle&) {
3493 if (!aElement.IsInNativeAnonymousSubtree()) {
3494 return nullptr;
3497 auto& generatedContent = static_cast<const GeneratedImageContent&>(aElement);
3498 if (generatedContent.IsForListStyleImageMarker()) {
3499 static constexpr FrameConstructionData sImgData(
3500 NS_NewImageFrameForListStyleImage);
3501 return &sImgData;
3504 static constexpr FrameConstructionData sImgData(
3505 NS_NewImageFrameForGeneratedContentIndex);
3506 return &sImgData;
3509 /* static */
3510 const nsCSSFrameConstructor::FrameConstructionData*
3511 nsCSSFrameConstructor::FindImgData(const Element& aElement,
3512 ComputedStyle& aStyle) {
3513 if (nsImageFrame::ImageFrameTypeFor(aElement, aStyle) !=
3514 nsImageFrame::ImageFrameType::ForElementRequest) {
3515 // content: url gets handled by the generic code-path.
3516 return nullptr;
3519 static constexpr FrameConstructionData sImgData(NS_NewImageFrame);
3520 return &sImgData;
3523 /* static */
3524 const nsCSSFrameConstructor::FrameConstructionData*
3525 nsCSSFrameConstructor::FindImgControlData(const Element& aElement,
3526 ComputedStyle& aStyle) {
3527 if (nsImageFrame::ImageFrameTypeFor(aElement, aStyle) !=
3528 nsImageFrame::ImageFrameType::ForElementRequest) {
3529 return nullptr;
3532 static constexpr FrameConstructionData sImgControlData(
3533 NS_NewImageControlFrame);
3534 return &sImgControlData;
3537 /* static */
3538 const nsCSSFrameConstructor::FrameConstructionData*
3539 nsCSSFrameConstructor::FindSearchControlData(const Element& aElement,
3540 ComputedStyle& aStyle) {
3541 if (StaticPrefs::layout_forms_input_type_search_enabled()) {
3542 static constexpr FrameConstructionData sSearchControlData(
3543 NS_NewSearchControlFrame);
3544 return &sSearchControlData;
3547 static constexpr FrameConstructionData sTextControlData(
3548 NS_NewTextControlFrame);
3549 return &sTextControlData;
3552 /* static */
3553 const nsCSSFrameConstructor::FrameConstructionData*
3554 nsCSSFrameConstructor::FindInputData(const Element& aElement,
3555 ComputedStyle& aStyle) {
3556 static constexpr FrameConstructionDataByInt sInputData[] = {
3557 SIMPLE_INT_CREATE(FormControlType::InputCheckbox,
3558 ToCreationFunc(NS_NewCheckboxRadioFrame)),
3559 SIMPLE_INT_CREATE(FormControlType::InputRadio,
3560 ToCreationFunc(NS_NewCheckboxRadioFrame)),
3561 SIMPLE_INT_CREATE(FormControlType::InputFile, NS_NewFileControlFrame),
3562 SIMPLE_INT_CHAIN(FormControlType::InputImage,
3563 nsCSSFrameConstructor::FindImgControlData),
3564 SIMPLE_INT_CREATE(FormControlType::InputEmail, NS_NewTextControlFrame),
3565 SIMPLE_INT_CREATE(FormControlType::InputText, NS_NewTextControlFrame),
3566 SIMPLE_INT_CREATE(FormControlType::InputTel, NS_NewTextControlFrame),
3567 SIMPLE_INT_CREATE(FormControlType::InputUrl, NS_NewTextControlFrame),
3568 SIMPLE_INT_CREATE(FormControlType::InputRange, NS_NewRangeFrame),
3569 SIMPLE_INT_CREATE(FormControlType::InputPassword, NS_NewTextControlFrame),
3570 {int32_t(FormControlType::InputColor),
3571 {NS_NewColorControlFrame, 0, PseudoStyleType::buttonContent}},
3573 SIMPLE_INT_CHAIN(FormControlType::InputSearch,
3574 nsCSSFrameConstructor::FindSearchControlData),
3575 SIMPLE_INT_CREATE(FormControlType::InputNumber, NS_NewNumberControlFrame),
3576 SIMPLE_INT_CREATE(FormControlType::InputTime, NS_NewDateTimeControlFrame),
3577 SIMPLE_INT_CREATE(FormControlType::InputDate, NS_NewDateTimeControlFrame),
3578 SIMPLE_INT_CREATE(FormControlType::InputDatetimeLocal,
3579 NS_NewDateTimeControlFrame),
3580 // TODO: this is temporary until a frame is written: bug 888320
3581 SIMPLE_INT_CREATE(FormControlType::InputMonth, NS_NewTextControlFrame),
3582 // TODO: this is temporary until a frame is written: bug 888320
3583 SIMPLE_INT_CREATE(FormControlType::InputWeek, NS_NewTextControlFrame),
3584 {int32_t(FormControlType::InputSubmit),
3585 {ToCreationFunc(NS_NewGfxButtonControlFrame), 0,
3586 PseudoStyleType::buttonContent}},
3587 {int32_t(FormControlType::InputReset),
3588 {ToCreationFunc(NS_NewGfxButtonControlFrame), 0,
3589 PseudoStyleType::buttonContent}},
3590 {int32_t(FormControlType::InputButton),
3591 {ToCreationFunc(NS_NewGfxButtonControlFrame), 0,
3592 PseudoStyleType::buttonContent}}
3593 // Keeping hidden inputs out of here on purpose for so they get frames by
3594 // display (in practice, none).
3597 auto controlType = HTMLInputElement::FromNode(aElement)->ControlType();
3599 // radio and checkbox inputs with appearance:none should be constructed
3600 // by display type. (Note that we're not checking that appearance is
3601 // not (respectively) StyleAppearance::Radio and StyleAppearance::Checkbox.)
3602 if ((controlType == FormControlType::InputCheckbox ||
3603 controlType == FormControlType::InputRadio) &&
3604 !aStyle.StyleDisplay()->HasAppearance()) {
3605 return nullptr;
3608 return FindDataByInt(int32_t(controlType), aElement, aStyle, sInputData,
3609 ArrayLength(sInputData));
3612 /* static */
3613 const nsCSSFrameConstructor::FrameConstructionData*
3614 nsCSSFrameConstructor::FindObjectData(const Element& aElement,
3615 ComputedStyle& aStyle) {
3616 uint32_t type;
3617 nsCOMPtr<nsIObjectLoadingContent> objContent =
3618 do_QueryInterface(const_cast<Element*>(&aElement));
3619 NS_ASSERTION(objContent,
3620 "embed and object must implement "
3621 "nsIObjectLoadingContent!");
3622 objContent->GetDisplayedType(&type);
3624 static constexpr FrameConstructionDataByInt sObjectData[] = {
3625 // TODO(emilio): Can we remove the NS_NewEmptyFrame case and just use a
3626 // subdocument frame here?
3627 SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_LOADING,
3628 NS_NewEmptyFrame),
3629 SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_DOCUMENT,
3630 NS_NewSubDocumentFrame),
3631 // Nothing for TYPE_FALLBACK so we'll construct frames by display there
3634 return FindDataByInt((int32_t)type, aElement, aStyle, sObjectData,
3635 ArrayLength(sObjectData));
3638 /* static */
3639 const nsCSSFrameConstructor::FrameConstructionData*
3640 nsCSSFrameConstructor::FindCanvasData(const Element& aElement,
3641 ComputedStyle& aStyle) {
3642 // We want to check whether script is enabled on the document that
3643 // could be painting to the canvas. That's the owner document of
3644 // the canvas, except when the owner document is a static document,
3645 // in which case it's the original document it was cloned from.
3646 Document* doc = aElement.OwnerDoc();
3647 if (doc->IsStaticDocument()) {
3648 doc = doc->GetOriginalDocument();
3650 if (!doc->IsScriptEnabled()) {
3651 return nullptr;
3654 static constexpr FrameConstructionData sCanvasData(
3655 NS_NewHTMLCanvasFrame, 0, PseudoStyleType::htmlCanvasContent);
3656 return &sCanvasData;
3659 static MOZ_NEVER_INLINE void DestroyFramesInList(PresShell* aPs,
3660 nsFrameList& aList) {
3661 nsIFrame::DestroyContext context(aPs);
3662 aList.DestroyFrames(context);
3665 void nsCSSFrameConstructor::ConstructFrameFromItemInternal(
3666 FrameConstructionItem& aItem, nsFrameConstructorState& aState,
3667 nsContainerFrame* aParentFrame, nsFrameList& aFrameList) {
3668 const FrameConstructionData* data = aItem.mFCData;
3669 NS_ASSERTION(data, "Must have frame construction data");
3671 uint32_t bits = data->mBits;
3673 NS_ASSERTION(!(bits & FCDATA_FUNC_IS_DATA_GETTER),
3674 "Should have dealt with this inside the data finder");
3676 // Some sets of bits are not compatible with each other
3677 #define CHECK_ONLY_ONE_BIT(_bit1, _bit2) \
3678 NS_ASSERTION(!(bits & _bit1) || !(bits & _bit2), \
3679 "Only one of these bits should be set")
3680 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR,
3681 FCDATA_FORCE_NULL_ABSPOS_CONTAINER);
3682 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_WRAP_KIDS_IN_BLOCKS);
3683 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_IS_POPUP);
3684 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_SKIP_ABSPOS_PUSH);
3685 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR,
3686 FCDATA_DISALLOW_GENERATED_CONTENT);
3687 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_ALLOW_BLOCK_STYLES);
3688 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR,
3689 FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS);
3690 CHECK_ONLY_ONE_BIT(FCDATA_WRAP_KIDS_IN_BLOCKS,
3691 FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS);
3692 #undef CHECK_ONLY_ONE_BIT
3693 MOZ_ASSERT(
3694 !(bits & FCDATA_IS_WRAPPER_ANON_BOX) || (bits & FCDATA_USE_CHILD_ITEMS),
3695 "Wrapper anon boxes should always have FCDATA_USE_CHILD_ITEMS");
3696 MOZ_ASSERT(!(bits & FCDATA_ALLOW_GRID_FLEX_COLUMN) ||
3697 (bits & FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS),
3698 "Need the block wrapper bit to create grid/flex/column.");
3700 // Don't create a subdocument frame for iframes if we're creating extra frames
3701 if (aState.mCreatingExtraFrames &&
3702 aItem.mContent->IsHTMLElement(nsGkAtoms::iframe)) {
3703 return;
3706 nsIContent* const content = aItem.mContent;
3707 nsIFrame* newFrame;
3708 nsIFrame* primaryFrame;
3709 ComputedStyle* const computedStyle = aItem.mComputedStyle;
3710 const nsStyleDisplay* display = computedStyle->StyleDisplay();
3711 if (bits & FCDATA_FUNC_IS_FULL_CTOR) {
3712 newFrame = (this->*(data->mFunc.mFullConstructor))(
3713 aState, aItem, aParentFrame, display, aFrameList);
3714 MOZ_ASSERT(newFrame, "Full constructor failed");
3715 primaryFrame = newFrame;
3716 } else {
3717 newFrame = (*data->mFunc.mCreationFunc)(mPresShell, computedStyle);
3719 const bool allowOutOfFlow = !(bits & FCDATA_DISALLOW_OUT_OF_FLOW);
3720 const bool isPopup = aItem.mIsPopup;
3722 nsContainerFrame* geometricParent =
3723 (isPopup || allowOutOfFlow)
3724 ? aState.GetGeometricParent(*display, aParentFrame)
3725 : aParentFrame;
3727 // In the non-scrollframe case, primaryFrame and newFrame are equal; in the
3728 // scrollframe case, newFrame is the scrolled frame while primaryFrame is
3729 // the scrollframe.
3730 if ((bits & FCDATA_MAY_NEED_SCROLLFRAME) &&
3731 display->IsScrollableOverflow()) {
3732 nsContainerFrame* scrollframe = nullptr;
3733 BuildScrollFrame(aState, content, computedStyle, newFrame,
3734 geometricParent, scrollframe);
3735 primaryFrame = scrollframe;
3736 } else {
3737 InitAndRestoreFrame(aState, content, geometricParent, newFrame);
3738 primaryFrame = newFrame;
3741 // If we need to create a block formatting context to wrap our
3742 // kids, do it now.
3743 nsIFrame* maybeAbsoluteContainingBlockStyleFrame = primaryFrame;
3744 nsIFrame* maybeAbsoluteContainingBlock = newFrame;
3745 nsIFrame* possiblyLeafFrame = newFrame;
3746 nsContainerFrame* outerFrame = nullptr;
3747 if (bits & FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS) {
3748 RefPtr<ComputedStyle> outerStyle =
3749 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
3750 data->mAnonBoxPseudo, computedStyle);
3751 #ifdef DEBUG
3752 nsContainerFrame* containerFrame = do_QueryFrame(newFrame);
3753 MOZ_ASSERT(containerFrame);
3754 #endif
3755 nsContainerFrame* container = static_cast<nsContainerFrame*>(newFrame);
3756 nsContainerFrame* innerFrame;
3757 if (bits & FCDATA_ALLOW_GRID_FLEX_COLUMN) {
3758 switch (display->DisplayInside()) {
3759 case StyleDisplayInside::Flex:
3760 outerFrame = NS_NewFlexContainerFrame(mPresShell, outerStyle);
3761 InitAndRestoreFrame(aState, content, container, outerFrame);
3762 innerFrame = outerFrame;
3763 break;
3764 case StyleDisplayInside::Grid:
3765 outerFrame = NS_NewGridContainerFrame(mPresShell, outerStyle);
3766 InitAndRestoreFrame(aState, content, container, outerFrame);
3767 innerFrame = outerFrame;
3768 break;
3769 default: {
3770 innerFrame = NS_NewBlockFrame(mPresShell, outerStyle);
3771 if (outerStyle->StyleColumn()->IsColumnContainerStyle()) {
3772 outerFrame = BeginBuildingColumns(aState, content, container,
3773 innerFrame, outerStyle);
3774 } else {
3775 // No need to create column container. Initialize innerFrame.
3776 InitAndRestoreFrame(aState, content, container, innerFrame);
3777 outerFrame = innerFrame;
3779 break;
3782 } else {
3783 innerFrame = NS_NewBlockFrame(mPresShell, outerStyle);
3784 InitAndRestoreFrame(aState, content, container, innerFrame);
3785 outerFrame = innerFrame;
3788 SetInitialSingleChild(container, outerFrame);
3790 container->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
3792 // Now figure out whether newFrame or outerFrame should be the
3793 // absolute container.
3794 if (outerFrame->IsAbsPosContainingBlock()) {
3795 maybeAbsoluteContainingBlock = outerFrame;
3796 maybeAbsoluteContainingBlockStyleFrame = outerFrame;
3797 innerFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
3800 // Our kids should go into the innerFrame.
3801 newFrame = innerFrame;
3804 aState.AddChild(primaryFrame, aFrameList, content, aParentFrame,
3805 allowOutOfFlow, allowOutOfFlow);
3807 nsContainerFrame* newFrameAsContainer = do_QueryFrame(newFrame);
3808 if (newFrameAsContainer) {
3809 // Process the child content if requested
3810 nsFrameList childList;
3811 nsFrameConstructorSaveState absoluteSaveState;
3813 if (bits & FCDATA_FORCE_NULL_ABSPOS_CONTAINER) {
3814 aState.PushAbsoluteContainingBlock(nullptr, nullptr, absoluteSaveState);
3815 } else if (!(bits & FCDATA_SKIP_ABSPOS_PUSH)) {
3816 maybeAbsoluteContainingBlock->AddStateBits(
3817 NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
3818 if (maybeAbsoluteContainingBlockStyleFrame->IsAbsPosContainingBlock()) {
3819 auto* cf =
3820 static_cast<nsContainerFrame*>(maybeAbsoluteContainingBlock);
3821 aState.PushAbsoluteContainingBlock(
3822 cf, maybeAbsoluteContainingBlockStyleFrame, absoluteSaveState);
3826 nsFrameConstructorSaveState floatSaveState;
3827 aState.MaybePushFloatContainingBlock(newFrameAsContainer, floatSaveState);
3829 if (bits & FCDATA_USE_CHILD_ITEMS) {
3830 // At this point, we have not set up the auto value for this frame, and
3831 // no caller will have set it so it is not redundant and therefor will
3832 // not assert.
3833 AutoFrameConstructionPageName pageNameTracker(aState,
3834 newFrameAsContainer);
3835 ConstructFramesFromItemList(
3836 aState, aItem.mChildItems, newFrameAsContainer,
3837 bits & FCDATA_IS_WRAPPER_ANON_BOX, childList);
3838 } else {
3839 // Process the child frames.
3840 ProcessChildren(aState, content, computedStyle, newFrameAsContainer,
3841 !(bits & FCDATA_DISALLOW_GENERATED_CONTENT), childList,
3842 (bits & FCDATA_ALLOW_BLOCK_STYLES) != 0,
3843 possiblyLeafFrame);
3846 if (bits & FCDATA_WRAP_KIDS_IN_BLOCKS) {
3847 nsFrameList newList;
3848 nsFrameList currentBlockList;
3849 nsIFrame* f;
3850 while ((f = childList.FirstChild()) != nullptr) {
3851 bool wrapFrame = IsInlineFrame(f) || IsFramePartOfIBSplit(f);
3852 if (!wrapFrame) {
3853 FlushAccumulatedBlock(aState, content, newFrameAsContainer,
3854 currentBlockList, newList);
3857 childList.RemoveFrame(f);
3858 if (wrapFrame) {
3859 currentBlockList.AppendFrame(nullptr, f);
3860 } else {
3861 newList.AppendFrame(nullptr, f);
3864 FlushAccumulatedBlock(aState, content, newFrameAsContainer,
3865 currentBlockList, newList);
3867 if (childList.NotEmpty()) {
3868 // an error must have occurred, delete unprocessed frames
3869 DestroyFramesInList(mPresShell, childList);
3872 childList = std::move(newList);
3875 if (!(bits & FCDATA_ALLOW_GRID_FLEX_COLUMN) ||
3876 !MayNeedToCreateColumnSpanSiblings(newFrameAsContainer, childList)) {
3877 // Set the frame's initial child list. Note that MathML depends on this
3878 // being called even if childList is empty!
3879 newFrameAsContainer->SetInitialChildList(FrameChildListID::Principal,
3880 std::move(childList));
3881 } else {
3882 // Extract any initial non-column-span kids, and put them in inner
3883 // frame's child list.
3884 nsFrameList initialNonColumnSpanKids =
3885 childList.Split([](nsIFrame* f) { return f->IsColumnSpan(); });
3886 newFrameAsContainer->SetInitialChildList(
3887 FrameChildListID::Principal, std::move(initialNonColumnSpanKids));
3889 if (childList.NotEmpty()) {
3890 nsFrameList columnSpanSiblings = CreateColumnSpanSiblings(
3891 aState, newFrameAsContainer, childList,
3892 // Column content should never be a absolute/fixed positioned
3893 // containing block. Pass nullptr as aPositionedFrame.
3894 nullptr);
3896 MOZ_ASSERT(outerFrame,
3897 "outerFrame should be non-null if multi-column container "
3898 "is created.");
3899 FinishBuildingColumns(aState, outerFrame, newFrameAsContainer,
3900 columnSpanSiblings);
3906 NS_ASSERTION(newFrame->IsLineParticipant() ==
3907 ((bits & FCDATA_IS_LINE_PARTICIPANT) != 0),
3908 "Incorrectly set FCDATA_IS_LINE_PARTICIPANT bits");
3910 // Even if mCreatingExtraFrames is set, we may need to SetPrimaryFrame for
3911 // generated content that doesn't have one yet. Note that we have to examine
3912 // the frame bit, because by this point mIsGeneratedContent has been cleared
3913 // on aItem.
3914 if ((!aState.mCreatingExtraFrames ||
3915 (aItem.mContent->IsRootOfNativeAnonymousSubtree() &&
3916 !aItem.mContent->GetPrimaryFrame())) &&
3917 !(bits & FCDATA_SKIP_FRAMESET)) {
3918 aItem.mContent->SetPrimaryFrame(primaryFrame);
3919 ActiveLayerTracker::TransferActivityToFrame(aItem.mContent, primaryFrame);
3923 static void GatherSubtreeElements(Element* aElement,
3924 nsTArray<Element*>& aElements) {
3925 aElements.AppendElement(aElement);
3926 StyleChildrenIterator iter(aElement);
3927 for (nsIContent* c = iter.GetNextChild(); c; c = iter.GetNextChild()) {
3928 if (!c->IsElement()) {
3929 continue;
3931 GatherSubtreeElements(c->AsElement(), aElements);
3935 nsresult nsCSSFrameConstructor::GetAnonymousContent(
3936 nsIContent* aParent, nsIFrame* aParentFrame,
3937 nsTArray<nsIAnonymousContentCreator::ContentInfo>& aContent) {
3938 nsIAnonymousContentCreator* creator = do_QueryFrame(aParentFrame);
3939 if (!creator) {
3940 return NS_OK;
3943 nsresult rv = creator->CreateAnonymousContent(aContent);
3944 if (NS_FAILED(rv)) {
3945 // CreateAnonymousContent failed, e.g. because the page has a <use> loop.
3946 return rv;
3949 if (aContent.IsEmpty()) {
3950 return NS_OK;
3953 const bool devtoolsEventsEnabled =
3954 mDocument->DevToolsAnonymousAndShadowEventsEnabled();
3956 MOZ_ASSERT(aParent->IsElement());
3957 for (const auto& info : aContent) {
3958 // get our child's content and set its parent to our content
3959 nsIContent* content = info.mContent;
3960 content->SetIsNativeAnonymousRoot();
3962 BindContext context(*aParent->AsElement(), BindContext::ForNativeAnonymous);
3963 rv = content->BindToTree(context, *aParent);
3965 if (NS_FAILED(rv)) {
3966 content->UnbindFromTree();
3967 return rv;
3970 if (devtoolsEventsEnabled) {
3971 content->QueueDevtoolsAnonymousEvent(/* aIsRemove = */ false);
3975 // Some situations where we don't cache anonymous content styles:
3977 // * when visibility or pointer-events is anything other than the initial
3978 // value; we rely on visibility and pointer-events inheriting into anonymous
3979 // content, but don't bother adding this state to the AnonymousContentKey,
3980 // since it's not so common. Note that with overlay scrollbars, scrollbars
3981 // always start off with pointer-events: none so we don't need to check for
3982 // that in that case.
3984 // * when the medium is anything other than screen; some UA style sheet rules
3985 // apply in e.g. print medium, and will give different results from the
3986 // cached styles
3987 Maybe<bool> computedAllowStyleCaching;
3988 auto ComputeAllowStyleCaching = [&] {
3989 if (!StaticPrefs::layout_css_cached_scrollbar_styles_enabled()) {
3990 return false;
3992 if (aParentFrame->StyleVisibility()->mVisible != StyleVisibility::Visible) {
3993 return false;
3995 nsPresContext* pc = mPresShell->GetPresContext();
3996 if (!pc->UseOverlayScrollbars() &&
3997 aParentFrame->StyleUI()->ComputedPointerEvents() !=
3998 StylePointerEvents::Auto) {
3999 return false;
4001 if (pc->Medium() != nsGkAtoms::screen) {
4002 return false;
4004 return true;
4007 auto AllowStyleCaching = [&] {
4008 if (computedAllowStyleCaching.isNothing()) {
4009 computedAllowStyleCaching.emplace(ComputeAllowStyleCaching());
4011 return computedAllowStyleCaching.value();
4014 // Compute styles for the anonymous content tree.
4015 ServoStyleSet* styleSet = mPresShell->StyleSet();
4016 for (auto& info : aContent) {
4017 Element* e = Element::FromNode(info.mContent);
4018 if (!e) {
4019 continue;
4022 if (info.mKey == AnonymousContentKey::None || !AllowStyleCaching()) {
4023 // Most NAC subtrees do not use caching of computed styles. Just go
4024 // ahead and eagerly style the subtree.
4025 styleSet->StyleNewSubtree(e);
4026 continue;
4029 // We have a NAC subtree for which we can use cached styles.
4030 AutoTArray<RefPtr<ComputedStyle>, 2> cachedStyles;
4031 AutoTArray<Element*, 2> elements;
4033 GatherSubtreeElements(e, elements);
4034 styleSet->GetCachedAnonymousContentStyles(info.mKey, cachedStyles);
4036 if (cachedStyles.IsEmpty()) {
4037 // We haven't stored cached styles for this kind of NAC subtree yet.
4038 // Eagerly compute those styles, then cache them for later.
4039 styleSet->StyleNewSubtree(e);
4040 for (Element* e : elements) {
4041 if (e->HasServoData()) {
4042 cachedStyles.AppendElement(ServoStyleSet::ResolveServoStyle(*e));
4043 } else {
4044 cachedStyles.AppendElement(nullptr);
4047 styleSet->PutCachedAnonymousContentStyles(info.mKey,
4048 std::move(cachedStyles));
4049 continue;
4052 // We previously stored cached styles for this kind of NAC subtree.
4053 // Iterate over them and set them on the subtree's elements.
4054 MOZ_ASSERT(cachedStyles.Length() == elements.Length(),
4055 "should always produce the same size NAC subtree");
4056 for (size_t i = 0, len = cachedStyles.Length(); i != len; ++i) {
4057 if (cachedStyles[i]) {
4058 #ifdef DEBUG
4059 // Assert that our cached style is the same as one we could compute.
4060 RefPtr<ComputedStyle> cs = styleSet->ResolveStyleLazily(*elements[i]);
4061 MOZ_ASSERT(
4062 cachedStyles[i]->EqualForCachedAnonymousContentStyle(*cs),
4063 "cached anonymous content styles should be identical to those we "
4064 "would compute normally");
4065 // All overlay scrollbars start off as inactive, so we can rely on their
4066 // pointer-events value being always none.
4067 MOZ_ASSERT(!mPresShell->GetPresContext()->UseOverlayScrollbars() ||
4068 cs->StyleUI()->ComputedPointerEvents() ==
4069 StylePointerEvents::None);
4070 #endif
4071 Servo_SetExplicitStyle(elements[i], cachedStyles[i]);
4076 return NS_OK;
4079 // XUL frames are not allowed to be out of flow.
4080 #define SIMPLE_XUL_FCDATA(_func) \
4081 FrameConstructionData(_func, \
4082 FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_SKIP_ABSPOS_PUSH)
4083 #define SCROLLABLE_XUL_FCDATA(_func) \
4084 FrameConstructionData(_func, FCDATA_DISALLOW_OUT_OF_FLOW | \
4085 FCDATA_SKIP_ABSPOS_PUSH | \
4086 FCDATA_MAY_NEED_SCROLLFRAME)
4087 // .. but we allow some XUL frames to be _containers_ for out-of-flow content
4088 // (This is the same as SCROLLABLE_XUL_FCDATA, but w/o FCDATA_SKIP_ABSPOS_PUSH)
4089 #define SCROLLABLE_ABSPOS_CONTAINER_XUL_FCDATA(_func) \
4090 FrameConstructionData( \
4091 _func, FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_MAY_NEED_SCROLLFRAME)
4093 #define SIMPLE_XUL_CREATE(_tag, _func) \
4094 { nsGkAtoms::_tag, SIMPLE_XUL_FCDATA(_func) }
4095 #define SCROLLABLE_XUL_CREATE(_tag, _func) \
4096 { nsGkAtoms::_tag, SCROLLABLE_XUL_FCDATA(_func) }
4098 /* static */
4099 const nsCSSFrameConstructor::FrameConstructionData*
4100 nsCSSFrameConstructor::FindXULTagData(const Element& aElement,
4101 ComputedStyle& aStyle) {
4102 MOZ_ASSERT(aElement.IsXULElement());
4103 static constexpr FrameConstructionData kPopupData(
4104 NS_NewMenuPopupFrame, FCDATA_IS_POPUP | FCDATA_SKIP_ABSPOS_PUSH);
4106 static constexpr FrameConstructionDataByTag sXULTagData[] = {
4107 SIMPLE_XUL_CREATE(image, NS_NewXULImageFrame),
4108 SIMPLE_XUL_CREATE(treechildren, NS_NewTreeBodyFrame),
4109 SIMPLE_TAG_CHAIN(label,
4110 nsCSSFrameConstructor::FindXULLabelOrDescriptionData),
4111 SIMPLE_TAG_CHAIN(description,
4112 nsCSSFrameConstructor::FindXULLabelOrDescriptionData),
4113 #ifdef XP_MACOSX
4114 SIMPLE_TAG_CHAIN(menubar, nsCSSFrameConstructor::FindXULMenubarData),
4115 #endif /* XP_MACOSX */
4116 SIMPLE_XUL_CREATE(iframe, NS_NewSubDocumentFrame),
4117 SIMPLE_XUL_CREATE(editor, NS_NewSubDocumentFrame),
4118 SIMPLE_XUL_CREATE(browser, NS_NewSubDocumentFrame),
4119 SIMPLE_XUL_CREATE(splitter, NS_NewSplitterFrame),
4120 SIMPLE_XUL_CREATE(scrollbar, NS_NewScrollbarFrame),
4121 SIMPLE_XUL_CREATE(slider, NS_NewSliderFrame),
4122 SIMPLE_XUL_CREATE(thumb, NS_NewSimpleXULLeafFrame),
4123 SIMPLE_XUL_CREATE(scrollcorner, NS_NewSimpleXULLeafFrame),
4124 SIMPLE_XUL_CREATE(resizer, NS_NewSimpleXULLeafFrame),
4125 SIMPLE_XUL_CREATE(scrollbarbutton, NS_NewScrollbarButtonFrame),
4126 {nsGkAtoms::panel, kPopupData},
4127 {nsGkAtoms::menupopup, kPopupData},
4128 {nsGkAtoms::tooltip, kPopupData},
4131 return FindDataByTag(aElement, aStyle, sXULTagData, ArrayLength(sXULTagData));
4134 /* static */
4135 const nsCSSFrameConstructor::FrameConstructionData*
4136 nsCSSFrameConstructor::FindXULLabelOrDescriptionData(const Element& aElement,
4137 ComputedStyle&) {
4138 // Follow CSS display value if no value attribute
4139 if (!aElement.HasAttr(nsGkAtoms::value)) {
4140 return nullptr;
4143 // Follow CSS display if there's no crop="center".
4144 if (!aElement.AttrValueIs(kNameSpaceID_None, nsGkAtoms::crop,
4145 nsGkAtoms::center, eCaseMatters)) {
4146 return nullptr;
4149 static constexpr FrameConstructionData sMiddleCroppingData =
4150 SIMPLE_XUL_FCDATA(NS_NewMiddleCroppingLabelFrame);
4151 return &sMiddleCroppingData;
4154 #ifdef XP_MACOSX
4155 /* static */
4156 const nsCSSFrameConstructor::FrameConstructionData*
4157 nsCSSFrameConstructor::FindXULMenubarData(const Element& aElement,
4158 ComputedStyle&) {
4159 if (aElement.OwnerDoc()->IsInChromeDocShell()) {
4160 BrowsingContext* bc = aElement.OwnerDoc()->GetBrowsingContext();
4161 bool isRoot = bc && !bc->GetParent();
4162 if (isRoot) {
4163 // This is the root. Suppress the menubar, since on Mac
4164 // window menus are not attached to the window.
4165 static constexpr FrameConstructionData sSuppressData = SUPPRESS_FCDATA();
4166 return &sSuppressData;
4170 return nullptr;
4172 #endif /* XP_MACOSX */
4174 already_AddRefed<ComputedStyle> nsCSSFrameConstructor::BeginBuildingScrollFrame(
4175 nsFrameConstructorState& aState, nsIContent* aContent,
4176 ComputedStyle* aContentStyle, nsContainerFrame* aParentFrame,
4177 PseudoStyleType aScrolledPseudo, bool aIsRoot,
4178 nsContainerFrame*& aNewFrame) {
4179 nsContainerFrame* gfxScrollFrame = aNewFrame;
4181 if (!gfxScrollFrame) {
4182 gfxScrollFrame = NS_NewHTMLScrollFrame(mPresShell, aContentStyle, aIsRoot);
4183 InitAndRestoreFrame(aState, aContent, aParentFrame, gfxScrollFrame);
4186 MOZ_ASSERT(gfxScrollFrame);
4188 // if there are any anonymous children for the scroll frame, create
4189 // frames for them.
4191 // We can't take the normal ProcessChildren path, because the NAC needs to
4192 // be parented to the scrollframe, and everything else needs to be parented
4193 // to the scrolledframe.
4194 AutoTArray<nsIAnonymousContentCreator::ContentInfo, 4> scrollNAC;
4195 DebugOnly<nsresult> rv =
4196 GetAnonymousContent(aContent, gfxScrollFrame, scrollNAC);
4197 MOZ_ASSERT(NS_SUCCEEDED(rv));
4198 nsFrameList anonymousList;
4199 if (!scrollNAC.IsEmpty()) {
4200 nsFrameConstructorSaveState floatSaveState;
4201 aState.MaybePushFloatContainingBlock(gfxScrollFrame, floatSaveState);
4203 AutoFrameConstructionItemList items(this);
4204 AutoFrameConstructionPageName pageNameTracker(aState, gfxScrollFrame);
4205 AddFCItemsForAnonymousContent(aState, gfxScrollFrame, scrollNAC, items,
4206 pageNameTracker);
4207 ConstructFramesFromItemList(aState, items, gfxScrollFrame,
4208 /* aParentIsWrapperAnonBox = */ false,
4209 anonymousList);
4212 aNewFrame = gfxScrollFrame;
4213 gfxScrollFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
4215 // we used the style that was passed in. So resolve another one.
4216 ServoStyleSet* styleSet = mPresShell->StyleSet();
4217 RefPtr<ComputedStyle> scrolledChildStyle =
4218 styleSet->ResolveInheritingAnonymousBoxStyle(aScrolledPseudo,
4219 aContentStyle);
4221 gfxScrollFrame->SetInitialChildList(FrameChildListID::Principal,
4222 std::move(anonymousList));
4224 return scrolledChildStyle.forget();
4227 void nsCSSFrameConstructor::FinishBuildingScrollFrame(
4228 nsContainerFrame* aScrollFrame, nsIFrame* aScrolledFrame) {
4229 aScrollFrame->AppendFrames(FrameChildListID::Principal,
4230 nsFrameList(aScrolledFrame, aScrolledFrame));
4234 * Called to wrap a gfx scrollframe around a frame. The hierarchy will look like
4235 * this
4237 * ------- for gfx scrollbars ------
4240 * ScrollFrame
4243 * Frame (scrolled frame you passed in)
4246 * -----------------------------------
4247 * LEGEND:
4249 * ScrollFrame: This is a frame that manages gfx cross platform frame based
4250 * scrollbars.
4252 * @param aContent the content node of the child to wrap.
4254 * @param aScrolledFrame The frame of the content to wrap. This should not be
4255 * Initialized. This method will initialize it with a scrolled pseudo and no
4256 * nsIContent. The content will be attached to the scrollframe returned.
4258 * @param aContentStyle the style that has already been resolved for the content
4259 * being passed in.
4261 * @param aParentFrame The parent to attach the scroll frame to
4263 * @param aNewFrame The new scrollframe or gfx scrollframe that we create. It
4264 * will contain the scrolled frame you passed in. (returned) If this is not
4265 * null, we'll just use it
4267 * @param aScrolledContentStyle the style that was resolved for the scrolled
4268 * frame. (returned)
4270 void nsCSSFrameConstructor::BuildScrollFrame(nsFrameConstructorState& aState,
4271 nsIContent* aContent,
4272 ComputedStyle* aContentStyle,
4273 nsIFrame* aScrolledFrame,
4274 nsContainerFrame* aParentFrame,
4275 nsContainerFrame*& aNewFrame) {
4276 RefPtr<ComputedStyle> scrolledContentStyle = BeginBuildingScrollFrame(
4277 aState, aContent, aContentStyle, aParentFrame,
4278 PseudoStyleType::scrolledContent, false, aNewFrame);
4280 aScrolledFrame->SetComputedStyleWithoutNotification(scrolledContentStyle);
4281 InitAndRestoreFrame(aState, aContent, aNewFrame, aScrolledFrame);
4283 FinishBuildingScrollFrame(aNewFrame, aScrolledFrame);
4286 const nsCSSFrameConstructor::FrameConstructionData*
4287 nsCSSFrameConstructor::FindDisplayData(const nsStyleDisplay& aDisplay,
4288 const Element& aElement) {
4289 static_assert(eParentTypeCount < (1 << (32 - FCDATA_PARENT_TYPE_OFFSET)),
4290 "Check eParentTypeCount should not overflow");
4292 // The style system ensures that floated and positioned frames are
4293 // block-level.
4294 NS_ASSERTION(
4295 !(aDisplay.IsFloatingStyle() || aDisplay.IsAbsolutelyPositionedStyle()) ||
4296 aDisplay.IsBlockOutsideStyle(),
4297 "Style system did not apply CSS2.1 section 9.7 fixups");
4299 // If this is "body", try propagating its scroll style to the viewport
4300 // Note that we need to do this even if the body is NOT scrollable;
4301 // it might have dynamically changed from scrollable to not scrollable,
4302 // and that might need to be propagated.
4303 // XXXbz is this the right place to do this? If this code moves,
4304 // make this function static.
4305 bool propagatedScrollToViewport = false;
4306 if (aElement.IsHTMLElement(nsGkAtoms::body)) {
4307 if (nsPresContext* presContext = mPresShell->GetPresContext()) {
4308 propagatedScrollToViewport =
4309 presContext->UpdateViewportScrollStylesOverride() == &aElement;
4310 MOZ_ASSERT(!propagatedScrollToViewport ||
4311 !mPresShell->GetPresContext()->IsPaginated(),
4312 "Shouldn't propagate scroll in paginated contexts");
4316 switch (aDisplay.DisplayInside()) {
4317 case StyleDisplayInside::Flow:
4318 case StyleDisplayInside::FlowRoot: {
4319 if (aDisplay.IsInlineFlow()) {
4320 static constexpr FrameConstructionData data(
4321 &nsCSSFrameConstructor::ConstructInline,
4322 FCDATA_IS_INLINE | FCDATA_IS_LINE_PARTICIPANT);
4323 return &data;
4326 // If the frame is a block-level frame and is scrollable, then wrap it in
4327 // a scroll frame. Except we don't want to do that for paginated contexts
4328 // for frames that are block-outside and aren't frames for native
4329 // anonymous stuff.
4330 // XXX Ignore tables for the time being (except caption)
4331 const uint32_t kCaptionCtorFlags =
4332 FCDATA_IS_TABLE_PART | FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable);
4333 const bool caption = aDisplay.mDisplay == StyleDisplay::TableCaption;
4334 const bool needScrollFrame =
4335 aDisplay.IsScrollableOverflow() && !propagatedScrollToViewport;
4336 if (needScrollFrame) {
4337 const bool suppressScrollFrame =
4338 mPresShell->GetPresContext()->IsPaginated() &&
4339 aDisplay.IsBlockOutsideStyle() &&
4340 !aElement.IsInNativeAnonymousSubtree();
4341 if (!suppressScrollFrame) {
4342 static constexpr FrameConstructionData sScrollableBlockData[2] = {
4343 {&nsCSSFrameConstructor::ConstructScrollableBlock},
4344 {&nsCSSFrameConstructor::ConstructScrollableBlock,
4345 kCaptionCtorFlags}};
4346 return &sScrollableBlockData[caption];
4350 // Handle various non-scrollable blocks.
4351 static constexpr FrameConstructionData sNonScrollableBlockData[2] = {
4352 {&nsCSSFrameConstructor::ConstructNonScrollableBlock},
4353 {&nsCSSFrameConstructor::ConstructNonScrollableBlock,
4354 kCaptionCtorFlags}};
4355 return &sNonScrollableBlockData[caption];
4357 case StyleDisplayInside::Table: {
4358 static constexpr FrameConstructionData data(
4359 &nsCSSFrameConstructor::ConstructTable);
4360 return &data;
4362 // NOTE: In the unlikely event that we add another table-part here that
4363 // has a desired-parent-type (& hence triggers table fixup), we'll need to
4364 // also update the flexbox chunk in ComputedStyle::ApplyStyleFixups().
4365 case StyleDisplayInside::TableRowGroup: {
4366 static constexpr FrameConstructionData data(
4367 &nsCSSFrameConstructor::ConstructTableRowOrRowGroup,
4368 FCDATA_IS_TABLE_PART |
4369 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable));
4370 return &data;
4372 case StyleDisplayInside::TableColumn: {
4373 static constexpr FrameConstructionData data(
4374 &nsCSSFrameConstructor::ConstructTableCol,
4375 FCDATA_IS_TABLE_PART |
4376 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeColGroup));
4377 return &data;
4379 case StyleDisplayInside::TableColumnGroup: {
4380 static constexpr FrameConstructionData data(
4381 ToCreationFunc(NS_NewTableColGroupFrame),
4382 FCDATA_IS_TABLE_PART | FCDATA_DISALLOW_OUT_OF_FLOW |
4383 FCDATA_SKIP_ABSPOS_PUSH |
4384 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable));
4385 return &data;
4387 case StyleDisplayInside::TableHeaderGroup: {
4388 static constexpr FrameConstructionData data(
4389 &nsCSSFrameConstructor::ConstructTableRowOrRowGroup,
4390 FCDATA_IS_TABLE_PART |
4391 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable));
4392 return &data;
4394 case StyleDisplayInside::TableFooterGroup: {
4395 static constexpr FrameConstructionData data(
4396 &nsCSSFrameConstructor::ConstructTableRowOrRowGroup,
4397 FCDATA_IS_TABLE_PART |
4398 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable));
4399 return &data;
4401 case StyleDisplayInside::TableRow: {
4402 static constexpr FrameConstructionData data(
4403 &nsCSSFrameConstructor::ConstructTableRowOrRowGroup,
4404 FCDATA_IS_TABLE_PART |
4405 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRowGroup));
4406 return &data;
4408 case StyleDisplayInside::TableCell: {
4409 static constexpr FrameConstructionData data(
4410 &nsCSSFrameConstructor::ConstructTableCell,
4411 FCDATA_IS_TABLE_PART | FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRow));
4412 return &data;
4414 case StyleDisplayInside::Flex:
4415 case StyleDisplayInside::WebkitBox: {
4416 static constexpr FrameConstructionData nonScrollableData(
4417 ToCreationFunc(NS_NewFlexContainerFrame));
4418 static constexpr FrameConstructionData data(
4419 ToCreationFunc(NS_NewFlexContainerFrame),
4420 FCDATA_MAY_NEED_SCROLLFRAME);
4421 return MOZ_UNLIKELY(propagatedScrollToViewport) ? &nonScrollableData
4422 : &data;
4424 case StyleDisplayInside::Grid: {
4425 static constexpr FrameConstructionData nonScrollableData(
4426 ToCreationFunc(NS_NewGridContainerFrame));
4427 static constexpr FrameConstructionData data(
4428 ToCreationFunc(NS_NewGridContainerFrame),
4429 FCDATA_MAY_NEED_SCROLLFRAME);
4430 return MOZ_UNLIKELY(propagatedScrollToViewport) ? &nonScrollableData
4431 : &data;
4433 case StyleDisplayInside::Ruby: {
4434 static constexpr FrameConstructionData data[] = {
4435 {&nsCSSFrameConstructor::ConstructBlockRubyFrame,
4436 FCDATA_MAY_NEED_SCROLLFRAME},
4437 {ToCreationFunc(NS_NewRubyFrame), FCDATA_IS_LINE_PARTICIPANT}};
4438 bool isInline = aDisplay.DisplayOutside() == StyleDisplayOutside::Inline;
4439 return &data[isInline];
4441 case StyleDisplayInside::RubyBase: {
4442 static constexpr FrameConstructionData data(
4443 ToCreationFunc(NS_NewRubyBaseFrame),
4444 FCDATA_IS_LINE_PARTICIPANT |
4445 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRubyBaseContainer));
4446 return &data;
4448 case StyleDisplayInside::RubyBaseContainer: {
4449 static constexpr FrameConstructionData data(
4450 ToCreationFunc(NS_NewRubyBaseContainerFrame),
4451 FCDATA_IS_LINE_PARTICIPANT |
4452 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRuby));
4453 return &data;
4455 case StyleDisplayInside::RubyText: {
4456 static constexpr FrameConstructionData data(
4457 ToCreationFunc(NS_NewRubyTextFrame),
4458 FCDATA_IS_LINE_PARTICIPANT |
4459 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRubyTextContainer));
4460 return &data;
4462 case StyleDisplayInside::RubyTextContainer: {
4463 static constexpr FrameConstructionData data(
4464 ToCreationFunc(NS_NewRubyTextContainerFrame),
4465 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRuby));
4466 return &data;
4468 default:
4469 MOZ_ASSERT_UNREACHABLE("unknown 'display' value");
4470 return nullptr;
4474 nsIFrame* nsCSSFrameConstructor::ConstructScrollableBlock(
4475 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
4476 nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay,
4477 nsFrameList& aFrameList) {
4478 nsIContent* const content = aItem.mContent;
4479 ComputedStyle* const computedStyle = aItem.mComputedStyle;
4481 nsContainerFrame* newFrame = nullptr;
4482 RefPtr<ComputedStyle> scrolledContentStyle = BeginBuildingScrollFrame(
4483 aState, content, computedStyle,
4484 aState.GetGeometricParent(*aDisplay, aParentFrame),
4485 PseudoStyleType::scrolledContent, false, newFrame);
4487 // Create our block frame
4488 // pass a temporary stylecontext, the correct one will be set later
4489 nsContainerFrame* scrolledFrame = NS_NewBlockFrame(mPresShell, computedStyle);
4491 // Make sure to AddChild before we call ConstructBlock so that we
4492 // end up before our descendants in fixed-pos lists as needed.
4493 aState.AddChild(newFrame, aFrameList, content, aParentFrame);
4495 nsFrameList blockList;
4496 ConstructBlock(aState, content, newFrame, newFrame, scrolledContentStyle,
4497 &scrolledFrame, blockList,
4498 newFrame->IsAbsPosContainingBlock() ? newFrame : nullptr);
4500 MOZ_ASSERT(blockList.OnlyChild() == scrolledFrame,
4501 "Scrollframe's frameList should be exactly the scrolled frame!");
4502 FinishBuildingScrollFrame(newFrame, scrolledFrame);
4504 return newFrame;
4507 nsIFrame* nsCSSFrameConstructor::ConstructNonScrollableBlock(
4508 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
4509 nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay,
4510 nsFrameList& aFrameList) {
4511 ComputedStyle* const computedStyle = aItem.mComputedStyle;
4512 nsContainerFrame* newFrame = NS_NewBlockFrame(mPresShell, computedStyle);
4513 ConstructBlock(aState, aItem.mContent,
4514 aState.GetGeometricParent(*aDisplay, aParentFrame),
4515 aParentFrame, computedStyle, &newFrame, aFrameList,
4516 newFrame->IsAbsPosContainingBlock() ? newFrame : nullptr);
4517 return newFrame;
4520 void nsCSSFrameConstructor::InitAndRestoreFrame(
4521 const nsFrameConstructorState& aState, nsIContent* aContent,
4522 nsContainerFrame* aParentFrame, nsIFrame* aNewFrame, bool aAllowCounters) {
4523 MOZ_ASSERT(aNewFrame, "Null frame cannot be initialized");
4525 // Initialize the frame
4526 aNewFrame->Init(aContent, aParentFrame, nullptr);
4527 aNewFrame->AddStateBits(aState.mAdditionalStateBits);
4529 if (aState.mFrameState) {
4530 // Restore frame state for just the newly created frame.
4531 RestoreFrameStateFor(aNewFrame, aState.mFrameState);
4534 if (aAllowCounters &&
4535 mContainStyleScopeManager.AddCounterChanges(aNewFrame)) {
4536 CountersDirty();
4540 already_AddRefed<ComputedStyle> nsCSSFrameConstructor::ResolveComputedStyle(
4541 nsIContent* aContent) {
4542 if (auto* element = Element::FromNode(aContent)) {
4543 return ServoStyleSet::ResolveServoStyle(*element);
4546 MOZ_ASSERT(aContent->IsText(),
4547 "shouldn't waste time creating ComputedStyles for "
4548 "comments and processing instructions");
4550 Element* parent = aContent->GetFlattenedTreeParentElement();
4551 MOZ_ASSERT(parent, "Text out of the flattened tree?");
4553 // FIXME(emilio): The const_cast is unfortunate, but it's not worse than what
4554 // we did before.
4556 // We could use ResolveServoStyle, but that would involve extra unnecessary
4557 // refcount traffic...
4558 auto* parentStyle =
4559 const_cast<ComputedStyle*>(Servo_Element_GetMaybeOutOfDateStyle(parent));
4560 MOZ_ASSERT(parentStyle,
4561 "How are we inserting text frames in an unstyled element?");
4562 return mPresShell->StyleSet()->ResolveStyleForText(aContent, parentStyle);
4565 // MathML Mod - RBS
4566 void nsCSSFrameConstructor::FlushAccumulatedBlock(
4567 nsFrameConstructorState& aState, nsIContent* aContent,
4568 nsContainerFrame* aParentFrame, nsFrameList& aBlockList,
4569 nsFrameList& aNewList) {
4570 if (aBlockList.IsEmpty()) {
4571 // Nothing to do
4572 return;
4575 auto anonPseudo = PseudoStyleType::mozMathMLAnonymousBlock;
4577 ComputedStyle* parentContext =
4578 nsIFrame::CorrectStyleParentFrame(aParentFrame, anonPseudo)->Style();
4579 ServoStyleSet* styleSet = mPresShell->StyleSet();
4580 RefPtr<ComputedStyle> blockContext =
4581 styleSet->ResolveInheritingAnonymousBoxStyle(anonPseudo, parentContext);
4583 // then, create a block frame that will wrap the child frames. Make it a
4584 // MathML frame so that Get(Absolute/Float)ContainingBlockFor know that this
4585 // is not a suitable block.
4586 nsContainerFrame* blockFrame =
4587 NS_NewMathMLmathBlockFrame(mPresShell, blockContext);
4589 InitAndRestoreFrame(aState, aContent, aParentFrame, blockFrame);
4590 ReparentFrames(this, blockFrame, aBlockList, false);
4591 // We have to walk over aBlockList before we hand it over to blockFrame.
4592 for (nsIFrame* f : aBlockList) {
4593 f->SetParentIsWrapperAnonBox();
4595 // abs-pos and floats are disabled in MathML children so we don't have to
4596 // worry about messing up those.
4597 blockFrame->SetInitialChildList(FrameChildListID::Principal,
4598 std::move(aBlockList));
4599 aNewList.AppendFrame(nullptr, blockFrame);
4602 // Only <math> elements can be floated or positioned. All other MathML
4603 // should be in-flow.
4604 #define SIMPLE_MATHML_CREATE(_tag, _func) \
4606 nsGkAtoms::_tag, { \
4607 _func, FCDATA_DISALLOW_OUT_OF_FLOW | \
4608 FCDATA_FORCE_NULL_ABSPOS_CONTAINER | \
4609 FCDATA_WRAP_KIDS_IN_BLOCKS \
4613 /* static */
4614 const nsCSSFrameConstructor::FrameConstructionData*
4615 nsCSSFrameConstructor::FindMathMLData(const Element& aElement,
4616 ComputedStyle& aStyle) {
4617 MOZ_ASSERT(aElement.IsMathMLElement());
4619 nsAtom* tag = aElement.NodeInfo()->NameAtom();
4621 // Handle <math> specially, because it sometimes produces inlines
4622 if (tag == nsGkAtoms::math) {
4623 // The IsBlockOutsideStyle() check must match what
4624 // specified::Display::equivalent_block_display is checking for
4625 // already-block-outside things. Though the behavior here for the
4626 // display:table case is pretty weird...
4627 if (aStyle.StyleDisplay()->IsBlockOutsideStyle()) {
4628 static constexpr FrameConstructionData sBlockMathData(
4629 ToCreationFunc(NS_NewMathMLmathBlockFrame),
4630 FCDATA_FORCE_NULL_ABSPOS_CONTAINER | FCDATA_WRAP_KIDS_IN_BLOCKS);
4631 return &sBlockMathData;
4634 static constexpr FrameConstructionData sInlineMathData(
4635 ToCreationFunc(NS_NewMathMLmathInlineFrame),
4636 FCDATA_FORCE_NULL_ABSPOS_CONTAINER | FCDATA_IS_LINE_PARTICIPANT |
4637 FCDATA_WRAP_KIDS_IN_BLOCKS);
4638 return &sInlineMathData;
4641 static constexpr FrameConstructionDataByTag sMathMLData[] = {
4642 SIMPLE_MATHML_CREATE(annotation_, NS_NewMathMLTokenFrame),
4643 SIMPLE_MATHML_CREATE(annotation_xml_, NS_NewMathMLmrowFrame),
4644 SIMPLE_MATHML_CREATE(mi_, NS_NewMathMLTokenFrame),
4645 SIMPLE_MATHML_CREATE(mn_, NS_NewMathMLTokenFrame),
4646 SIMPLE_MATHML_CREATE(ms_, NS_NewMathMLTokenFrame),
4647 SIMPLE_MATHML_CREATE(mtext_, NS_NewMathMLTokenFrame),
4648 SIMPLE_MATHML_CREATE(mo_, NS_NewMathMLmoFrame),
4649 SIMPLE_MATHML_CREATE(mfrac_, NS_NewMathMLmfracFrame),
4650 SIMPLE_MATHML_CREATE(msup_, NS_NewMathMLmmultiscriptsFrame),
4651 SIMPLE_MATHML_CREATE(msub_, NS_NewMathMLmmultiscriptsFrame),
4652 SIMPLE_MATHML_CREATE(msubsup_, NS_NewMathMLmmultiscriptsFrame),
4653 SIMPLE_MATHML_CREATE(munder_, NS_NewMathMLmunderoverFrame),
4654 SIMPLE_MATHML_CREATE(mover_, NS_NewMathMLmunderoverFrame),
4655 SIMPLE_MATHML_CREATE(munderover_, NS_NewMathMLmunderoverFrame),
4656 SIMPLE_MATHML_CREATE(mphantom_, NS_NewMathMLmrowFrame),
4657 SIMPLE_MATHML_CREATE(mpadded_, NS_NewMathMLmpaddedFrame),
4658 SIMPLE_MATHML_CREATE(mspace_, NS_NewMathMLmspaceFrame),
4659 SIMPLE_MATHML_CREATE(none, NS_NewMathMLmrowFrame),
4660 SIMPLE_MATHML_CREATE(mprescripts_, NS_NewMathMLmrowFrame),
4661 SIMPLE_MATHML_CREATE(mfenced_, NS_NewMathMLmrowFrame),
4662 SIMPLE_MATHML_CREATE(mmultiscripts_, NS_NewMathMLmmultiscriptsFrame),
4663 SIMPLE_MATHML_CREATE(mstyle_, NS_NewMathMLmrowFrame),
4664 SIMPLE_MATHML_CREATE(msqrt_, NS_NewMathMLmsqrtFrame),
4665 SIMPLE_MATHML_CREATE(mroot_, NS_NewMathMLmrootFrame),
4666 SIMPLE_MATHML_CREATE(maction_, NS_NewMathMLmrowFrame),
4667 SIMPLE_MATHML_CREATE(mrow_, NS_NewMathMLmrowFrame),
4668 SIMPLE_MATHML_CREATE(merror_, NS_NewMathMLmrowFrame),
4669 SIMPLE_MATHML_CREATE(menclose_, NS_NewMathMLmencloseFrame),
4670 SIMPLE_MATHML_CREATE(semantics_, NS_NewMathMLmrowFrame)};
4672 return FindDataByTag(aElement, aStyle, sMathMLData, ArrayLength(sMathMLData));
4675 nsContainerFrame* nsCSSFrameConstructor::ConstructFrameWithAnonymousChild(
4676 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
4677 nsContainerFrame* aParentFrame, nsFrameList& aFrameList,
4678 ContainerFrameCreationFunc aConstructor,
4679 ContainerFrameCreationFunc aInnerConstructor, PseudoStyleType aInnerPseudo,
4680 bool aCandidateRootFrame) {
4681 nsIContent* const content = aItem.mContent;
4682 ComputedStyle* const computedStyle = aItem.mComputedStyle;
4684 // Create the outer frame:
4685 nsContainerFrame* newFrame = aConstructor(mPresShell, computedStyle);
4687 InitAndRestoreFrame(aState, content,
4688 aCandidateRootFrame
4689 ? aState.GetGeometricParent(
4690 *computedStyle->StyleDisplay(), aParentFrame)
4691 : aParentFrame,
4692 newFrame);
4693 newFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
4695 // Create the pseudo SC for the anonymous wrapper child as a child of the SC:
4696 RefPtr<ComputedStyle> scForAnon =
4697 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(aInnerPseudo,
4698 computedStyle);
4700 // Create the anonymous inner wrapper frame
4701 nsContainerFrame* innerFrame = aInnerConstructor(mPresShell, scForAnon);
4703 InitAndRestoreFrame(aState, content, newFrame, innerFrame);
4705 // Put the newly created frames into the right child list
4706 SetInitialSingleChild(newFrame, innerFrame);
4708 aState.AddChild(newFrame, aFrameList, content, aParentFrame,
4709 aCandidateRootFrame, aCandidateRootFrame);
4711 if (!mRootElementFrame && aCandidateRootFrame) {
4712 mRootElementFrame = newFrame;
4715 nsFrameConstructorSaveState floatSaveState;
4716 aState.MaybePushFloatContainingBlock(innerFrame, floatSaveState);
4718 nsFrameList childList;
4720 // Process children
4721 if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) {
4722 ConstructFramesFromItemList(
4723 aState, aItem.mChildItems, innerFrame,
4724 aItem.mFCData->mBits & FCDATA_IS_WRAPPER_ANON_BOX, childList);
4725 } else {
4726 ProcessChildren(aState, content, computedStyle, innerFrame, true, childList,
4727 false);
4730 // Set the inner wrapper frame's initial primary list
4731 innerFrame->SetInitialChildList(FrameChildListID::Principal,
4732 std::move(childList));
4734 return newFrame;
4737 nsIFrame* nsCSSFrameConstructor::ConstructOuterSVG(
4738 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
4739 nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay,
4740 nsFrameList& aFrameList) {
4741 return ConstructFrameWithAnonymousChild(
4742 aState, aItem, aParentFrame, aFrameList, NS_NewSVGOuterSVGFrame,
4743 NS_NewSVGOuterSVGAnonChildFrame, PseudoStyleType::mozSVGOuterSVGAnonChild,
4744 true);
4747 nsIFrame* nsCSSFrameConstructor::ConstructMarker(
4748 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
4749 nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay,
4750 nsFrameList& aFrameList) {
4751 return ConstructFrameWithAnonymousChild(
4752 aState, aItem, aParentFrame, aFrameList, NS_NewSVGMarkerFrame,
4753 NS_NewSVGMarkerAnonChildFrame, PseudoStyleType::mozSVGMarkerAnonChild,
4754 false);
4757 // Only outer <svg> elements can be floated or positioned. All other SVG
4758 // should be in-flow.
4759 #define SIMPLE_SVG_FCDATA(_func) \
4760 FrameConstructionData(ToCreationFunc(_func), \
4761 FCDATA_DISALLOW_OUT_OF_FLOW | \
4762 FCDATA_SKIP_ABSPOS_PUSH | \
4763 FCDATA_DISALLOW_GENERATED_CONTENT)
4764 #define SIMPLE_SVG_CREATE(_tag, _func) \
4765 { nsGkAtoms::_tag, SIMPLE_SVG_FCDATA(_func) }
4767 /* static */
4768 const nsCSSFrameConstructor::FrameConstructionData*
4769 nsCSSFrameConstructor::FindSVGData(const Element& aElement,
4770 nsIFrame* aParentFrame,
4771 bool aIsWithinSVGText,
4772 bool aAllowsTextPathChild,
4773 ComputedStyle& aStyle) {
4774 MOZ_ASSERT(aElement.IsSVGElement());
4776 static constexpr FrameConstructionData sSuppressData = SUPPRESS_FCDATA();
4777 static constexpr FrameConstructionData sContainerData =
4778 SIMPLE_SVG_FCDATA(NS_NewSVGContainerFrame);
4780 bool parentIsSVG = aIsWithinSVGText;
4781 nsIContent* parentContent =
4782 aParentFrame ? aParentFrame->GetContent() : nullptr;
4784 nsAtom* tag = aElement.NodeInfo()->NameAtom();
4786 // XXXbz should this really be based on the tag of the parent frame's content?
4787 // Should it not be based on the type of the parent frame (e.g. whether it's
4788 // an SVG frame)?
4789 if (parentContent) {
4790 // It's not clear whether the SVG spec intends to allow any SVG
4791 // content within svg:foreignObject at all (SVG 1.1, section
4792 // 23.2), but if it does, it better be svg:svg. So given that
4793 // we're allowing it, treat it as a non-SVG parent.
4794 parentIsSVG =
4795 parentContent->IsSVGElement() &&
4796 parentContent->NodeInfo()->NameAtom() != nsGkAtoms::foreignObject;
4799 if ((tag != nsGkAtoms::svg && !parentIsSVG) ||
4800 (tag == nsGkAtoms::desc || tag == nsGkAtoms::title ||
4801 tag == nsGkAtoms::metadata)) {
4802 // Sections 5.1 and G.4 of SVG 1.1 say that SVG elements other than
4803 // svg:svg not contained within svg:svg are incorrect, although they
4804 // don't seem to specify error handling. Ignore them, since many of
4805 // our frame classes can't deal. It *may* be that the document
4806 // should at that point be considered in error according to F.2, but
4807 // it's hard to tell.
4809 // Style mutation can't change this situation, so don't bother
4810 // adding to the undisplayed content map.
4812 // We don't currently handle any UI for desc/title/metadata
4813 return &sSuppressData;
4816 // We don't need frames for animation elements
4817 if (aElement.IsSVGAnimationElement()) {
4818 return &sSuppressData;
4821 if (tag == nsGkAtoms::svg && !parentIsSVG) {
4822 // We need outer <svg> elements to have an SVGOuterSVGFrame regardless
4823 // of whether they fail conditional processing attributes, since various
4824 // SVG frames assume that one exists. We handle the non-rendering
4825 // of failing outer <svg> element contents like <switch> statements,
4826 // and do the PassesConditionalProcessingTests call in
4827 // SVGOuterSVGFrame::Init.
4828 static constexpr FrameConstructionData sOuterSVGData(
4829 &nsCSSFrameConstructor::ConstructOuterSVG);
4830 return &sOuterSVGData;
4833 if (tag == nsGkAtoms::marker) {
4834 static constexpr FrameConstructionData sMarkerSVGData(
4835 &nsCSSFrameConstructor::ConstructMarker);
4836 return &sMarkerSVGData;
4839 if (!aElement.PassesConditionalProcessingTests()) {
4840 // Elements with failing conditional processing attributes never get
4841 // rendered. Note that this is not where we select which frame in a
4842 // <switch> to render! That happens in SVGSwitchFrame::PaintSVG.
4843 if (aIsWithinSVGText) {
4844 // SVGTextFrame doesn't handle conditional processing attributes,
4845 // so don't create frames for descendants of <text> with failing
4846 // attributes. We need frames not to be created so that text layout
4847 // is correct.
4848 return &sSuppressData;
4850 // If we're not inside <text>, create an SVGContainerFrame (which is a
4851 // frame that doesn't render) so that paint servers can still be referenced,
4852 // even if they live inside an element with failing conditional processing
4853 // attributes.
4854 return &sContainerData;
4857 // Ensure that a stop frame is a child of a gradient and that gradients
4858 // can only have stop children.
4859 bool parentIsGradient = aParentFrame && static_cast<SVGGradientFrame*>(
4860 do_QueryFrame(aParentFrame));
4861 bool stop = (tag == nsGkAtoms::stop);
4862 if ((parentIsGradient && !stop) || (!parentIsGradient && stop)) {
4863 return &sSuppressData;
4866 // Prevent bad frame types being children of filters or parents of filter
4867 // primitives. If aParentFrame is null, we know that the frame that will
4868 // be created will be an nsInlineFrame, so it can never be a filter.
4869 bool parentIsFilter = aParentFrame && aParentFrame->IsSVGFilterFrame();
4870 if ((parentIsFilter && !aElement.IsSVGFilterPrimitiveElement()) ||
4871 (!parentIsFilter && aElement.IsSVGFilterPrimitiveElement())) {
4872 return &sSuppressData;
4875 // Prevent bad frame types being children of filter primitives or parents of
4876 // filter primitive children. If aParentFrame is null, we know that the frame
4877 // that will be created will be an nsInlineFrame, so it can never be a filter
4878 // primitive.
4879 bool parentIsFEContainerFrame =
4880 aParentFrame && aParentFrame->IsSVGFEContainerFrame();
4881 if ((parentIsFEContainerFrame &&
4882 !aElement.IsSVGFilterPrimitiveChildElement()) ||
4883 (!parentIsFEContainerFrame &&
4884 aElement.IsSVGFilterPrimitiveChildElement())) {
4885 return &sSuppressData;
4888 // Special cases for text/tspan/textPath, because the kind of frame
4889 // they get depends on the parent frame. We ignore 'a' elements when
4890 // determining the parent, however.
4891 if (aIsWithinSVGText) {
4892 // If aIsWithinSVGText is true, then we know that the "SVG text uses
4893 // CSS frames" pref was true when this SVG fragment was first constructed.
4895 // FIXME(bug 1588477) Don't render stuff in display: contents / Shadow DOM
4896 // subtrees, because TextCorrespondenceRecorder in the SVG text code doesn't
4897 // really know how to deal with it. This kinda sucks. :(
4898 if (aParentFrame && aParentFrame->GetContent() != aElement.GetParent()) {
4899 return &sSuppressData;
4902 // We don't use ConstructInline because we want different behavior
4903 // for generated content.
4904 static constexpr FrameConstructionData sTSpanData(
4905 ToCreationFunc(NS_NewInlineFrame),
4906 FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_SKIP_ABSPOS_PUSH |
4907 FCDATA_DISALLOW_GENERATED_CONTENT | FCDATA_IS_LINE_PARTICIPANT |
4908 FCDATA_IS_INLINE | FCDATA_USE_CHILD_ITEMS);
4909 if (tag == nsGkAtoms::textPath) {
4910 if (aAllowsTextPathChild) {
4911 return &sTSpanData;
4913 } else if (tag == nsGkAtoms::tspan || tag == nsGkAtoms::a) {
4914 return &sTSpanData;
4916 return &sSuppressData;
4917 } else if (tag == nsGkAtoms::tspan || tag == nsGkAtoms::textPath) {
4918 return &sSuppressData;
4921 static constexpr FrameConstructionDataByTag sSVGData[] = {
4922 SIMPLE_SVG_CREATE(svg, NS_NewSVGInnerSVGFrame),
4923 SIMPLE_SVG_CREATE(g, NS_NewSVGGFrame),
4924 SIMPLE_SVG_CREATE(svgSwitch, NS_NewSVGSwitchFrame),
4925 SIMPLE_SVG_CREATE(symbol, NS_NewSVGSymbolFrame),
4926 SIMPLE_SVG_CREATE(polygon, NS_NewSVGGeometryFrame),
4927 SIMPLE_SVG_CREATE(polyline, NS_NewSVGGeometryFrame),
4928 SIMPLE_SVG_CREATE(circle, NS_NewSVGGeometryFrame),
4929 SIMPLE_SVG_CREATE(ellipse, NS_NewSVGGeometryFrame),
4930 SIMPLE_SVG_CREATE(line, NS_NewSVGGeometryFrame),
4931 SIMPLE_SVG_CREATE(rect, NS_NewSVGGeometryFrame),
4932 SIMPLE_SVG_CREATE(path, NS_NewSVGGeometryFrame),
4933 SIMPLE_SVG_CREATE(defs, NS_NewSVGContainerFrame),
4934 {nsGkAtoms::text,
4935 {NS_NewSVGTextFrame,
4936 FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_ALLOW_BLOCK_STYLES,
4937 PseudoStyleType::mozSVGText}},
4938 {nsGkAtoms::foreignObject,
4939 {ToCreationFunc(NS_NewSVGForeignObjectFrame),
4940 FCDATA_DISALLOW_OUT_OF_FLOW, PseudoStyleType::mozSVGForeignContent}},
4941 SIMPLE_SVG_CREATE(a, NS_NewSVGAFrame),
4942 SIMPLE_SVG_CREATE(linearGradient, NS_NewSVGLinearGradientFrame),
4943 SIMPLE_SVG_CREATE(radialGradient, NS_NewSVGRadialGradientFrame),
4944 SIMPLE_SVG_CREATE(stop, NS_NewSVGStopFrame),
4945 SIMPLE_SVG_CREATE(use, NS_NewSVGUseFrame),
4946 SIMPLE_SVG_CREATE(view, NS_NewSVGViewFrame),
4947 SIMPLE_SVG_CREATE(image, NS_NewSVGImageFrame),
4948 SIMPLE_SVG_CREATE(clipPath, NS_NewSVGClipPathFrame),
4949 SIMPLE_SVG_CREATE(filter, NS_NewSVGFilterFrame),
4950 SIMPLE_SVG_CREATE(pattern, NS_NewSVGPatternFrame),
4951 SIMPLE_SVG_CREATE(mask, NS_NewSVGMaskFrame),
4952 SIMPLE_SVG_CREATE(feDistantLight, NS_NewSVGFEUnstyledLeafFrame),
4953 SIMPLE_SVG_CREATE(fePointLight, NS_NewSVGFEUnstyledLeafFrame),
4954 SIMPLE_SVG_CREATE(feSpotLight, NS_NewSVGFEUnstyledLeafFrame),
4955 SIMPLE_SVG_CREATE(feBlend, NS_NewSVGFELeafFrame),
4956 SIMPLE_SVG_CREATE(feColorMatrix, NS_NewSVGFELeafFrame),
4957 SIMPLE_SVG_CREATE(feFuncR, NS_NewSVGFEUnstyledLeafFrame),
4958 SIMPLE_SVG_CREATE(feFuncG, NS_NewSVGFEUnstyledLeafFrame),
4959 SIMPLE_SVG_CREATE(feFuncB, NS_NewSVGFEUnstyledLeafFrame),
4960 SIMPLE_SVG_CREATE(feFuncA, NS_NewSVGFEUnstyledLeafFrame),
4961 SIMPLE_SVG_CREATE(feComposite, NS_NewSVGFELeafFrame),
4962 SIMPLE_SVG_CREATE(feComponentTransfer, NS_NewSVGFEContainerFrame),
4963 SIMPLE_SVG_CREATE(feConvolveMatrix, NS_NewSVGFELeafFrame),
4964 SIMPLE_SVG_CREATE(feDiffuseLighting, NS_NewSVGFEContainerFrame),
4965 SIMPLE_SVG_CREATE(feDisplacementMap, NS_NewSVGFELeafFrame),
4966 SIMPLE_SVG_CREATE(feDropShadow, NS_NewSVGFELeafFrame),
4967 SIMPLE_SVG_CREATE(feFlood, NS_NewSVGFELeafFrame),
4968 SIMPLE_SVG_CREATE(feGaussianBlur, NS_NewSVGFELeafFrame),
4969 SIMPLE_SVG_CREATE(feImage, NS_NewSVGFEImageFrame),
4970 SIMPLE_SVG_CREATE(feMerge, NS_NewSVGFEContainerFrame),
4971 SIMPLE_SVG_CREATE(feMergeNode, NS_NewSVGFEUnstyledLeafFrame),
4972 SIMPLE_SVG_CREATE(feMorphology, NS_NewSVGFELeafFrame),
4973 SIMPLE_SVG_CREATE(feOffset, NS_NewSVGFELeafFrame),
4974 SIMPLE_SVG_CREATE(feSpecularLighting, NS_NewSVGFEContainerFrame),
4975 SIMPLE_SVG_CREATE(feTile, NS_NewSVGFELeafFrame),
4976 SIMPLE_SVG_CREATE(feTurbulence, NS_NewSVGFELeafFrame)};
4978 const FrameConstructionData* data =
4979 FindDataByTag(aElement, aStyle, sSVGData, ArrayLength(sSVGData));
4981 if (!data) {
4982 data = &sContainerData;
4985 return data;
4988 void nsCSSFrameConstructor::InsertPageBreakItem(
4989 nsIContent* aContent, FrameConstructionItemList& aItems,
4990 InsertPageBreakLocation location) {
4991 RefPtr<ComputedStyle> pseudoStyle =
4992 mPresShell->StyleSet()->ResolveNonInheritingAnonymousBoxStyle(
4993 PseudoStyleType::pageBreak);
4995 MOZ_ASSERT(pseudoStyle->StyleDisplay()->mDisplay == StyleDisplay::Block,
4996 "Unexpected display");
4998 static constexpr FrameConstructionData sPageBreakData(NS_NewPageBreakFrame,
4999 FCDATA_SKIP_FRAMESET);
5000 if (location == InsertPageBreakLocation::eBefore) {
5001 aItems.PrependItem(this, &sPageBreakData, aContent, pseudoStyle.forget(),
5002 true);
5003 } else {
5004 aItems.AppendItem(this, &sPageBreakData, aContent, pseudoStyle.forget(),
5005 true);
5009 bool nsCSSFrameConstructor::ShouldCreateItemsForChild(
5010 nsFrameConstructorState& aState, nsIContent* aContent,
5011 nsContainerFrame* aParentFrame) {
5012 aContent->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
5013 // XXX the GetContent() != aContent check is needed due to bug 135040.
5014 // Remove it once that's fixed.
5015 if (aContent->GetPrimaryFrame() &&
5016 aContent->GetPrimaryFrame()->GetContent() == aContent &&
5017 !aState.mCreatingExtraFrames) {
5018 MOZ_ASSERT(false,
5019 "asked to create frame construction item for a node that "
5020 "already has a frame");
5021 return false;
5024 // don't create a whitespace frame if aParent doesn't want it
5025 if (!NeedFrameFor(aState, aParentFrame, aContent)) {
5026 return false;
5029 // never create frames for comments or PIs
5030 if (aContent->IsComment() || aContent->IsProcessingInstruction()) {
5031 return false;
5034 return true;
5037 void nsCSSFrameConstructor::AddFrameConstructionItems(
5038 nsFrameConstructorState& aState, nsIContent* aContent,
5039 bool aSuppressWhiteSpaceOptimizations, const ComputedStyle& aParentStyle,
5040 const InsertionPoint& aInsertion, FrameConstructionItemList& aItems,
5041 ItemFlags aFlags) {
5042 nsContainerFrame* parentFrame = aInsertion.mParentFrame;
5043 if (!ShouldCreateItemsForChild(aState, aContent, parentFrame)) {
5044 return;
5046 if (MOZ_UNLIKELY(aParentStyle.StyleContent()->mContent.IsNone()) &&
5047 StaticPrefs::layout_css_element_content_none_enabled()) {
5048 return;
5051 RefPtr<ComputedStyle> computedStyle = ResolveComputedStyle(aContent);
5052 auto flags = aFlags + ItemFlag::AllowPageBreak;
5053 if (parentFrame) {
5054 if (parentFrame->IsInSVGTextSubtree()) {
5055 flags += ItemFlag::IsWithinSVGText;
5057 if (parentFrame->IsBlockFrame() && parentFrame->GetParent() &&
5058 parentFrame->GetParent()->IsSVGTextFrame()) {
5059 flags += ItemFlag::AllowTextPathChild;
5062 AddFrameConstructionItemsInternal(aState, aContent, parentFrame,
5063 aSuppressWhiteSpaceOptimizations,
5064 computedStyle, flags, aItems);
5067 // Whether we should suppress frames for a child under a <select> frame.
5069 // Never create frames for non-option/optgroup kids of <select> and non-option
5070 // kids of <optgroup> inside a <select>.
5071 static bool ShouldSuppressFrameInSelect(const nsIContent* aParent,
5072 const nsIContent& aChild) {
5073 if (!aParent ||
5074 !aParent->IsAnyOfHTMLElements(nsGkAtoms::select, nsGkAtoms::optgroup,
5075 nsGkAtoms::option)) {
5076 return false;
5079 // Allow native anonymous content no matter what.
5080 if (aChild.IsRootOfNativeAnonymousSubtree()) {
5081 return false;
5084 // Options with labels have their label text added in ::before by forms.css.
5085 // Suppress frames for their child text.
5086 if (aParent->IsHTMLElement(nsGkAtoms::option)) {
5087 return aParent->AsElement()->HasNonEmptyAttr(nsGkAtoms::label);
5090 // If we're in any display: contents subtree, just suppress the frame.
5092 // We can't be regular NAC, since display: contents has no frame to generate
5093 // them off.
5094 if (aChild.GetParent() != aParent) {
5095 return true;
5098 // Option is always fine.
5099 if (aChild.IsHTMLElement(nsGkAtoms::option)) {
5100 return false;
5103 // <optgroup> is OK in <select> but not in <optgroup>.
5104 if (aChild.IsHTMLElement(nsGkAtoms::optgroup) &&
5105 aParent->IsHTMLElement(nsGkAtoms::select)) {
5106 return false;
5109 // Anything else is not ok.
5110 return true;
5113 const nsCSSFrameConstructor::FrameConstructionData*
5114 nsCSSFrameConstructor::FindDataForContent(nsIContent& aContent,
5115 ComputedStyle& aStyle,
5116 nsIFrame* aParentFrame,
5117 ItemFlags aFlags) {
5118 MOZ_ASSERT(aStyle.StyleDisplay()->mDisplay != StyleDisplay::None &&
5119 aStyle.StyleDisplay()->mDisplay != StyleDisplay::Contents,
5120 "These two special display values should be handled earlier");
5122 if (auto* text = Text::FromNode(aContent)) {
5123 return FindTextData(*text, aParentFrame);
5126 return FindElementData(*aContent.AsElement(), aStyle, aParentFrame, aFlags);
5129 const nsCSSFrameConstructor::FrameConstructionData*
5130 nsCSSFrameConstructor::FindElementData(const Element& aElement,
5131 ComputedStyle& aStyle,
5132 nsIFrame* aParentFrame,
5133 ItemFlags aFlags) {
5134 // Don't create frames for non-SVG element children of SVG elements.
5135 if (!aElement.IsSVGElement()) {
5136 if (aParentFrame && IsFrameForSVG(aParentFrame) &&
5137 !aParentFrame->IsSVGForeignObjectFrame()) {
5138 return nullptr;
5140 if (aFlags.contains(ItemFlag::IsWithinSVGText)) {
5141 return nullptr;
5145 if (auto* data = FindElementTagData(aElement, aStyle, aParentFrame, aFlags)) {
5146 return data;
5149 // Check for 'content: <image-url>' on the element (which makes us ignore
5150 // 'display' values other than 'none' or 'contents').
5151 if (nsImageFrame::ShouldCreateImageFrameForContentProperty(aElement,
5152 aStyle)) {
5153 static constexpr FrameConstructionData sImgData(
5154 NS_NewImageFrameForContentProperty);
5155 return &sImgData;
5158 const bool shouldBlockify = aFlags.contains(ItemFlag::IsForRenderedLegend) ||
5159 aFlags.contains(ItemFlag::IsForOutsideMarker);
5160 if (shouldBlockify && !aStyle.StyleDisplay()->IsBlockOutsideStyle()) {
5161 // Make a temp copy of StyleDisplay and blockify its mDisplay value.
5162 auto display = *aStyle.StyleDisplay();
5163 bool isRootElement = false;
5164 uint16_t rawDisplayValue =
5165 Servo_ComputedValues_BlockifiedDisplay(&aStyle, isRootElement);
5166 display.mDisplay = StyleDisplay{rawDisplayValue};
5167 return FindDisplayData(display, aElement);
5170 const auto& display = *aStyle.StyleDisplay();
5171 return FindDisplayData(display, aElement);
5174 const nsCSSFrameConstructor::FrameConstructionData*
5175 nsCSSFrameConstructor::FindElementTagData(const Element& aElement,
5176 ComputedStyle& aStyle,
5177 nsIFrame* aParentFrame,
5178 ItemFlags aFlags) {
5179 switch (aElement.GetNameSpaceID()) {
5180 case kNameSpaceID_XHTML:
5181 return FindHTMLData(aElement, aParentFrame, aStyle);
5182 case kNameSpaceID_MathML:
5183 return FindMathMLData(aElement, aStyle);
5184 case kNameSpaceID_SVG:
5185 return FindSVGData(aElement, aParentFrame,
5186 aFlags.contains(ItemFlag::IsWithinSVGText),
5187 aFlags.contains(ItemFlag::AllowTextPathChild), aStyle);
5188 case kNameSpaceID_XUL:
5189 return FindXULTagData(aElement, aStyle);
5190 default:
5191 return nullptr;
5195 void nsCSSFrameConstructor::AddFrameConstructionItemsInternal(
5196 nsFrameConstructorState& aState, nsIContent* aContent,
5197 nsContainerFrame* aParentFrame, bool aSuppressWhiteSpaceOptimizations,
5198 ComputedStyle* aComputedStyle, ItemFlags aFlags,
5199 FrameConstructionItemList& aItems) {
5200 MOZ_ASSERT(aContent->IsText() || aContent->IsElement(),
5201 "Shouldn't get anything else here!");
5202 MOZ_ASSERT(aContent->IsInComposedDoc());
5203 MOZ_ASSERT(!aContent->GetPrimaryFrame() || aState.mCreatingExtraFrames ||
5204 aContent->NodeInfo()->NameAtom() == nsGkAtoms::area);
5206 const bool withinSVGText = aFlags.contains(ItemFlag::IsWithinSVGText);
5207 const bool isGeneratedContent = aFlags.contains(ItemFlag::IsGeneratedContent);
5208 MOZ_ASSERT(!isGeneratedContent || aComputedStyle->IsPseudoElement(),
5209 "Generated content should be a pseudo-element");
5211 FrameConstructionItem* item = nullptr;
5212 auto cleanupGeneratedContent = mozilla::MakeScopeExit([&]() {
5213 if (isGeneratedContent && !item) {
5214 MOZ_ASSERT(!IsDisplayContents(aContent),
5215 "This would need to change if we support display: contents "
5216 "in generated content");
5217 aContent->UnbindFromTree();
5221 // 'display:none' elements never creates any frames at all.
5222 const nsStyleDisplay& display = *aComputedStyle->StyleDisplay();
5223 if (display.mDisplay == StyleDisplay::None) {
5224 return;
5227 if (display.mDisplay == StyleDisplay::Contents) {
5228 // See the mDisplay fixup code in StyleAdjuster::adjust.
5229 MOZ_ASSERT(!aContent->AsElement()->IsRootOfNativeAnonymousSubtree(),
5230 "display:contents on anonymous content is unsupported");
5232 // FIXME(bug 1588477): <svg:text>'s TextNodeCorrespondenceRecorder has
5233 // trouble with everything that looks like display: contents.
5234 if (withinSVGText) {
5235 return;
5238 CreateGeneratedContentItem(aState, aParentFrame, *aContent->AsElement(),
5239 *aComputedStyle, PseudoStyleType::before,
5240 aItems);
5242 FlattenedChildIterator iter(aContent);
5243 InsertionPoint insertion(aParentFrame, aContent);
5244 for (nsIContent* child = iter.GetNextChild(); child;
5245 child = iter.GetNextChild()) {
5246 AddFrameConstructionItems(aState, child, aSuppressWhiteSpaceOptimizations,
5247 *aComputedStyle, insertion, aItems, aFlags);
5249 aItems.SetParentHasNoShadowDOM(!iter.ShadowDOMInvolved());
5251 CreateGeneratedContentItem(aState, aParentFrame, *aContent->AsElement(),
5252 *aComputedStyle, PseudoStyleType::after, aItems);
5253 return;
5256 nsIContent* parent = aParentFrame ? aParentFrame->GetContent() : nullptr;
5257 if (ShouldSuppressFrameInSelect(parent, *aContent)) {
5258 return;
5261 if (aContent->IsHTMLElement(nsGkAtoms::legend) && aParentFrame) {
5262 const nsFieldSetFrame* const fs = GetFieldSetFrameFor(aParentFrame);
5263 if (fs && !fs->GetLegend() && !aState.mHasRenderedLegend &&
5264 !aComputedStyle->StyleDisplay()->IsFloatingStyle() &&
5265 !aComputedStyle->StyleDisplay()->IsAbsolutelyPositionedStyle()) {
5266 aState.mHasRenderedLegend = true;
5267 aFlags += ItemFlag::IsForRenderedLegend;
5271 const FrameConstructionData* const data =
5272 FindDataForContent(*aContent, *aComputedStyle, aParentFrame, aFlags);
5273 if (!data || data->mBits & FCDATA_SUPPRESS_FRAME) {
5274 return;
5277 const bool isPopup = data->mBits & FCDATA_IS_POPUP;
5279 const uint32_t bits = data->mBits;
5281 // Inside colgroups, suppress everything except columns.
5282 if (aParentFrame && aParentFrame->IsTableColGroupFrame() &&
5283 (!(bits & FCDATA_IS_TABLE_PART) ||
5284 display.mDisplay != StyleDisplay::TableColumn)) {
5285 return;
5288 const bool canHavePageBreak =
5289 aFlags.contains(ItemFlag::AllowPageBreak) &&
5290 aState.mPresContext->IsPaginated() &&
5291 !display.IsAbsolutelyPositionedStyle() &&
5292 !(aParentFrame && aParentFrame->IsGridContainerFrame()) &&
5293 !(bits & FCDATA_IS_TABLE_PART) && !(bits & FCDATA_IS_SVG_TEXT);
5294 if (canHavePageBreak && display.BreakBefore()) {
5295 AppendPageBreakItem(aContent, aItems);
5298 if (!item) {
5299 item = aItems.AppendItem(this, data, aContent, do_AddRef(aComputedStyle),
5300 aSuppressWhiteSpaceOptimizations);
5301 if (aFlags.contains(ItemFlag::IsForRenderedLegend)) {
5302 item->mIsRenderedLegend = true;
5305 item->mIsText = !aContent->IsElement();
5306 item->mIsGeneratedContent = isGeneratedContent;
5307 if (isGeneratedContent) {
5308 // We need to keep this alive until the frame takes ownership.
5309 // This corresponds to the Release in ConstructFramesFromItem.
5310 item->mContent->AddRef();
5312 item->mIsPopup = isPopup;
5314 if (canHavePageBreak && display.BreakAfter()) {
5315 AppendPageBreakItem(aContent, aItems);
5318 if (bits & FCDATA_IS_INLINE) {
5319 // To correctly set item->mIsAllInline we need to build up our child items
5320 // right now.
5321 BuildInlineChildItems(aState, *item,
5322 aFlags.contains(ItemFlag::IsWithinSVGText),
5323 aFlags.contains(ItemFlag::AllowTextPathChild));
5324 item->mIsBlock = false;
5325 } else {
5326 // Compute a boolean isInline which is guaranteed to be false for blocks
5327 // (but may also be false for some inlines).
5328 const bool isInline =
5329 // Table-internal things are inline-outside if and only if they're kids
5330 // of inlines, since they'll trigger construction of inline-table
5331 // pseudos.
5332 ((bits & FCDATA_IS_TABLE_PART) &&
5333 (!aParentFrame || // No aParentFrame means inline
5334 aParentFrame->StyleDisplay()->IsInlineFlow())) ||
5335 // Things that are inline-outside but aren't inline frames are inline
5336 display.IsInlineOutsideStyle() ||
5337 // Popups that are certainly out of flow.
5338 isPopup;
5340 // Set mIsAllInline conservatively. It just might be that even an inline
5341 // that has mIsAllInline false doesn't need an {ib} split. So this is just
5342 // an optimization to keep from doing too much work in cases when we can
5343 // show that mIsAllInline is true..
5344 item->mIsAllInline =
5345 isInline ||
5346 // Figure out whether we're guaranteed this item will be out of flow.
5347 // This is not a precise test, since one of our ancestor inlines might
5348 // add an absolute containing block (if it's relatively positioned) when
5349 // there wasn't such a containing block before. But it's conservative
5350 // in the sense that anything that will really end up as an in-flow
5351 // non-inline will test false here. In other words, if this test is
5352 // true we're guaranteed to be inline; if it's false we don't know what
5353 // we'll end up as.
5355 // If we make this test precise, we can remove some of the code dealing
5356 // with the imprecision in ConstructInline and adjust the comments on
5357 // mIsAllInline and mIsBlock in the header.
5358 (!(bits & FCDATA_DISALLOW_OUT_OF_FLOW) &&
5359 aState.GetGeometricParent(display, nullptr));
5361 // Set mIsBlock conservatively. It's OK to set it false for some real
5362 // blocks, but not OK to set it true for things that aren't blocks. Since
5363 // isOutOfFlow might be false even in cases when the frame will end up
5364 // out-of-flow, we can't use it here. But we _can_ say that the frame will
5365 // for sure end up in-flow if it's not floated or absolutely positioned.
5366 item->mIsBlock = !isInline && !display.IsAbsolutelyPositionedStyle() &&
5367 !display.IsFloatingStyle() && !(bits & FCDATA_IS_SVG_TEXT);
5370 if (item->mIsAllInline) {
5371 aItems.InlineItemAdded();
5372 } else if (item->mIsBlock) {
5373 aItems.BlockItemAdded();
5378 * Return true if the frame construction item pointed to by aIter will
5379 * create a frame adjacent to a line boundary in the frame tree, and that
5380 * line boundary is induced by a content node adjacent to the frame's
5381 * content node in the content tree. The latter condition is necessary so
5382 * that ContentAppended/ContentInserted/ContentRemoved can easily find any
5383 * text nodes that were suppressed here.
5385 bool nsCSSFrameConstructor::AtLineBoundary(FCItemIterator& aIter) {
5386 if (aIter.item().mSuppressWhiteSpaceOptimizations) {
5387 return false;
5390 if (aIter.AtStart()) {
5391 if (aIter.List()->HasLineBoundaryAtStart() &&
5392 !aIter.item().mContent->GetPreviousSibling())
5393 return true;
5394 } else {
5395 FCItemIterator prev = aIter;
5396 prev.Prev();
5397 if (prev.item().IsLineBoundary() &&
5398 !prev.item().mSuppressWhiteSpaceOptimizations &&
5399 aIter.item().mContent->GetPreviousSibling() == prev.item().mContent)
5400 return true;
5403 FCItemIterator next = aIter;
5404 next.Next();
5405 if (next.IsDone()) {
5406 if (aIter.List()->HasLineBoundaryAtEnd() &&
5407 !aIter.item().mContent->GetNextSibling())
5408 return true;
5409 } else {
5410 if (next.item().IsLineBoundary() &&
5411 !next.item().mSuppressWhiteSpaceOptimizations &&
5412 aIter.item().mContent->GetNextSibling() == next.item().mContent)
5413 return true;
5416 return false;
5419 void nsCSSFrameConstructor::ConstructFramesFromItem(
5420 nsFrameConstructorState& aState, FCItemIterator& aIter,
5421 nsContainerFrame* aParentFrame, nsFrameList& aFrameList) {
5422 FrameConstructionItem& item = aIter.item();
5423 ComputedStyle* computedStyle = item.mComputedStyle;
5424 if (item.mIsText) {
5425 // If this is collapsible whitespace next to a line boundary,
5426 // don't create a frame. item.IsWhitespace() also sets the
5427 // NS_CREATE_FRAME_IF_NON_WHITESPACE flag in the text node. (If we
5428 // end up creating a frame, nsTextFrame::Init will clear the flag.)
5429 // We don't do this for generated content, because some generated
5430 // text content is empty text nodes that are about to be initialized.
5431 // (We check mAdditionalStateBits because only the generated content
5432 // container's frame construction item is marked with
5433 // mIsGeneratedContent, and we might not have an aParentFrame.)
5434 // We don't do it for content that may have Shadow DOM siblings / insertion
5435 // points, because they make it difficult to correctly create the frame due
5436 // to dynamic changes.
5437 // We don't do it for SVG text, since we might need to position and
5438 // measure the white space glyphs due to x/y/dx/dy attributes.
5439 if (AtLineBoundary(aIter) &&
5440 !computedStyle->StyleText()->WhiteSpaceOrNewlineIsSignificant() &&
5441 aIter.List()->ParentHasNoShadowDOM() &&
5442 !(aState.mAdditionalStateBits & NS_FRAME_GENERATED_CONTENT) &&
5443 (item.mFCData->mBits & FCDATA_IS_LINE_PARTICIPANT) &&
5444 !(item.mFCData->mBits & FCDATA_IS_SVG_TEXT) &&
5445 !mAlwaysCreateFramesForIgnorableWhitespace && item.IsWhitespace(aState))
5446 return;
5448 ConstructTextFrame(item.mFCData, aState, item.mContent, aParentFrame,
5449 computedStyle, aFrameList);
5450 return;
5453 AutoRestore<nsFrameState> savedStateBits(aState.mAdditionalStateBits);
5454 if (item.mIsGeneratedContent) {
5455 // Ensure that frames created here are all tagged with
5456 // NS_FRAME_GENERATED_CONTENT.
5457 aState.mAdditionalStateBits |= NS_FRAME_GENERATED_CONTENT;
5460 // XXXbz maybe just inline ConstructFrameFromItemInternal here or something?
5461 ConstructFrameFromItemInternal(item, aState, aParentFrame, aFrameList);
5463 if (item.mIsGeneratedContent) {
5464 // This corresponds to the AddRef in AddFrameConstructionItemsInternal.
5465 // The frame owns the generated content now.
5466 item.mContent->Release();
5468 // Now that we've passed ownership of item.mContent to the frame, unset
5469 // our generated content flag so we don't release or unbind it ourselves.
5470 item.mIsGeneratedContent = false;
5474 nsContainerFrame* nsCSSFrameConstructor::GetAbsoluteContainingBlock(
5475 nsIFrame* aFrame, ContainingBlockType aType) {
5476 // Starting with aFrame, look for a frame that is absolutely positioned or
5477 // relatively positioned (and transformed, if aType is FIXED)
5478 for (nsIFrame* frame = aFrame; frame; frame = frame->GetParent()) {
5479 if (frame->IsMathMLFrame()) {
5480 // If it's mathml, bail out -- no absolute positioning out from inside
5481 // mathml frames. Note that we don't make this part of the loop
5482 // condition because of the stuff at the end of this method...
5483 return nullptr;
5486 // Look for the ICB.
5487 if (aType == FIXED_POS) {
5488 LayoutFrameType t = frame->Type();
5489 if (t == LayoutFrameType::Viewport || t == LayoutFrameType::PageContent) {
5490 return static_cast<nsContainerFrame*>(frame);
5494 // If the frame is positioned, we will probably return it as the containing
5495 // block (see the exceptions below). Otherwise, we'll start looking at the
5496 // parent frame, unless we're dealing with a scrollframe.
5497 // Scrollframes are special since they're not positioned, but their
5498 // scrolledframe might be. So, we need to check this special case to return
5499 // the correct containing block (the scrolledframe) in that case.
5500 // If we're looking for a fixed-pos containing block and the frame is
5501 // not transformed, skip it.
5502 if (!frame->IsAbsPosContainingBlock()) {
5503 continue;
5505 if (aType == FIXED_POS && !frame->IsFixedPosContainingBlock()) {
5506 continue;
5508 nsIFrame* absPosCBCandidate = frame;
5509 LayoutFrameType type = absPosCBCandidate->Type();
5510 if (type == LayoutFrameType::FieldSet) {
5511 absPosCBCandidate =
5512 static_cast<nsFieldSetFrame*>(absPosCBCandidate)->GetInner();
5513 if (!absPosCBCandidate) {
5514 continue;
5516 type = absPosCBCandidate->Type();
5518 if (type == LayoutFrameType::Scroll) {
5519 nsIScrollableFrame* scrollFrame = do_QueryFrame(absPosCBCandidate);
5520 absPosCBCandidate = scrollFrame->GetScrolledFrame();
5521 if (!absPosCBCandidate) {
5522 continue;
5524 type = absPosCBCandidate->Type();
5526 // Only first continuations can be containing blocks.
5527 absPosCBCandidate = absPosCBCandidate->FirstContinuation();
5528 // Is the frame really an absolute container?
5529 if (!absPosCBCandidate->IsAbsoluteContainer()) {
5530 continue;
5533 // For tables, skip the inner frame and consider the table wrapper frame.
5534 if (type == LayoutFrameType::Table) {
5535 continue;
5537 // For table wrapper frames, we can just return absPosCBCandidate.
5538 MOZ_ASSERT((nsContainerFrame*)do_QueryFrame(absPosCBCandidate),
5539 "abs.pos. containing block must be nsContainerFrame sub-class");
5540 return static_cast<nsContainerFrame*>(absPosCBCandidate);
5543 MOZ_ASSERT(aType != FIXED_POS, "no ICB in this frame tree?");
5545 // It is possible for the search for the containing block to fail, because
5546 // no absolute container can be found in the parent chain. In those cases,
5547 // we fall back to the document element's containing block.
5548 return mDocElementContainingBlock;
5551 nsContainerFrame* nsCSSFrameConstructor::GetFloatContainingBlock(
5552 nsIFrame* aFrame) {
5553 // Starting with aFrame, look for a frame that is a float containing block.
5554 // If we hit a frame which prevents its descendants from floating, bail out.
5555 // The logic here needs to match the logic in MaybePushFloatContainingBlock().
5556 for (nsIFrame* containingBlock = aFrame;
5557 containingBlock && !ShouldSuppressFloatingOfDescendants(containingBlock);
5558 containingBlock = containingBlock->GetParent()) {
5559 if (containingBlock->IsFloatContainingBlock()) {
5560 MOZ_ASSERT((nsContainerFrame*)do_QueryFrame(containingBlock),
5561 "float containing block must be nsContainerFrame sub-class");
5562 return static_cast<nsContainerFrame*>(containingBlock);
5566 // If we didn't find a containing block, then there just isn't
5567 // one.... return null
5568 return nullptr;
5572 * This function will get the previous sibling to use for an append operation.
5574 * It takes a parent frame (must not be null) and the next insertion sibling, if
5575 * the parent content is display: contents or has ::after content (may be null).
5577 static nsIFrame* FindAppendPrevSibling(nsIFrame* aParentFrame,
5578 nsIFrame* aNextSibling) {
5579 aParentFrame->DrainSelfOverflowList();
5581 if (aNextSibling) {
5582 MOZ_ASSERT(
5583 aNextSibling->GetParent()->GetContentInsertionFrame() == aParentFrame,
5584 "Wrong parent");
5585 return aNextSibling->GetPrevSibling();
5588 return aParentFrame->PrincipalChildList().LastChild();
5592 * Finds the right parent frame to append content to aParentFrame.
5594 * Cannot return or receive null.
5596 static nsContainerFrame* ContinuationToAppendTo(
5597 nsContainerFrame* aParentFrame) {
5598 MOZ_ASSERT(aParentFrame);
5600 if (IsFramePartOfIBSplit(aParentFrame)) {
5601 // If the frame we are manipulating is a ib-split frame (that is, one that's
5602 // been created as a result of a block-in-inline situation) then we need to
5603 // append to the last ib-split sibling, not to the frame itself.
5605 // Always make sure to look at the last continuation of the frame for the
5606 // {ib} case, even if that continuation is empty.
5608 // We don't do this for the non-ib-split-frame case, since in the other
5609 // cases appending to the last nonempty continuation is fine and in fact not
5610 // doing that can confuse code that doesn't know to pull kids from
5611 // continuations other than its next one.
5612 return static_cast<nsContainerFrame*>(
5613 GetLastIBSplitSibling(aParentFrame)->LastContinuation());
5616 return nsLayoutUtils::LastContinuationWithChild(aParentFrame);
5620 * This function will get the next sibling for a frame insert operation given
5621 * the parent and previous sibling. aPrevSibling may be null.
5623 static nsIFrame* GetInsertNextSibling(nsIFrame* aParentFrame,
5624 nsIFrame* aPrevSibling) {
5625 if (aPrevSibling) {
5626 return aPrevSibling->GetNextSibling();
5629 return aParentFrame->PrincipalChildList().FirstChild();
5632 void nsCSSFrameConstructor::AppendFramesToParent(
5633 nsFrameConstructorState& aState, nsContainerFrame* aParentFrame,
5634 nsFrameList& aFrameList, nsIFrame* aPrevSibling, bool aIsRecursiveCall) {
5635 MOZ_ASSERT(
5636 !IsFramePartOfIBSplit(aParentFrame) || !GetIBSplitSibling(aParentFrame) ||
5637 !GetIBSplitSibling(aParentFrame)->PrincipalChildList().FirstChild(),
5638 "aParentFrame has a ib-split sibling with kids?");
5639 MOZ_ASSERT(!aPrevSibling || aPrevSibling->GetParent() == aParentFrame,
5640 "Parent and prevsibling don't match");
5641 MOZ_ASSERT(
5642 !aParentFrame->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR) ||
5643 !IsFramePartOfIBSplit(aParentFrame),
5644 "We should have wiped aParentFrame in WipeContainingBlock() "
5645 "if it's part of an IB split!");
5647 nsIFrame* nextSibling = ::GetInsertNextSibling(aParentFrame, aPrevSibling);
5649 NS_ASSERTION(nextSibling || !aParentFrame->GetNextContinuation() ||
5650 !aParentFrame->GetNextContinuation()
5651 ->PrincipalChildList()
5652 .FirstChild() ||
5653 aIsRecursiveCall,
5654 "aParentFrame has later continuations with kids?");
5655 NS_ASSERTION(
5656 nextSibling || !IsFramePartOfIBSplit(aParentFrame) ||
5657 (IsInlineFrame(aParentFrame) && !GetIBSplitSibling(aParentFrame) &&
5658 !aParentFrame->GetNextContinuation()) ||
5659 aIsRecursiveCall,
5660 "aParentFrame is not last?");
5662 // If we're inserting a list of frames at the end of the trailing inline
5663 // of an {ib} split, we may need to create additional {ib} siblings to parent
5664 // them.
5665 if (!nextSibling && IsFramePartOfIBSplit(aParentFrame)) {
5666 // When we get here, our frame list might start with a block. If it does
5667 // so, and aParentFrame is an inline, and it and all its previous
5668 // continuations have no siblings, then put the initial blocks from the
5669 // frame list into the previous block of the {ib} split. Note that we
5670 // didn't want to stop at the block part of the split when figuring out
5671 // initial parent, because that could screw up float parenting; it's easier
5672 // to do this little fixup here instead.
5673 if (aFrameList.NotEmpty() && aFrameList.FirstChild()->IsBlockOutside()) {
5674 // See whether our trailing inline is empty
5675 nsIFrame* firstContinuation = aParentFrame->FirstContinuation();
5676 if (firstContinuation->PrincipalChildList().IsEmpty()) {
5677 // Our trailing inline is empty. Collect our starting blocks from
5678 // aFrameList, get the right parent frame for them, and put them in.
5679 nsFrameList blockKids =
5680 aFrameList.Split([](nsIFrame* f) { return !f->IsBlockOutside(); });
5681 NS_ASSERTION(blockKids.NotEmpty(), "No blocks?");
5683 nsContainerFrame* prevBlock = GetIBSplitPrevSibling(firstContinuation);
5684 prevBlock =
5685 static_cast<nsContainerFrame*>(prevBlock->LastContinuation());
5686 NS_ASSERTION(prevBlock, "Should have previous block here");
5688 MoveChildrenTo(aParentFrame, prevBlock, blockKids);
5692 // We want to put some of the frames into this inline frame.
5693 nsFrameList inlineKids =
5694 aFrameList.Split([](nsIFrame* f) { return f->IsBlockOutside(); });
5696 if (!inlineKids.IsEmpty()) {
5697 AppendFrames(aParentFrame, FrameChildListID::Principal,
5698 std::move(inlineKids));
5701 if (!aFrameList.IsEmpty()) {
5702 nsFrameList ibSiblings;
5703 CreateIBSiblings(aState, aParentFrame,
5704 aParentFrame->IsAbsPosContainingBlock(), aFrameList,
5705 ibSiblings);
5707 // Make sure to trigger reflow of the inline that used to be our
5708 // last one and now isn't anymore, since its GetSkipSides() has
5709 // changed.
5710 mPresShell->FrameNeedsReflow(aParentFrame,
5711 IntrinsicDirty::FrameAndAncestors,
5712 NS_FRAME_HAS_DIRTY_CHILDREN);
5714 // Recurse so we create new ib siblings as needed for aParentFrame's
5715 // parent
5716 return AppendFramesToParent(aState, aParentFrame->GetParent(), ibSiblings,
5717 aParentFrame, true);
5719 return;
5722 // If we're appending a list of frames to the last continuations of a
5723 // ::-moz-column-content, we may need to create column-span siblings for them.
5724 if (!nextSibling && IsLastContinuationForColumnContent(aParentFrame)) {
5725 // Extract any initial non-column-span kids, and append them to
5726 // ::-moz-column-content's child list.
5727 nsFrameList initialNonColumnSpanKids =
5728 aFrameList.Split([](nsIFrame* f) { return f->IsColumnSpan(); });
5729 AppendFrames(aParentFrame, FrameChildListID::Principal,
5730 std::move(initialNonColumnSpanKids));
5732 if (aFrameList.IsEmpty()) {
5733 // No more kids to process (there weren't any column-span kids).
5734 return;
5737 nsFrameList columnSpanSiblings = CreateColumnSpanSiblings(
5738 aState, aParentFrame, aFrameList,
5739 // Column content should never be a absolute/fixed positioned containing
5740 // block. Pass nullptr as aPositionedFrame.
5741 nullptr);
5743 nsContainerFrame* columnSetWrapper = aParentFrame->GetParent();
5744 while (!columnSetWrapper->IsColumnSetWrapperFrame()) {
5745 columnSetWrapper = columnSetWrapper->GetParent();
5747 MOZ_ASSERT(columnSetWrapper,
5748 "No ColumnSetWrapperFrame ancestor for -moz-column-content?");
5750 FinishBuildingColumns(aState, columnSetWrapper, aParentFrame,
5751 columnSpanSiblings);
5753 MOZ_ASSERT(columnSpanSiblings.IsEmpty(),
5754 "The column-span siblings should be moved to the proper place!");
5755 return;
5758 // Insert the frames after our aPrevSibling
5759 InsertFrames(aParentFrame, FrameChildListID::Principal, aPrevSibling,
5760 std::move(aFrameList));
5763 // This gets called to see if the frames corresponding to aSibling and aContent
5764 // should be siblings in the frame tree. Although (1) rows and cols, (2) row
5765 // groups and col groups, (3) row groups and captions, (4) legends and content
5766 // inside fieldsets, (5) popups and other kids of the menu are siblings from a
5767 // content perspective, they are not considered siblings in the frame tree.
5768 bool nsCSSFrameConstructor::IsValidSibling(nsIFrame* aSibling,
5769 nsIContent* aContent,
5770 Maybe<StyleDisplay>& aDisplay) {
5771 StyleDisplay siblingDisplay = aSibling->GetDisplay();
5772 if (StyleDisplay::TableColumnGroup == siblingDisplay ||
5773 StyleDisplay::TableColumn == siblingDisplay ||
5774 StyleDisplay::TableCaption == siblingDisplay ||
5775 StyleDisplay::TableHeaderGroup == siblingDisplay ||
5776 StyleDisplay::TableRowGroup == siblingDisplay ||
5777 StyleDisplay::TableFooterGroup == siblingDisplay) {
5778 // if we haven't already, resolve a style to find the display type of
5779 // aContent.
5780 if (aDisplay.isNothing()) {
5781 if (aContent->IsComment() || aContent->IsProcessingInstruction()) {
5782 // Comments and processing instructions never have frames, so we should
5783 // not try to generate styles for them.
5784 return false;
5786 // FIXME(emilio): This is buggy some times, see bug 1424656.
5787 RefPtr<ComputedStyle> computedStyle = ResolveComputedStyle(aContent);
5788 const nsStyleDisplay* display = computedStyle->StyleDisplay();
5789 aDisplay.emplace(display->mDisplay);
5792 StyleDisplay display = aDisplay.value();
5793 // To have decent performance we want to return false in cases in which
5794 // reordering the two siblings has no effect on display. To ensure
5795 // correctness, we MUST return false in cases where the two siblings have
5796 // the same desired parent type and live on different display lists.
5797 // Specificaly, columns and column groups should only consider columns and
5798 // column groups as valid siblings. Captions should only consider other
5799 // captions. All other things should consider each other as valid
5800 // siblings. The restriction in the |if| above on siblingDisplay is ok,
5801 // because for correctness the only part that really needs to happen is to
5802 // not consider captions, column groups, and row/header/footer groups
5803 // siblings of each other. Treating a column or colgroup as a valid
5804 // sibling of a non-table-related frame will just mean we end up reframing.
5805 if ((siblingDisplay == StyleDisplay::TableCaption) !=
5806 (display == StyleDisplay::TableCaption)) {
5807 // One's a caption and the other is not. Not valid siblings.
5808 return false;
5811 if ((siblingDisplay == StyleDisplay::TableColumnGroup ||
5812 siblingDisplay == StyleDisplay::TableColumn) !=
5813 (display == StyleDisplay::TableColumnGroup ||
5814 display == StyleDisplay::TableColumn)) {
5815 // One's a column or column group and the other is not. Not valid
5816 // siblings.
5817 return false;
5819 // Fall through; it's possible that the display type was overridden and
5820 // a different sort of frame was constructed, so we may need to return false
5821 // below.
5824 return true;
5827 // FIXME(emilio): If we ever kill IsValidSibling() we can simplify this quite a
5828 // bit (no need to pass aTargetContent or aTargetContentDisplay, and the
5829 // adjust() calls can be responsibility of the caller).
5830 template <nsCSSFrameConstructor::SiblingDirection aDirection>
5831 nsIFrame* nsCSSFrameConstructor::FindSiblingInternal(
5832 FlattenedChildIterator& aIter, nsIContent* aTargetContent,
5833 Maybe<StyleDisplay>& aTargetContentDisplay) {
5834 auto adjust = [&](nsIFrame* aPotentialSiblingFrame) -> nsIFrame* {
5835 return AdjustSiblingFrame(aPotentialSiblingFrame, aTargetContent,
5836 aTargetContentDisplay, aDirection);
5839 auto nextDomSibling = [](FlattenedChildIterator& aIter) -> nsIContent* {
5840 return aDirection == SiblingDirection::Forward ? aIter.GetNextChild()
5841 : aIter.GetPreviousChild();
5844 auto getInsideMarkerFrame = [](const nsIContent* aContent) -> nsIFrame* {
5845 auto* marker = nsLayoutUtils::GetMarkerFrame(aContent);
5846 const bool isInsideMarker =
5847 marker && marker->GetInFlowParent()->StyleList()->mListStylePosition ==
5848 StyleListStylePosition::Inside;
5849 return isInsideMarker ? marker : nullptr;
5852 auto getNearPseudo = [&](const nsIContent* aContent) -> nsIFrame* {
5853 if (aDirection == SiblingDirection::Forward) {
5854 if (auto* marker = getInsideMarkerFrame(aContent)) {
5855 return marker;
5857 return nsLayoutUtils::GetBeforeFrame(aContent);
5859 return nsLayoutUtils::GetAfterFrame(aContent);
5862 auto getFarPseudo = [&](const nsIContent* aContent) -> nsIFrame* {
5863 if (aDirection == SiblingDirection::Forward) {
5864 return nsLayoutUtils::GetAfterFrame(aContent);
5866 if (auto* before = nsLayoutUtils::GetBeforeFrame(aContent)) {
5867 return before;
5869 return getInsideMarkerFrame(aContent);
5872 while (nsIContent* sibling = nextDomSibling(aIter)) {
5873 // NOTE(emilio): It's important to check GetPrimaryFrame() before
5874 // IsDisplayContents to get the correct insertion point when multiple
5875 // siblings go from display: non-none to display: contents.
5876 if (nsIFrame* primaryFrame = sibling->GetPrimaryFrame()) {
5877 // XXX the GetContent() == sibling check is needed due to bug 135040.
5878 // Remove it once that's fixed.
5879 if (primaryFrame->GetContent() == sibling) {
5880 if (nsIFrame* frame = adjust(primaryFrame)) {
5881 return frame;
5886 if (IsDisplayContents(sibling)) {
5887 if (nsIFrame* frame = adjust(getNearPseudo(sibling))) {
5888 return frame;
5891 const bool startFromBeginning = aDirection == SiblingDirection::Forward;
5892 FlattenedChildIterator iter(sibling, startFromBeginning);
5893 nsIFrame* sibling = FindSiblingInternal<aDirection>(
5894 iter, aTargetContent, aTargetContentDisplay);
5895 if (sibling) {
5896 return sibling;
5901 return adjust(getFarPseudo(aIter.Parent()));
5904 nsIFrame* nsCSSFrameConstructor::AdjustSiblingFrame(
5905 nsIFrame* aSibling, nsIContent* aTargetContent,
5906 Maybe<StyleDisplay>& aTargetContentDisplay, SiblingDirection aDirection) {
5907 if (!aSibling) {
5908 return nullptr;
5911 if (aSibling->IsRenderedLegend()) {
5912 return nullptr;
5915 if (aSibling->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
5916 aSibling = aSibling->GetPlaceholderFrame();
5917 MOZ_ASSERT(aSibling);
5920 MOZ_ASSERT(!aSibling->GetPrevContinuation(), "How?");
5921 if (aDirection == SiblingDirection::Backward) {
5922 // The frame may be a ib-split frame (a split inline frame that contains a
5923 // block). Get the last part of that split.
5924 if (IsFramePartOfIBSplit(aSibling)) {
5925 aSibling = GetLastIBSplitSibling(aSibling);
5928 // The frame may have a continuation. If so, we want the last
5929 // non-overflow-container continuation as our previous sibling.
5930 aSibling = aSibling->GetTailContinuation();
5933 if (!IsValidSibling(aSibling, aTargetContent, aTargetContentDisplay)) {
5934 return nullptr;
5937 return aSibling;
5940 nsIFrame* nsCSSFrameConstructor::FindPreviousSibling(
5941 const FlattenedChildIterator& aIter,
5942 Maybe<StyleDisplay>& aTargetContentDisplay) {
5943 return FindSibling<SiblingDirection::Backward>(aIter, aTargetContentDisplay);
5946 nsIFrame* nsCSSFrameConstructor::FindNextSibling(
5947 const FlattenedChildIterator& aIter,
5948 Maybe<StyleDisplay>& aTargetContentDisplay) {
5949 return FindSibling<SiblingDirection::Forward>(aIter, aTargetContentDisplay);
5952 template <nsCSSFrameConstructor::SiblingDirection aDirection>
5953 nsIFrame* nsCSSFrameConstructor::FindSibling(
5954 const FlattenedChildIterator& aIter,
5955 Maybe<StyleDisplay>& aTargetContentDisplay) {
5956 nsIContent* targetContent = aIter.Get();
5957 FlattenedChildIterator siblingIter = aIter;
5958 nsIFrame* sibling = FindSiblingInternal<aDirection>(
5959 siblingIter, targetContent, aTargetContentDisplay);
5960 if (sibling) {
5961 return sibling;
5964 // Our siblings (if any) do not have a frame to guide us. The frame for the
5965 // target content should be inserted whereever a frame for the container would
5966 // be inserted. This is needed when inserting into display: contents nodes.
5967 const nsIContent* current = aIter.Parent();
5968 while (IsDisplayContents(current)) {
5969 const nsIContent* parent = current->GetFlattenedTreeParent();
5970 MOZ_ASSERT(parent, "No display: contents on the root");
5972 FlattenedChildIterator iter(parent);
5973 iter.Seek(current);
5974 sibling = FindSiblingInternal<aDirection>(iter, targetContent,
5975 aTargetContentDisplay);
5976 if (sibling) {
5977 return sibling;
5980 current = parent;
5983 return nullptr;
5986 // For fieldsets, returns the area frame, if the child is not a legend.
5987 static nsContainerFrame* GetAdjustedParentFrame(nsContainerFrame* aParentFrame,
5988 nsIContent* aChildContent) {
5989 MOZ_ASSERT(!aParentFrame->IsTableWrapperFrame(), "Shouldn't be happening!");
5991 nsContainerFrame* newParent = nullptr;
5992 if (aParentFrame->IsFieldSetFrame()) {
5993 // If the parent is a fieldSet, use the fieldSet's area frame as the
5994 // parent unless the new content is a legend.
5995 if (!aChildContent->IsHTMLElement(nsGkAtoms::legend)) {
5996 newParent = static_cast<nsFieldSetFrame*>(aParentFrame)->GetInner();
5997 if (newParent) {
5998 newParent = newParent->GetContentInsertionFrame();
6002 return newParent ? newParent : aParentFrame;
6005 nsIFrame* nsCSSFrameConstructor::GetInsertionPrevSibling(
6006 InsertionPoint* aInsertion, nsIContent* aChild, bool* aIsAppend,
6007 bool* aIsRangeInsertSafe, nsIContent* aStartSkipChild,
6008 nsIContent* aEndSkipChild) {
6009 MOZ_ASSERT(aInsertion->mParentFrame, "Must have parent frame to start with");
6011 *aIsAppend = false;
6013 // Find the frame that precedes the insertion point.
6014 FlattenedChildIterator iter(aInsertion->mContainer);
6015 if (iter.ShadowDOMInvolved() || !aChild->IsRootOfNativeAnonymousSubtree()) {
6016 // The check for IsRootOfNativeAnonymousSubtree() is because editor is
6017 // severely broken and calls us directly for native anonymous
6018 // nodes that it creates.
6019 if (aStartSkipChild) {
6020 iter.Seek(aStartSkipChild);
6021 } else {
6022 iter.Seek(aChild);
6024 } else {
6025 // Prime the iterator for the call to FindPreviousSibling.
6026 iter.GetNextChild();
6027 MOZ_ASSERT(aChild->GetProperty(nsGkAtoms::restylableAnonymousNode),
6028 "Someone passed native anonymous content directly into frame "
6029 "construction. Stop doing that!");
6032 // Note that FindPreviousSibling is passed the iterator by value, so that
6033 // the later usage of the iterator starts from the same place.
6034 Maybe<StyleDisplay> childDisplay;
6035 nsIFrame* prevSibling = FindPreviousSibling(iter, childDisplay);
6037 // Now, find the geometric parent so that we can handle
6038 // continuations properly. Use the prev sibling if we have it;
6039 // otherwise use the next sibling.
6040 if (prevSibling) {
6041 aInsertion->mParentFrame =
6042 prevSibling->GetParent()->GetContentInsertionFrame();
6043 } else {
6044 // If there is no previous sibling, then find the frame that follows
6046 // FIXME(emilio): This is really complex and probably shouldn't be.
6047 if (aEndSkipChild) {
6048 iter.Seek(aEndSkipChild);
6049 iter.GetPreviousChild();
6051 if (nsIFrame* nextSibling = FindNextSibling(iter, childDisplay)) {
6052 aInsertion->mParentFrame =
6053 nextSibling->GetParent()->GetContentInsertionFrame();
6054 } else {
6055 // No previous or next sibling, so treat this like an appended frame.
6056 *aIsAppend = true;
6058 // Deal with fieldsets.
6059 aInsertion->mParentFrame =
6060 ::GetAdjustedParentFrame(aInsertion->mParentFrame, aChild);
6062 aInsertion->mParentFrame =
6063 ::ContinuationToAppendTo(aInsertion->mParentFrame);
6065 prevSibling = ::FindAppendPrevSibling(aInsertion->mParentFrame, nullptr);
6069 *aIsRangeInsertSafe = childDisplay.isNothing();
6070 return prevSibling;
6073 nsContainerFrame* nsCSSFrameConstructor::GetContentInsertionFrameFor(
6074 nsIContent* aContent) {
6075 nsIFrame* frame;
6076 while (!(frame = aContent->GetPrimaryFrame())) {
6077 if (!IsDisplayContents(aContent)) {
6078 return nullptr;
6081 aContent = aContent->GetFlattenedTreeParent();
6082 if (!aContent) {
6083 return nullptr;
6087 // If the content of the frame is not the desired content then this is not
6088 // really a frame for the desired content.
6089 // XXX This check is needed due to bug 135040. Remove it once that's fixed.
6090 if (frame->GetContent() != aContent) {
6091 return nullptr;
6094 nsContainerFrame* insertionFrame = frame->GetContentInsertionFrame();
6096 NS_ASSERTION(!insertionFrame || insertionFrame == frame || !frame->IsLeaf(),
6097 "The insertion frame is the primary frame or the primary frame "
6098 "isn't a leaf");
6100 return insertionFrame;
6103 static bool IsSpecialFramesetChild(nsIContent* aContent) {
6104 // IMPORTANT: This must match the conditions in nsHTMLFramesetFrame::Init.
6105 return aContent->IsAnyOfHTMLElements(nsGkAtoms::frameset, nsGkAtoms::frame);
6108 static void InvalidateCanvasIfNeeded(PresShell* aPresShell, nsIContent* aNode);
6110 void nsCSSFrameConstructor::AddTextItemIfNeeded(
6111 nsFrameConstructorState& aState, const ComputedStyle& aParentStyle,
6112 const InsertionPoint& aInsertion, nsIContent* aPossibleTextContent,
6113 FrameConstructionItemList& aItems) {
6114 MOZ_ASSERT(aPossibleTextContent, "Must have node");
6115 if (!aPossibleTextContent->IsText() ||
6116 !aPossibleTextContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE) ||
6117 aPossibleTextContent->HasFlag(NODE_NEEDS_FRAME)) {
6118 // Not text, or not suppressed due to being all-whitespace (if it were being
6119 // suppressed, it would have the NS_CREATE_FRAME_IF_NON_WHITESPACE flag), or
6120 // going to be reframed anyway.
6121 return;
6123 MOZ_ASSERT(!aPossibleTextContent->GetPrimaryFrame(),
6124 "Text node has a frame and NS_CREATE_FRAME_IF_NON_WHITESPACE");
6125 AddFrameConstructionItems(aState, aPossibleTextContent, false, aParentStyle,
6126 aInsertion, aItems);
6129 void nsCSSFrameConstructor::ReframeTextIfNeeded(nsIContent* aContent) {
6130 if (!aContent->IsText() ||
6131 !aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE) ||
6132 aContent->HasFlag(NODE_NEEDS_FRAME)) {
6133 // Not text, or not suppressed due to being all-whitespace (if it were being
6134 // suppressed, it would have the NS_CREATE_FRAME_IF_NON_WHITESPACE flag), or
6135 // going to be reframed anyway.
6136 return;
6138 MOZ_ASSERT(!aContent->GetPrimaryFrame(),
6139 "Text node has a frame and NS_CREATE_FRAME_IF_NON_WHITESPACE");
6140 ContentInserted(aContent, InsertionKind::Async);
6143 #ifdef DEBUG
6144 void nsCSSFrameConstructor::CheckBitsForLazyFrameConstruction(
6145 nsIContent* aParent) {
6146 // If we hit a node with no primary frame, or the NODE_NEEDS_FRAME bit set
6147 // we want to assert, but leaf frames that process their own children and may
6148 // ignore anonymous children (eg framesets) make this complicated. So we set
6149 // these two booleans if we encounter these situations and unset them if we
6150 // hit a node with a leaf frame.
6152 // It's fine if one of node without primary frame is in a display:none
6153 // subtree.
6155 // Also, it's fine if one of the nodes without primary frame is a display:
6156 // contents node.
6157 bool noPrimaryFrame = false;
6158 bool needsFrameBitSet = false;
6159 nsIContent* content = aParent;
6160 while (content && !content->HasFlag(NODE_DESCENDANTS_NEED_FRAMES)) {
6161 if (content->GetPrimaryFrame() && content->GetPrimaryFrame()->IsLeaf()) {
6162 noPrimaryFrame = needsFrameBitSet = false;
6164 if (!noPrimaryFrame && !content->GetPrimaryFrame()) {
6165 noPrimaryFrame = !IsDisplayContents(content);
6167 if (!needsFrameBitSet && content->HasFlag(NODE_NEEDS_FRAME)) {
6168 needsFrameBitSet = true;
6171 content = content->GetFlattenedTreeParent();
6173 if (content && content->GetPrimaryFrame() &&
6174 content->GetPrimaryFrame()->IsLeaf()) {
6175 noPrimaryFrame = needsFrameBitSet = false;
6177 MOZ_ASSERT(!noPrimaryFrame,
6178 "Ancestors of nodes with frames to be "
6179 "constructed lazily should have frames");
6180 MOZ_ASSERT(!needsFrameBitSet,
6181 "Ancestors of nodes with frames to be "
6182 "constructed lazily should not have NEEDS_FRAME bit set");
6184 #endif
6186 // Returns true if this operation can be lazy, false if not.
6188 // FIXME(emilio, bug 1410020): This function assumes that the flattened tree
6189 // parent of all the appended children is the same, which, afaict, is not
6190 // necessarily true.
6191 void nsCSSFrameConstructor::ConstructLazily(Operation aOperation,
6192 nsIContent* aChild) {
6193 MOZ_ASSERT(aChild->GetParent());
6195 // We can construct lazily; just need to set suitable bits in the content
6196 // tree.
6197 Element* parent = aChild->GetFlattenedTreeParentElement();
6198 if (!parent) {
6199 // Not part of the flat tree, nothing to do.
6200 return;
6203 if (Servo_Element_IsDisplayNone(parent)) {
6204 // Nothing to do either.
6206 // FIXME(emilio): This should be an assert, except for weird <frameset>
6207 // stuff that does its own frame construction. Such an assert would fire in
6208 // layout/style/crashtests/1411478.html, for example.
6209 return;
6212 // Set NODE_NEEDS_FRAME on the new nodes.
6213 if (aOperation == CONTENTINSERT) {
6214 NS_ASSERTION(!aChild->GetPrimaryFrame() ||
6215 aChild->GetPrimaryFrame()->GetContent() != aChild,
6216 // XXX the aChild->GetPrimaryFrame()->GetContent() != aChild
6217 // check is needed due to bug 135040. Remove it once that's
6218 // fixed.
6219 "setting NEEDS_FRAME on a node that already has a frame?");
6220 aChild->SetFlags(NODE_NEEDS_FRAME);
6221 } else { // CONTENTAPPEND
6222 for (nsIContent* child = aChild; child; child = child->GetNextSibling()) {
6223 NS_ASSERTION(!child->GetPrimaryFrame() ||
6224 child->GetPrimaryFrame()->GetContent() != child,
6225 // XXX the child->GetPrimaryFrame()->GetContent() != child
6226 // check is needed due to bug 135040. Remove it once that's
6227 // fixed.
6228 "setting NEEDS_FRAME on a node that already has a frame?");
6229 child->SetFlags(NODE_NEEDS_FRAME);
6233 CheckBitsForLazyFrameConstruction(parent);
6234 parent->NoteDescendantsNeedFramesForServo();
6237 void nsCSSFrameConstructor::IssueSingleInsertNofications(
6238 nsIContent* aStartChild, nsIContent* aEndChild,
6239 InsertionKind aInsertionKind) {
6240 for (nsIContent* child = aStartChild; child != aEndChild;
6241 child = child->GetNextSibling()) {
6242 // XXX the GetContent() != child check is needed due to bug 135040.
6243 // Remove it once that's fixed.
6244 MOZ_ASSERT(!child->GetPrimaryFrame() ||
6245 child->GetPrimaryFrame()->GetContent() != child);
6247 // Call ContentRangeInserted with this node.
6248 ContentRangeInserted(child, child->GetNextSibling(), aInsertionKind);
6252 bool nsCSSFrameConstructor::InsertionPoint::IsMultiple() const {
6253 // Fieldset frames have multiple normal flow child frame lists so handle it
6254 // the same as if it had multiple content insertion points.
6255 return mParentFrame && mParentFrame->IsFieldSetFrame();
6258 nsCSSFrameConstructor::InsertionPoint
6259 nsCSSFrameConstructor::GetRangeInsertionPoint(nsIContent* aStartChild,
6260 nsIContent* aEndChild,
6261 InsertionKind aInsertionKind) {
6262 MOZ_ASSERT(aStartChild);
6264 nsIContent* parent = aStartChild->GetParent();
6265 if (!parent) {
6266 IssueSingleInsertNofications(aStartChild, aEndChild, aInsertionKind);
6267 return {};
6270 // If the children of the container may be distributed to different insertion
6271 // points, insert them separately and bail out, letting ContentInserted handle
6272 // the mess.
6273 if (parent->GetShadowRoot()) {
6274 IssueSingleInsertNofications(aStartChild, aEndChild, aInsertionKind);
6275 return {};
6278 #ifdef DEBUG
6280 nsIContent* expectedParent = aStartChild->GetFlattenedTreeParent();
6281 for (nsIContent* child = aStartChild->GetNextSibling(); child;
6282 child = child->GetNextSibling()) {
6283 MOZ_ASSERT(child->GetFlattenedTreeParent() == expectedParent);
6286 #endif
6288 // Now the flattened tree parent of all the siblings is the same, just use the
6289 // same insertion point and take the fast path, unless it's a multiple
6290 // insertion point.
6291 InsertionPoint ip = GetInsertionPoint(aStartChild);
6292 if (ip.IsMultiple()) {
6293 IssueSingleInsertNofications(aStartChild, aEndChild, aInsertionKind);
6294 return {};
6297 return ip;
6300 bool nsCSSFrameConstructor::MaybeRecreateForFrameset(nsIFrame* aParentFrame,
6301 nsIContent* aStartChild,
6302 nsIContent* aEndChild) {
6303 if (aParentFrame->IsFrameSetFrame()) {
6304 // Check whether we have any kids we care about.
6305 for (nsIContent* cur = aStartChild; cur != aEndChild;
6306 cur = cur->GetNextSibling()) {
6307 if (IsSpecialFramesetChild(cur)) {
6308 // Just reframe the parent, since framesets are weird like that.
6309 RecreateFramesForContent(aParentFrame->GetContent(),
6310 InsertionKind::Async);
6311 return true;
6315 return false;
6318 void nsCSSFrameConstructor::LazilyStyleNewChildRange(nsIContent* aStartChild,
6319 nsIContent* aEndChild) {
6320 for (nsIContent* child = aStartChild; child != aEndChild;
6321 child = child->GetNextSibling()) {
6322 if (child->IsElement()) {
6323 child->AsElement()->NoteDirtyForServo();
6328 #ifdef DEBUG
6329 static bool IsFlattenedTreeChild(nsIContent* aParent, nsIContent* aChild) {
6330 FlattenedChildIterator iter(aParent);
6331 for (nsIContent* node = iter.GetNextChild(); node;
6332 node = iter.GetNextChild()) {
6333 if (node == aChild) {
6334 return true;
6337 return false;
6339 #endif
6341 void nsCSSFrameConstructor::StyleNewChildRange(nsIContent* aStartChild,
6342 nsIContent* aEndChild) {
6343 ServoStyleSet* styleSet = mPresShell->StyleSet();
6345 for (nsIContent* child = aStartChild; child != aEndChild;
6346 child = child->GetNextSibling()) {
6347 if (!child->IsElement()) {
6348 continue;
6351 Element* childElement = child->AsElement();
6353 // We only come in here from non-lazy frame construction, so the children
6354 // should be unstyled.
6355 MOZ_ASSERT(!childElement->HasServoData());
6357 #ifdef DEBUG
6359 // Furthermore, all of them should have the same flattened tree parent
6360 // (GetRangeInsertionPoint ensures it). And that parent should be styled,
6361 // otherwise we would've never found an insertion point at all.
6362 Element* parent = childElement->GetFlattenedTreeParentElement();
6363 MOZ_ASSERT(parent);
6364 MOZ_ASSERT(parent->HasServoData());
6365 MOZ_ASSERT(
6366 IsFlattenedTreeChild(parent, child),
6367 "GetFlattenedTreeParent and ChildIterator don't agree, fix this!");
6369 #endif
6371 styleSet->StyleNewSubtree(childElement);
6375 nsIFrame* nsCSSFrameConstructor::FindNextSiblingForAppend(
6376 const InsertionPoint& aInsertion) {
6377 auto SlowPath = [&]() -> nsIFrame* {
6378 FlattenedChildIterator iter(aInsertion.mContainer,
6379 /* aStartAtBeginning = */ false);
6380 iter.GetPreviousChild(); // Prime the iterator.
6381 Maybe<StyleDisplay> unused;
6382 return FindNextSibling(iter, unused);
6385 if (!IsDisplayContents(aInsertion.mContainer) &&
6386 !nsLayoutUtils::GetAfterFrame(aInsertion.mContainer)) {
6387 MOZ_ASSERT(!SlowPath());
6388 return nullptr;
6391 return SlowPath();
6394 void nsCSSFrameConstructor::ContentAppended(nsIContent* aFirstNewContent,
6395 InsertionKind aInsertionKind) {
6396 MOZ_ASSERT(aInsertionKind == InsertionKind::Sync ||
6397 !RestyleManager()->IsInStyleRefresh());
6399 AUTO_PROFILER_LABEL_HOT("nsCSSFrameConstructor::ContentAppended",
6400 LAYOUT_FrameConstruction);
6401 AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
6403 #ifdef DEBUG
6404 if (gNoisyContentUpdates) {
6405 printf(
6406 "nsCSSFrameConstructor::ContentAppended container=%p "
6407 "first-child=%p lazy=%d\n",
6408 aFirstNewContent->GetParent(), aFirstNewContent,
6409 aInsertionKind == InsertionKind::Async);
6410 if (gReallyNoisyContentUpdates && aFirstNewContent->GetParent()) {
6411 aFirstNewContent->GetParent()->List(stdout, 0);
6415 for (nsIContent* child = aFirstNewContent; child;
6416 child = child->GetNextSibling()) {
6417 // XXX the GetContent() != child check is needed due to bug 135040.
6418 // Remove it once that's fixed.
6419 MOZ_ASSERT(
6420 !child->GetPrimaryFrame() ||
6421 child->GetPrimaryFrame()->GetContent() != child,
6422 "asked to construct a frame for a node that already has a frame");
6424 #endif
6426 LAYOUT_PHASE_TEMP_EXIT();
6427 InsertionPoint insertion =
6428 GetRangeInsertionPoint(aFirstNewContent, nullptr, aInsertionKind);
6429 nsContainerFrame*& parentFrame = insertion.mParentFrame;
6430 LAYOUT_PHASE_TEMP_REENTER();
6431 if (!parentFrame) {
6432 // We're punting on frame construction because there's no container frame.
6433 // The Servo-backed style system handles this case like the lazy frame
6434 // construction case, except when we're already constructing frames, in
6435 // which case we shouldn't need to do anything else.
6436 if (aInsertionKind == InsertionKind::Async) {
6437 LazilyStyleNewChildRange(aFirstNewContent, nullptr);
6439 return;
6442 if (aInsertionKind == InsertionKind::Async) {
6443 ConstructLazily(CONTENTAPPEND, aFirstNewContent);
6444 LazilyStyleNewChildRange(aFirstNewContent, nullptr);
6445 return;
6448 LAYOUT_PHASE_TEMP_EXIT();
6449 if (MaybeRecreateForFrameset(parentFrame, aFirstNewContent, nullptr)) {
6450 LAYOUT_PHASE_TEMP_REENTER();
6451 return;
6453 LAYOUT_PHASE_TEMP_REENTER();
6455 if (parentFrame->IsLeaf()) {
6456 // Nothing to do here; we shouldn't be constructing kids of leaves
6457 // Clear lazy bits so we don't try to construct again.
6458 ClearLazyBits(aFirstNewContent, nullptr);
6459 return;
6462 LAYOUT_PHASE_TEMP_EXIT();
6463 if (WipeInsertionParent(parentFrame)) {
6464 LAYOUT_PHASE_TEMP_REENTER();
6465 return;
6467 LAYOUT_PHASE_TEMP_REENTER();
6469 #ifdef DEBUG
6470 if (gNoisyContentUpdates && IsFramePartOfIBSplit(parentFrame)) {
6471 printf("nsCSSFrameConstructor::ContentAppended: parentFrame=");
6472 parentFrame->ListTag(stdout);
6473 printf(" is ib-split\n");
6475 #endif
6477 // We should never get here with fieldsets, since they have
6478 // multiple insertion points.
6479 MOZ_ASSERT(!parentFrame->IsFieldSetFrame(),
6480 "Parent frame should not be fieldset!");
6482 nsIFrame* nextSibling = FindNextSiblingForAppend(insertion);
6483 if (nextSibling) {
6484 parentFrame = nextSibling->GetParent()->GetContentInsertionFrame();
6485 } else {
6486 parentFrame = ::ContinuationToAppendTo(parentFrame);
6489 nsContainerFrame* containingBlock = GetFloatContainingBlock(parentFrame);
6491 // See if the containing block has :first-letter style applied.
6492 const bool haveFirstLetterStyle =
6493 containingBlock && HasFirstLetterStyle(containingBlock);
6495 const bool haveFirstLineStyle =
6496 containingBlock && ShouldHaveFirstLineStyle(containingBlock->GetContent(),
6497 containingBlock->Style());
6499 if (haveFirstLetterStyle) {
6500 AutoWeakFrame wf(nextSibling);
6502 // Before we get going, remove the current letter frames
6503 RemoveLetterFrames(mPresShell, containingBlock);
6505 // Reget nextSibling, since we may have killed it.
6507 // FIXME(emilio): This kinda sucks! :(
6508 if (nextSibling && !wf) {
6509 nextSibling = FindNextSiblingForAppend(insertion);
6510 if (nextSibling) {
6511 parentFrame = nextSibling->GetParent()->GetContentInsertionFrame();
6512 containingBlock = GetFloatContainingBlock(parentFrame);
6517 // Create some new frames
6518 nsFrameConstructorState state(
6519 mPresShell, GetAbsoluteContainingBlock(parentFrame, FIXED_POS),
6520 GetAbsoluteContainingBlock(parentFrame, ABS_POS), containingBlock);
6522 if (mPresShell->GetPresContext()->IsPaginated()) {
6523 // Because this function can be called outside frame construction, we need
6524 // to set state.mAutoPageNameValue based on what the parent frame's auto
6525 // value is.
6526 // Calling this from outside the frame constructor can violate many of the
6527 // expectations in AutoFrameConstructionPageName, and unlike during frame
6528 // construction we already have an auto value from parentFrame, so we do
6529 // not use AutoFrameConstructionPageName here.
6530 state.mAutoPageNameValue = parentFrame->GetAutoPageValue();
6531 #ifdef DEBUG
6532 parentFrame->mWasVisitedByAutoFrameConstructionPageName = true;
6533 #endif
6536 LayoutFrameType frameType = parentFrame->Type();
6538 RefPtr<ComputedStyle> parentStyle =
6539 ResolveComputedStyle(insertion.mContainer);
6540 FlattenedChildIterator iter(insertion.mContainer);
6541 const bool haveNoShadowDOM =
6542 !iter.ShadowDOMInvolved() || !iter.GetNextChild();
6544 AutoFrameConstructionItemList items(this);
6545 if (aFirstNewContent->GetPreviousSibling() &&
6546 GetParentType(frameType) == eTypeBlock && haveNoShadowDOM) {
6547 // If there's a text node in the normal content list just before the new
6548 // items, and it has no frame, make a frame construction item for it. If it
6549 // doesn't need a frame, ConstructFramesFromItemList below won't give it
6550 // one. No need to do all this if our parent type is not block, though,
6551 // since WipeContainingBlock already handles that situation.
6553 // Because we're appending, we don't need to worry about any text
6554 // after the appended content; there can only be generated content
6555 // (and bare text nodes are not generated). Native anonymous content
6556 // generated by frames never participates in inline layout.
6557 AddTextItemIfNeeded(state, *parentStyle, insertion,
6558 aFirstNewContent->GetPreviousSibling(), items);
6560 for (nsIContent* child = aFirstNewContent; child;
6561 child = child->GetNextSibling()) {
6562 AddFrameConstructionItems(state, child, false, *parentStyle, insertion,
6563 items);
6566 nsIFrame* prevSibling = ::FindAppendPrevSibling(parentFrame, nextSibling);
6568 // Perform special check for diddling around with the frames in
6569 // a ib-split inline frame.
6570 // If we're appending before :after content, then we're not really
6571 // appending, so let WipeContainingBlock know that.
6572 LAYOUT_PHASE_TEMP_EXIT();
6573 if (WipeContainingBlock(state, containingBlock, parentFrame, items, true,
6574 prevSibling)) {
6575 LAYOUT_PHASE_TEMP_REENTER();
6576 return;
6578 LAYOUT_PHASE_TEMP_REENTER();
6580 // If the parent is a block frame, and we're not in a special case
6581 // where frames can be moved around, determine if the list is for the
6582 // start or end of the block.
6583 if (parentFrame->IsBlockFrameOrSubclass() && !haveFirstLetterStyle &&
6584 !haveFirstLineStyle && !IsFramePartOfIBSplit(parentFrame)) {
6585 items.SetLineBoundaryAtStart(!prevSibling ||
6586 !prevSibling->IsInlineOutside() ||
6587 prevSibling->IsBrFrame());
6588 // :after content can't be <br> so no need to check it
6590 // FIXME(emilio): A display: contents sibling could! Write a test-case and
6591 // fix.
6592 items.SetLineBoundaryAtEnd(!nextSibling || !nextSibling->IsInlineOutside());
6594 // To suppress whitespace-only text frames, we have to verify that
6595 // our container's DOM child list matches its flattened tree child list.
6596 items.SetParentHasNoShadowDOM(haveNoShadowDOM);
6598 nsFrameConstructorSaveState floatSaveState;
6599 state.MaybePushFloatContainingBlock(parentFrame, floatSaveState);
6601 nsFrameList frameList;
6602 ConstructFramesFromItemList(state, items, parentFrame,
6603 ParentIsWrapperAnonBox(parentFrame), frameList);
6605 for (nsIContent* child = aFirstNewContent; child;
6606 child = child->GetNextSibling()) {
6607 // Invalidate now instead of before the WipeContainingBlock call, just in
6608 // case we do wipe; in that case we don't need to do this walk at all.
6609 // XXXbz does that matter? Would it make more sense to save some virtual
6610 // GetChildAt_Deprecated calls instead and do this during construction of
6611 // our FrameConstructionItemList?
6612 InvalidateCanvasIfNeeded(mPresShell, child);
6615 // If the container is a table and a caption was appended, it needs to be put
6616 // in the table wrapper frame's additional child list.
6617 nsFrameList captionList;
6618 if (LayoutFrameType::Table == frameType) {
6619 // Pull out the captions. Note that we don't want to do that as we go,
6620 // because processing a single caption can add a whole bunch of things to
6621 // the frame items due to pseudoframe processing. So we'd have to pull
6622 // captions from a list anyway; might as well do that here.
6623 // XXXbz this is no longer true; we could pull captions directly out of the
6624 // FrameConstructionItemList now.
6625 PullOutCaptionFrames(frameList, captionList);
6628 if (haveFirstLineStyle && parentFrame == containingBlock) {
6629 // It's possible that some of the new frames go into a
6630 // first-line frame. Look at them and see...
6631 AppendFirstLineFrames(state, containingBlock->GetContent(), containingBlock,
6632 frameList);
6633 // That moved things into line frames as needed, reparenting their
6634 // styles. Nothing else needs to be done.
6635 } else if (parentFrame->Style()->HasPseudoElementData()) {
6636 // parentFrame might be inside a ::first-line frame. Check whether it is,
6637 // and if so fix up our styles.
6638 CheckForFirstLineInsertion(parentFrame, frameList);
6639 CheckForFirstLineInsertion(parentFrame, captionList);
6642 // Notify the parent frame passing it the list of new frames
6643 // Append the flowed frames to the principal child list; captions
6644 // need special treatment
6645 if (captionList.NotEmpty()) { // append the caption to the table wrapper
6646 NS_ASSERTION(LayoutFrameType::Table == frameType, "how did that happen?");
6647 nsContainerFrame* outerTable = parentFrame->GetParent();
6648 captionList.ApplySetParent(outerTable);
6649 AppendFrames(outerTable, FrameChildListID::Caption, std::move(captionList));
6652 LAYOUT_PHASE_TEMP_EXIT();
6653 if (MaybeRecreateForColumnSpan(state, parentFrame, frameList, prevSibling)) {
6654 LAYOUT_PHASE_TEMP_REENTER();
6655 return;
6657 LAYOUT_PHASE_TEMP_REENTER();
6659 if (frameList.NotEmpty()) { // append the in-flow kids
6660 AppendFramesToParent(state, parentFrame, frameList, prevSibling);
6663 // Recover first-letter frames
6664 if (haveFirstLetterStyle) {
6665 RecoverLetterFrames(containingBlock);
6668 #ifdef DEBUG
6669 if (gReallyNoisyContentUpdates) {
6670 printf("nsCSSFrameConstructor::ContentAppended: resulting frame model:\n");
6671 parentFrame->List(stdout);
6673 #endif
6675 #ifdef ACCESSIBILITY
6676 if (nsAccessibilityService* accService = GetAccService()) {
6677 accService->ContentRangeInserted(mPresShell, aFirstNewContent, nullptr);
6679 #endif
6682 void nsCSSFrameConstructor::ContentInserted(nsIContent* aChild,
6683 InsertionKind aInsertionKind) {
6684 ContentRangeInserted(aChild, aChild->GetNextSibling(), aInsertionKind);
6687 // ContentRangeInserted handles creating frames for a range of nodes that
6688 // aren't at the end of their childlist. ContentRangeInserted isn't a real
6689 // content notification, but rather it handles regular ContentInserted calls
6690 // for a single node as well as the lazy construction of frames for a range of
6691 // nodes when called from CreateNeededFrames. For a range of nodes to be
6692 // suitable to have its frames constructed all at once they must meet the same
6693 // conditions that ContentAppended imposes (GetRangeInsertionPoint checks
6694 // these), plus more. Namely when finding the insertion prevsibling we must not
6695 // need to consult something specific to any one node in the range, so that the
6696 // insertion prevsibling would be the same for each node in the range. So we
6697 // pass the first node in the range to GetInsertionPrevSibling, and if
6698 // IsValidSibling (the only place GetInsertionPrevSibling might look at the
6699 // passed in node itself) needs to resolve style on the node we record this and
6700 // return that this range needs to be split up and inserted separately. Table
6701 // captions need extra attention as we need to determine where to insert them
6702 // in the caption list, while skipping any nodes in the range being inserted
6703 // (because when we treat the caption frames the other nodes have had their
6704 // frames constructed but not yet inserted into the frame tree).
6705 void nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aStartChild,
6706 nsIContent* aEndChild,
6707 InsertionKind aInsertionKind) {
6708 MOZ_ASSERT(aInsertionKind == InsertionKind::Sync ||
6709 !RestyleManager()->IsInStyleRefresh());
6711 AUTO_PROFILER_LABEL_HOT("nsCSSFrameConstructor::ContentRangeInserted",
6712 LAYOUT_FrameConstruction);
6713 AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
6715 MOZ_ASSERT(aStartChild, "must always pass a child");
6717 #ifdef DEBUG
6718 if (gNoisyContentUpdates) {
6719 printf(
6720 "nsCSSFrameConstructor::ContentRangeInserted container=%p "
6721 "start-child=%p end-child=%p lazy=%d\n",
6722 aStartChild->GetParent(), aStartChild, aEndChild,
6723 aInsertionKind == InsertionKind::Async);
6724 if (gReallyNoisyContentUpdates) {
6725 if (aStartChild->GetParent()) {
6726 aStartChild->GetParent()->List(stdout, 0);
6727 } else {
6728 aStartChild->List(stdout, 0);
6733 for (nsIContent* child = aStartChild; child != aEndChild;
6734 child = child->GetNextSibling()) {
6735 // XXX the GetContent() != child check is needed due to bug 135040.
6736 // Remove it once that's fixed.
6737 NS_ASSERTION(
6738 !child->GetPrimaryFrame() ||
6739 child->GetPrimaryFrame()->GetContent() != child,
6740 "asked to construct a frame for a node that already has a frame");
6742 #endif
6744 bool isSingleInsert = (aStartChild->GetNextSibling() == aEndChild);
6745 NS_ASSERTION(isSingleInsert || aInsertionKind == InsertionKind::Sync,
6746 "range insert shouldn't be lazy");
6747 NS_ASSERTION(isSingleInsert || aEndChild,
6748 "range should not include all nodes after aStartChild");
6750 // If we have a null parent, then this must be the document element being
6751 // inserted, or some other child of the document in the DOM (might be a PI,
6752 // say).
6753 if (!aStartChild->GetParent()) {
6754 MOZ_ASSERT(isSingleInsert,
6755 "root node insertion should be a single insertion");
6756 Element* docElement = mDocument->GetRootElement();
6757 if (aStartChild != docElement) {
6758 // Not the root element; just bail out
6759 return;
6762 MOZ_ASSERT(!mRootElementFrame, "root element frame already created");
6763 if (aInsertionKind == InsertionKind::Async) {
6764 docElement->SetFlags(NODE_NEEDS_FRAME);
6765 LazilyStyleNewChildRange(docElement, nullptr);
6766 return;
6769 // Create frames for the document element and its child elements
6770 if (ConstructDocElementFrame(docElement)) {
6771 InvalidateCanvasIfNeeded(mPresShell, aStartChild);
6772 #ifdef DEBUG
6773 if (gReallyNoisyContentUpdates) {
6774 printf(
6775 "nsCSSFrameConstructor::ContentRangeInserted: resulting frame "
6776 "model:\n");
6777 mRootElementFrame->List(stdout);
6779 #endif
6782 #ifdef ACCESSIBILITY
6783 if (nsAccessibilityService* accService = GetAccService()) {
6784 accService->ContentRangeInserted(mPresShell, aStartChild, aEndChild);
6786 #endif
6788 return;
6791 InsertionPoint insertion;
6792 if (isSingleInsert) {
6793 // See if we have a Shadow DOM insertion point. If so, then that's our real
6794 // parent frame; if not, then the frame hasn't been built yet and we just
6795 // bail.
6796 insertion = GetInsertionPoint(aStartChild);
6797 } else {
6798 // Get our insertion point. If we need to issue single ContentInserteds
6799 // GetRangeInsertionPoint will take care of that for us.
6800 LAYOUT_PHASE_TEMP_EXIT();
6801 insertion = GetRangeInsertionPoint(aStartChild, aEndChild, aInsertionKind);
6802 LAYOUT_PHASE_TEMP_REENTER();
6805 if (!insertion.mParentFrame) {
6806 // We're punting on frame construction because there's no container frame.
6807 // The Servo-backed style system handles this case like the lazy frame
6808 // construction case, except when we're already constructing frames, in
6809 // which case we shouldn't need to do anything else.
6810 if (aInsertionKind == InsertionKind::Async) {
6811 LazilyStyleNewChildRange(aStartChild, aEndChild);
6813 return;
6816 if (aInsertionKind == InsertionKind::Async) {
6817 ConstructLazily(CONTENTINSERT, aStartChild);
6818 LazilyStyleNewChildRange(aStartChild, aEndChild);
6819 return;
6822 bool isAppend, isRangeInsertSafe;
6823 nsIFrame* prevSibling = GetInsertionPrevSibling(
6824 &insertion, aStartChild, &isAppend, &isRangeInsertSafe);
6826 // check if range insert is safe
6827 if (!isSingleInsert && !isRangeInsertSafe) {
6828 // must fall back to a single ContertInserted for each child in the range
6829 LAYOUT_PHASE_TEMP_EXIT();
6830 IssueSingleInsertNofications(aStartChild, aEndChild, InsertionKind::Sync);
6831 LAYOUT_PHASE_TEMP_REENTER();
6832 return;
6835 LayoutFrameType frameType = insertion.mParentFrame->Type();
6836 LAYOUT_PHASE_TEMP_EXIT();
6837 if (MaybeRecreateForFrameset(insertion.mParentFrame, aStartChild,
6838 aEndChild)) {
6839 LAYOUT_PHASE_TEMP_REENTER();
6840 return;
6842 LAYOUT_PHASE_TEMP_REENTER();
6844 // We should only get here with fieldsets when doing a single insert, because
6845 // fieldsets have multiple insertion points.
6846 NS_ASSERTION(isSingleInsert || frameType != LayoutFrameType::FieldSet,
6847 "Unexpected parent");
6848 // Note that this check is insufficient if aStartChild is not a legend with
6849 // display::contents that contains a legend. We'll catch that case in
6850 // WipeContainingBlock. (That code would also catch this case, but handling
6851 // this early is slightly faster.)
6852 // XXXmats we should be able to optimize this when the fieldset doesn't
6853 // currently have a rendered legend. ContentRangeInserted needs to be fixed
6854 // to use the inner frame as the content insertion frame in that case.
6855 if (GetFieldSetFrameFor(insertion.mParentFrame) &&
6856 aStartChild->NodeInfo()->NameAtom() == nsGkAtoms::legend) {
6857 // Just reframe the parent, since figuring out whether this
6858 // should be the new legend and then handling it is too complex.
6859 // We could do a little better here --- check if the fieldset already
6860 // has a legend which occurs earlier in its child list than this node,
6861 // and if so, proceed. But we'd have to extend nsFieldSetFrame
6862 // to locate this legend in the inserted frames and extract it.
6863 LAYOUT_PHASE_TEMP_EXIT();
6864 RecreateFramesForContent(insertion.mParentFrame->GetContent(),
6865 InsertionKind::Async);
6866 LAYOUT_PHASE_TEMP_REENTER();
6867 return;
6870 // Don't construct kids of leaves
6871 if (insertion.mParentFrame->IsLeaf()) {
6872 // Clear lazy bits so we don't try to construct again.
6873 ClearLazyBits(aStartChild, aEndChild);
6874 return;
6877 LAYOUT_PHASE_TEMP_EXIT();
6878 if (WipeInsertionParent(insertion.mParentFrame)) {
6879 LAYOUT_PHASE_TEMP_REENTER();
6880 return;
6882 LAYOUT_PHASE_TEMP_REENTER();
6884 nsFrameConstructorState state(
6885 mPresShell, GetAbsoluteContainingBlock(insertion.mParentFrame, FIXED_POS),
6886 GetAbsoluteContainingBlock(insertion.mParentFrame, ABS_POS),
6887 GetFloatContainingBlock(insertion.mParentFrame),
6888 do_AddRef(mFrameTreeState));
6890 // Recover state for the containing block - we need to know if
6891 // it has :first-letter or :first-line style applied to it. The
6892 // reason we care is that the internal structure in these cases
6893 // is not the normal structure and requires custom updating
6894 // logic.
6895 nsContainerFrame* containingBlock = state.mFloatedList.mContainingBlock;
6896 bool haveFirstLetterStyle = false;
6897 bool haveFirstLineStyle = false;
6899 // In order to shave off some cycles, we only dig up the
6900 // containing block haveFirst* flags if the parent frame where
6901 // the insertion/append is occurring is an inline or block
6902 // container. For other types of containers this isn't relevant.
6903 StyleDisplayInside parentDisplayInside =
6904 insertion.mParentFrame->StyleDisplay()->DisplayInside();
6906 // Examine the insertion.mParentFrame where the insertion is taking
6907 // place. If it's a certain kind of container then some special
6908 // processing is done.
6909 if (StyleDisplayInside::Flow == parentDisplayInside) {
6910 // Recover the special style flags for the containing block
6911 if (containingBlock) {
6912 haveFirstLetterStyle = HasFirstLetterStyle(containingBlock);
6913 haveFirstLineStyle = ShouldHaveFirstLineStyle(
6914 containingBlock->GetContent(), containingBlock->Style());
6917 if (haveFirstLetterStyle) {
6918 // If our current insertion.mParentFrame is a Letter frame, use its parent
6919 // as our new parent hint
6920 if (insertion.mParentFrame->IsLetterFrame()) {
6921 // If insertion.mParentFrame is out of flow, then we actually want the
6922 // parent of the placeholder frame.
6923 if (insertion.mParentFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
6924 nsPlaceholderFrame* placeholderFrame =
6925 insertion.mParentFrame->GetPlaceholderFrame();
6926 NS_ASSERTION(placeholderFrame, "No placeholder for out-of-flow?");
6927 insertion.mParentFrame = placeholderFrame->GetParent();
6928 } else {
6929 insertion.mParentFrame = insertion.mParentFrame->GetParent();
6933 // Remove the old letter frames before doing the insertion
6934 RemoveLetterFrames(mPresShell, state.mFloatedList.mContainingBlock);
6936 // Removing the letterframes messes around with the frame tree, removing
6937 // and creating frames. We need to reget our prevsibling, parent frame,
6938 // etc.
6939 prevSibling = GetInsertionPrevSibling(&insertion, aStartChild, &isAppend,
6940 &isRangeInsertSafe);
6942 // Need check whether a range insert is still safe.
6943 if (!isSingleInsert && !isRangeInsertSafe) {
6944 // Need to recover the letter frames first.
6945 RecoverLetterFrames(state.mFloatedList.mContainingBlock);
6947 // must fall back to a single ContertInserted for each child in the
6948 // range
6949 LAYOUT_PHASE_TEMP_EXIT();
6950 IssueSingleInsertNofications(aStartChild, aEndChild,
6951 InsertionKind::Sync);
6952 LAYOUT_PHASE_TEMP_REENTER();
6953 return;
6956 frameType = insertion.mParentFrame->Type();
6960 // This handles fallback to 'list-style-type' when a 'list-style-image' fails
6961 // to load.
6962 if (aStartChild->IsInNativeAnonymousSubtree() &&
6963 aStartChild->IsHTMLElement(nsGkAtoms::mozgeneratedcontentimage)) {
6964 MOZ_ASSERT(isSingleInsert);
6965 MOZ_ASSERT(insertion.mParentFrame->Style()->GetPseudoType() ==
6966 PseudoStyleType::marker,
6967 "we can only handle ::marker fallback for now");
6968 nsIContent* const nextSibling = aStartChild->GetNextSibling();
6969 MOZ_ASSERT(nextSibling && nextSibling->IsText(),
6970 "expected a text node after the list-style-image image");
6971 DestroyContext context(mPresShell);
6972 RemoveFrame(context, FrameChildListID::Principal,
6973 nextSibling->GetPrimaryFrame());
6974 auto* const container = aStartChild->GetParent()->AsElement();
6975 nsIContent* firstNewChild = nullptr;
6976 auto InsertChild = [this, container, nextSibling,
6977 &firstNewChild](RefPtr<nsIContent>&& aChild) {
6978 // We don't strictly have to set NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE
6979 // here; it would get set under AppendChildTo. But AppendChildTo might
6980 // think that we're going from not being anonymous to being anonymous and
6981 // do some extra work; setting the flag here avoids that.
6982 aChild->SetFlags(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE);
6983 container->InsertChildBefore(aChild, nextSibling, false, IgnoreErrors());
6984 if (auto* childElement = Element::FromNode(aChild)) {
6985 // If we created any children elements, Servo needs to traverse them,
6986 // but the root is already set up.
6987 mPresShell->StyleSet()->StyleNewSubtree(childElement);
6989 if (!firstNewChild) {
6990 firstNewChild = aChild;
6993 CreateGeneratedContentFromListStyleType(
6994 state, *insertion.mContainer->AsElement(),
6995 *insertion.mParentFrame->Style(), InsertChild);
6996 if (!firstNewChild) {
6997 // No fallback content - we're done.
6998 return;
7000 aStartChild = firstNewChild;
7001 MOZ_ASSERT(firstNewChild->GetNextSibling() == nextSibling,
7002 "list-style-type should only create one child");
7005 AutoFrameConstructionItemList items(this);
7006 RefPtr<ComputedStyle> parentStyle =
7007 ResolveComputedStyle(insertion.mContainer);
7008 ParentType parentType = GetParentType(frameType);
7009 FlattenedChildIterator iter(insertion.mContainer);
7010 const bool haveNoShadowDOM =
7011 !iter.ShadowDOMInvolved() || !iter.GetNextChild();
7012 if (aStartChild->GetPreviousSibling() && parentType == eTypeBlock &&
7013 haveNoShadowDOM) {
7014 // If there's a text node in the normal content list just before the
7015 // new nodes, and it has no frame, make a frame construction item for
7016 // it, because it might need a frame now. No need to do this if our
7017 // parent type is not block, though, since WipeContainingBlock
7018 // already handles that situation.
7019 AddTextItemIfNeeded(state, *parentStyle, insertion,
7020 aStartChild->GetPreviousSibling(), items);
7023 if (isSingleInsert) {
7024 AddFrameConstructionItems(state, aStartChild,
7025 aStartChild->IsRootOfNativeAnonymousSubtree(),
7026 *parentStyle, insertion, items);
7027 } else {
7028 for (nsIContent* child = aStartChild; child != aEndChild;
7029 child = child->GetNextSibling()) {
7030 AddFrameConstructionItems(state, child, false, *parentStyle, insertion,
7031 items);
7035 if (aEndChild && parentType == eTypeBlock && haveNoShadowDOM) {
7036 // If there's a text node in the normal content list just after the
7037 // new nodes, and it has no frame, make a frame construction item for
7038 // it, because it might need a frame now. No need to do this if our
7039 // parent type is not block, though, since WipeContainingBlock
7040 // already handles that situation.
7041 AddTextItemIfNeeded(state, *parentStyle, insertion, aEndChild, items);
7044 // Perform special check for diddling around with the frames in
7045 // a special inline frame.
7046 // If we're appending before :after content, then we're not really
7047 // appending, so let WipeContainingBlock know that.
7048 LAYOUT_PHASE_TEMP_EXIT();
7049 if (WipeContainingBlock(state, containingBlock, insertion.mParentFrame, items,
7050 isAppend, prevSibling)) {
7051 LAYOUT_PHASE_TEMP_REENTER();
7052 return;
7054 LAYOUT_PHASE_TEMP_REENTER();
7056 nsFrameConstructorSaveState floatSaveState;
7057 state.MaybePushFloatContainingBlock(insertion.mParentFrame, floatSaveState);
7059 if (state.mPresContext->IsPaginated()) {
7060 // Because this function can be called outside frame construction, we need
7061 // to set state.mAutoPageNameValue based on what the parent frame's auto
7062 // value is.
7063 // Calling this from outside the frame constructor can violate many of the
7064 // expectations in AutoFrameConstructionPageName, and unlike during frame
7065 // construction we already have an auto value from parentFrame, so we do
7066 // not use AutoFrameConstructionPageName here.
7067 state.mAutoPageNameValue = insertion.mParentFrame->GetAutoPageValue();
7068 #ifdef DEBUG
7069 insertion.mParentFrame->mWasVisitedByAutoFrameConstructionPageName = true;
7070 #endif
7073 // If the container is a table and a caption will be appended, it needs to be
7074 // put in the table wrapper frame's additional child list.
7075 // We make no attempt here to set flags to indicate whether the list
7076 // will be at the start or end of a block. It doesn't seem worthwhile.
7077 nsFrameList frameList, captionList;
7078 ConstructFramesFromItemList(state, items, insertion.mParentFrame,
7079 ParentIsWrapperAnonBox(insertion.mParentFrame),
7080 frameList);
7082 if (frameList.NotEmpty()) {
7083 for (nsIContent* child = aStartChild; child != aEndChild;
7084 child = child->GetNextSibling()) {
7085 InvalidateCanvasIfNeeded(mPresShell, child);
7088 if (LayoutFrameType::Table == frameType ||
7089 LayoutFrameType::TableWrapper == frameType) {
7090 PullOutCaptionFrames(frameList, captionList);
7094 if (haveFirstLineStyle && insertion.mParentFrame == containingBlock &&
7095 isAppend) {
7096 // It's possible that the new frame goes into a first-line
7097 // frame. Look at it and see...
7098 AppendFirstLineFrames(state, containingBlock->GetContent(), containingBlock,
7099 frameList);
7100 } else if (insertion.mParentFrame->Style()->HasPseudoElementData()) {
7101 CheckForFirstLineInsertion(insertion.mParentFrame, frameList);
7102 CheckForFirstLineInsertion(insertion.mParentFrame, captionList);
7105 // We might have captions; put them into the caption list of the
7106 // table wrapper frame.
7107 if (captionList.NotEmpty()) {
7108 NS_ASSERTION(LayoutFrameType::Table == frameType ||
7109 LayoutFrameType::TableWrapper == frameType,
7110 "parent for caption is not table?");
7111 // We need to determine where to put the caption items; start with the
7112 // the parent frame that has already been determined and get the insertion
7113 // prevsibling of the first caption item.
7114 bool captionIsAppend;
7115 nsIFrame* captionPrevSibling = nullptr;
7117 // aIsRangeInsertSafe is ignored on purpose because it is irrelevant here.
7118 bool ignored;
7119 InsertionPoint captionInsertion = insertion;
7120 if (isSingleInsert) {
7121 captionPrevSibling = GetInsertionPrevSibling(
7122 &captionInsertion, aStartChild, &captionIsAppend, &ignored);
7123 } else {
7124 nsIContent* firstCaption = captionList.FirstChild()->GetContent();
7125 // It is very important here that we skip the children in
7126 // [aStartChild,aEndChild) when looking for a
7127 // prevsibling.
7128 captionPrevSibling = GetInsertionPrevSibling(
7129 &captionInsertion, firstCaption, &captionIsAppend, &ignored,
7130 aStartChild, aEndChild);
7133 nsContainerFrame* outerTable =
7134 captionInsertion.mParentFrame->IsTableFrame()
7135 ? captionInsertion.mParentFrame->GetParent()
7136 : captionInsertion.mParentFrame;
7138 // If the parent is not a table wrapper frame we will try to add frames
7139 // to a named child list that the parent does not honor and the frames
7140 // will get lost.
7141 MOZ_ASSERT(outerTable->IsTableWrapperFrame(),
7142 "Pseudo frame construction failure; "
7143 "a caption can be only a child of a table wrapper frame");
7145 // If the parent of our current prevSibling is different from the frame
7146 // we'll actually use as the parent, then the calculated insertion
7147 // point is now invalid (bug 341382).
7148 if (captionPrevSibling && captionPrevSibling->GetParent() != outerTable) {
7149 captionPrevSibling = nullptr;
7152 captionList.ApplySetParent(outerTable);
7153 if (captionIsAppend) {
7154 AppendFrames(outerTable, FrameChildListID::Caption,
7155 std::move(captionList));
7156 } else {
7157 InsertFrames(outerTable, FrameChildListID::Caption, captionPrevSibling,
7158 std::move(captionList));
7162 LAYOUT_PHASE_TEMP_EXIT();
7163 if (MaybeRecreateForColumnSpan(state, insertion.mParentFrame, frameList,
7164 prevSibling)) {
7165 LAYOUT_PHASE_TEMP_REENTER();
7166 return;
7168 LAYOUT_PHASE_TEMP_REENTER();
7170 if (frameList.NotEmpty()) {
7171 // Notify the parent frame
7172 if (isAppend) {
7173 AppendFramesToParent(state, insertion.mParentFrame, frameList,
7174 prevSibling);
7175 } else {
7176 InsertFrames(insertion.mParentFrame, FrameChildListID::Principal,
7177 prevSibling, std::move(frameList));
7181 if (haveFirstLetterStyle) {
7182 // Recover the letter frames for the containing block when
7183 // it has first-letter style.
7184 RecoverLetterFrames(state.mFloatedList.mContainingBlock);
7187 #ifdef DEBUG
7188 if (gReallyNoisyContentUpdates && insertion.mParentFrame) {
7189 printf(
7190 "nsCSSFrameConstructor::ContentRangeInserted: resulting frame "
7191 "model:\n");
7192 insertion.mParentFrame->List(stdout);
7194 #endif
7196 #ifdef ACCESSIBILITY
7197 if (nsAccessibilityService* accService = GetAccService()) {
7198 accService->ContentRangeInserted(mPresShell, aStartChild, aEndChild);
7200 #endif
7203 bool nsCSSFrameConstructor::ContentRemoved(nsIContent* aChild,
7204 nsIContent* aOldNextSibling,
7205 RemoveFlags aFlags) {
7206 MOZ_ASSERT(aChild);
7207 MOZ_ASSERT(!aChild->IsRootOfNativeAnonymousSubtree() || !aOldNextSibling,
7208 "Anonymous roots don't have siblings");
7209 AUTO_PROFILER_LABEL_HOT("nsCSSFrameConstructor::ContentRemoved",
7210 LAYOUT_FrameConstruction);
7211 AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
7212 nsPresContext* presContext = mPresShell->GetPresContext();
7213 MOZ_ASSERT(presContext, "Our presShell should have a valid presContext");
7215 // We want to detect when the viewport override element stored in the
7216 // prescontext is in the subtree being removed. Except in fullscreen cases
7217 // (which are handled in Element::UnbindFromTree and do not get stored on the
7218 // prescontext), the override element is always either the root element or a
7219 // <body> child of the root element. So we can only be removing the stored
7220 // override element if the thing being removed is either the override element
7221 // itself or the root element (which can be a parent of the override element).
7222 if (aChild == presContext->GetViewportScrollStylesOverrideElement() ||
7223 (aChild->IsElement() && !aChild->GetParent())) {
7224 // We might be removing the element that we propagated viewport scrollbar
7225 // styles from. Recompute those. (This clause covers two of the three
7226 // possible scrollbar-propagation sources: the <body> [as aChild or a
7227 // descendant] and the root node. The other possible scrollbar-propagation
7228 // source is a fullscreen element, and we have code elsewhere to update
7229 // scrollbars after fullscreen elements are removed -- specifically, it's
7230 // part of the fullscreen cleanup code called by Element::UnbindFromTree.
7231 // We don't handle the fullscreen case here, because it doesn't change the
7232 // scrollbar styles override element stored on the prescontext.)
7233 Element* newOverrideElement =
7234 presContext->UpdateViewportScrollStylesOverride();
7236 // If aChild is the root, then we don't need to do any reframing of
7237 // newOverrideElement, because we're about to tear down the whole frame tree
7238 // anyway. And we need to make sure we don't do any such reframing, because
7239 // reframing the <body> can trigger a reframe of the <html> and then reenter
7240 // here.
7242 // But if aChild is not the root, and if newOverrideElement is not
7243 // the root and isn't aChild (which it could be if all we're doing
7244 // here is reframing the current override element), it needs
7245 // reframing. In particular, it used to have a scrollframe
7246 // (because its overflow was not "visible"), but now it will
7247 // propagate its overflow to the viewport, so it should not need a
7248 // scrollframe anymore.
7249 if (aChild->GetParent() && newOverrideElement &&
7250 newOverrideElement->GetParent() && newOverrideElement != aChild) {
7251 LAYOUT_PHASE_TEMP_EXIT();
7252 RecreateFramesForContent(newOverrideElement, InsertionKind::Async);
7253 LAYOUT_PHASE_TEMP_REENTER();
7257 #ifdef DEBUG
7258 if (gNoisyContentUpdates) {
7259 printf(
7260 "nsCSSFrameConstructor::ContentRemoved container=%p child=%p "
7261 "old-next-sibling=%p\n",
7262 aChild->GetParent(), aChild, aOldNextSibling);
7263 if (gReallyNoisyContentUpdates) {
7264 aChild->GetParent()->List(stdout, 0);
7267 #endif
7269 nsIFrame* childFrame = aChild->GetPrimaryFrame();
7270 if (!childFrame || childFrame->GetContent() != aChild) {
7271 // XXXbz the GetContent() != aChild check is needed due to bug 135040.
7272 // Remove it once that's fixed.
7273 childFrame = nullptr;
7276 // If we're removing the root, then make sure to remove things starting at
7277 // the viewport's child instead of the primary frame (which might even be
7278 // null if the root was display:none, even though the frames above it got
7279 // created). Detecting removal of a root is a little exciting; in particular,
7280 // having no parent is necessary but NOT sufficient.
7282 // Due to how we process reframes, the content node might not even be in our
7283 // document by now. So explicitly check whether the viewport's first kid's
7284 // content node is aChild.
7286 // FIXME(emilio): I think the "might not be in our document" bit is impossible
7287 // now.
7288 bool isRoot = false;
7289 if (!aChild->GetParent()) {
7290 if (nsIFrame* viewport = GetRootFrame()) {
7291 nsIFrame* firstChild = viewport->PrincipalChildList().FirstChild();
7292 if (firstChild && firstChild->GetContent() == aChild) {
7293 isRoot = true;
7294 childFrame = firstChild;
7295 NS_ASSERTION(!childFrame->GetNextSibling(), "How did that happen?");
7300 // We need to be conservative about when to determine whether something has
7301 // display: contents or not because at this point our actual display may be
7302 // different.
7304 // Consider the case of:
7306 // <div id="A" style="display: contents"><div id="B"></div></div>
7308 // If we reconstruct A because its display changed to "none", we still need to
7309 // cleanup the frame on B, but A's display is now "none", so we can't poke at
7310 // the style of it.
7312 // FIXME(emilio, bug 1450366): We can make this faster without adding much
7313 // complexity for the display: none -> other case, which right now
7314 // unnecessarily walks the content tree down.
7315 auto CouldHaveBeenDisplayContents = [aFlags](nsIContent* aContent) -> bool {
7316 return aFlags == REMOVE_FOR_RECONSTRUCTION || IsDisplayContents(aContent);
7319 if (!childFrame && CouldHaveBeenDisplayContents(aChild)) {
7320 // NOTE(emilio): We may iterate through ::before and ::after here and they
7321 // may be gone after the respective ContentRemoved call. Right now
7322 // StyleChildrenIterator handles that properly, so it's not an issue.
7323 StyleChildrenIterator iter(aChild);
7324 for (nsIContent* c = iter.GetNextChild(); c; c = iter.GetNextChild()) {
7325 if (c->GetPrimaryFrame() || CouldHaveBeenDisplayContents(c)) {
7326 LAYOUT_PHASE_TEMP_EXIT();
7327 bool didReconstruct = ContentRemoved(c, nullptr, aFlags);
7328 LAYOUT_PHASE_TEMP_REENTER();
7329 if (didReconstruct) {
7330 return true;
7334 return false;
7337 if (childFrame) {
7338 if (aFlags == REMOVE_FOR_RECONSTRUCTION) {
7339 // Before removing the frames associated with the content object,
7340 // ask them to save their state onto our state object.
7341 CaptureStateForFramesOf(aChild, mFrameTreeState);
7344 InvalidateCanvasIfNeeded(mPresShell, aChild);
7346 // See whether we need to remove more than just childFrame
7347 LAYOUT_PHASE_TEMP_EXIT();
7348 if (MaybeRecreateContainerForFrameRemoval(childFrame)) {
7349 LAYOUT_PHASE_TEMP_REENTER();
7350 return true;
7352 LAYOUT_PHASE_TEMP_REENTER();
7354 // Get the childFrame's parent frame
7355 nsIFrame* parentFrame = childFrame->GetParent();
7356 LayoutFrameType parentType = parentFrame->Type();
7358 if (parentType == LayoutFrameType::FrameSet &&
7359 IsSpecialFramesetChild(aChild)) {
7360 // Just reframe the parent, since framesets are weird like that.
7361 LAYOUT_PHASE_TEMP_EXIT();
7362 RecreateFramesForContent(parentFrame->GetContent(), InsertionKind::Async);
7363 LAYOUT_PHASE_TEMP_REENTER();
7364 return true;
7367 // If we're a child of MathML, then we should reframe the MathML content.
7368 // If we're non-MathML, then we would be wrapped in a block so we need to
7369 // check our grandparent in that case.
7370 nsIFrame* possibleMathMLAncestor = parentType == LayoutFrameType::Block
7371 ? parentFrame->GetParent()
7372 : parentFrame;
7373 if (possibleMathMLAncestor->IsMathMLFrame()) {
7374 LAYOUT_PHASE_TEMP_EXIT();
7375 RecreateFramesForContent(parentFrame->GetContent(), InsertionKind::Async);
7376 LAYOUT_PHASE_TEMP_REENTER();
7377 return true;
7380 #ifdef ACCESSIBILITY
7381 if (aFlags != REMOVE_FOR_RECONSTRUCTION) {
7382 if (nsAccessibilityService* accService = GetAccService()) {
7383 accService->ContentRemoved(mPresShell, aChild);
7386 #endif
7388 // Examine the containing-block for the removed content and see if
7389 // :first-letter style applies.
7390 nsIFrame* inflowChild = childFrame;
7391 if (childFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
7392 inflowChild = childFrame->GetPlaceholderFrame();
7393 NS_ASSERTION(inflowChild, "No placeholder for out-of-flow?");
7395 nsContainerFrame* containingBlock =
7396 GetFloatContainingBlock(inflowChild->GetParent());
7397 bool haveFLS = containingBlock && HasFirstLetterStyle(containingBlock);
7398 if (haveFLS) {
7399 // Trap out to special routine that handles adjusting a blocks
7400 // frame tree when first-letter style is present.
7401 #ifdef NOISY_FIRST_LETTER
7402 printf("ContentRemoved: containingBlock=");
7403 containingBlock->ListTag(stdout);
7404 printf(" parentFrame=");
7405 parentFrame->ListTag(stdout);
7406 printf(" childFrame=");
7407 childFrame->ListTag(stdout);
7408 printf("\n");
7409 #endif
7411 // First update the containing blocks structure by removing the
7412 // existing letter frames. This makes the subsequent logic
7413 // simpler.
7414 RemoveLetterFrames(mPresShell, containingBlock);
7416 // Recover childFrame and parentFrame
7417 childFrame = aChild->GetPrimaryFrame();
7418 if (!childFrame || childFrame->GetContent() != aChild) {
7419 // XXXbz the GetContent() != aChild check is needed due to bug 135040.
7420 // Remove it once that's fixed.
7421 return false;
7423 parentFrame = childFrame->GetParent();
7424 parentType = parentFrame->Type();
7426 #ifdef NOISY_FIRST_LETTER
7427 printf(" ==> revised parentFrame=");
7428 parentFrame->ListTag(stdout);
7429 printf(" childFrame=");
7430 childFrame->ListTag(stdout);
7431 printf("\n");
7432 #endif
7435 #ifdef DEBUG
7436 if (gReallyNoisyContentUpdates) {
7437 printf("nsCSSFrameConstructor::ContentRemoved: childFrame=");
7438 childFrame->ListTag(stdout);
7439 putchar('\n');
7440 parentFrame->List(stdout);
7442 #endif
7444 // Notify the parent frame that it should delete the frame
7445 if (childFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
7446 childFrame = childFrame->GetPlaceholderFrame();
7447 NS_ASSERTION(childFrame, "Missing placeholder frame for out of flow.");
7448 parentFrame = childFrame->GetParent();
7451 DestroyContext context(mPresShell);
7452 RemoveFrame(context, nsLayoutUtils::GetChildListNameFor(childFrame),
7453 childFrame);
7455 // NOTE(emilio): aChild could be dead here already if it is a ::before or
7456 // ::after pseudo-element (since in that case it was owned by childFrame,
7457 // which we just destroyed).
7459 if (isRoot) {
7460 mRootElementFrame = nullptr;
7461 mRootElementStyleFrame = nullptr;
7462 mDocElementContainingBlock = nullptr;
7463 mCanvasFrame = nullptr;
7464 mPageSequenceFrame = nullptr;
7467 if (haveFLS && mRootElementFrame) {
7468 RecoverLetterFrames(containingBlock);
7471 // If we're just reconstructing frames for the element, then the
7472 // following ContentInserted notification on the element will
7473 // take care of fixing up any adjacent text nodes. We don't need
7474 // to do this if the table parent type of our parent type is not
7475 // eTypeBlock, though, because in that case the whitespace isn't
7476 // being suppressed due to us anyway.
7477 if (aOldNextSibling && aFlags == REMOVE_CONTENT &&
7478 GetParentType(parentType) == eTypeBlock) {
7479 MOZ_ASSERT(aChild->GetParentNode(),
7480 "How did we have a sibling without a parent?");
7481 // Adjacent whitespace-only text nodes might have been suppressed if
7482 // this node does not have inline ends. Create frames for them now
7483 // if necessary.
7484 // Reframe any text node just before the node being removed, if there is
7485 // one, and if it's not the last child or the first child. If a whitespace
7486 // textframe was being suppressed and it's now the last child or first
7487 // child then it can stay suppressed since the parent must be a block
7488 // and hence it's adjacent to a block end.
7489 // If aOldNextSibling is null, then the text node before the node being
7490 // removed is the last node, and we don't need to worry about it.
7492 // FIXME(emilio): This should probably use the lazy frame construction
7493 // bits if possible instead of reframing it in place.
7494 nsIContent* prevSibling = aOldNextSibling->GetPreviousSibling();
7495 if (prevSibling && prevSibling->GetPreviousSibling()) {
7496 LAYOUT_PHASE_TEMP_EXIT();
7497 ReframeTextIfNeeded(prevSibling);
7498 LAYOUT_PHASE_TEMP_REENTER();
7500 // Reframe any text node just after the node being removed, if there is
7501 // one, and if it's not the last child or the first child.
7502 if (aOldNextSibling->GetNextSibling() &&
7503 aOldNextSibling->GetPreviousSibling()) {
7504 LAYOUT_PHASE_TEMP_EXIT();
7505 ReframeTextIfNeeded(aOldNextSibling);
7506 LAYOUT_PHASE_TEMP_REENTER();
7510 #ifdef DEBUG
7511 if (gReallyNoisyContentUpdates && parentFrame) {
7512 printf("nsCSSFrameConstructor::ContentRemoved: resulting frame model:\n");
7513 parentFrame->List(stdout);
7515 #endif
7518 return false;
7522 * This method invalidates the canvas when frames are removed or added for a
7523 * node that might have its background propagated to the canvas, i.e., a
7524 * document root node or an HTML BODY which is a child of the root node.
7526 * @param aFrame a frame for a content node about to be removed or a frame that
7527 * was just created for a content node that was inserted.
7529 static void InvalidateCanvasIfNeeded(PresShell* aPresShell, nsIContent* aNode) {
7530 MOZ_ASSERT(aPresShell->GetRootFrame(), "What happened here?");
7531 MOZ_ASSERT(aPresShell->GetPresContext(), "Say what?");
7533 // Note that both in ContentRemoved and ContentInserted the content node
7534 // will still have the right parent pointer, so looking at that is ok.
7536 nsIContent* parent = aNode->GetParent();
7537 if (parent) {
7538 // Has a parent; might not be what we want
7539 nsIContent* grandParent = parent->GetParent();
7540 if (grandParent) {
7541 // Has a grandparent, so not what we want
7542 return;
7545 // Check whether it's an HTML body
7546 if (!aNode->IsHTMLElement(nsGkAtoms::body)) {
7547 return;
7551 // At this point the node has no parent or it's an HTML <body> child of the
7552 // root. We might not need to invalidate in this case (eg we might be in
7553 // XHTML or something), but chances are we want to. Play it safe.
7554 // Invalidate the viewport.
7556 nsIFrame* rootFrame = aPresShell->GetRootFrame();
7557 rootFrame->InvalidateFrameSubtree();
7560 bool nsCSSFrameConstructor::EnsureFrameForTextNodeIsCreatedAfterFlush(
7561 CharacterData* aContent) {
7562 if (!aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE)) {
7563 return false;
7566 if (mAlwaysCreateFramesForIgnorableWhitespace) {
7567 return false;
7570 // Text frame may have been suppressed. Disable suppression and signal that a
7571 // flush should be performed. We do this on a document-wide basis so that
7572 // pages that repeatedly query metrics for collapsed-whitespace text nodes
7573 // don't trigger pathological behavior.
7574 mAlwaysCreateFramesForIgnorableWhitespace = true;
7575 Element* root = mDocument->GetRootElement();
7576 if (!root) {
7577 return false;
7580 RestyleManager()->PostRestyleEvent(root, RestyleHint{0},
7581 nsChangeHint_ReconstructFrame);
7582 return true;
7585 void nsCSSFrameConstructor::CharacterDataChanged(
7586 nsIContent* aContent, const CharacterDataChangeInfo& aInfo) {
7587 AUTO_PROFILER_LABEL_HOT("nsCSSFrameConstructor::CharacterDataChanged",
7588 LAYOUT_FrameConstruction);
7589 AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
7591 if ((aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE) &&
7592 !aContent->TextIsOnlyWhitespace()) ||
7593 (aContent->HasFlag(NS_REFRAME_IF_WHITESPACE) &&
7594 aContent->TextIsOnlyWhitespace())) {
7595 #ifdef DEBUG
7596 nsIFrame* frame = aContent->GetPrimaryFrame();
7597 NS_ASSERTION(!frame || !frame->IsGeneratedContentFrame(),
7598 "Bit should never be set on generated content");
7599 #endif
7600 LAYOUT_PHASE_TEMP_EXIT();
7601 RecreateFramesForContent(aContent, InsertionKind::Async);
7602 LAYOUT_PHASE_TEMP_REENTER();
7603 return;
7606 // It's possible the frame whose content changed isn't inserted into the
7607 // frame hierarchy yet, or that there is no frame that maps the content
7608 if (nsIFrame* frame = aContent->GetPrimaryFrame()) {
7609 #if 0
7610 NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
7611 ("nsCSSFrameConstructor::CharacterDataChanged: content=%p[%s] subcontent=%p frame=%p",
7612 aContent, ContentTag(aContent, 0),
7613 aSubContent, frame));
7614 #endif
7616 if (frame->HasAnyStateBits(NS_FRAME_IS_IN_SINGLE_CHAR_MI)) {
7617 LAYOUT_PHASE_TEMP_EXIT();
7618 RecreateFramesForContent(aContent, InsertionKind::Async);
7619 LAYOUT_PHASE_TEMP_REENTER();
7620 return;
7623 // Special check for text content that is a child of a letter frame. If
7624 // this happens, we should remove the letter frame, do whatever we're
7625 // planning to do with this notification, then put the letter frame back.
7626 // Note that this is basically what RecreateFramesForContent ends up doing;
7627 // the reason we dont' want to call that here is that our text content
7628 // could be native anonymous, in which case RecreateFramesForContent would
7629 // completely barf on it. And recreating the non-anonymous ancestor would
7630 // just lead us to come back into this notification (e.g. if quotes or
7631 // counters are involved), leading to a loop.
7632 nsContainerFrame* block = GetFloatContainingBlock(frame);
7633 bool haveFirstLetterStyle = false;
7634 if (block) {
7635 // See if the block has first-letter style applied to it.
7636 haveFirstLetterStyle = HasFirstLetterStyle(block);
7637 if (haveFirstLetterStyle) {
7638 RemoveLetterFrames(mPresShell, block);
7639 // Reget |frame|, since we might have killed it.
7640 // Do we really need to call CharacterDataChanged in this case, though?
7641 frame = aContent->GetPrimaryFrame();
7642 NS_ASSERTION(frame, "Should have frame here!");
7646 // Notify the first frame that maps the content. It will generate a reflow
7647 // command
7648 frame->CharacterDataChanged(aInfo);
7650 if (haveFirstLetterStyle) {
7651 RecoverLetterFrames(block);
7656 void nsCSSFrameConstructor::RecalcQuotesAndCounters() {
7657 nsAutoScriptBlocker scriptBlocker;
7659 if (mQuotesDirty) {
7660 mQuotesDirty = false;
7661 mContainStyleScopeManager.RecalcAllQuotes();
7664 if (mCountersDirty) {
7665 mCountersDirty = false;
7666 mContainStyleScopeManager.RecalcAllCounters();
7669 NS_ASSERTION(!mQuotesDirty, "Quotes updates will be lost");
7670 NS_ASSERTION(!mCountersDirty, "Counter updates will be lost");
7673 void nsCSSFrameConstructor::NotifyCounterStylesAreDirty() {
7674 mContainStyleScopeManager.SetAllCountersDirty();
7675 CountersDirty();
7678 void nsCSSFrameConstructor::WillDestroyFrameTree() {
7679 #if defined(DEBUG_dbaron_off)
7680 mContainStyleScopeManager.DumpCounters();
7681 #endif
7683 // Prevent frame tree destruction from being O(N^2)
7684 mContainStyleScopeManager.Clear();
7685 nsFrameManager::Destroy();
7688 // STATIC
7690 // XXXbz I'd really like this method to go away. Once we have inline-block and
7691 // I can just use that for sized broken images, that can happen, maybe.
7693 // NOTE(emilio): This needs to match MozAltContent handling.
7694 void nsCSSFrameConstructor::GetAlternateTextFor(const Element& aElement,
7695 nsAString& aAltText) {
7696 // The "alt" attribute specifies alternate text that is rendered
7697 // when the image can not be displayed.
7698 if (aElement.GetAttr(nsGkAtoms::alt, aAltText)) {
7699 return;
7702 if (aElement.IsHTMLElement(nsGkAtoms::input)) {
7703 // If there's no "alt" attribute, and aElement is an input element, then use
7704 // the value of the "value" attribute.
7705 if (aElement.GetAttr(nsGkAtoms::value, aAltText)) {
7706 return;
7709 // If there's no "value" attribute either, then use the localized string for
7710 // "Submit" as the alternate text.
7711 nsContentUtils::GetMaybeLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
7712 "Submit", aElement.OwnerDoc(),
7713 aAltText);
7717 nsIFrame* nsCSSFrameConstructor::CreateContinuingOuterTableFrame(
7718 nsIFrame* aFrame, nsContainerFrame* aParentFrame, nsIContent* aContent,
7719 ComputedStyle* aComputedStyle) {
7720 nsTableWrapperFrame* newFrame =
7721 NS_NewTableWrapperFrame(mPresShell, aComputedStyle);
7723 newFrame->Init(aContent, aParentFrame, aFrame);
7725 // Create a continuing inner table frame, and if there's a caption then
7726 // replicate the caption
7727 nsFrameList newChildFrames;
7729 nsIFrame* childFrame = aFrame->PrincipalChildList().FirstChild();
7730 if (childFrame) {
7731 nsIFrame* continuingTableFrame =
7732 CreateContinuingFrame(childFrame, newFrame);
7733 newChildFrames.AppendFrame(nullptr, continuingTableFrame);
7735 NS_ASSERTION(!childFrame->GetNextSibling(),
7736 "there can be only one inner table frame");
7739 // Set the table wrapper's initial child list
7740 newFrame->SetInitialChildList(FrameChildListID::Principal,
7741 std::move(newChildFrames));
7743 return newFrame;
7746 nsIFrame* nsCSSFrameConstructor::CreateContinuingTableFrame(
7747 nsIFrame* aFrame, nsContainerFrame* aParentFrame, nsIContent* aContent,
7748 ComputedStyle* aComputedStyle) {
7749 nsTableFrame* newFrame = NS_NewTableFrame(mPresShell, aComputedStyle);
7751 newFrame->Init(aContent, aParentFrame, aFrame);
7753 // Replicate any header/footer frames
7754 nsFrameList childFrames;
7755 for (nsIFrame* childFrame : aFrame->PrincipalChildList()) {
7756 // See if it's a header/footer, possibly wrapped in a scroll frame.
7757 nsTableRowGroupFrame* rowGroupFrame =
7758 static_cast<nsTableRowGroupFrame*>(childFrame);
7759 // If the row group was continued, then don't replicate it.
7760 nsIFrame* rgNextInFlow = rowGroupFrame->GetNextInFlow();
7761 if (rgNextInFlow) {
7762 rowGroupFrame->SetRepeatable(false);
7763 } else if (rowGroupFrame->IsRepeatable()) {
7764 // Replicate the header/footer frame.
7765 nsTableRowGroupFrame* headerFooterFrame;
7766 nsFrameList childList;
7768 nsFrameConstructorState state(
7769 mPresShell, GetAbsoluteContainingBlock(newFrame, FIXED_POS),
7770 GetAbsoluteContainingBlock(newFrame, ABS_POS), nullptr);
7771 state.mCreatingExtraFrames = true;
7773 ComputedStyle* const headerFooterComputedStyle = rowGroupFrame->Style();
7774 headerFooterFrame = static_cast<nsTableRowGroupFrame*>(
7775 NS_NewTableRowGroupFrame(mPresShell, headerFooterComputedStyle));
7777 nsIContent* headerFooter = rowGroupFrame->GetContent();
7778 headerFooterFrame->Init(headerFooter, newFrame, nullptr);
7780 nsFrameConstructorSaveState absoluteSaveState;
7781 MakeTablePartAbsoluteContainingBlock(state, absoluteSaveState,
7782 headerFooterFrame);
7784 nsFrameConstructorSaveState floatSaveState;
7785 state.MaybePushFloatContainingBlock(headerFooterFrame, floatSaveState);
7787 ProcessChildren(state, headerFooter, rowGroupFrame->Style(),
7788 headerFooterFrame, true, childList, false, nullptr);
7789 NS_ASSERTION(state.mFloatedList.IsEmpty(), "unexpected floated element");
7790 headerFooterFrame->SetInitialChildList(FrameChildListID::Principal,
7791 std::move(childList));
7792 headerFooterFrame->SetRepeatable(true);
7794 // Table specific initialization
7795 headerFooterFrame->InitRepeatedFrame(rowGroupFrame);
7797 // XXX Deal with absolute and fixed frames...
7798 childFrames.AppendFrame(nullptr, headerFooterFrame);
7802 // Set the table frame's initial child list
7803 newFrame->SetInitialChildList(FrameChildListID::Principal,
7804 std::move(childFrames));
7806 return newFrame;
7809 nsIFrame* nsCSSFrameConstructor::CreateContinuingFrame(
7810 nsIFrame* aFrame, nsContainerFrame* aParentFrame, bool aIsFluid) {
7811 ComputedStyle* computedStyle = aFrame->Style();
7812 nsIFrame* newFrame = nullptr;
7813 nsIFrame* nextContinuation = aFrame->GetNextContinuation();
7814 nsIFrame* nextInFlow = aFrame->GetNextInFlow();
7816 // Use the frame type to determine what type of frame to create
7817 LayoutFrameType frameType = aFrame->Type();
7818 nsIContent* content = aFrame->GetContent();
7820 if (LayoutFrameType::Text == frameType) {
7821 newFrame = NS_NewContinuingTextFrame(mPresShell, computedStyle);
7822 newFrame->Init(content, aParentFrame, aFrame);
7823 } else if (LayoutFrameType::Inline == frameType) {
7824 newFrame = NS_NewInlineFrame(mPresShell, computedStyle);
7825 newFrame->Init(content, aParentFrame, aFrame);
7826 } else if (LayoutFrameType::Block == frameType) {
7827 MOZ_ASSERT(!aFrame->IsTableCaption(),
7828 "no support for fragmenting table captions yet");
7829 newFrame = NS_NewBlockFrame(mPresShell, computedStyle);
7830 newFrame->Init(content, aParentFrame, aFrame);
7831 } else if (LayoutFrameType::ColumnSetWrapper == frameType) {
7832 newFrame =
7833 NS_NewColumnSetWrapperFrame(mPresShell, computedStyle, nsFrameState(0));
7834 newFrame->Init(content, aParentFrame, aFrame);
7835 } else if (LayoutFrameType::ColumnSet == frameType) {
7836 MOZ_ASSERT(!aFrame->IsTableCaption(),
7837 "no support for fragmenting table captions yet");
7838 newFrame = NS_NewColumnSetFrame(mPresShell, computedStyle, nsFrameState(0));
7839 newFrame->Init(content, aParentFrame, aFrame);
7840 } else if (LayoutFrameType::PrintedSheet == frameType) {
7841 newFrame = ConstructPrintedSheetFrame(mPresShell, aParentFrame, aFrame);
7842 } else if (LayoutFrameType::Page == frameType) {
7843 nsCanvasFrame* canvasFrame; // (unused outparam for ConstructPageFrame)
7844 newFrame =
7845 ConstructPageFrame(mPresShell, aParentFrame, aFrame, canvasFrame);
7846 } else if (LayoutFrameType::TableWrapper == frameType) {
7847 newFrame = CreateContinuingOuterTableFrame(aFrame, aParentFrame, content,
7848 computedStyle);
7849 } else if (LayoutFrameType::Table == frameType) {
7850 newFrame = CreateContinuingTableFrame(aFrame, aParentFrame, content,
7851 computedStyle);
7852 } else if (LayoutFrameType::TableRowGroup == frameType) {
7853 newFrame = NS_NewTableRowGroupFrame(mPresShell, computedStyle);
7854 newFrame->Init(content, aParentFrame, aFrame);
7855 } else if (LayoutFrameType::TableRow == frameType) {
7856 nsTableRowFrame* rowFrame = NS_NewTableRowFrame(mPresShell, computedStyle);
7858 rowFrame->Init(content, aParentFrame, aFrame);
7860 // Create a continuing frame for each table cell frame
7861 nsFrameList newChildList;
7862 nsIFrame* cellFrame = aFrame->PrincipalChildList().FirstChild();
7863 while (cellFrame) {
7864 // See if it's a table cell frame
7865 if (cellFrame->IsTableCellFrame()) {
7866 nsIFrame* continuingCellFrame =
7867 CreateContinuingFrame(cellFrame, rowFrame);
7868 newChildList.AppendFrame(nullptr, continuingCellFrame);
7870 cellFrame = cellFrame->GetNextSibling();
7873 rowFrame->SetInitialChildList(FrameChildListID::Principal,
7874 std::move(newChildList));
7875 newFrame = rowFrame;
7877 } else if (LayoutFrameType::TableCell == frameType) {
7878 // Warning: If you change this and add a wrapper frame around table cell
7879 // frames, make sure Bug 368554 doesn't regress!
7880 // See IsInAutoWidthTableCellForQuirk() in nsImageFrame.cpp.
7881 nsTableFrame* tableFrame =
7882 static_cast<nsTableRowFrame*>(aParentFrame)->GetTableFrame();
7883 nsTableCellFrame* cellFrame =
7884 NS_NewTableCellFrame(mPresShell, computedStyle, tableFrame);
7886 cellFrame->Init(content, aParentFrame, aFrame);
7888 // Create a continuing area frame
7889 nsIFrame* blockFrame = aFrame->PrincipalChildList().FirstChild();
7890 nsIFrame* continuingBlockFrame =
7891 CreateContinuingFrame(blockFrame, cellFrame);
7893 SetInitialSingleChild(cellFrame, continuingBlockFrame);
7894 newFrame = cellFrame;
7895 } else if (LayoutFrameType::Line == frameType) {
7896 newFrame = NS_NewFirstLineFrame(mPresShell, computedStyle);
7897 newFrame->Init(content, aParentFrame, aFrame);
7898 } else if (LayoutFrameType::Letter == frameType) {
7899 newFrame = NS_NewFirstLetterFrame(mPresShell, computedStyle);
7900 newFrame->Init(content, aParentFrame, aFrame);
7901 } else if (LayoutFrameType::Image == frameType) {
7902 auto* imageFrame = static_cast<nsImageFrame*>(aFrame);
7903 newFrame = imageFrame->CreateContinuingFrame(mPresShell, computedStyle);
7904 newFrame->Init(content, aParentFrame, aFrame);
7905 } else if (LayoutFrameType::ImageControl == frameType) {
7906 newFrame = NS_NewImageControlFrame(mPresShell, computedStyle);
7907 newFrame->Init(content, aParentFrame, aFrame);
7908 } else if (LayoutFrameType::FieldSet == frameType) {
7909 newFrame = NS_NewFieldSetFrame(mPresShell, computedStyle);
7910 newFrame->Init(content, aParentFrame, aFrame);
7911 } else if (LayoutFrameType::FlexContainer == frameType) {
7912 newFrame = NS_NewFlexContainerFrame(mPresShell, computedStyle);
7913 newFrame->Init(content, aParentFrame, aFrame);
7914 } else if (LayoutFrameType::GridContainer == frameType) {
7915 newFrame = NS_NewGridContainerFrame(mPresShell, computedStyle);
7916 newFrame->Init(content, aParentFrame, aFrame);
7917 } else if (LayoutFrameType::Ruby == frameType) {
7918 newFrame = NS_NewRubyFrame(mPresShell, computedStyle);
7919 newFrame->Init(content, aParentFrame, aFrame);
7920 } else if (LayoutFrameType::RubyBaseContainer == frameType) {
7921 newFrame = NS_NewRubyBaseContainerFrame(mPresShell, computedStyle);
7922 newFrame->Init(content, aParentFrame, aFrame);
7923 } else if (LayoutFrameType::RubyTextContainer == frameType) {
7924 newFrame = NS_NewRubyTextContainerFrame(mPresShell, computedStyle);
7925 newFrame->Init(content, aParentFrame, aFrame);
7926 } else {
7927 MOZ_CRASH("unexpected frame type");
7930 // Init() set newFrame to be a fluid continuation of aFrame.
7931 // If we want a non-fluid continuation, we need to call SetPrevContinuation()
7932 // to reset NS_FRAME_IS_FLUID_CONTINUATION.
7933 if (!aIsFluid) {
7934 newFrame->SetPrevContinuation(aFrame);
7937 // If a continuing frame needs to carry frame state bits from its previous
7938 // continuation or parent, set them in nsIFrame::Init(), or in any derived
7939 // frame class's Init() if the bits are belong to specific group.
7941 if (nextInFlow) {
7942 nextInFlow->SetPrevInFlow(newFrame);
7943 newFrame->SetNextInFlow(nextInFlow);
7944 } else if (nextContinuation) {
7945 nextContinuation->SetPrevContinuation(newFrame);
7946 newFrame->SetNextContinuation(nextContinuation);
7949 // aFrame cannot be a dynamic reflow root because it has a continuation now.
7950 aFrame->RemoveStateBits(NS_FRAME_DYNAMIC_REFLOW_ROOT);
7952 MOZ_ASSERT(!newFrame->GetNextSibling(), "unexpected sibling");
7953 return newFrame;
7956 void nsCSSFrameConstructor::MaybeSetNextPageContentFramePageName(
7957 const nsIFrame* aFrame) {
7958 MOZ_ASSERT(aFrame, "Frame should not be null");
7959 // No parent means the root frame, which isn't what this funciton is for.
7960 MOZ_ASSERT(aFrame->GetParent(),
7961 "Frame should be the first child placed on a new page, not the "
7962 "root frame.");
7963 if (mNextPageContentFramePageName) {
7964 return;
7966 const nsAtom* const autoValue = aFrame->GetParent()->GetAutoPageValue();
7967 mNextPageContentFramePageName = aFrame->ComputePageValue(autoValue);
7970 nsresult nsCSSFrameConstructor::ReplicateFixedFrames(
7971 nsPageContentFrame* aParentFrame) {
7972 // Now deal with fixed-pos things.... They should appear on all pages,
7973 // so we want to move over the placeholders when processing the child
7974 // of the pageContentFrame.
7976 nsIFrame* prevPageContentFrame = aParentFrame->GetPrevInFlow();
7977 if (!prevPageContentFrame) {
7978 return NS_OK;
7980 nsContainerFrame* canvasFrame =
7981 do_QueryFrame(aParentFrame->PrincipalChildList().FirstChild());
7982 nsIFrame* prevCanvasFrame =
7983 prevPageContentFrame->PrincipalChildList().FirstChild();
7984 if (!canvasFrame || !prevCanvasFrame) {
7985 // document's root element frame missing
7986 return NS_ERROR_UNEXPECTED;
7989 nsFrameList fixedPlaceholders;
7990 nsIFrame* firstFixed =
7991 prevPageContentFrame->GetChildList(FrameChildListID::Fixed).FirstChild();
7992 if (!firstFixed) {
7993 return NS_OK;
7996 // Don't allow abs-pos descendants of the fixed content to escape the content.
7997 // This should not normally be possible (because fixed-pos elements should
7998 // be absolute containers) but fixed-pos tables currently aren't abs-pos
7999 // containers.
8000 nsFrameConstructorState state(mPresShell, aParentFrame, nullptr,
8001 mRootElementFrame);
8002 state.mCreatingExtraFrames = true;
8004 // We can't use an ancestor filter here, because we're not going to
8005 // be usefully recurring down the tree. This means that other
8006 // places in frame construction can't assume a filter is
8007 // initialized!
8009 // Iterate across fixed frames and replicate each whose placeholder is a
8010 // descendant of aFrame. (We don't want to explicitly copy placeholders that
8011 // are within fixed frames, because that would cause duplicates on the new
8012 // page - bug 389619)
8013 for (nsIFrame* fixed = firstFixed; fixed; fixed = fixed->GetNextSibling()) {
8014 nsIFrame* prevPlaceholder = fixed->GetPlaceholderFrame();
8015 if (prevPlaceholder && nsLayoutUtils::IsProperAncestorFrame(
8016 prevCanvasFrame, prevPlaceholder)) {
8017 // We want to use the same style as the primary style frame for
8018 // our content
8019 nsIContent* content = fixed->GetContent();
8020 ComputedStyle* computedStyle =
8021 nsLayoutUtils::GetStyleFrame(content)->Style();
8022 AutoFrameConstructionItemList items(this);
8023 AddFrameConstructionItemsInternal(state, content, canvasFrame, true,
8024 computedStyle,
8025 {ItemFlag::AllowPageBreak}, items);
8026 ConstructFramesFromItemList(state, items, canvasFrame,
8027 /* aParentIsWrapperAnonBox = */ false,
8028 fixedPlaceholders);
8032 // Add the placeholders to our primary child list.
8033 // XXXbz this is a little screwed up, since the fixed frames will have
8034 // broken auto-positioning. Oh, well.
8035 NS_ASSERTION(!canvasFrame->PrincipalChildList().FirstChild(),
8036 "leaking frames; doc root continuation must be empty");
8037 canvasFrame->SetInitialChildList(FrameChildListID::Principal,
8038 std::move(fixedPlaceholders));
8039 return NS_OK;
8042 nsCSSFrameConstructor::InsertionPoint nsCSSFrameConstructor::GetInsertionPoint(
8043 nsIContent* aChild) {
8044 MOZ_ASSERT(aChild);
8045 nsIContent* insertionElement = aChild->GetFlattenedTreeParent();
8046 if (!insertionElement) {
8047 // The element doesn't belong in the flattened tree, and thus we don't want
8048 // to render it.
8049 return {};
8052 return {GetContentInsertionFrameFor(insertionElement), insertionElement};
8055 // Capture state for the frame tree rooted at the frame associated with the
8056 // content object, aContent
8057 void nsCSSFrameConstructor::CaptureStateForFramesOf(
8058 nsIContent* aContent, nsILayoutHistoryState* aHistoryState) {
8059 if (!aHistoryState) {
8060 return;
8062 nsIFrame* frame = aContent->GetPrimaryFrame();
8063 if (frame == mRootElementFrame) {
8064 frame = mRootElementFrame
8065 ? GetAbsoluteContainingBlock(mRootElementFrame, FIXED_POS)
8066 : GetRootFrame();
8068 for (; frame;
8069 frame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(frame)) {
8070 CaptureFrameState(frame, aHistoryState);
8074 static bool IsWhitespaceFrame(nsIFrame* aFrame) {
8075 MOZ_ASSERT(aFrame, "invalid argument");
8076 return aFrame->IsTextFrame() && aFrame->GetContent()->TextIsOnlyWhitespace();
8079 static nsIFrame* FindFirstNonWhitespaceChild(nsIFrame* aParentFrame) {
8080 nsIFrame* f = aParentFrame->PrincipalChildList().FirstChild();
8081 while (f && IsWhitespaceFrame(f)) {
8082 f = f->GetNextSibling();
8084 return f;
8087 static nsIFrame* FindNextNonWhitespaceSibling(nsIFrame* aFrame) {
8088 nsIFrame* f = aFrame;
8089 do {
8090 f = f->GetNextSibling();
8091 } while (f && IsWhitespaceFrame(f));
8092 return f;
8095 static nsIFrame* FindPreviousNonWhitespaceSibling(nsIFrame* aFrame) {
8096 nsIFrame* f = aFrame;
8097 do {
8098 f = f->GetPrevSibling();
8099 } while (f && IsWhitespaceFrame(f));
8100 return f;
8103 bool nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(
8104 nsIFrame* aFrame) {
8105 #define TRACE(reason) \
8106 PROFILER_MARKER("MaybeRecreateContainerForFrameRemoval: " reason, LAYOUT, \
8107 {}, Tracing, "Layout")
8108 MOZ_ASSERT(aFrame, "Must have a frame");
8109 MOZ_ASSERT(aFrame->GetParent(), "Frame shouldn't be root");
8110 MOZ_ASSERT(aFrame == aFrame->FirstContinuation(),
8111 "aFrame not the result of GetPrimaryFrame()?");
8113 nsIFrame* inFlowFrame = aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)
8114 ? aFrame->GetPlaceholderFrame()
8115 : aFrame;
8116 MOZ_ASSERT(inFlowFrame, "How did that happen?");
8117 MOZ_ASSERT(inFlowFrame == inFlowFrame->FirstContinuation(),
8118 "placeholder for primary frame has previous continuations?");
8119 nsIFrame* parent = inFlowFrame->GetParent();
8121 if (inFlowFrame->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR)) {
8122 nsIFrame* grandparent = parent->GetParent();
8123 MOZ_ASSERT(grandparent);
8125 bool needsReframe =
8126 // 1. Removing a column-span may lead to an empty
8127 // ::-moz-column-span-wrapper.
8128 inFlowFrame->IsColumnSpan() ||
8129 // 2. Removing a frame which has any column-span siblings may also
8130 // lead to an empty ::-moz-column-span-wrapper subtree. The
8131 // column-span siblings were the frame's children, but later become
8132 // the frame's siblings after CreateColumnSpanSiblings().
8133 inFlowFrame->HasColumnSpanSiblings() ||
8134 // 3. Removing the only child of a ::-moz-column-content, whose
8135 // ColumnSet grandparent has a previous column-span sibling, requires
8136 // reframing since we might connect the ColumnSet's next column-span
8137 // sibling (if there's one). Note that this isn't actually needed if
8138 // the ColumnSet is at the end of ColumnSetWrapper since we create
8139 // empty ones at the end anyway, but we're not worried about
8140 // optimizing that case.
8141 (parent->Style()->GetPseudoType() == PseudoStyleType::columnContent &&
8142 // The only child in ::-moz-column-content (might be tall enough to
8143 // split across columns)
8144 !inFlowFrame->GetPrevSibling() && !inFlowFrame->GetNextSibling() &&
8145 // That ::-moz-column-content is the first column.
8146 !parent->GetPrevInFlow() &&
8147 // The ColumnSet grandparent has a previous sibling that is a
8148 // column-span.
8149 grandparent->GetPrevSibling());
8151 if (needsReframe) {
8152 nsContainerFrame* containingBlock =
8153 GetMultiColumnContainingBlockFor(inFlowFrame);
8155 #ifdef DEBUG
8156 if (IsFramePartOfIBSplit(inFlowFrame)) {
8157 nsIFrame* ibContainingBlock = GetIBContainingBlockFor(inFlowFrame);
8158 MOZ_ASSERT(containingBlock == ibContainingBlock ||
8159 nsLayoutUtils::IsProperAncestorFrame(containingBlock,
8160 ibContainingBlock),
8161 "Multi-column containing block should be equal to or be the "
8162 "ancestor of the IB containing block!");
8164 #endif
8166 TRACE("Multi-column");
8167 RecreateFramesForContent(containingBlock->GetContent(),
8168 InsertionKind::Async);
8169 return true;
8173 if (IsFramePartOfIBSplit(aFrame)) {
8174 // The removal functions can't handle removal of an {ib} split directly; we
8175 // need to rebuild the containing block.
8176 TRACE("IB split removal");
8177 ReframeContainingBlock(aFrame);
8178 return true;
8181 if (inFlowFrame->IsRenderedLegend()) {
8182 TRACE("Fieldset / Legend");
8183 RecreateFramesForContent(parent->GetContent(), InsertionKind::Async);
8184 return true;
8187 // Now check for possibly needing to reconstruct due to a pseudo parent
8188 // For the case of ruby pseudo parent, effectively, only pseudo rb/rt frame
8189 // need to be checked here, since all other types of parent will be catched
8190 // by "Check ruby containers" section below.
8191 if (IsTableOrRubyPseudo(parent)) {
8192 if (FindFirstNonWhitespaceChild(parent) == inFlowFrame ||
8193 !FindNextNonWhitespaceSibling(inFlowFrame->LastContinuation()) ||
8194 // If it is a whitespace, and is the only child of the parent, the
8195 // pseudo parent was created for the space, and should now be removed.
8196 (IsWhitespaceFrame(aFrame) &&
8197 parent->PrincipalChildList().OnlyChild()) ||
8198 // If we're a table-column-group, then the OnlyChild check above is
8199 // not going to catch cases when we're the first child.
8200 (inFlowFrame->IsTableColGroupFrame() &&
8201 parent->GetChildList(FrameChildListID::ColGroup).FirstChild() ==
8202 inFlowFrame) ||
8203 // Similar if we're a table-caption.
8204 (inFlowFrame->IsTableCaption() &&
8205 parent->GetChildList(FrameChildListID::Caption).FirstChild() ==
8206 inFlowFrame)) {
8207 TRACE("Table or ruby pseudo parent");
8208 RecreateFramesForContent(parent->GetContent(), InsertionKind::Async);
8209 return true;
8213 // Might need to reconstruct things if this frame's nextSibling is a table
8214 // or ruby pseudo, since removal of this frame might mean that this pseudo
8215 // needs to get merged with the frame's prevSibling if that's also a table
8216 // or ruby pseudo.
8217 nsIFrame* nextSibling =
8218 FindNextNonWhitespaceSibling(inFlowFrame->LastContinuation());
8219 NS_ASSERTION(!IsTableOrRubyPseudo(inFlowFrame), "Shouldn't happen here");
8220 // Effectively, for the ruby pseudo sibling case, only pseudo <ruby> frame
8221 // need to be checked here, since all other types of such frames will have
8222 // a ruby container parent, and be catched by "Check ruby containers" below.
8223 if (nextSibling && IsTableOrRubyPseudo(nextSibling)) {
8224 nsIFrame* prevSibling = FindPreviousNonWhitespaceSibling(inFlowFrame);
8225 if (prevSibling && IsTableOrRubyPseudo(prevSibling)) {
8226 TRACE("Table or ruby pseudo sibling");
8227 // Good enough to recreate frames for aFrame's parent's content; even if
8228 // aFrame's parent is a pseudo, that'll be the right content node.
8229 RecreateFramesForContent(parent->GetContent(), InsertionKind::Async);
8230 return true;
8234 // Check ruby containers
8235 LayoutFrameType parentType = parent->Type();
8236 if (parentType == LayoutFrameType::Ruby ||
8237 RubyUtils::IsRubyContainerBox(parentType)) {
8238 // In ruby containers, pseudo frames may be created from
8239 // whitespaces or even nothing. There are two cases we actually
8240 // need to handle here, but hard to check exactly:
8241 // 1. Status of spaces beside the frame may vary, and related
8242 // frames may be constructed or destroyed accordingly.
8243 // 2. The type of the first child of a ruby frame determines
8244 // whether a pseudo ruby base container should exist.
8245 TRACE("Ruby container");
8246 RecreateFramesForContent(parent->GetContent(), InsertionKind::Async);
8247 return true;
8250 // Might need to reconstruct things if the removed frame's nextSibling is an
8251 // anonymous flex item. The removed frame might've been what divided two
8252 // runs of inline content into two anonymous flex items, which would now
8253 // need to be merged.
8254 // NOTE: It's fine that we've advanced nextSibling past whitespace (up above);
8255 // we're only interested in anonymous flex items here, and those can never
8256 // be adjacent to whitespace, since they absorb contiguous runs of inline
8257 // non-replaced content (including whitespace).
8258 if (nextSibling && IsAnonymousItem(nextSibling)) {
8259 AssertAnonymousFlexOrGridItemParent(nextSibling, parent);
8260 TRACE("Anon flex or grid item next sibling");
8261 // Recreate frames for the flex container (the removed frame's parent)
8262 RecreateFramesForContent(parent->GetContent(), InsertionKind::Async);
8263 return true;
8266 // Might need to reconstruct things if the removed frame's nextSibling is
8267 // null and its parent is an anonymous flex item. (This might be the last
8268 // remaining child of that anonymous flex item, which can then go away.)
8269 if (!nextSibling && IsAnonymousItem(parent)) {
8270 AssertAnonymousFlexOrGridItemParent(parent, parent->GetParent());
8271 TRACE("Anon flex or grid item parent");
8272 // Recreate frames for the flex container (the removed frame's grandparent)
8273 RecreateFramesForContent(parent->GetParent()->GetContent(),
8274 InsertionKind::Async);
8275 return true;
8278 // Reconstruct if inflowFrame is parent's only child, and parent is, or has,
8279 // a non-fluid continuation, i.e. it was split by bidi resolution
8280 if (!inFlowFrame->GetPrevSibling() && !inFlowFrame->GetNextSibling() &&
8281 ((parent->GetPrevContinuation() && !parent->GetPrevInFlow()) ||
8282 (parent->GetNextContinuation() && !parent->GetNextInFlow()))) {
8283 TRACE("Removing last child of non-fluid split parent");
8284 RecreateFramesForContent(parent->GetContent(), InsertionKind::Async);
8285 return true;
8288 // We might still need to reconstruct things if the parent of inFlowFrame is
8289 // ib-split, since in that case the removal of aFrame might affect the
8290 // splitting of its parent.
8291 if (!IsFramePartOfIBSplit(parent)) {
8292 return false;
8295 // If inFlowFrame is not the only in-flow child of |parent|, then removing
8296 // it will change nothing about the {ib} split.
8297 if (inFlowFrame != parent->PrincipalChildList().FirstChild() ||
8298 inFlowFrame->LastContinuation()->GetNextSibling()) {
8299 return false;
8302 // If the parent is the first or last part of the {ib} split, then
8303 // removing one of its kids will have no effect on the splitting.
8304 // Get the first continuation up front so we don't have to do it twice.
8305 nsIFrame* parentFirstContinuation = parent->FirstContinuation();
8306 if (!GetIBSplitSibling(parentFirstContinuation) ||
8307 !GetIBSplitPrevSibling(parentFirstContinuation)) {
8308 return false;
8311 TRACE("IB split parent");
8312 ReframeContainingBlock(parent);
8313 return true;
8314 #undef TRACE
8317 void nsCSSFrameConstructor::UpdateTableCellSpans(nsIContent* aContent) {
8318 nsTableCellFrame* cellFrame = do_QueryFrame(aContent->GetPrimaryFrame());
8320 // It's possible that this warning could fire if some other style change
8321 // simultaneously changes the 'display' of the element and makes it no
8322 // longer be a table cell.
8323 NS_WARNING_ASSERTION(cellFrame, "Hint should only be posted on table cells!");
8325 if (cellFrame) {
8326 cellFrame->GetTableFrame()->RowOrColSpanChanged(cellFrame);
8330 static nsIContent* GetTopmostMathMLElement(nsIContent* aMathMLContent) {
8331 MOZ_ASSERT(aMathMLContent->IsMathMLElement());
8332 MOZ_ASSERT(aMathMLContent->GetPrimaryFrame());
8333 MOZ_ASSERT(aMathMLContent->GetPrimaryFrame()->IsMathMLFrame());
8334 nsIContent* root = aMathMLContent;
8336 for (nsIContent* parent = aMathMLContent->GetFlattenedTreeParent(); parent;
8337 parent = parent->GetFlattenedTreeParent()) {
8338 nsIFrame* frame = parent->GetPrimaryFrame();
8339 if (!frame || !frame->IsMathMLFrame()) {
8340 break;
8342 root = parent;
8345 return root;
8348 // We don't know how to re-insert an anonymous subtree root, so recreate the
8349 // closest non-generated ancestor instead, except for a few special cases...
8350 static bool ShouldRecreateContainerForNativeAnonymousContentRoot(
8351 nsIContent* aContent) {
8352 if (!aContent->IsRootOfNativeAnonymousSubtree()) {
8353 return false;
8355 if (ManualNACPtr::IsManualNAC(aContent)) {
8356 // Editor NAC, would enter an infinite loop, and we sorta get away with it
8357 // because it's all abspos.
8358 return false;
8360 if (auto* el = Element::FromNode(aContent)) {
8361 if (auto* classes = el->GetClasses()) {
8362 if (classes->Contains(nsGkAtoms::mozCustomContentContainer,
8363 eCaseMatters)) {
8364 // Canvas anonymous content (like the custom content container) is also
8365 // fine, because its only sibling is a tooltip which is also abspos, so
8366 // relative insertion order doesn't really matter.
8368 // This is important because the inspector uses it, and we don't want
8369 // inspecting the page to change behavior heavily (and reframing
8370 // unfortunately has side-effects sometimes, even though they're bugs).
8371 return false;
8376 return true;
8379 void nsCSSFrameConstructor::RecreateFramesForContent(
8380 nsIContent* aContent, InsertionKind aInsertionKind) {
8381 MOZ_ASSERT(aContent);
8383 // If there is no document, we don't want to recreate frames for it. (You
8384 // shouldn't generally be giving this method content without a document
8385 // anyway).
8386 // Rebuilding the frame tree can have bad effects, especially if it's the
8387 // frame tree for chrome (see bug 157322).
8388 if (NS_WARN_IF(!aContent->GetComposedDoc())) {
8389 return;
8392 // TODO(emilio): We technically can find the right insertion point nowadays
8393 // using StyleChildrenIterator rather than FlattenedTreeIterator. But we'd
8394 // need to tweak the setup to insert into replaced elements to filter which
8395 // anonymous roots can be allowed, and which can't.
8397 // TODO(emilio, 2022): Is this true? If we have a replaced element we wouldn't
8398 // have generated e.g., a ::before/::after pseudo-element to begin with (which
8399 // is what this code is about, so maybe we can just remove this piece of code
8400 // altogether).
8401 if (ShouldRecreateContainerForNativeAnonymousContentRoot(aContent)) {
8402 do {
8403 aContent = aContent->GetParent();
8404 } while (ShouldRecreateContainerForNativeAnonymousContentRoot(aContent));
8405 return RecreateFramesForContent(aContent, InsertionKind::Async);
8408 nsIFrame* frame = aContent->GetPrimaryFrame();
8409 if (frame && frame->IsMathMLFrame()) {
8410 // Reframe the topmost MathML element to prevent exponential blowup
8411 // (see bug 397518).
8412 aContent = GetTopmostMathMLElement(aContent);
8413 frame = aContent->GetPrimaryFrame();
8416 if (frame) {
8417 nsIFrame* parent = frame->GetParent();
8418 nsIContent* parentContent = parent ? parent->GetContent() : nullptr;
8419 // If the parent frame is a leaf then the subsequent insert will fail to
8420 // create a frame, so we need to recreate the parent content. This happens
8421 // with native anonymous content from the editor.
8422 if (parent && parent->IsLeaf() && parentContent &&
8423 parentContent != aContent) {
8424 return RecreateFramesForContent(parentContent, InsertionKind::Async);
8428 if (frame && MaybeRecreateContainerForFrameRemoval(frame)) {
8429 return;
8432 MOZ_ASSERT(aContent->GetParentNode());
8434 // Remove the frames associated with the content object.
8435 nsIContent* nextSibling = aContent->IsRootOfNativeAnonymousSubtree()
8436 ? nullptr
8437 : aContent->GetNextSibling();
8438 bool didReconstruct =
8439 ContentRemoved(aContent, nextSibling, REMOVE_FOR_RECONSTRUCTION);
8441 if (!didReconstruct) {
8442 if (aInsertionKind == InsertionKind::Async && aContent->IsElement()) {
8443 // FIXME(emilio, bug 1397239): There's nothing removing the frame state
8444 // for elements that go away before we come back to the frame
8445 // constructor.
8447 // Also, it'd be nice to just use the `ContentRangeInserted` path for
8448 // both elements and non-elements, but we need to make lazy frame
8449 // construction to apply to all elements first.
8450 RestyleManager()->PostRestyleEvent(aContent->AsElement(), RestyleHint{0},
8451 nsChangeHint_ReconstructFrame);
8452 } else {
8453 // Now, recreate the frames associated with this content object. If
8454 // ContentRemoved triggered reconstruction, then we don't need to do this
8455 // because the frames will already have been built.
8456 ContentRangeInserted(aContent, aContent->GetNextSibling(),
8457 aInsertionKind);
8462 bool nsCSSFrameConstructor::DestroyFramesFor(nsIContent* aContent) {
8463 MOZ_ASSERT(aContent && aContent->GetParentNode());
8465 nsIContent* nextSibling = aContent->IsRootOfNativeAnonymousSubtree()
8466 ? nullptr
8467 : aContent->GetNextSibling();
8469 return ContentRemoved(aContent, nextSibling, REMOVE_FOR_RECONSTRUCTION);
8472 //////////////////////////////////////////////////////////////////////
8474 // Block frame construction code
8476 already_AddRefed<ComputedStyle> nsCSSFrameConstructor::GetFirstLetterStyle(
8477 nsIContent* aContent, ComputedStyle* aComputedStyle) {
8478 if (aContent) {
8479 return mPresShell->StyleSet()->ResolvePseudoElementStyle(
8480 *aContent->AsElement(), PseudoStyleType::firstLetter, nullptr,
8481 aComputedStyle);
8483 return nullptr;
8486 already_AddRefed<ComputedStyle> nsCSSFrameConstructor::GetFirstLineStyle(
8487 nsIContent* aContent, ComputedStyle* aComputedStyle) {
8488 if (aContent) {
8489 return mPresShell->StyleSet()->ResolvePseudoElementStyle(
8490 *aContent->AsElement(), PseudoStyleType::firstLine, nullptr,
8491 aComputedStyle);
8493 return nullptr;
8496 // Predicate to see if a given content (block element) has
8497 // first-letter style applied to it.
8498 bool nsCSSFrameConstructor::ShouldHaveFirstLetterStyle(
8499 nsIContent* aContent, ComputedStyle* aComputedStyle) {
8500 return nsLayoutUtils::HasPseudoStyle(aContent, aComputedStyle,
8501 PseudoStyleType::firstLetter,
8502 mPresShell->GetPresContext());
8505 bool nsCSSFrameConstructor::HasFirstLetterStyle(nsIFrame* aBlockFrame) {
8506 MOZ_ASSERT(aBlockFrame, "Need a frame");
8507 NS_ASSERTION(aBlockFrame->IsBlockFrameOrSubclass(), "Not a block frame?");
8509 return aBlockFrame->HasAnyStateBits(NS_BLOCK_HAS_FIRST_LETTER_STYLE);
8512 bool nsCSSFrameConstructor::ShouldHaveFirstLineStyle(
8513 nsIContent* aContent, ComputedStyle* aComputedStyle) {
8514 bool hasFirstLine = nsLayoutUtils::HasPseudoStyle(
8515 aContent, aComputedStyle, PseudoStyleType::firstLine,
8516 mPresShell->GetPresContext());
8517 return hasFirstLine && !aContent->IsHTMLElement(nsGkAtoms::fieldset);
8520 void nsCSSFrameConstructor::ShouldHaveSpecialBlockStyle(
8521 nsIContent* aContent, ComputedStyle* aComputedStyle,
8522 bool* aHaveFirstLetterStyle, bool* aHaveFirstLineStyle) {
8523 *aHaveFirstLetterStyle = ShouldHaveFirstLetterStyle(aContent, aComputedStyle);
8524 *aHaveFirstLineStyle = ShouldHaveFirstLineStyle(aContent, aComputedStyle);
8527 /* static */
8528 const nsCSSFrameConstructor::PseudoParentData
8529 nsCSSFrameConstructor::sPseudoParentData[eParentTypeCount] = {
8530 // Cell
8531 {{&nsCSSFrameConstructor::ConstructTableCell,
8532 FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET | FCDATA_USE_CHILD_ITEMS |
8533 FCDATA_IS_WRAPPER_ANON_BOX |
8534 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRow)},
8535 PseudoStyleType::tableCell},
8536 // Row
8537 {{&nsCSSFrameConstructor::ConstructTableRowOrRowGroup,
8538 FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET | FCDATA_USE_CHILD_ITEMS |
8539 FCDATA_IS_WRAPPER_ANON_BOX |
8540 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRowGroup)},
8541 PseudoStyleType::tableRow},
8542 // Row group
8543 {{&nsCSSFrameConstructor::ConstructTableRowOrRowGroup,
8544 FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET | FCDATA_USE_CHILD_ITEMS |
8545 FCDATA_IS_WRAPPER_ANON_BOX |
8546 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable)},
8547 PseudoStyleType::tableRowGroup},
8548 // Column group
8549 {{ToCreationFunc(NS_NewTableColGroupFrame),
8550 FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET |
8551 FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_USE_CHILD_ITEMS |
8552 FCDATA_SKIP_ABSPOS_PUSH |
8553 // Not FCDATA_IS_WRAPPER_ANON_BOX, because we don't need to
8554 // restyle these: they have non-inheriting styles.
8555 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable)},
8556 PseudoStyleType::tableColGroup},
8557 // Table
8558 {{&nsCSSFrameConstructor::ConstructTable,
8559 FCDATA_SKIP_FRAMESET | FCDATA_USE_CHILD_ITEMS |
8560 FCDATA_IS_WRAPPER_ANON_BOX},
8561 PseudoStyleType::table},
8562 // Ruby
8563 {{ToCreationFunc(NS_NewRubyFrame),
8564 FCDATA_IS_LINE_PARTICIPANT | FCDATA_USE_CHILD_ITEMS |
8565 FCDATA_IS_WRAPPER_ANON_BOX | FCDATA_SKIP_FRAMESET},
8566 PseudoStyleType::ruby},
8567 // Ruby Base
8568 {{ToCreationFunc(NS_NewRubyBaseFrame),
8569 FCDATA_USE_CHILD_ITEMS | FCDATA_IS_LINE_PARTICIPANT |
8570 FCDATA_IS_WRAPPER_ANON_BOX |
8571 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRubyBaseContainer) |
8572 FCDATA_SKIP_FRAMESET},
8573 PseudoStyleType::rubyBase},
8574 // Ruby Base Container
8575 {{ToCreationFunc(NS_NewRubyBaseContainerFrame),
8576 FCDATA_USE_CHILD_ITEMS | FCDATA_IS_LINE_PARTICIPANT |
8577 FCDATA_IS_WRAPPER_ANON_BOX |
8578 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRuby) |
8579 FCDATA_SKIP_FRAMESET},
8580 PseudoStyleType::rubyBaseContainer},
8581 // Ruby Text
8582 {{ToCreationFunc(NS_NewRubyTextFrame),
8583 FCDATA_USE_CHILD_ITEMS | FCDATA_IS_LINE_PARTICIPANT |
8584 FCDATA_IS_WRAPPER_ANON_BOX |
8585 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRubyTextContainer) |
8586 FCDATA_SKIP_FRAMESET},
8587 PseudoStyleType::rubyText},
8588 // Ruby Text Container
8589 {{ToCreationFunc(NS_NewRubyTextContainerFrame),
8590 FCDATA_USE_CHILD_ITEMS | FCDATA_IS_WRAPPER_ANON_BOX |
8591 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRuby) |
8592 FCDATA_SKIP_FRAMESET},
8593 PseudoStyleType::rubyTextContainer}};
8595 void nsCSSFrameConstructor::CreateNeededAnonFlexOrGridItems(
8596 nsFrameConstructorState& aState, FrameConstructionItemList& aItems,
8597 nsIFrame* aParentFrame) {
8598 if (aItems.IsEmpty()) {
8599 return;
8602 if (!aParentFrame->IsFlexOrGridContainer()) {
8603 return;
8606 const bool isLegacyWebKitBox =
8607 IsFlexContainerForLegacyWebKitBox(aParentFrame);
8608 FCItemIterator iter(aItems);
8609 do {
8610 // Advance iter past children that don't want to be wrapped
8611 if (iter.SkipItemsThatDontNeedAnonFlexOrGridItem(aState,
8612 isLegacyWebKitBox)) {
8613 // Hit the end of the items without finding any remaining children that
8614 // need to be wrapped. We're finished!
8615 return;
8618 // If our next potentially-wrappable child is whitespace, then see if
8619 // there's anything wrappable immediately after it. If not, we just drop
8620 // the whitespace and move on. (We're not supposed to create any anonymous
8621 // flex/grid items that _only_ contain whitespace).
8622 // (BUT if this is generated content, then we don't give whitespace nodes
8623 // any special treatment, because they're probably not really whitespace --
8624 // they're just temporarily empty, waiting for their generated text.)
8625 // XXXdholbert If this node's generated text will *actually end up being
8626 // entirely whitespace*, then we technically should still skip over it, per
8627 // the CSS grid & flexbox specs. I'm not bothering with that at this point,
8628 // since it's a pretty extreme edge case.
8629 if (!aParentFrame->IsGeneratedContentFrame() &&
8630 iter.item().IsWhitespace(aState)) {
8631 FCItemIterator afterWhitespaceIter(iter);
8632 bool hitEnd = afterWhitespaceIter.SkipWhitespace(aState);
8633 bool nextChildNeedsAnonItem =
8634 !hitEnd && afterWhitespaceIter.item().NeedsAnonFlexOrGridItem(
8635 aState, isLegacyWebKitBox);
8637 if (!nextChildNeedsAnonItem) {
8638 // There's nothing after the whitespace that we need to wrap, so we
8639 // just drop this run of whitespace.
8640 iter.DeleteItemsTo(this, afterWhitespaceIter);
8641 if (hitEnd) {
8642 // Nothing left to do -- we're finished!
8643 return;
8645 // else, we have a next child and it does not want to be wrapped. So,
8646 // we jump back to the beginning of the loop to skip over that child
8647 // (and anything else non-wrappable after it)
8648 MOZ_ASSERT(!iter.IsDone() && !iter.item().NeedsAnonFlexOrGridItem(
8649 aState, isLegacyWebKitBox),
8650 "hitEnd and/or nextChildNeedsAnonItem lied");
8651 continue;
8655 // Now |iter| points to the first child that needs to be wrapped in an
8656 // anonymous flex/grid item. Now we see how many children after it also want
8657 // to be wrapped in an anonymous flex/grid item.
8658 FCItemIterator endIter(iter); // iterator to find the end of the group
8659 endIter.SkipItemsThatNeedAnonFlexOrGridItem(aState, isLegacyWebKitBox);
8661 NS_ASSERTION(iter != endIter,
8662 "Should've had at least one wrappable child to seek past");
8664 // Now, we create the anonymous flex or grid item to contain the children
8665 // between |iter| and |endIter|.
8666 nsIContent* parentContent = aParentFrame->GetContent();
8667 RefPtr<ComputedStyle> wrapperStyle =
8668 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
8669 PseudoStyleType::anonymousItem, aParentFrame->Style());
8671 static constexpr FrameConstructionData sBlockFCData(
8672 ToCreationFunc(NS_NewBlockFrame), FCDATA_SKIP_FRAMESET |
8673 FCDATA_USE_CHILD_ITEMS |
8674 FCDATA_IS_WRAPPER_ANON_BOX);
8676 // Use the content of our parent frame
8677 auto* newItem = new (this) FrameConstructionItem(
8678 &sBlockFCData, parentContent, wrapperStyle.forget(), true);
8680 newItem->mIsAllInline =
8681 newItem->mComputedStyle->StyleDisplay()->IsInlineOutsideStyle();
8682 newItem->mIsBlock = !newItem->mIsAllInline;
8684 MOZ_ASSERT(!newItem->mIsAllInline && newItem->mIsBlock,
8685 "expecting anonymous flex/grid items to be block-level "
8686 "(this will make a difference when we encounter "
8687 "'align-items: baseline')");
8689 // Anonymous flex and grid items induce line boundaries around their
8690 // contents.
8691 newItem->mChildItems.SetLineBoundaryAtStart(true);
8692 newItem->mChildItems.SetLineBoundaryAtEnd(true);
8693 // The parent of the items in aItems is also the parent of the items
8694 // in mChildItems
8695 newItem->mChildItems.SetParentHasNoShadowDOM(aItems.ParentHasNoShadowDOM());
8697 // Eat up all items between |iter| and |endIter| and put them in our
8698 // wrapper. This advances |iter| to point to |endIter|.
8699 iter.AppendItemsToList(this, endIter, newItem->mChildItems);
8701 iter.InsertItem(newItem);
8702 } while (!iter.IsDone());
8705 /* static */ nsCSSFrameConstructor::RubyWhitespaceType
8706 nsCSSFrameConstructor::ComputeRubyWhitespaceType(StyleDisplay aPrevDisplay,
8707 StyleDisplay aNextDisplay) {
8708 MOZ_ASSERT(aPrevDisplay.IsRuby() && aNextDisplay.IsRuby());
8709 if (aPrevDisplay == aNextDisplay &&
8710 (aPrevDisplay == StyleDisplay::RubyBase ||
8711 aPrevDisplay == StyleDisplay::RubyText)) {
8712 return eRubyInterLeafWhitespace;
8714 if (aNextDisplay == StyleDisplay::RubyText ||
8715 aNextDisplay == StyleDisplay::RubyTextContainer) {
8716 return eRubyInterLevelWhitespace;
8718 return eRubyInterSegmentWhitespace;
8722 * This function checks the content from |aStartIter| to |aEndIter|,
8723 * determines whether it contains only whitespace, and if yes,
8724 * interprets the type of whitespace. This method does not change
8725 * any of the iters.
8727 /* static */ nsCSSFrameConstructor::RubyWhitespaceType
8728 nsCSSFrameConstructor::InterpretRubyWhitespace(nsFrameConstructorState& aState,
8729 const FCItemIterator& aStartIter,
8730 const FCItemIterator& aEndIter) {
8731 if (!aStartIter.item().IsWhitespace(aState)) {
8732 return eRubyNotWhitespace;
8735 FCItemIterator spaceEndIter(aStartIter);
8736 spaceEndIter.SkipWhitespace(aState);
8737 if (spaceEndIter != aEndIter) {
8738 return eRubyNotWhitespace;
8741 // Any leading or trailing whitespace in non-pseudo ruby box
8742 // should have been trimmed, hence there should not be any
8743 // whitespace at the start or the end.
8744 MOZ_ASSERT(!aStartIter.AtStart() && !aEndIter.IsDone());
8745 FCItemIterator prevIter(aStartIter);
8746 prevIter.Prev();
8747 return ComputeRubyWhitespaceType(
8748 prevIter.item().mComputedStyle->StyleDisplay()->mDisplay,
8749 aEndIter.item().mComputedStyle->StyleDisplay()->mDisplay);
8753 * This function eats up consecutive items which do not want the current
8754 * parent into either a ruby base box or a ruby text box. When it
8755 * returns, |aIter| points to the first item it doesn't wrap.
8757 void nsCSSFrameConstructor::WrapItemsInPseudoRubyLeafBox(
8758 FCItemIterator& aIter, ComputedStyle* aParentStyle,
8759 nsIContent* aParentContent) {
8760 StyleDisplay parentDisplay = aParentStyle->StyleDisplay()->mDisplay;
8761 ParentType parentType, wrapperType;
8762 if (parentDisplay == StyleDisplay::RubyTextContainer) {
8763 parentType = eTypeRubyTextContainer;
8764 wrapperType = eTypeRubyText;
8765 } else {
8766 MOZ_ASSERT(parentDisplay == StyleDisplay::RubyBaseContainer);
8767 parentType = eTypeRubyBaseContainer;
8768 wrapperType = eTypeRubyBase;
8771 MOZ_ASSERT(aIter.item().DesiredParentType() != parentType,
8772 "Should point to something needs to be wrapped.");
8774 FCItemIterator endIter(aIter);
8775 endIter.SkipItemsNotWantingParentType(parentType);
8777 WrapItemsInPseudoParent(aParentContent, aParentStyle, wrapperType, aIter,
8778 endIter);
8782 * This function eats up consecutive items into a ruby level container.
8783 * It may create zero or one level container. When it returns, |aIter|
8784 * points to the first item it doesn't wrap.
8786 void nsCSSFrameConstructor::WrapItemsInPseudoRubyLevelContainer(
8787 nsFrameConstructorState& aState, FCItemIterator& aIter,
8788 ComputedStyle* aParentStyle, nsIContent* aParentContent) {
8789 MOZ_ASSERT(aIter.item().DesiredParentType() != eTypeRuby,
8790 "Pointing to a level container?");
8792 FrameConstructionItem& firstItem = aIter.item();
8793 ParentType wrapperType = firstItem.DesiredParentType();
8794 if (wrapperType != eTypeRubyTextContainer) {
8795 // If the first item is not ruby text,
8796 // it should be in a base container.
8797 wrapperType = eTypeRubyBaseContainer;
8800 FCItemIterator endIter(aIter);
8801 do {
8802 if (endIter.SkipItemsWantingParentType(wrapperType) ||
8803 // If the skipping above stops at some item which wants a
8804 // different ruby parent, then we have finished.
8805 IsRubyParentType(endIter.item().DesiredParentType())) {
8806 // No more items need to be wrapped in this level container.
8807 break;
8810 FCItemIterator contentEndIter(endIter);
8811 contentEndIter.SkipItemsNotWantingRubyParent();
8812 // endIter must be on something doesn't want a ruby parent.
8813 MOZ_ASSERT(contentEndIter != endIter);
8815 // InterpretRubyWhitespace depends on the fact that any leading or
8816 // trailing whitespace described in the spec have been trimmed at
8817 // this point. With this precondition, it is safe not to check
8818 // whether contentEndIter has been done.
8819 RubyWhitespaceType whitespaceType =
8820 InterpretRubyWhitespace(aState, endIter, contentEndIter);
8821 if (whitespaceType == eRubyInterLevelWhitespace) {
8822 // Remove inter-level whitespace.
8823 bool atStart = (aIter == endIter);
8824 endIter.DeleteItemsTo(this, contentEndIter);
8825 if (atStart) {
8826 aIter = endIter;
8828 } else if (whitespaceType == eRubyInterSegmentWhitespace) {
8829 // If this level container starts with inter-segment whitespaces,
8830 // wrap them. Break at contentEndIter. Otherwise, leave it here.
8831 // Break at endIter. They will be wrapped when we are here again.
8832 if (aIter == endIter) {
8833 MOZ_ASSERT(wrapperType == eTypeRubyBaseContainer,
8834 "Inter-segment whitespace should be wrapped in rbc");
8835 endIter = contentEndIter;
8837 break;
8838 } else if (wrapperType == eTypeRubyTextContainer &&
8839 whitespaceType != eRubyInterLeafWhitespace) {
8840 // Misparented inline content that's not inter-annotation
8841 // whitespace doesn't belong in a pseudo ruby text container.
8842 // Break at endIter.
8843 break;
8844 } else {
8845 endIter = contentEndIter;
8847 } while (!endIter.IsDone());
8849 // It is possible that everything our parent wants us to wrap is
8850 // simply an inter-level whitespace, which has been trimmed, or
8851 // an inter-segment whitespace, which will be wrapped later.
8852 // In those cases, don't create anything.
8853 if (aIter != endIter) {
8854 WrapItemsInPseudoParent(aParentContent, aParentStyle, wrapperType, aIter,
8855 endIter);
8860 * This function trims leading and trailing whitespaces
8861 * in the given item list.
8863 void nsCSSFrameConstructor::TrimLeadingAndTrailingWhitespaces(
8864 nsFrameConstructorState& aState, FrameConstructionItemList& aItems) {
8865 FCItemIterator iter(aItems);
8866 if (!iter.IsDone() && iter.item().IsWhitespace(aState)) {
8867 FCItemIterator spaceEndIter(iter);
8868 spaceEndIter.SkipWhitespace(aState);
8869 iter.DeleteItemsTo(this, spaceEndIter);
8872 iter.SetToEnd();
8873 if (!iter.AtStart()) {
8874 FCItemIterator spaceEndIter(iter);
8875 do {
8876 iter.Prev();
8877 if (iter.AtStart()) {
8878 // It's fine to not check the first item, because we
8879 // should have trimmed leading whitespaces above.
8880 break;
8882 } while (iter.item().IsWhitespace(aState));
8883 iter.Next();
8884 if (iter != spaceEndIter) {
8885 iter.DeleteItemsTo(this, spaceEndIter);
8891 * This function walks through the child list (aItems) and creates
8892 * needed pseudo ruby boxes to wrap misparented children.
8894 void nsCSSFrameConstructor::CreateNeededPseudoInternalRubyBoxes(
8895 nsFrameConstructorState& aState, FrameConstructionItemList& aItems,
8896 nsIFrame* aParentFrame) {
8897 const ParentType ourParentType = GetParentType(aParentFrame);
8898 if (!IsRubyParentType(ourParentType) ||
8899 aItems.AllWantParentType(ourParentType)) {
8900 return;
8903 if (!IsRubyPseudo(aParentFrame) ||
8904 ourParentType == eTypeRuby /* for 'display:block ruby' */) {
8905 // Normally, ruby pseudo frames start from and end at some elements,
8906 // which means they don't have leading and trailing whitespaces at
8907 // all. But there are two cases where they do actually have leading
8908 // or trailing whitespaces:
8909 // 1. It is an inter-segment whitespace which in an individual ruby
8910 // base container.
8911 // 2. The pseudo frame starts from or ends at consecutive inline
8912 // content, which is not pure whitespace, but includes some.
8913 // In either case, the whitespaces are not the leading or trailing
8914 // whitespaces defined in the spec, and thus should not be trimmed.
8915 TrimLeadingAndTrailingWhitespaces(aState, aItems);
8918 FCItemIterator iter(aItems);
8919 nsIContent* parentContent = aParentFrame->GetContent();
8920 ComputedStyle* parentStyle = aParentFrame->Style();
8921 while (!iter.IsDone()) {
8922 if (!iter.SkipItemsWantingParentType(ourParentType)) {
8923 if (ourParentType == eTypeRuby) {
8924 WrapItemsInPseudoRubyLevelContainer(aState, iter, parentStyle,
8925 parentContent);
8926 } else {
8927 WrapItemsInPseudoRubyLeafBox(iter, parentStyle, parentContent);
8934 * This function works as follows: we walk through the child list (aItems) and
8935 * find items that cannot have aParentFrame as their parent. We wrap
8936 * continuous runs of such items into a FrameConstructionItem for a frame that
8937 * gets them closer to their desired parents. For example, a run of non-row
8938 * children of a row-group will get wrapped in a row. When we later construct
8939 * the frame for this wrapper (in this case for the row), it'll be the correct
8940 * parent for the cells in the set of items we wrapped or we'll wrap cells
8941 * around everything else. At the end of this method, aItems is guaranteed to
8942 * contain only items for frames that can be direct kids of aParentFrame.
8944 void nsCSSFrameConstructor::CreateNeededPseudoContainers(
8945 nsFrameConstructorState& aState, FrameConstructionItemList& aItems,
8946 nsIFrame* aParentFrame) {
8947 ParentType ourParentType = GetParentType(aParentFrame);
8948 if (IsRubyParentType(ourParentType) ||
8949 aItems.AllWantParentType(ourParentType)) {
8950 // Nothing to do here
8951 return;
8954 FCItemIterator iter(aItems);
8955 do {
8956 if (iter.SkipItemsWantingParentType(ourParentType)) {
8957 // Nothing else to do here; we're finished
8958 return;
8961 // Now we're pointing to the first child that wants a different parent
8962 // type.
8964 // Now try to figure out what kids we can group together. We can generally
8965 // group everything that has a different desired parent type from us. Two
8966 // exceptions to this:
8967 // 1) If our parent type is table, we can't group columns with anything
8968 // else other than whitespace.
8969 // 2) Whitespace that lies between two things we can group which both want
8970 // a non-block parent should be dropped, even if we can't group them
8971 // with each other and even if the whitespace wants a parent of
8972 // ourParentType. Ends of the list count as things that don't want a
8973 // block parent (so that for example we'll drop a whitespace-only list).
8975 FCItemIterator endIter(iter); /* iterator to find the end of the group */
8976 ParentType groupingParentType = endIter.item().DesiredParentType();
8977 if (aItems.AllWantParentType(groupingParentType) &&
8978 groupingParentType != eTypeBlock) {
8979 // Just group them all and be done with it. We need the check for
8980 // eTypeBlock here to catch the "all the items are whitespace" case
8981 // described above.
8982 endIter.SetToEnd();
8983 } else {
8984 // Locate the end of the group.
8986 // Keep track of the type the previous item wanted, in case we have to
8987 // deal with whitespace. Start it off with ourParentType, since that's
8988 // the last thing |iter| would have skipped over.
8989 ParentType prevParentType = ourParentType;
8990 do {
8991 // Walk an iterator past any whitespace that we might be able to drop
8992 // from the list
8993 FCItemIterator spaceEndIter(endIter);
8994 if (prevParentType != eTypeBlock &&
8995 !aParentFrame->IsGeneratedContentFrame() &&
8996 spaceEndIter.item().IsWhitespace(aState)) {
8997 bool trailingSpaces = spaceEndIter.SkipWhitespace(aState);
8999 // We drop the whitespace in the following cases:
9000 // 1) If these are not trailing spaces and the next item wants a table
9001 // or table-part parent
9002 // 2) If these are trailing spaces and aParentFrame is a
9003 // tabular container according to rule 1.3 of CSS 2.1 Sec 17.2.1.
9004 // (Being a tabular container pretty much means ourParentType is
9005 // not eTypeBlock besides the eTypeColGroup case, which won't
9006 // reach here.)
9007 if ((!trailingSpaces &&
9008 IsTableParentType(spaceEndIter.item().DesiredParentType())) ||
9009 (trailingSpaces && ourParentType != eTypeBlock)) {
9010 bool updateStart = (iter == endIter);
9011 endIter.DeleteItemsTo(this, spaceEndIter);
9012 NS_ASSERTION(trailingSpaces == endIter.IsDone(),
9013 "These should match");
9015 if (updateStart) {
9016 iter = endIter;
9019 if (trailingSpaces) {
9020 break; /* Found group end */
9023 if (updateStart) {
9024 // Update groupingParentType, since it might have been eTypeBlock
9025 // just because of the whitespace.
9026 groupingParentType = iter.item().DesiredParentType();
9031 // Now endIter points to a non-whitespace item or a non-droppable
9032 // whitespace item. In the latter case, if this is the end of the group
9033 // we'll traverse this whitespace again. But it'll all just be quick
9034 // DesiredParentType() checks which will match ourParentType (that's
9035 // what it means that this is the group end), so it's OK.
9036 // However, when we are grouping a ruby parent, and endIter points to
9037 // a non-droppable whitespace, if the next non-whitespace item also
9038 // wants a ruby parent, the whitespace should also be included into
9039 // the current ruby container.
9040 prevParentType = endIter.item().DesiredParentType();
9041 if (prevParentType == ourParentType &&
9042 (endIter == spaceEndIter || spaceEndIter.IsDone() ||
9043 !IsRubyParentType(groupingParentType) ||
9044 !IsRubyParentType(spaceEndIter.item().DesiredParentType()))) {
9045 // End the group at endIter.
9046 break;
9049 if (ourParentType == eTypeTable &&
9050 (prevParentType == eTypeColGroup) !=
9051 (groupingParentType == eTypeColGroup)) {
9052 // Either we started with columns and now found something else, or
9053 // vice versa. In any case, end the grouping.
9054 break;
9057 // If we have some whitespace that we were not able to drop and there is
9058 // an item after the whitespace that is already properly parented, then
9059 // make sure to include the spaces in our group but stop the group after
9060 // that.
9061 if (spaceEndIter != endIter && !spaceEndIter.IsDone() &&
9062 ourParentType == spaceEndIter.item().DesiredParentType()) {
9063 endIter = spaceEndIter;
9064 break;
9067 // Include the whitespace we didn't drop (if any) in the group.
9068 endIter = spaceEndIter;
9069 prevParentType = endIter.item().DesiredParentType();
9071 endIter.Next();
9072 } while (!endIter.IsDone());
9075 if (iter == endIter) {
9076 // Nothing to wrap here; just skipped some whitespace
9077 continue;
9080 // Now group together all the items between iter and endIter. The right
9081 // parent type to use depends on ourParentType.
9082 ParentType wrapperType;
9083 switch (ourParentType) {
9084 case eTypeRow:
9085 // The parent type for a cell is eTypeBlock, since that's what a cell
9086 // looks like to its kids.
9087 wrapperType = eTypeBlock;
9088 break;
9089 case eTypeRowGroup:
9090 wrapperType = eTypeRow;
9091 break;
9092 case eTypeTable:
9093 // Either colgroup or rowgroup, depending on what we're grouping.
9094 wrapperType =
9095 groupingParentType == eTypeColGroup ? eTypeColGroup : eTypeRowGroup;
9096 break;
9097 case eTypeColGroup:
9098 MOZ_CRASH("Colgroups should be suppresing non-col child items");
9099 default:
9100 NS_ASSERTION(ourParentType == eTypeBlock, "Unrecognized parent type");
9101 if (IsRubyParentType(groupingParentType)) {
9102 wrapperType = eTypeRuby;
9103 } else {
9104 NS_ASSERTION(IsTableParentType(groupingParentType),
9105 "groupingParentType should be either Ruby or table");
9106 wrapperType = eTypeTable;
9110 ComputedStyle* parentStyle = aParentFrame->Style();
9111 WrapItemsInPseudoParent(aParentFrame->GetContent(), parentStyle,
9112 wrapperType, iter, endIter);
9114 // Now |iter| points to the item that was the first one we didn't wrap;
9115 // loop and see whether we need to skip it or wrap it in something
9116 // different.
9117 } while (!iter.IsDone());
9121 * This method wraps frame construction item from |aIter| to
9122 * |aEndIter|. After it returns, aIter points to the first item
9123 * after the wrapper.
9125 void nsCSSFrameConstructor::WrapItemsInPseudoParent(
9126 nsIContent* aParentContent, ComputedStyle* aParentStyle,
9127 ParentType aWrapperType, FCItemIterator& aIter,
9128 const FCItemIterator& aEndIter) {
9129 const PseudoParentData& pseudoData = sPseudoParentData[aWrapperType];
9130 PseudoStyleType pseudoType = pseudoData.mPseudoType;
9131 auto& parentDisplay = *aParentStyle->StyleDisplay();
9132 auto parentDisplayInside = parentDisplay.DisplayInside();
9134 // XXXmats should we use IsInlineInsideStyle() here instead? seems odd to
9135 // exclude RubyBaseContainer/RubyTextContainer...
9136 if (pseudoType == PseudoStyleType::table &&
9137 (parentDisplay.IsInlineFlow() ||
9138 parentDisplayInside == StyleDisplayInside::RubyBase ||
9139 parentDisplayInside == StyleDisplayInside::RubyText)) {
9140 pseudoType = PseudoStyleType::inlineTable;
9143 RefPtr<ComputedStyle> wrapperStyle;
9144 if (pseudoData.mFCData.mBits & FCDATA_IS_WRAPPER_ANON_BOX) {
9145 wrapperStyle = mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
9146 pseudoType, aParentStyle);
9147 } else {
9148 wrapperStyle =
9149 mPresShell->StyleSet()->ResolveNonInheritingAnonymousBoxStyle(
9150 pseudoType);
9153 // Use the content of our parent frame
9154 auto* newItem = new (this) FrameConstructionItem(
9155 &pseudoData.mFCData, aParentContent, wrapperStyle.forget(), true);
9157 const nsStyleDisplay* disp = newItem->mComputedStyle->StyleDisplay();
9158 // Here we're cheating a tad... technically, table-internal items should be
9159 // inline if aParentFrame is inline, but they'll get wrapped in an
9160 // inline-table in the end, so it'll all work out. In any case, arguably
9161 // we don't need to maintain this state at this point... but it's better
9162 // to, I guess.
9163 newItem->mIsAllInline = disp->IsInlineOutsideStyle();
9165 bool isRuby = disp->IsRubyDisplayType();
9166 if (!isRuby) {
9167 // Table pseudo frames always induce line boundaries around their
9168 // contents.
9169 newItem->mChildItems.SetLineBoundaryAtStart(true);
9170 newItem->mChildItems.SetLineBoundaryAtEnd(true);
9172 // The parent of the items in aItems is also the parent of the items
9173 // in mChildItems
9174 newItem->mChildItems.SetParentHasNoShadowDOM(
9175 aIter.List()->ParentHasNoShadowDOM());
9177 // Eat up all items between |aIter| and |aEndIter| and put them in our
9178 // wrapper Advances |aIter| to point to |aEndIter|.
9179 aIter.AppendItemsToList(this, aEndIter, newItem->mChildItems);
9181 aIter.InsertItem(newItem);
9184 void nsCSSFrameConstructor::CreateNeededPseudoSiblings(
9185 nsFrameConstructorState& aState, FrameConstructionItemList& aItems,
9186 nsIFrame* aParentFrame) {
9187 if (aItems.IsEmpty() || GetParentType(aParentFrame) != eTypeRuby) {
9188 return;
9191 FCItemIterator iter(aItems);
9192 StyleDisplay firstDisplay =
9193 iter.item().mComputedStyle->StyleDisplay()->mDisplay;
9194 if (firstDisplay == StyleDisplay::RubyBaseContainer) {
9195 return;
9197 NS_ASSERTION(firstDisplay == StyleDisplay::RubyTextContainer,
9198 "Child of ruby frame should either a rbc or a rtc");
9200 const PseudoParentData& pseudoData =
9201 sPseudoParentData[eTypeRubyBaseContainer];
9202 RefPtr<ComputedStyle> pseudoStyle =
9203 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
9204 pseudoData.mPseudoType, aParentFrame->Style());
9205 FrameConstructionItem* newItem = new (this) FrameConstructionItem(
9206 &pseudoData.mFCData,
9207 // Use the content of the parent frame
9208 aParentFrame->GetContent(), pseudoStyle.forget(), true);
9209 newItem->mIsAllInline = true;
9210 newItem->mChildItems.SetParentHasNoShadowDOM(true);
9211 iter.InsertItem(newItem);
9214 #ifdef DEBUG
9216 * Returns true iff aFrame should be wrapped in an anonymous flex/grid item,
9217 * rather than being a direct child of aContainerFrame.
9219 * NOTE: aContainerFrame must be a flex or grid container - this function is
9220 * purely for sanity-checking the children of these container types.
9221 * NOTE: See also NeedsAnonFlexOrGridItem(), for the non-debug version of this
9222 * logic (which operates a bit earlier, on FCData instead of frames).
9224 static bool FrameWantsToBeInAnonymousItem(const nsIFrame* aContainerFrame,
9225 const nsIFrame* aFrame) {
9226 MOZ_ASSERT(aContainerFrame->IsFlexOrGridContainer());
9228 // Any line-participant frames (e.g. text) definitely want to be wrapped in
9229 // an anonymous flex/grid item.
9230 if (aFrame->IsLineParticipant()) {
9231 return true;
9234 // If the container is a -webkit-{inline-}box container, then placeholders
9235 // also need to be wrapped, for compatibility.
9236 if (IsFlexContainerForLegacyWebKitBox(aContainerFrame) &&
9237 aFrame->IsPlaceholderFrame()) {
9238 return true;
9241 return false;
9243 #endif
9245 static void VerifyGridFlexContainerChildren(nsIFrame* aParentFrame,
9246 const nsFrameList& aChildren) {
9247 #ifdef DEBUG
9248 if (!aParentFrame->IsFlexOrGridContainer()) {
9249 return;
9252 bool prevChildWasAnonItem = false;
9253 for (const nsIFrame* child : aChildren) {
9254 MOZ_ASSERT(!FrameWantsToBeInAnonymousItem(aParentFrame, child),
9255 "frame wants to be inside an anonymous item, but it isn't");
9256 if (IsAnonymousItem(child)) {
9257 AssertAnonymousFlexOrGridItemParent(child, aParentFrame);
9258 MOZ_ASSERT(!prevChildWasAnonItem, "two anon items in a row");
9259 nsIFrame* firstWrappedChild = child->PrincipalChildList().FirstChild();
9260 MOZ_ASSERT(firstWrappedChild, "anonymous item shouldn't be empty");
9261 prevChildWasAnonItem = true;
9262 } else {
9263 prevChildWasAnonItem = false;
9266 #endif
9269 static bool FrameHasOnlyPlaceholderPrevSiblings(const nsIFrame* aFrame) {
9270 // Check for prev siblings, ignoring placeholder frames.
9271 MOZ_ASSERT(aFrame, "frame must not be null");
9272 const nsIFrame* prevSibling = aFrame;
9273 do {
9274 prevSibling = prevSibling->GetPrevSibling();
9275 } while (prevSibling && prevSibling->IsPlaceholderFrame());
9276 return !prevSibling;
9279 static bool FrameHasOnlyPlaceholderNextSiblings(const nsIFrame* aFrame) {
9280 // Check for next siblings, ignoring placeholder frames.
9281 MOZ_ASSERT(aFrame, "frame must not be null");
9282 const nsIFrame* nextSibling = aFrame;
9283 do {
9284 nextSibling = nextSibling->GetNextSibling();
9285 } while (nextSibling && nextSibling->IsPlaceholderFrame());
9286 return !nextSibling;
9289 static void SetPageValues(nsIFrame* const aFrame,
9290 const nsAtom* const aAutoValue,
9291 const nsAtom* const aStartValue,
9292 const nsAtom* const aEndValue) {
9293 MOZ_ASSERT(aAutoValue, "Auto page value should never be null");
9294 MOZ_ASSERT(aStartValue || aEndValue, "Should not have called with no values");
9295 nsIFrame::PageValues* pageValues =
9296 aFrame->GetProperty(nsIFrame::PageValuesProperty());
9298 if (aStartValue) {
9299 if (aStartValue == aAutoValue) {
9300 // If the page value struct already exists, set the start value to null
9301 // to indicate the auto value.
9302 if (pageValues) {
9303 pageValues->mStartPageValue = nullptr;
9305 } else {
9306 // The start value is not auto, so we need to store it, creating the
9307 // page values struct if it does not already exist.
9308 if (!pageValues) {
9309 pageValues = new nsIFrame::PageValues();
9310 aFrame->SetProperty(nsIFrame::PageValuesProperty(), pageValues);
9312 pageValues->mStartPageValue = aStartValue;
9315 if (aEndValue) {
9316 if (aEndValue == aAutoValue) {
9317 // If the page value struct already exists, set the end value to null
9318 // to indicate the auto value.
9319 if (pageValues) {
9320 pageValues->mEndPageValue = nullptr;
9322 } else {
9323 // The end value is not auto, so we need to store it, creating the
9324 // page values struct if it does not already exist.
9325 if (!pageValues) {
9326 pageValues = new nsIFrame::PageValues();
9327 aFrame->SetProperty(nsIFrame::PageValuesProperty(), pageValues);
9329 pageValues->mEndPageValue = aEndValue;
9334 inline void nsCSSFrameConstructor::ConstructFramesFromItemList(
9335 nsFrameConstructorState& aState, FrameConstructionItemList& aItems,
9336 nsContainerFrame* aParentFrame, bool aParentIsWrapperAnonBox,
9337 nsFrameList& aFrameList) {
9338 #ifdef DEBUG
9339 if (aParentFrame->StyleContent()->mContent.IsNone() &&
9340 StaticPrefs::layout_css_element_content_none_enabled()) {
9341 for (FCItemIterator iter(aItems); !iter.IsDone(); iter.Next()) {
9342 MOZ_ASSERT(iter.item().mContent->IsInNativeAnonymousSubtree() ||
9343 iter.item().mComputedStyle->IsPseudoOrAnonBox());
9347 // The assertion condition should match the logic in
9348 // MaybePushFloatContainingBlock().
9349 MOZ_ASSERT(!(ShouldSuppressFloatingOfDescendants(aParentFrame) ||
9350 aParentFrame->IsFloatContainingBlock()) ||
9351 aState.mFloatCBCandidate == aParentFrame,
9352 "Our caller or ProcessChildren()'s caller should call "
9353 "MaybePushFloatContainingBlock() to handle the float containing "
9354 "block candidate!");
9355 aState.mFloatCBCandidate = nullptr;
9356 #endif
9358 // Ensure aParentIsWrapperAnonBox is correct. We _could_ compute it directly,
9359 // but it would be a bit slow, which is why we pass it from callers, who have
9360 // that information offhand in many cases.
9361 MOZ_ASSERT(ParentIsWrapperAnonBox(aParentFrame) == aParentIsWrapperAnonBox);
9363 // Note: we explicitly exclude TableColGroupFrame because it doesn't
9364 // have the FCDATA_IS_WRAPPER_ANON_BOX on pseudos so aParentIsWrapperAnonBox
9365 // is false for such pseudos (see sPseudoParentData below).
9366 if (!aParentIsWrapperAnonBox && aState.mHasRenderedLegend &&
9367 aParentFrame->GetContent()->IsHTMLElement(nsGkAtoms::fieldset) &&
9368 !aParentFrame->IsTableColGroupFrame()) {
9369 DebugOnly<bool> found = false;
9370 for (FCItemIterator iter(aItems); !iter.IsDone(); iter.Next()) {
9371 if (iter.item().mIsRenderedLegend) {
9372 // This makes the rendered legend the first frame in the fieldset child
9373 // list which makes keyboard traversal follow the visual order.
9374 nsFieldSetFrame* fieldSetFrame = GetFieldSetFrameFor(aParentFrame);
9375 nsFrameList renderedLegend;
9376 ConstructFramesFromItem(aState, iter, fieldSetFrame, renderedLegend);
9377 MOZ_ASSERT(renderedLegend.OnlyChild(),
9378 "a rendered legend should have exactly one frame");
9379 fieldSetFrame->InsertFrames(FrameChildListID::Principal, nullptr,
9380 nullptr, std::move(renderedLegend));
9381 FCItemIterator next = iter;
9382 next.Next();
9383 iter.DeleteItemsTo(this, next);
9384 found = true;
9385 break;
9388 MOZ_ASSERT(found, "should have found our rendered legend");
9391 CreateNeededPseudoContainers(aState, aItems, aParentFrame);
9392 CreateNeededAnonFlexOrGridItems(aState, aItems, aParentFrame);
9393 CreateNeededPseudoInternalRubyBoxes(aState, aItems, aParentFrame);
9394 CreateNeededPseudoSiblings(aState, aItems, aParentFrame);
9396 for (FCItemIterator iter(aItems); !iter.IsDone(); iter.Next()) {
9397 MOZ_ASSERT(!iter.item().mIsRenderedLegend,
9398 "Only one item can be the rendered legend, "
9399 "and it should've been handled above");
9400 NS_ASSERTION(iter.item().DesiredParentType() == GetParentType(aParentFrame),
9401 "Needed pseudos didn't get created; expect bad things");
9402 ConstructFramesFromItem(aState, iter, aParentFrame, aFrameList);
9405 VerifyGridFlexContainerChildren(aParentFrame, aFrameList);
9407 // Calculate and propagate page-name values for each frame in the frame list.
9408 // We do not want to compute and propagate page-name values from frames that
9409 // are children of any subclasses of block frames, but not actually a block
9410 // frame. The page-name property does not apply to frames which cannot create
9411 // class A breakpoints (currently no subclass of BlockFrame can). Because the
9412 // property does not apply, those children also cannot propagate page-name
9413 // values.
9414 // This assumption helps avoid unnecessarily handling page-names for frames
9415 // such as form controls, which also avoids bug 1819468.
9416 if (aState.mPresContext->IsPaginated() && aParentFrame->IsBlockFrame()) {
9417 // Set the start/end page values while iterating the frame list, to walk
9418 // up the frame tree only once after iterating the frame list.
9419 // This also avoids extra property lookups on these frames.
9420 MOZ_ASSERT(aState.mAutoPageNameValue == aParentFrame->GetAutoPageValue(),
9421 "aState.mAutoPageNameValue should have been equivalent to "
9422 "the auto value stored on our parent frame.");
9423 // Even though we store null for page values that equal the "auto" resolved
9424 // value on frames, we always want startPageValue/endPageValue to be the
9425 // actual atoms reflecting the start/end values. This is because when we
9426 // propagate the values up the frame tree, we will need to compare them to
9427 // the auto value for each ancestor. This value might be different than the
9428 // auto value for this frame.
9429 const nsAtom* startPageValue = nullptr;
9430 const nsAtom* endPageValue = nullptr;
9431 for (nsIFrame* f : aFrameList) {
9432 if (f->IsPlaceholderFrame()) {
9433 continue;
9435 // Resolve auto against the parent frame's used page name, which has been
9436 // determined and set on aState.mAutoPageNameValue. If this item is not
9437 // block-level then we use the value that auto resolves to.
9439 // This is to achieve the propagation behavior described in the spec:
9441 // "A start page value and end page value is determined for each box as
9442 // the value (if any) propagated from its first or last child box
9443 // (respectively), else the used value on the box itself."
9445 // "A child propagates its own start or end page value if and only if the
9446 // page property applies to it."
9448 // The page property only applies to "boxes that create class A break
9449 // points". When taken together, this means that non block-level children
9450 // do not propagate start/end page values, and instead we use "the used
9451 // value on the box itself", the "box itself" being aParentFrame. This
9452 // value has been determined and saved as aState.mAutoPageNameValue
9454 // https://www.w3.org/TR/css-page-3/#using-named-pages
9455 // https://www.w3.org/TR/css-break-3/#btw-blocks
9456 const StylePageName& pageName = f->StylePage()->mPage;
9457 const nsAtom* const pageNameAtom =
9458 (pageName.IsPageName() && f->IsBlockOutside())
9459 ? pageName.AsPageName().AsAtom()
9460 : aState.mAutoPageNameValue;
9461 nsIFrame::PageValues* pageValues =
9462 f->GetProperty(nsIFrame::PageValuesProperty());
9463 // If this frame has any children, it will already have had its page
9464 // values set at this point. However, if no page values have been set,
9465 // we must ensure that the appropriate PageValuesProperty value has been
9466 // set.
9467 // If the page name is equal to the auto value, then PageValuesProperty
9468 // should remain null to indicate that the start/end values are both
9469 // equal to the auto value.
9470 if (pageNameAtom != aState.mAutoPageNameValue && !pageValues) {
9471 pageValues = new nsIFrame::PageValues{pageNameAtom, pageNameAtom};
9472 f->SetProperty(nsIFrame::PageValuesProperty(), pageValues);
9474 // We don't want to use GetStartPageValue() or GetEndPageValue(), as each
9475 // requires a property lookup which we can avoid here.
9476 if (!startPageValue) {
9477 startPageValue = (pageValues && pageValues->mStartPageValue)
9478 ? pageValues->mStartPageValue.get()
9479 : aState.mAutoPageNameValue;
9481 endPageValue = (pageValues && pageValues->mEndPageValue)
9482 ? pageValues->mEndPageValue.get()
9483 : aState.mAutoPageNameValue;
9484 MOZ_ASSERT(startPageValue && endPageValue,
9485 "Should have found start/end page value");
9487 MOZ_ASSERT(!startPageValue == !endPageValue,
9488 "Should have set both or neither page values");
9489 if (startPageValue) {
9490 // Walk up the frame tree from our parent frame, propagating start and
9491 // end page values.
9492 // As we go, if we find that, for a frame, we are not contributing one of
9493 // the start/end page values, then our subtree will not contribute this
9494 // value from that frame onward. startPageValue/endPageValue are set to
9495 // null to indicate this.
9496 // Stop iterating when we are not contributing either start or end
9497 // values, when we hit the root frame (no parent), or when we find a
9498 // frame that is not a block frame.
9499 for (nsContainerFrame* ancestorFrame = aParentFrame;
9500 (startPageValue || endPageValue) && ancestorFrame &&
9501 ancestorFrame->IsBlockFrame();
9502 ancestorFrame = ancestorFrame->GetParent()) {
9503 MOZ_ASSERT(!ancestorFrame->GetPrevInFlow(),
9504 "Should not have fragmentation yet");
9505 MOZ_ASSERT(ancestorFrame->mWasVisitedByAutoFrameConstructionPageName,
9506 "Frame should have been visited by "
9507 "AutoFrameConstructionPageName");
9509 // Get what the auto value is, based on this frame's parent.
9510 // For the root frame, `auto` resolves to the empty atom.
9511 const nsContainerFrame* const parent = ancestorFrame->GetParent();
9512 const nsAtom* const parentAuto = MOZ_LIKELY(parent)
9513 ? parent->GetAutoPageValue()
9514 : nsGkAtoms::_empty;
9515 SetPageValues(ancestorFrame, parentAuto, startPageValue,
9516 endPageValue);
9518 // Once we stop contributing start/end values, we know there is a
9519 // sibling subtree that contributed that value to our shared parent
9520 // instead of our starting frame's subtree. This means once
9521 // startPageValue/endPageValue becomes null, indicating that we are no
9522 // longer contributing that page value, it should stay null and we no
9523 // longer need to check for siblings in that direction.
9524 if (startPageValue &&
9525 !FrameHasOnlyPlaceholderPrevSiblings(ancestorFrame)) {
9526 startPageValue = nullptr;
9528 if (endPageValue &&
9529 !FrameHasOnlyPlaceholderNextSiblings(ancestorFrame)) {
9530 endPageValue = nullptr;
9536 if (aParentIsWrapperAnonBox) {
9537 for (nsIFrame* f : aFrameList) {
9538 f->SetParentIsWrapperAnonBox();
9543 void nsCSSFrameConstructor::AddFCItemsForAnonymousContent(
9544 nsFrameConstructorState& aState, nsContainerFrame* aFrame,
9545 const nsTArray<nsIAnonymousContentCreator::ContentInfo>& aAnonymousItems,
9546 FrameConstructionItemList& aItemsToConstruct,
9547 const AutoFrameConstructionPageName&) {
9548 for (const auto& info : aAnonymousItems) {
9549 nsIContent* content = info.mContent;
9550 // Gecko-styled nodes should have no pending restyle flags.
9551 // Assert some things about this content
9552 MOZ_ASSERT(!(content->GetFlags() &
9553 (NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME)),
9554 "Should not be marked as needing frames");
9555 MOZ_ASSERT(!content->GetPrimaryFrame(), "Should have no existing frame");
9556 MOZ_ASSERT(!content->IsComment() && !content->IsProcessingInstruction(),
9557 "Why is someone creating garbage anonymous content");
9559 // Make sure we eagerly performed the servo cascade when the anonymous
9560 // nodes were created.
9561 MOZ_ASSERT(!content->IsElement() || content->AsElement()->HasServoData());
9563 RefPtr<ComputedStyle> computedStyle = ResolveComputedStyle(content);
9565 AddFrameConstructionItemsInternal(aState, content, aFrame, true,
9566 computedStyle, {ItemFlag::AllowPageBreak},
9567 aItemsToConstruct);
9571 void nsCSSFrameConstructor::ProcessChildren(
9572 nsFrameConstructorState& aState, nsIContent* aContent,
9573 ComputedStyle* aComputedStyle, nsContainerFrame* aFrame,
9574 const bool aCanHaveGeneratedContent, nsFrameList& aFrameList,
9575 const bool aAllowBlockStyles, nsIFrame* aPossiblyLeafFrame) {
9576 MOZ_ASSERT(aFrame, "Must have parent frame here");
9577 MOZ_ASSERT(aFrame->GetContentInsertionFrame() == aFrame,
9578 "Parent frame in ProcessChildren should be its own "
9579 "content insertion frame");
9581 const uint32_t kMaxDepth = 2 * MAX_REFLOW_DEPTH;
9582 static_assert(kMaxDepth <= UINT16_MAX, "mCurrentDepth type is too narrow");
9583 AutoRestore<uint16_t> savedDepth(mCurrentDepth);
9584 if (mCurrentDepth != UINT16_MAX) {
9585 ++mCurrentDepth;
9588 if (!aPossiblyLeafFrame) {
9589 aPossiblyLeafFrame = aFrame;
9592 // XXXbz ideally, this would do all the pushing of various
9593 // containing blocks as needed, so callers don't have to do it...
9595 // Check that our parent frame is a block before allowing ::first-letter/line.
9596 // E.g. <button style="display:grid"> should not allow it.
9597 const bool allowFirstPseudos =
9598 aAllowBlockStyles && aFrame->IsBlockFrameOrSubclass();
9599 bool haveFirstLetterStyle = false, haveFirstLineStyle = false;
9600 if (allowFirstPseudos) {
9601 ShouldHaveSpecialBlockStyle(aContent, aComputedStyle, &haveFirstLetterStyle,
9602 &haveFirstLineStyle);
9605 AutoFrameConstructionItemList itemsToConstruct(this);
9606 AutoFrameConstructionPageName pageNameTracker(aState, aFrame);
9608 // If we have first-letter or first-line style then frames can get
9609 // moved around so don't set these flags.
9610 if (allowFirstPseudos && !haveFirstLetterStyle && !haveFirstLineStyle) {
9611 itemsToConstruct.SetLineBoundaryAtStart(true);
9612 itemsToConstruct.SetLineBoundaryAtEnd(true);
9615 // Create any anonymous frames we need here.
9616 AutoTArray<nsIAnonymousContentCreator::ContentInfo, 4> anonymousItems;
9617 GetAnonymousContent(aContent, aPossiblyLeafFrame, anonymousItems);
9618 #ifdef DEBUG
9619 for (uint32_t i = 0; i < anonymousItems.Length(); ++i) {
9620 MOZ_ASSERT(anonymousItems[i].mContent->IsRootOfNativeAnonymousSubtree(),
9621 "Content should know it's an anonymous subtree");
9623 #endif
9624 AddFCItemsForAnonymousContent(aState, aFrame, anonymousItems,
9625 itemsToConstruct, pageNameTracker);
9627 nsBlockFrame* listItem = nullptr;
9628 bool isOutsideMarker = false;
9629 if (!aPossiblyLeafFrame->IsLeaf()) {
9630 // :before/:after content should have the same style parent as normal kids.
9632 // Note that we don't use this style for looking up things like special
9633 // block styles because in some cases involving table pseudo-frames it has
9634 // nothing to do with the parent frame's desired behavior.
9635 auto* styleParentFrame =
9636 nsIFrame::CorrectStyleParentFrame(aFrame, PseudoStyleType::NotPseudo);
9637 ComputedStyle* computedStyle = styleParentFrame->Style();
9639 if (aCanHaveGeneratedContent) {
9640 if (computedStyle->StyleDisplay()->IsListItem() &&
9641 (listItem = do_QueryFrame(aFrame)) &&
9642 !styleParentFrame->IsFieldSetFrame()) {
9643 isOutsideMarker = computedStyle->StyleList()->mListStylePosition ==
9644 StyleListStylePosition::Outside;
9645 ItemFlags extraFlags;
9646 if (isOutsideMarker) {
9647 extraFlags += ItemFlag::IsForOutsideMarker;
9649 CreateGeneratedContentItem(aState, aFrame, *aContent->AsElement(),
9650 *computedStyle, PseudoStyleType::marker,
9651 itemsToConstruct, extraFlags);
9653 // Probe for generated content before
9654 CreateGeneratedContentItem(aState, aFrame, *aContent->AsElement(),
9655 *computedStyle, PseudoStyleType::before,
9656 itemsToConstruct);
9659 const bool addChildItems = MOZ_LIKELY(mCurrentDepth < kMaxDepth);
9660 if (!addChildItems) {
9661 NS_WARNING("ProcessChildren max depth exceeded");
9664 FlattenedChildIterator iter(aContent);
9665 const InsertionPoint insertion(aFrame, aContent);
9666 for (nsIContent* child = iter.GetNextChild(); child;
9667 child = iter.GetNextChild()) {
9668 MOZ_ASSERT(insertion.mContainer == GetInsertionPoint(child).mContainer,
9669 "GetInsertionPoint should agree with us");
9670 if (addChildItems) {
9671 AddFrameConstructionItems(aState, child, iter.ShadowDOMInvolved(),
9672 *computedStyle, insertion, itemsToConstruct);
9673 } else {
9674 ClearLazyBits(child, child->GetNextSibling());
9677 itemsToConstruct.SetParentHasNoShadowDOM(!iter.ShadowDOMInvolved());
9679 if (aCanHaveGeneratedContent) {
9680 // Probe for generated content after
9681 CreateGeneratedContentItem(aState, aFrame, *aContent->AsElement(),
9682 *computedStyle, PseudoStyleType::after,
9683 itemsToConstruct);
9685 } else {
9686 ClearLazyBits(aContent->GetFirstChild(), nullptr);
9689 ConstructFramesFromItemList(aState, itemsToConstruct, aFrame,
9690 /* aParentIsWrapperAnonBox = */ false,
9691 aFrameList);
9693 if (listItem) {
9694 if (auto* markerFrame = nsLayoutUtils::GetMarkerFrame(aContent)) {
9695 for (auto* childFrame : aFrameList) {
9696 if (markerFrame == childFrame) {
9697 if (isOutsideMarker) {
9698 // SetMarkerFrameForListItem will add childFrame to the
9699 // FrameChildListID::Bullet
9700 aFrameList.RemoveFrame(childFrame);
9701 auto* grandParent = listItem->GetParent()->GetParent();
9702 if (listItem->Style()->GetPseudoType() ==
9703 PseudoStyleType::columnContent &&
9704 grandParent && grandParent->IsColumnSetWrapperFrame()) {
9705 listItem = do_QueryFrame(grandParent);
9706 MOZ_ASSERT(listItem,
9707 "ColumnSetWrapperFrame is expected to be "
9708 "a nsBlockFrame subclass");
9709 childFrame->SetParent(listItem);
9712 listItem->SetMarkerFrameForListItem(childFrame);
9713 MOZ_ASSERT(listItem->HasAnyStateBits(
9714 NS_BLOCK_FRAME_HAS_OUTSIDE_MARKER) == isOutsideMarker);
9715 #ifdef ACCESSIBILITY
9716 if (nsAccessibilityService* accService = GetAccService()) {
9717 auto* marker = markerFrame->GetContent();
9718 accService->ContentRangeInserted(mPresShell, marker, nullptr);
9720 #endif
9721 break;
9727 if (haveFirstLetterStyle) {
9728 WrapFramesInFirstLetterFrame(aFrame, aFrameList);
9730 if (haveFirstLineStyle) {
9731 WrapFramesInFirstLineFrame(aState, aContent, aFrame, nullptr, aFrameList);
9735 //----------------------------------------------------------------------
9737 // Support for :first-line style
9739 // Special routine to handle placing a list of frames into a block
9740 // frame that has first-line style. The routine ensures that the first
9741 // collection of inline frames end up in a first-line frame.
9742 // NOTE: aState may have containing block information related to a
9743 // different part of the frame tree than where the first line occurs.
9744 // In particular aState may be set up for where ContentInserted or
9745 // ContentAppended is inserting content, which may be some
9746 // non-first-in-flow continuation of the block to which the first-line
9747 // belongs. So this function needs to be careful about how it uses
9748 // aState.
9749 void nsCSSFrameConstructor::WrapFramesInFirstLineFrame(
9750 nsFrameConstructorState& aState, nsIContent* aBlockContent,
9751 nsContainerFrame* aBlockFrame, nsFirstLineFrame* aLineFrame,
9752 nsFrameList& aFrameList) {
9753 // Extract any initial inline frames from aFrameList so we can put them
9754 // in the first-line.
9755 nsFrameList firstLineChildren =
9756 aFrameList.Split([](nsIFrame* f) { return !f->IsInlineOutside(); });
9758 if (firstLineChildren.IsEmpty()) {
9759 // Nothing is supposed to go into the first-line; nothing to do
9760 return;
9763 if (!aLineFrame) {
9764 // Create line frame
9765 ComputedStyle* parentStyle = nsIFrame::CorrectStyleParentFrame(
9766 aBlockFrame, PseudoStyleType::firstLine)
9767 ->Style();
9768 RefPtr<ComputedStyle> firstLineStyle =
9769 GetFirstLineStyle(aBlockContent, parentStyle);
9771 aLineFrame = NS_NewFirstLineFrame(mPresShell, firstLineStyle);
9773 // Initialize the line frame
9774 InitAndRestoreFrame(aState, aBlockContent, aBlockFrame, aLineFrame);
9776 // The lineFrame will be the block's first child; the rest of the
9777 // frame list (after lastInlineFrame) will be the second and
9778 // subsequent children; insert lineFrame into aFrameList.
9779 aFrameList.InsertFrame(nullptr, nullptr, aLineFrame);
9781 NS_ASSERTION(aLineFrame->Style() == firstLineStyle,
9782 "Bogus style on line frame");
9785 // Give the inline frames to the lineFrame <b>after</b> reparenting them
9786 ReparentFrames(this, aLineFrame, firstLineChildren, true);
9787 if (aLineFrame->PrincipalChildList().IsEmpty() &&
9788 aLineFrame->HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
9789 aLineFrame->SetInitialChildList(FrameChildListID::Principal,
9790 std::move(firstLineChildren));
9791 } else {
9792 AppendFrames(aLineFrame, FrameChildListID::Principal,
9793 std::move(firstLineChildren));
9797 // Special routine to handle appending a new frame to a block frame's
9798 // child list. Takes care of placing the new frame into the right
9799 // place when first-line style is present.
9800 void nsCSSFrameConstructor::AppendFirstLineFrames(
9801 nsFrameConstructorState& aState, nsIContent* aBlockContent,
9802 nsContainerFrame* aBlockFrame, nsFrameList& aFrameList) {
9803 // It's possible that aBlockFrame needs to have a first-line frame
9804 // created because it doesn't currently have any children.
9805 const nsFrameList& blockKids = aBlockFrame->PrincipalChildList();
9806 if (blockKids.IsEmpty()) {
9807 WrapFramesInFirstLineFrame(aState, aBlockContent, aBlockFrame, nullptr,
9808 aFrameList);
9809 return;
9812 // Examine the last block child - if it's a first-line frame then
9813 // appended frames need special treatment.
9814 nsIFrame* lastBlockKid = blockKids.LastChild();
9815 if (!lastBlockKid->IsLineFrame()) {
9816 // No first-line frame at the end of the list, therefore there is
9817 // an intervening block between any first-line frame the frames
9818 // we are appending. Therefore, we don't need any special
9819 // treatment of the appended frames.
9820 return;
9823 nsFirstLineFrame* lineFrame = static_cast<nsFirstLineFrame*>(lastBlockKid);
9824 WrapFramesInFirstLineFrame(aState, aBlockContent, aBlockFrame, lineFrame,
9825 aFrameList);
9828 void nsCSSFrameConstructor::CheckForFirstLineInsertion(
9829 nsIFrame* aParentFrame, nsFrameList& aFrameList) {
9830 MOZ_ASSERT(aParentFrame->Style()->HasPseudoElementData(),
9831 "Why were we called?");
9833 if (aFrameList.IsEmpty()) {
9834 // Happens often enough, with the caption stuff. No need to do the ancestor
9835 // walk here.
9836 return;
9839 class RestyleManager* restyleManager = RestyleManager();
9841 // Check whether there's a ::first-line on the path up from aParentFrame.
9842 // Note that we can't stop until we've run out of ancestors with
9843 // pseudo-element data, because the first-letter might be somewhere way up the
9844 // tree; in particular it might be past our containing block.
9845 nsIFrame* ancestor = aParentFrame;
9846 while (ancestor) {
9847 if (!ancestor->Style()->HasPseudoElementData()) {
9848 // We know we won't find a ::first-line now.
9849 return;
9852 if (!ancestor->IsLineFrame()) {
9853 ancestor = ancestor->GetParent();
9854 continue;
9857 if (!ancestor->Style()->IsPseudoElement()) {
9858 // This is a continuation lineframe, not the first line; no need to do
9859 // anything to the styles.
9860 return;
9863 // Fix up the styles of aFrameList for ::first-line.
9864 for (nsIFrame* f : aFrameList) {
9865 restyleManager->ReparentComputedStyleForFirstLine(f);
9867 return;
9871 //----------------------------------------------------------------------
9873 // First-letter support
9875 // Determine how many characters in the text fragment apply to the
9876 // first letter
9877 static int32_t FirstLetterCount(const nsTextFragment* aFragment) {
9878 int32_t count = 0;
9879 int32_t firstLetterLength = 0;
9881 const uint32_t n = aFragment->GetLength();
9882 for (uint32_t i = 0; i < n; i++) {
9883 const char16_t ch = aFragment->CharAt(i);
9884 // FIXME: take content language into account when deciding whitespace.
9885 if (dom::IsSpaceCharacter(ch)) {
9886 if (firstLetterLength) {
9887 break;
9889 count++;
9890 continue;
9892 // XXX I18n
9893 if ((ch == '\'') || (ch == '\"')) {
9894 if (firstLetterLength) {
9895 break;
9897 // keep looping
9898 firstLetterLength = 1;
9899 } else {
9900 count++;
9901 break;
9905 return count;
9908 static bool NeedFirstLetterContinuation(Text* aText) {
9909 MOZ_ASSERT(aText, "null ptr");
9910 int32_t flc = FirstLetterCount(&aText->TextFragment());
9911 int32_t tl = aText->TextDataLength();
9912 return flc < tl;
9915 static bool IsFirstLetterContent(Text* aText) {
9916 return aText->TextDataLength() && !aText->TextIsOnlyWhitespace();
9920 * Create a letter frame, only make it a floating frame.
9922 nsFirstLetterFrame* nsCSSFrameConstructor::CreateFloatingLetterFrame(
9923 nsFrameConstructorState& aState, Text* aTextContent, nsIFrame* aTextFrame,
9924 nsContainerFrame* aParentFrame, ComputedStyle* aParentStyle,
9925 ComputedStyle* aComputedStyle, nsFrameList& aResult) {
9926 MOZ_ASSERT(aParentStyle);
9928 nsFirstLetterFrame* letterFrame =
9929 NS_NewFloatingFirstLetterFrame(mPresShell, aComputedStyle);
9930 // We don't want to use a text content for a non-text frame (because we want
9931 // its primary frame to be a text frame).
9932 nsIContent* letterContent = aParentFrame->GetContent();
9933 nsContainerFrame* containingBlock =
9934 aState.GetGeometricParent(*aComputedStyle->StyleDisplay(), aParentFrame);
9935 InitAndRestoreFrame(aState, letterContent, containingBlock, letterFrame);
9937 // Init the text frame to refer to the letter frame.
9939 // Make sure we get a proper style for it (the one passed in is for the letter
9940 // frame and will have the float property set on it; the text frame shouldn't
9941 // have that set).
9942 ServoStyleSet* styleSet = mPresShell->StyleSet();
9943 RefPtr<ComputedStyle> textSC =
9944 styleSet->ResolveStyleForText(aTextContent, aComputedStyle);
9945 aTextFrame->SetComputedStyleWithoutNotification(textSC);
9946 InitAndRestoreFrame(aState, aTextContent, letterFrame, aTextFrame);
9948 // And then give the text frame to the letter frame
9949 SetInitialSingleChild(letterFrame, aTextFrame);
9951 // See if we will need to continue the text frame (does it contain
9952 // more than just the first-letter text or not?) If it does, then we
9953 // create (in advance) a continuation frame for it.
9954 nsIFrame* nextTextFrame = nullptr;
9955 if (NeedFirstLetterContinuation(aTextContent)) {
9956 // Create continuation
9957 nextTextFrame = CreateContinuingFrame(aTextFrame, aParentFrame);
9958 RefPtr<ComputedStyle> newSC =
9959 styleSet->ResolveStyleForText(aTextContent, aParentStyle);
9960 nextTextFrame->SetComputedStyle(newSC);
9963 NS_ASSERTION(aResult.IsEmpty(), "aResult should be an empty nsFrameList!");
9964 // Put the new float before any of the floats in the block we're doing
9965 // first-letter for, that is, before any floats whose parent is
9966 // containingBlock.
9967 nsIFrame* prevSibling = nullptr;
9968 for (nsIFrame* f : aState.mFloatedList) {
9969 if (f->GetParent() == containingBlock) {
9970 break;
9972 prevSibling = f;
9975 aState.AddChild(letterFrame, aResult, letterContent, aParentFrame, false,
9976 true, true, prevSibling);
9978 if (nextTextFrame) {
9979 aResult.AppendFrame(nullptr, nextTextFrame);
9982 return letterFrame;
9986 * Create a new letter frame for aTextFrame. The letter frame will be
9987 * a child of aParentFrame.
9989 void nsCSSFrameConstructor::CreateLetterFrame(
9990 nsContainerFrame* aBlockFrame, nsContainerFrame* aBlockContinuation,
9991 Text* aTextContent, nsContainerFrame* aParentFrame, nsFrameList& aResult) {
9992 NS_ASSERTION(aBlockFrame->IsBlockFrameOrSubclass(), "Not a block frame?");
9994 // Get a ComputedStyle for the first-letter-frame.
9996 // Keep this in sync with nsBlockFrame::UpdatePseudoElementStyles.
9997 nsIFrame* parentFrame = nsIFrame::CorrectStyleParentFrame(
9998 aParentFrame, PseudoStyleType::firstLetter);
10000 ComputedStyle* parentComputedStyle = parentFrame->Style();
10001 ComputedStyle* parentComputedStyleIgnoringFirstLine = parentComputedStyle;
10002 if (parentFrame->IsLineFrame()) {
10003 parentComputedStyleIgnoringFirstLine =
10004 nsIFrame::CorrectStyleParentFrame(aBlockFrame,
10005 PseudoStyleType::firstLetter)
10006 ->Style();
10009 // Use content from containing block so that we can actually
10010 // find a matching style rule.
10011 nsIContent* blockContent = aBlockFrame->GetContent();
10013 // Create first-letter style rule, ignoring first line. If we already have a
10014 // first-line we'll reparent the style below.
10015 RefPtr<ComputedStyle> sc =
10016 GetFirstLetterStyle(blockContent, parentComputedStyleIgnoringFirstLine);
10018 if (sc) {
10019 if (parentComputedStyleIgnoringFirstLine != parentComputedStyle) {
10020 sc = mPresShell->StyleSet()->ReparentComputedStyle(
10021 sc, parentComputedStyle, parentComputedStyle,
10022 blockContent->AsElement());
10025 RefPtr<ComputedStyle> textSC =
10026 mPresShell->StyleSet()->ResolveStyleForText(aTextContent, sc);
10028 // Create a new text frame (the original one will be discarded)
10029 // pass a temporary stylecontext, the correct one will be set
10030 // later. Start off by unsetting the primary frame for
10031 // aTextContent, so it's no longer pointing to the to-be-destroyed
10032 // frame.
10033 // XXXbz it would be really nice to destroy the old frame _first_,
10034 // then create the new one, so we could avoid this hack.
10035 aTextContent->SetPrimaryFrame(nullptr);
10036 nsIFrame* textFrame = NS_NewTextFrame(mPresShell, textSC);
10038 NS_ASSERTION(aBlockContinuation == GetFloatContainingBlock(aParentFrame),
10039 "Containing block is confused");
10040 nsFrameConstructorState state(
10041 mPresShell, GetAbsoluteContainingBlock(aParentFrame, FIXED_POS),
10042 GetAbsoluteContainingBlock(aParentFrame, ABS_POS), aBlockContinuation);
10044 // Create the right type of first-letter frame
10045 const nsStyleDisplay* display = sc->StyleDisplay();
10046 nsFirstLetterFrame* letterFrame;
10047 if (display->IsFloatingStyle() && !aParentFrame->IsInSVGTextSubtree()) {
10048 // Make a floating first-letter frame
10049 letterFrame = CreateFloatingLetterFrame(state, aTextContent, textFrame,
10050 aParentFrame, parentComputedStyle,
10051 sc, aResult);
10052 } else {
10053 // Make an inflow first-letter frame
10054 letterFrame = NS_NewFirstLetterFrame(mPresShell, sc);
10056 // Initialize the first-letter-frame. We don't want to use a text
10057 // content for a non-text frame (because we want its primary frame to
10058 // be a text frame).
10059 nsIContent* letterContent = aParentFrame->GetContent();
10060 letterFrame->Init(letterContent, aParentFrame, nullptr);
10062 InitAndRestoreFrame(state, aTextContent, letterFrame, textFrame);
10064 SetInitialSingleChild(letterFrame, textFrame);
10065 aResult.Clear();
10066 aResult.AppendFrame(nullptr, letterFrame);
10067 NS_ASSERTION(!aBlockFrame->GetPrevContinuation(),
10068 "should have the first continuation here");
10069 aBlockFrame->AddStateBits(NS_BLOCK_HAS_FIRST_LETTER_CHILD);
10071 MOZ_ASSERT(
10072 !aBlockFrame->GetPrevContinuation(),
10073 "Setting up a first-letter frame on a non-first block continuation?");
10074 auto parent =
10075 static_cast<nsContainerFrame*>(aParentFrame->FirstContinuation());
10076 if (MOZ_UNLIKELY(parent->IsLineFrame())) {
10077 parent = static_cast<nsContainerFrame*>(
10078 parent->GetParent()->FirstContinuation());
10080 parent->SetHasFirstLetterChild();
10081 aBlockFrame->SetProperty(nsContainerFrame::FirstLetterProperty(),
10082 letterFrame);
10083 aTextContent->SetPrimaryFrame(textFrame);
10087 void nsCSSFrameConstructor::WrapFramesInFirstLetterFrame(
10088 nsContainerFrame* aBlockFrame, nsFrameList& aBlockFrames) {
10089 aBlockFrame->AddStateBits(NS_BLOCK_HAS_FIRST_LETTER_STYLE);
10091 nsContainerFrame* parentFrame = nullptr;
10092 nsIFrame* textFrame = nullptr;
10093 nsIFrame* prevFrame = nullptr;
10094 nsFrameList letterFrames;
10095 bool stopLooking = false;
10096 WrapFramesInFirstLetterFrame(
10097 aBlockFrame, aBlockFrame, aBlockFrame, aBlockFrames.FirstChild(),
10098 &parentFrame, &textFrame, &prevFrame, letterFrames, &stopLooking);
10099 if (!parentFrame) {
10100 return;
10102 DestroyContext context(mPresShell);
10103 if (parentFrame == aBlockFrame) {
10104 // Take textFrame out of the block's frame list and substitute the
10105 // letter frame(s) instead.
10106 aBlockFrames.DestroyFrame(context, textFrame);
10107 aBlockFrames.InsertFrames(nullptr, prevFrame, std::move(letterFrames));
10108 } else {
10109 // Take the old textFrame out of the inline parent's child list
10110 RemoveFrame(context, FrameChildListID::Principal, textFrame);
10112 // Insert in the letter frame(s)
10113 parentFrame->InsertFrames(FrameChildListID::Principal, prevFrame, nullptr,
10114 std::move(letterFrames));
10118 void nsCSSFrameConstructor::WrapFramesInFirstLetterFrame(
10119 nsContainerFrame* aBlockFrame, nsContainerFrame* aBlockContinuation,
10120 nsContainerFrame* aParentFrame, nsIFrame* aParentFrameList,
10121 nsContainerFrame** aModifiedParent, nsIFrame** aTextFrame,
10122 nsIFrame** aPrevFrame, nsFrameList& aLetterFrames, bool* aStopLooking) {
10123 nsIFrame* prevFrame = nullptr;
10124 nsIFrame* frame = aParentFrameList;
10126 // This loop attempts to implement "Finding the First Letter":
10127 // https://drafts.csswg.org/css-pseudo-4/#application-in-css
10128 // FIXME: we don't handle nested blocks correctly yet though (bug 214004)
10129 while (frame) {
10130 nsIFrame* nextFrame = frame->GetNextSibling();
10132 // Skip all ::markers and placeholders.
10133 if (frame->Style()->GetPseudoType() == PseudoStyleType::marker ||
10134 frame->IsPlaceholderFrame()) {
10135 prevFrame = frame;
10136 frame = nextFrame;
10137 continue;
10139 LayoutFrameType frameType = frame->Type();
10140 if (LayoutFrameType::Text == frameType) {
10141 // Wrap up first-letter content in a letter frame
10142 Text* textContent = frame->GetContent()->AsText();
10143 if (IsFirstLetterContent(textContent)) {
10144 // Create letter frame to wrap up the text
10145 CreateLetterFrame(aBlockFrame, aBlockContinuation, textContent,
10146 aParentFrame, aLetterFrames);
10148 // Provide adjustment information for parent
10149 *aModifiedParent = aParentFrame;
10150 *aTextFrame = frame;
10151 *aPrevFrame = prevFrame;
10152 *aStopLooking = true;
10153 return;
10155 } else if (IsInlineFrame(frame) && frameType != LayoutFrameType::Br) {
10156 nsIFrame* kids = frame->PrincipalChildList().FirstChild();
10157 WrapFramesInFirstLetterFrame(aBlockFrame, aBlockContinuation,
10158 static_cast<nsContainerFrame*>(frame), kids,
10159 aModifiedParent, aTextFrame, aPrevFrame,
10160 aLetterFrames, aStopLooking);
10161 if (*aStopLooking) {
10162 return;
10164 } else {
10165 // This will stop us looking to create more letter frames. For
10166 // example, maybe the frame-type is "letterFrame" or
10167 // "placeholderFrame". This keeps us from creating extra letter
10168 // frames, and also prevents us from creating letter frames when
10169 // the first real content child of a block is not text (e.g. an
10170 // image, hr, etc.)
10171 *aStopLooking = true;
10172 break;
10175 prevFrame = frame;
10176 frame = nextFrame;
10180 static nsIFrame* FindFirstLetterFrame(nsIFrame* aFrame,
10181 FrameChildListID aListID) {
10182 for (nsIFrame* f : aFrame->GetChildList(aListID)) {
10183 if (f->IsLetterFrame()) {
10184 return f;
10187 return nullptr;
10190 static void ClearHasFirstLetterChildFrom(nsContainerFrame* aParentFrame) {
10191 MOZ_ASSERT(aParentFrame);
10192 auto* parent =
10193 static_cast<nsContainerFrame*>(aParentFrame->FirstContinuation());
10194 if (MOZ_UNLIKELY(parent->IsLineFrame())) {
10195 MOZ_ASSERT(!parent->HasFirstLetterChild());
10196 parent = static_cast<nsContainerFrame*>(
10197 parent->GetParent()->FirstContinuation());
10199 MOZ_ASSERT(parent->HasFirstLetterChild());
10200 parent->ClearHasFirstLetterChild();
10203 void nsCSSFrameConstructor::RemoveFloatingFirstLetterFrames(
10204 PresShell* aPresShell, nsIFrame* aBlockFrame) {
10205 // Look for the first letter frame on the FrameChildListID::Float, then
10206 // FrameChildListID::PushedFloats.
10207 nsIFrame* floatFrame =
10208 ::FindFirstLetterFrame(aBlockFrame, FrameChildListID::Float);
10209 if (!floatFrame) {
10210 floatFrame =
10211 ::FindFirstLetterFrame(aBlockFrame, FrameChildListID::PushedFloats);
10212 if (!floatFrame) {
10213 return;
10217 // Take the text frame away from the letter frame (so it isn't
10218 // destroyed when we destroy the letter frame).
10219 nsIFrame* textFrame = floatFrame->PrincipalChildList().FirstChild();
10220 if (!textFrame) {
10221 return;
10224 // Discover the placeholder frame for the letter frame
10225 nsPlaceholderFrame* placeholderFrame = floatFrame->GetPlaceholderFrame();
10226 if (!placeholderFrame) {
10227 // Somethings really wrong
10228 return;
10230 nsContainerFrame* parentFrame = placeholderFrame->GetParent();
10231 if (!parentFrame) {
10232 // Somethings really wrong
10233 return;
10236 ClearHasFirstLetterChildFrom(parentFrame);
10238 // Create a new text frame with the right style that maps all of the content
10239 // that was previously part of the letter frame (and probably continued
10240 // elsewhere).
10241 ComputedStyle* parentSC = parentFrame->Style();
10242 nsIContent* textContent = textFrame->GetContent();
10243 if (!textContent) {
10244 return;
10246 RefPtr<ComputedStyle> newSC =
10247 aPresShell->StyleSet()->ResolveStyleForText(textContent, parentSC);
10248 nsIFrame* newTextFrame = NS_NewTextFrame(aPresShell, newSC);
10249 newTextFrame->Init(textContent, parentFrame, nullptr);
10251 // Destroy the old text frame's continuations (the old text frame
10252 // will be destroyed when its letter frame is destroyed).
10253 nsIFrame* frameToDelete = textFrame->LastContinuation();
10254 DestroyContext context(mPresShell);
10255 while (frameToDelete != textFrame) {
10256 nsIFrame* nextFrameToDelete = frameToDelete->GetPrevContinuation();
10257 RemoveFrame(context, FrameChildListID::Principal, frameToDelete);
10258 frameToDelete = nextFrameToDelete;
10261 nsIFrame* prevSibling = placeholderFrame->GetPrevSibling();
10263 // Now that everything is set...
10264 #ifdef NOISY_FIRST_LETTER
10265 printf(
10266 "RemoveFloatingFirstLetterFrames: textContent=%p oldTextFrame=%p "
10267 "newTextFrame=%p\n",
10268 textContent.get(), textFrame, newTextFrame);
10269 #endif
10271 // Remove placeholder frame and the float
10272 RemoveFrame(context, FrameChildListID::Principal, placeholderFrame);
10274 // Now that the old frames are gone, we can start pointing to our
10275 // new primary frame.
10276 textContent->SetPrimaryFrame(newTextFrame);
10278 // Wallpaper bug 822910.
10279 bool offsetsNeedFixing = prevSibling && prevSibling->IsTextFrame();
10280 if (offsetsNeedFixing) {
10281 prevSibling->AddStateBits(TEXT_OFFSETS_NEED_FIXING);
10284 // Insert text frame in its place
10285 InsertFrames(parentFrame, FrameChildListID::Principal, prevSibling,
10286 nsFrameList(newTextFrame, newTextFrame));
10288 if (offsetsNeedFixing) {
10289 prevSibling->RemoveStateBits(TEXT_OFFSETS_NEED_FIXING);
10293 void nsCSSFrameConstructor::RemoveFirstLetterFrames(
10294 PresShell* aPresShell, nsContainerFrame* aFrame,
10295 nsContainerFrame* aBlockFrame, bool* aStopLooking) {
10296 nsIFrame* prevSibling = nullptr;
10297 nsIFrame* kid = aFrame->PrincipalChildList().FirstChild();
10299 while (kid) {
10300 if (kid->IsLetterFrame()) {
10301 ClearHasFirstLetterChildFrom(aFrame);
10302 nsIFrame* textFrame = kid->PrincipalChildList().FirstChild();
10303 if (!textFrame) {
10304 break;
10307 // Create a new textframe
10308 ComputedStyle* parentSC = aFrame->Style();
10309 if (!parentSC) {
10310 break;
10312 nsIContent* textContent = textFrame->GetContent();
10313 if (!textContent) {
10314 break;
10316 RefPtr<ComputedStyle> newSC =
10317 aPresShell->StyleSet()->ResolveStyleForText(textContent, parentSC);
10318 textFrame = NS_NewTextFrame(aPresShell, newSC);
10319 textFrame->Init(textContent, aFrame, nullptr);
10321 DestroyContext context(mPresShell);
10323 // Next rip out the kid and replace it with the text frame
10324 RemoveFrame(context, FrameChildListID::Principal, kid);
10326 // Now that the old frames are gone, we can start pointing to our
10327 // new primary frame.
10328 textContent->SetPrimaryFrame(textFrame);
10330 // Wallpaper bug 822910.
10331 bool offsetsNeedFixing = prevSibling && prevSibling->IsTextFrame();
10332 if (offsetsNeedFixing) {
10333 prevSibling->AddStateBits(TEXT_OFFSETS_NEED_FIXING);
10336 // Insert text frame in its place
10337 InsertFrames(aFrame, FrameChildListID::Principal, prevSibling,
10338 nsFrameList(textFrame, textFrame));
10340 if (offsetsNeedFixing) {
10341 prevSibling->RemoveStateBits(TEXT_OFFSETS_NEED_FIXING);
10344 *aStopLooking = true;
10345 NS_ASSERTION(!aBlockFrame->GetPrevContinuation(),
10346 "should have the first continuation here");
10347 aBlockFrame->RemoveStateBits(NS_BLOCK_HAS_FIRST_LETTER_CHILD);
10348 break;
10350 if (IsInlineFrame(kid)) {
10351 nsContainerFrame* kidAsContainerFrame = do_QueryFrame(kid);
10352 if (kidAsContainerFrame) {
10353 // Look inside child inline frame for the letter frame.
10354 RemoveFirstLetterFrames(aPresShell, kidAsContainerFrame, aBlockFrame,
10355 aStopLooking);
10356 if (*aStopLooking) {
10357 break;
10361 prevSibling = kid;
10362 kid = kid->GetNextSibling();
10366 void nsCSSFrameConstructor::RemoveLetterFrames(PresShell* aPresShell,
10367 nsContainerFrame* aBlockFrame) {
10368 aBlockFrame =
10369 static_cast<nsContainerFrame*>(aBlockFrame->FirstContinuation());
10370 aBlockFrame->RemoveProperty(nsContainerFrame::FirstLetterProperty());
10371 nsContainerFrame* continuation = aBlockFrame;
10373 bool stopLooking = false;
10374 do {
10375 RemoveFloatingFirstLetterFrames(aPresShell, continuation);
10376 RemoveFirstLetterFrames(aPresShell, continuation, aBlockFrame,
10377 &stopLooking);
10378 if (stopLooking) {
10379 break;
10381 continuation =
10382 static_cast<nsContainerFrame*>(continuation->GetNextContinuation());
10383 } while (continuation);
10386 // Fixup the letter frame situation for the given block
10387 void nsCSSFrameConstructor::RecoverLetterFrames(nsContainerFrame* aBlockFrame) {
10388 aBlockFrame =
10389 static_cast<nsContainerFrame*>(aBlockFrame->FirstContinuation());
10390 nsContainerFrame* continuation = aBlockFrame;
10392 nsContainerFrame* parentFrame = nullptr;
10393 nsIFrame* textFrame = nullptr;
10394 nsIFrame* prevFrame = nullptr;
10395 nsFrameList letterFrames;
10396 bool stopLooking = false;
10397 do {
10398 // XXX shouldn't this bit be set already (bug 408493), assert instead?
10399 continuation->AddStateBits(NS_BLOCK_HAS_FIRST_LETTER_STYLE);
10400 WrapFramesInFirstLetterFrame(
10401 aBlockFrame, continuation, continuation,
10402 continuation->PrincipalChildList().FirstChild(), &parentFrame,
10403 &textFrame, &prevFrame, letterFrames, &stopLooking);
10404 if (stopLooking) {
10405 break;
10407 continuation =
10408 static_cast<nsContainerFrame*>(continuation->GetNextContinuation());
10409 } while (continuation);
10411 if (!parentFrame) {
10412 return;
10414 // Take the old textFrame out of the parent's child list
10415 DestroyContext context(mPresShell);
10416 RemoveFrame(context, FrameChildListID::Principal, textFrame);
10418 // Insert in the letter frame(s)
10419 parentFrame->InsertFrames(FrameChildListID::Principal, prevFrame, nullptr,
10420 std::move(letterFrames));
10423 //----------------------------------------------------------------------
10425 void nsCSSFrameConstructor::ConstructBlock(
10426 nsFrameConstructorState& aState, nsIContent* aContent,
10427 nsContainerFrame* aParentFrame, nsContainerFrame* aContentParentFrame,
10428 ComputedStyle* aComputedStyle, nsContainerFrame** aNewFrame,
10429 nsFrameList& aFrameList, nsIFrame* aPositionedFrameForAbsPosContainer) {
10430 // clang-format off
10432 // If a block frame is in a multi-column subtree, its children may need to
10433 // be chopped into runs of blocks containing column-spans and runs of
10434 // blocks containing no column-spans. Each run containing column-spans
10435 // will be wrapped by an anonymous block. See CreateColumnSpanSiblings() for
10436 // the implementation.
10438 // If a block frame is a multi-column container, its children will need to
10439 // be processed as above. Moreover, it creates a ColumnSetWrapperFrame as
10440 // its outermost frame, and its children which have no
10441 // -moz-column-span-wrapper pseudo will be wrapped in ColumnSetFrames. See
10442 // FinishBuildingColumns() for the implementation.
10444 // The multi-column subtree maintains the following invariants:
10446 // 1) All the frames have the frame state bit
10447 // NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR set, except for top-level
10448 // ColumnSetWrapperFrame and those children in the column-span subtrees.
10450 // 2) The first and last frame under ColumnSetWrapperFrame are always
10451 // ColumnSetFrame.
10453 // 3) ColumnSetFrames are linked together as continuations.
10455 // 4) Those column-span wrappers are *not* linked together with themselves nor
10456 // with the original block frame. The continuation chain consists of the
10457 // original block frame and the original block's continuations wrapping
10458 // non-column-spans.
10460 // For example, this HTML
10461 // <div id="x" style="column-count: 2;">
10462 // <div style="column-span: all">a</div>
10463 // <div id="y">
10464 // b
10465 // <div style="column-span: all">c</div>
10466 // <div style="column-span: all">d</div>
10467 // e
10468 // </div>
10469 // </div>
10470 // <div style="column-span: all">f</div>
10472 // yields the following frame tree.
10474 // A) ColumnSetWrapper (original style)
10475 // B) ColumnSet (-moz-column-set) <-- always created by BeginBuildingColumns
10476 // C) Block (-moz-column-content)
10477 // D) Block (-moz-column-span-wrapper, created by x)
10478 // E) Block (div)
10479 // F) Text ("a")
10480 // G) ColumnSet (-moz-column-set)
10481 // H) Block (-moz-column-content, created by x)
10482 // I) Block (div, y)
10483 // J) Text ("b")
10484 // K) Block (-moz-column-span-wrapper, created by x)
10485 // L) Block (-moz-column-span-wrapper, created by y)
10486 // M) Block (div, new BFC)
10487 // N) Text ("c")
10488 // O) Block (div, new BFC)
10489 // P) Text ("d")
10490 // Q) ColumnSet (-moz-column-set)
10491 // R) Block (-moz-column-content, created by x)
10492 // S) Block (div, y)
10493 // T) Text ("e")
10494 // U) Block (div, new BFC) <-- not in multi-column hierarchy
10495 // V) Text ("f")
10497 // ColumnSet linkage described in 3): B -> G -> Q
10499 // Block linkage described in 4): C -> H -> R and I -> S
10501 // clang-format on
10503 nsBlockFrame* blockFrame = do_QueryFrame(*aNewFrame);
10504 MOZ_ASSERT(blockFrame && blockFrame->IsBlockFrame(), "not a block frame?");
10506 // Create column hierarchy if necessary.
10507 const bool needsColumn =
10508 aComputedStyle->StyleColumn()->IsColumnContainerStyle();
10509 if (needsColumn) {
10510 *aNewFrame = BeginBuildingColumns(aState, aContent, aParentFrame,
10511 blockFrame, aComputedStyle);
10513 if (aPositionedFrameForAbsPosContainer == blockFrame) {
10514 aPositionedFrameForAbsPosContainer = *aNewFrame;
10516 } else {
10517 // No need to create column hierarchy. Initialize block frame.
10518 blockFrame->SetComputedStyleWithoutNotification(aComputedStyle);
10519 InitAndRestoreFrame(aState, aContent, aParentFrame, blockFrame);
10522 aState.AddChild(*aNewFrame, aFrameList, aContent,
10523 aContentParentFrame ? aContentParentFrame : aParentFrame);
10524 if (!mRootElementFrame) {
10525 mRootElementFrame = *aNewFrame;
10528 // We should make the outer frame be the absolute containing block,
10529 // if one is required. We have to do this because absolute
10530 // positioning must be computed with respect to the CSS dimensions
10531 // of the element, which are the dimensions of the outer block. But
10532 // we can't really do that because only blocks can have absolute
10533 // children. So use the block and try to compensate with hacks
10534 // in nsBlockFrame::CalculateContainingBlockSizeForAbsolutes.
10535 nsFrameConstructorSaveState absoluteSaveState;
10536 (*aNewFrame)->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
10537 if (aPositionedFrameForAbsPosContainer) {
10538 aState.PushAbsoluteContainingBlock(
10539 *aNewFrame, aPositionedFrameForAbsPosContainer, absoluteSaveState);
10542 nsFrameConstructorSaveState floatSaveState;
10543 aState.MaybePushFloatContainingBlock(blockFrame, floatSaveState);
10545 if (aParentFrame->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR) &&
10546 !ShouldSuppressColumnSpanDescendants(aParentFrame)) {
10547 blockFrame->AddStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR);
10550 // Process the child content
10551 nsFrameList childList;
10552 ProcessChildren(aState, aContent, aComputedStyle, blockFrame, true, childList,
10553 true);
10555 if (!MayNeedToCreateColumnSpanSiblings(blockFrame, childList)) {
10556 // No need to create column-span siblings.
10557 blockFrame->SetInitialChildList(FrameChildListID::Principal,
10558 std::move(childList));
10559 return;
10562 // Extract any initial non-column-span kids, and put them in block frame's
10563 // child list.
10564 nsFrameList initialNonColumnSpanKids =
10565 childList.Split([](nsIFrame* f) { return f->IsColumnSpan(); });
10566 blockFrame->SetInitialChildList(FrameChildListID::Principal,
10567 std::move(initialNonColumnSpanKids));
10569 if (childList.IsEmpty()) {
10570 // No more kids to process (there weren't any column-span kids).
10571 return;
10574 nsFrameList columnSpanSiblings = CreateColumnSpanSiblings(
10575 aState, blockFrame, childList,
10576 // If we're constructing a column container, pass nullptr as
10577 // aPositionedFrame to forbid reparenting absolute/fixed positioned frames
10578 // to column contents or column-span wrappers.
10579 needsColumn ? nullptr : aPositionedFrameForAbsPosContainer);
10581 if (needsColumn) {
10582 // We're constructing a column container; need to finish building it.
10583 FinishBuildingColumns(aState, *aNewFrame, blockFrame, columnSpanSiblings);
10584 } else {
10585 // We're constructing a normal block which has column-span children in a
10586 // column hierarchy such as "x" in the following example.
10588 // <div style="column-count: 2">
10589 // <div id="x">
10590 // <div>normal child</div>
10591 // <div style="column-span">spanner</div>
10592 // </div>
10593 // </div>
10594 aFrameList.AppendFrames(nullptr, std::move(columnSpanSiblings));
10597 MOZ_ASSERT(columnSpanSiblings.IsEmpty(),
10598 "The column-span siblings should be moved to the proper place!");
10601 nsBlockFrame* nsCSSFrameConstructor::BeginBuildingColumns(
10602 nsFrameConstructorState& aState, nsIContent* aContent,
10603 nsContainerFrame* aParentFrame, nsContainerFrame* aColumnContent,
10604 ComputedStyle* aComputedStyle) {
10605 MOZ_ASSERT(aColumnContent->IsBlockFrame(),
10606 "aColumnContent should be a block frame.");
10607 MOZ_ASSERT(aComputedStyle->StyleColumn()->IsColumnContainerStyle(),
10608 "No need to build a column hierarchy!");
10610 // The initial column hierarchy looks like this:
10612 // ColumnSetWrapper (original style)
10613 // ColumnSet (-moz-column-set)
10614 // Block (-moz-column-content)
10616 nsBlockFrame* columnSetWrapper = NS_NewColumnSetWrapperFrame(
10617 mPresShell, aComputedStyle, nsFrameState(NS_FRAME_OWNS_ANON_BOXES));
10618 InitAndRestoreFrame(aState, aContent, aParentFrame, columnSetWrapper);
10619 if (aParentFrame->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR) &&
10620 !ShouldSuppressColumnSpanDescendants(aParentFrame)) {
10621 columnSetWrapper->AddStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR);
10624 AutoFrameConstructionPageName pageNameTracker(aState, columnSetWrapper);
10625 RefPtr<ComputedStyle> columnSetStyle =
10626 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
10627 PseudoStyleType::columnSet, aComputedStyle);
10628 nsContainerFrame* columnSet = NS_NewColumnSetFrame(
10629 mPresShell, columnSetStyle, nsFrameState(NS_FRAME_OWNS_ANON_BOXES));
10630 InitAndRestoreFrame(aState, aContent, columnSetWrapper, columnSet);
10631 columnSet->AddStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR);
10633 RefPtr<ComputedStyle> blockStyle =
10634 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
10635 PseudoStyleType::columnContent, columnSetStyle);
10636 aColumnContent->SetComputedStyleWithoutNotification(blockStyle);
10637 InitAndRestoreFrame(aState, aContent, columnSet, aColumnContent);
10638 aColumnContent->AddStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR);
10640 // Set up the parent-child chain.
10641 SetInitialSingleChild(columnSetWrapper, columnSet);
10642 SetInitialSingleChild(columnSet, aColumnContent);
10644 return columnSetWrapper;
10647 void nsCSSFrameConstructor::FinishBuildingColumns(
10648 nsFrameConstructorState& aState, nsContainerFrame* aColumnSetWrapper,
10649 nsContainerFrame* aColumnContent, nsFrameList& aColumnContentSiblings) {
10650 nsContainerFrame* prevColumnSet = aColumnContent->GetParent();
10652 MOZ_ASSERT(prevColumnSet->IsColumnSetFrame() &&
10653 prevColumnSet->GetParent() == aColumnSetWrapper,
10654 "Should have established column hierarchy!");
10656 // Tag the first ColumnSet to have column-span siblings so that the bit can
10657 // propagate to all the continuations. We don't want the last ColumnSet to
10658 // have this bit, so we will unset the bit for it at the end of this function.
10659 prevColumnSet->SetHasColumnSpanSiblings(true);
10661 nsFrameList finalList;
10662 while (aColumnContentSiblings.NotEmpty()) {
10663 nsIFrame* f = aColumnContentSiblings.RemoveFirstChild();
10664 if (f->IsColumnSpan()) {
10665 // Do nothing for column-span wrappers. Just move it to the final
10666 // items.
10667 finalList.AppendFrame(aColumnSetWrapper, f);
10668 } else {
10669 auto* continuingColumnSet = static_cast<nsContainerFrame*>(
10670 CreateContinuingFrame(prevColumnSet, aColumnSetWrapper, false));
10671 MOZ_ASSERT(continuingColumnSet->HasColumnSpanSiblings(),
10672 "The bit should propagate to the next continuation!");
10674 f->SetParent(continuingColumnSet);
10675 SetInitialSingleChild(continuingColumnSet, f);
10676 finalList.AppendFrame(aColumnSetWrapper, continuingColumnSet);
10677 prevColumnSet = continuingColumnSet;
10681 // Unset the bit because the last ColumnSet has no column-span siblings.
10682 prevColumnSet->SetHasColumnSpanSiblings(false);
10684 aColumnSetWrapper->AppendFrames(FrameChildListID::Principal,
10685 std::move(finalList));
10688 bool nsCSSFrameConstructor::MayNeedToCreateColumnSpanSiblings(
10689 nsContainerFrame* aBlockFrame, const nsFrameList& aChildList) {
10690 if (!aBlockFrame->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR)) {
10691 // The block frame isn't in a multi-column block formatting context.
10692 return false;
10695 if (ShouldSuppressColumnSpanDescendants(aBlockFrame)) {
10696 // No need to create column-span siblings for a frame that suppresses them.
10697 return false;
10700 if (aChildList.IsEmpty()) {
10701 // No child needs to be processed.
10702 return false;
10705 // Need to actually look into the child list.
10706 return true;
10709 nsFrameList nsCSSFrameConstructor::CreateColumnSpanSiblings(
10710 nsFrameConstructorState& aState, nsContainerFrame* aInitialBlock,
10711 nsFrameList& aChildList, nsIFrame* aPositionedFrame) {
10712 MOZ_ASSERT(aInitialBlock->IsBlockFrameOrSubclass());
10713 MOZ_ASSERT(!aPositionedFrame || aPositionedFrame->IsAbsPosContainingBlock());
10715 nsIContent* const content = aInitialBlock->GetContent();
10716 nsContainerFrame* const parentFrame = aInitialBlock->GetParent();
10717 const bool isInitialBlockFloatCB = aInitialBlock->IsFloatContainingBlock();
10719 nsFrameList siblings;
10720 nsContainerFrame* lastNonColumnSpanWrapper = aInitialBlock;
10722 // Tag the first non-column-span wrapper to have column-span siblings so that
10723 // the bit can propagate to all the continuations. We don't want the last
10724 // wrapper to have this bit, so we will unset the bit for it at the end of
10725 // this function.
10726 lastNonColumnSpanWrapper->SetHasColumnSpanSiblings(true);
10727 do {
10728 MOZ_ASSERT(aChildList.NotEmpty(), "Why call this if child list is empty?");
10729 MOZ_ASSERT(aChildList.FirstChild()->IsColumnSpan(),
10730 "Must have the child starting with column-span!");
10732 // Grab the consecutive column-span kids, and reparent them into a
10733 // block frame.
10734 RefPtr<ComputedStyle> columnSpanWrapperStyle =
10735 mPresShell->StyleSet()->ResolveNonInheritingAnonymousBoxStyle(
10736 PseudoStyleType::columnSpanWrapper);
10737 nsBlockFrame* columnSpanWrapper =
10738 NS_NewBlockFrame(mPresShell, columnSpanWrapperStyle);
10739 InitAndRestoreFrame(aState, content, parentFrame, columnSpanWrapper, false);
10740 columnSpanWrapper->AddStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR |
10741 NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
10743 nsFrameList columnSpanKids =
10744 aChildList.Split([](nsIFrame* f) { return !f->IsColumnSpan(); });
10745 columnSpanKids.ApplySetParent(columnSpanWrapper);
10746 columnSpanWrapper->SetInitialChildList(FrameChildListID::Principal,
10747 std::move(columnSpanKids));
10748 if (aPositionedFrame) {
10749 aState.ReparentAbsoluteItems(columnSpanWrapper);
10752 siblings.AppendFrame(nullptr, columnSpanWrapper);
10754 // Grab the consecutive non-column-span kids, and reparent them into a new
10755 // continuation of the last non-column-span wrapper frame.
10756 auto* nonColumnSpanWrapper = static_cast<nsContainerFrame*>(
10757 CreateContinuingFrame(lastNonColumnSpanWrapper, parentFrame, false));
10758 nonColumnSpanWrapper->AddStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR |
10759 NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
10760 MOZ_ASSERT(nonColumnSpanWrapper->HasColumnSpanSiblings(),
10761 "The bit should propagate to the next continuation!");
10763 if (aChildList.NotEmpty()) {
10764 nsFrameList nonColumnSpanKids =
10765 aChildList.Split([](nsIFrame* f) { return f->IsColumnSpan(); });
10767 nonColumnSpanKids.ApplySetParent(nonColumnSpanWrapper);
10768 nonColumnSpanWrapper->SetInitialChildList(FrameChildListID::Principal,
10769 std::move(nonColumnSpanKids));
10770 if (aPositionedFrame) {
10771 aState.ReparentAbsoluteItems(nonColumnSpanWrapper);
10773 if (isInitialBlockFloatCB) {
10774 aState.ReparentFloats(nonColumnSpanWrapper);
10778 siblings.AppendFrame(nullptr, nonColumnSpanWrapper);
10780 lastNonColumnSpanWrapper = nonColumnSpanWrapper;
10781 } while (aChildList.NotEmpty());
10783 // Unset the bit because the last non-column-span wrapper has no column-span
10784 // siblings.
10785 lastNonColumnSpanWrapper->SetHasColumnSpanSiblings(false);
10787 return siblings;
10790 bool nsCSSFrameConstructor::MaybeRecreateForColumnSpan(
10791 nsFrameConstructorState& aState, nsContainerFrame* aParentFrame,
10792 nsFrameList& aFrameList, nsIFrame* aPrevSibling) {
10793 if (!aParentFrame->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR)) {
10794 return false;
10797 if (aFrameList.IsEmpty()) {
10798 return false;
10801 MOZ_ASSERT(!IsFramePartOfIBSplit(aParentFrame),
10802 "We should have wiped aParentFrame in WipeContainingBlock if it's "
10803 "part of IB split!");
10805 nsIFrame* nextSibling = ::GetInsertNextSibling(aParentFrame, aPrevSibling);
10806 if (!nextSibling && IsLastContinuationForColumnContent(aParentFrame)) {
10807 // We are appending a list of frames to the last continuation of a
10808 // ::-moz-column-content. This is the case where we can fix the frame tree
10809 // instead of reframing the containing block. Return false and let
10810 // AppendFramesToParent() deal with this.
10811 return false;
10814 auto HasColumnSpan = [](const nsFrameList& aList) {
10815 for (nsIFrame* f : aList) {
10816 if (f->IsColumnSpan()) {
10817 return true;
10820 return false;
10823 if (HasColumnSpan(aFrameList)) {
10824 // If any frame in the frame list has "column-span:all" style, i.e. a
10825 // -moz-column-span-wrapper frame, we need to reframe the multi-column
10826 // containing block.
10828 // We can only be here if none of the new inserted nsIContent* nodes (via
10829 // ContentAppended or ContentRangeInserted) have column-span:all style, yet
10830 // some of them have column-span:all descendants. Sadly, there's no way to
10831 // detect this by checking FrameConstructionItems in WipeContainingBlock().
10832 // Otherwise, we would have already wiped the multi-column containing block.
10833 PROFILER_MARKER("Reframe multi-column after constructing frame list",
10834 LAYOUT, {}, Tracing, "Layout");
10836 // aFrameList can contain placeholder frames. In order to destroy their
10837 // associated out-of-flow frames properly, we need to manually flush all the
10838 // out-of-flow frames in aState to their container frames.
10839 aState.ProcessFrameInsertionsForAllLists();
10840 DestroyContext context(mPresShell);
10841 aFrameList.DestroyFrames(context);
10842 RecreateFramesForContent(
10843 GetMultiColumnContainingBlockFor(aParentFrame)->GetContent(),
10844 InsertionKind::Async);
10845 return true;
10848 return false;
10851 nsIFrame* nsCSSFrameConstructor::ConstructInline(
10852 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
10853 nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay,
10854 nsFrameList& aFrameList) {
10855 // If an inline frame has non-inline kids, then we chop up the child list
10856 // into runs of blocks and runs of inlines, create anonymous block frames to
10857 // contain the runs of blocks, inline frames with our style for the runs of
10858 // inlines, and put all these frames, in order, into aFrameList.
10860 // When there are column-span blocks in a run of blocks, instead of creating
10861 // an anonymous block to wrap them, we create multiple anonymous blocks,
10862 // wrapping runs of non-column-spans and runs of column-spans.
10864 // We return the the first one. The whole setup is called an {ib}
10865 // split; in what follows "frames in the split" refers to the anonymous blocks
10866 // and inlines that contain our children.
10868 // {ib} splits maintain the following invariants:
10869 // 1) All frames in the split have the NS_FRAME_PART_OF_IBSPLIT bit
10870 // set.
10872 // 2) Each frame in the split has the nsIFrame::IBSplitSibling
10873 // property pointing to the next frame in the split, except for the last
10874 // one, which does not have it set.
10876 // 3) Each frame in the split has the nsIFrame::IBSplitPrevSibling
10877 // property pointing to the previous frame in the split, except for the
10878 // first one, which does not have it set.
10880 // 4) The first and last frame in the split are always inlines.
10882 // 5) The frames wrapping runs of non-column-spans are linked together as
10883 // continuations. The frames wrapping runs of column-spans are *not*
10884 // linked with each other nor with other non-column-span wrappers.
10886 // 6) The first and last frame in the chains of blocks are always wrapping
10887 // non-column-spans. Both of them are created even if they're empty.
10889 // An invariant that is NOT maintained is that the wrappers are actually
10890 // linked via GetNextSibling linkage. A simple example is an inline
10891 // containing an inline that contains a block. The three parts of the inner
10892 // inline end up with three different parents.
10894 // For example, this HTML:
10895 // <span>
10896 // <div>a</div>
10897 // <span>
10898 // b
10899 // <div>c</div>
10900 // </span>
10901 // d
10902 // <div>e</div>
10903 // f
10904 // </span>
10905 // Gives the following frame tree:
10907 // Inline (outer span)
10908 // Block (anonymous, outer span)
10909 // Block (div)
10910 // Text("a")
10911 // Inline (outer span)
10912 // Inline (inner span)
10913 // Text("b")
10914 // Block (anonymous, outer span)
10915 // Block (anonymous, inner span)
10916 // Block (div)
10917 // Text("c")
10918 // Inline (outer span)
10919 // Inline (inner span)
10920 // Text("d")
10921 // Block (anonymous, outer span)
10922 // Block (div)
10923 // Text("e")
10924 // Inline (outer span)
10925 // Text("f")
10927 nsIContent* const content = aItem.mContent;
10928 ComputedStyle* const computedStyle = aItem.mComputedStyle;
10930 nsInlineFrame* newFrame = NS_NewInlineFrame(mPresShell, computedStyle);
10932 // Initialize the frame
10933 InitAndRestoreFrame(aState, content, aParentFrame, newFrame);
10935 // definition cannot be inside next block because the object's destructor is
10936 // significant. this is part of the fix for bug 42372
10937 nsFrameConstructorSaveState absoluteSaveState;
10939 bool isAbsPosCB = newFrame->IsAbsPosContainingBlock();
10940 newFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
10941 if (isAbsPosCB) {
10942 // Relatively positioned frames becomes a container for child
10943 // frames that are positioned
10944 aState.PushAbsoluteContainingBlock(newFrame, newFrame, absoluteSaveState);
10947 if (aParentFrame->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR) &&
10948 !ShouldSuppressColumnSpanDescendants(aParentFrame)) {
10949 newFrame->AddStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR);
10952 // Process the child content
10953 nsFrameList childList;
10954 ConstructFramesFromItemList(aState, aItem.mChildItems, newFrame,
10955 /* aParentIsWrapperAnonBox = */ false, childList);
10957 nsIFrame* firstBlock = nullptr;
10958 if (!aItem.mIsAllInline) {
10959 for (nsIFrame* f : childList) {
10960 if (f->IsBlockOutside()) {
10961 firstBlock = f;
10962 break;
10967 if (aItem.mIsAllInline || !firstBlock) {
10968 // This part is easy. We either already know we have no non-inline kids,
10969 // or haven't found any when constructing actual frames (the latter can
10970 // happen only if out-of-flows that we thought had no containing block
10971 // acquired one when ancestor inline frames and {ib} splits got
10972 // constructed). Just put all the kids into the single inline frame and
10973 // bail.
10974 newFrame->SetInitialChildList(FrameChildListID::Principal,
10975 std::move(childList));
10976 aState.AddChild(newFrame, aFrameList, content, aParentFrame);
10977 return newFrame;
10980 // This inline frame contains several types of children. Therefore this frame
10981 // has to be chopped into several pieces, as described above.
10983 // Grab the first inline's kids
10984 nsFrameList firstInlineKids = childList.TakeFramesBefore(firstBlock);
10985 newFrame->SetInitialChildList(FrameChildListID::Principal,
10986 std::move(firstInlineKids));
10988 aFrameList.AppendFrame(nullptr, newFrame);
10990 newFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
10991 CreateIBSiblings(aState, newFrame, isAbsPosCB, childList, aFrameList);
10993 return newFrame;
10996 void nsCSSFrameConstructor::CreateIBSiblings(nsFrameConstructorState& aState,
10997 nsContainerFrame* aInitialInline,
10998 bool aIsAbsPosCB,
10999 nsFrameList& aChildList,
11000 nsFrameList& aSiblings) {
11001 MOZ_ASSERT(aIsAbsPosCB == aInitialInline->IsAbsPosContainingBlock());
11003 nsIContent* content = aInitialInline->GetContent();
11004 ComputedStyle* computedStyle = aInitialInline->Style();
11005 nsContainerFrame* parentFrame = aInitialInline->GetParent();
11007 // Resolve the right style for our anonymous blocks.
11009 // The distinction in styles is needed because of CSS 2.1, section
11010 // 9.2.1.1, which says:
11012 // When such an inline box is affected by relative positioning, any
11013 // resulting translation also affects the block-level box contained
11014 // in the inline box.
11015 RefPtr<ComputedStyle> blockSC =
11016 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
11017 PseudoStyleType::mozBlockInsideInlineWrapper, computedStyle);
11019 nsContainerFrame* lastNewInline =
11020 static_cast<nsContainerFrame*>(aInitialInline->FirstContinuation());
11021 do {
11022 // On entry to this loop aChildList is not empty and the first frame in it
11023 // is block-level.
11024 MOZ_ASSERT(aChildList.NotEmpty(), "Should have child items");
11025 MOZ_ASSERT(aChildList.FirstChild()->IsBlockOutside(),
11026 "Must have list starting with block");
11028 // The initial run of blocks belongs to an anonymous block that we create
11029 // right now. The anonymous block will be the parent of these block
11030 // children of the inline.
11031 nsBlockFrame* blockFrame = NS_NewBlockFrame(mPresShell, blockSC);
11032 InitAndRestoreFrame(aState, content, parentFrame, blockFrame, false);
11033 if (aInitialInline->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR)) {
11034 blockFrame->AddStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR);
11037 // Find the first non-block child which defines the end of our block kids
11038 // and the start of our next inline's kids
11039 nsFrameList blockKids =
11040 aChildList.Split([](nsIFrame* f) { return !f->IsBlockOutside(); });
11042 if (!aInitialInline->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR)) {
11043 MoveChildrenTo(aInitialInline, blockFrame, blockKids);
11045 SetFrameIsIBSplit(lastNewInline, blockFrame);
11046 aSiblings.AppendFrame(nullptr, blockFrame);
11047 } else {
11048 // Extract any initial non-column-span frames, and put them in
11049 // blockFrame's child list.
11050 nsFrameList initialNonColumnSpanKids =
11051 blockKids.Split([](nsIFrame* f) { return f->IsColumnSpan(); });
11052 MoveChildrenTo(aInitialInline, blockFrame, initialNonColumnSpanKids);
11054 SetFrameIsIBSplit(lastNewInline, blockFrame);
11055 aSiblings.AppendFrame(nullptr, blockFrame);
11057 if (blockKids.NotEmpty()) {
11058 // Although SetFrameIsIBSplit() will add NS_FRAME_PART_OF_IBSPLIT for
11059 // blockFrame later, we manually add the bit earlier here to make all
11060 // the continuations of blockFrame created in
11061 // CreateColumnSpanSiblings(), i.e. non-column-span wrappers, have the
11062 // bit via nsIFrame::Init().
11063 blockFrame->AddStateBits(NS_FRAME_PART_OF_IBSPLIT);
11065 nsFrameList columnSpanSiblings =
11066 CreateColumnSpanSiblings(aState, blockFrame, blockKids,
11067 aIsAbsPosCB ? aInitialInline : nullptr);
11068 aSiblings.AppendFrames(nullptr, std::move(columnSpanSiblings));
11072 // Now grab the initial inlines in aChildList and put them into an inline
11073 // frame.
11074 nsInlineFrame* inlineFrame = NS_NewInlineFrame(mPresShell, computedStyle);
11075 InitAndRestoreFrame(aState, content, parentFrame, inlineFrame, false);
11076 inlineFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
11077 if (aInitialInline->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR)) {
11078 inlineFrame->AddStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR);
11081 if (aIsAbsPosCB) {
11082 inlineFrame->MarkAsAbsoluteContainingBlock();
11085 if (aChildList.NotEmpty()) {
11086 nsFrameList inlineKids =
11087 aChildList.Split([](nsIFrame* f) { return f->IsBlockOutside(); });
11088 MoveChildrenTo(aInitialInline, inlineFrame, inlineKids);
11091 SetFrameIsIBSplit(blockFrame, inlineFrame);
11092 aSiblings.AppendFrame(nullptr, inlineFrame);
11093 lastNewInline = inlineFrame;
11094 } while (aChildList.NotEmpty());
11096 SetFrameIsIBSplit(lastNewInline, nullptr);
11099 void nsCSSFrameConstructor::BuildInlineChildItems(
11100 nsFrameConstructorState& aState, FrameConstructionItem& aParentItem,
11101 bool aItemIsWithinSVGText, bool aItemAllowsTextPathChild) {
11102 ComputedStyle* const parentComputedStyle = aParentItem.mComputedStyle;
11103 nsIContent* const parentContent = aParentItem.mContent;
11105 if (!aItemIsWithinSVGText) {
11106 if (parentComputedStyle->StyleDisplay()->IsListItem()) {
11107 CreateGeneratedContentItem(aState, nullptr, *parentContent->AsElement(),
11108 *parentComputedStyle, PseudoStyleType::marker,
11109 aParentItem.mChildItems);
11111 // Probe for generated content before
11112 CreateGeneratedContentItem(aState, nullptr, *parentContent->AsElement(),
11113 *parentComputedStyle, PseudoStyleType::before,
11114 aParentItem.mChildItems);
11117 ItemFlags flags;
11118 if (aItemIsWithinSVGText) {
11119 flags += ItemFlag::IsWithinSVGText;
11121 if (aItemAllowsTextPathChild &&
11122 aParentItem.mContent->IsSVGElement(nsGkAtoms::a)) {
11123 flags += ItemFlag::AllowTextPathChild;
11126 FlattenedChildIterator iter(parentContent);
11127 for (nsIContent* content = iter.GetNextChild(); content;
11128 content = iter.GetNextChild()) {
11129 AddFrameConstructionItems(aState, content, iter.ShadowDOMInvolved(),
11130 *parentComputedStyle, InsertionPoint(),
11131 aParentItem.mChildItems, flags);
11134 if (!aItemIsWithinSVGText) {
11135 // Probe for generated content after
11136 CreateGeneratedContentItem(aState, nullptr, *parentContent->AsElement(),
11137 *parentComputedStyle, PseudoStyleType::after,
11138 aParentItem.mChildItems);
11141 aParentItem.mIsAllInline = aParentItem.mChildItems.AreAllItemsInline();
11144 // return whether it's ok to append (in the AppendFrames sense) to
11145 // aParentFrame if our nextSibling is aNextSibling. aParentFrame must
11146 // be an ib-split inline.
11147 static bool IsSafeToAppendToIBSplitInline(nsIFrame* aParentFrame,
11148 nsIFrame* aNextSibling) {
11149 MOZ_ASSERT(IsInlineFrame(aParentFrame), "Must have an inline parent here");
11151 do {
11152 NS_ASSERTION(IsFramePartOfIBSplit(aParentFrame),
11153 "How is this not part of an ib-split?");
11154 if (aNextSibling || aParentFrame->GetNextContinuation() ||
11155 GetIBSplitSibling(aParentFrame)) {
11156 return false;
11159 aNextSibling = aParentFrame->GetNextSibling();
11160 aParentFrame = aParentFrame->GetParent();
11161 } while (IsInlineFrame(aParentFrame));
11163 return true;
11166 bool nsCSSFrameConstructor::WipeInsertionParent(nsContainerFrame* aFrame) {
11167 #define TRACE(reason) \
11168 PROFILER_MARKER("WipeInsertionParent: " reason, LAYOUT, {}, Tracing, \
11169 "Layout");
11171 const LayoutFrameType frameType = aFrame->Type();
11173 // FIXME(emilio): This looks terribly inefficient if you insert elements deep
11174 // in a MathML subtree.
11175 if (aFrame->IsMathMLFrame()) {
11176 TRACE("MathML");
11177 RecreateFramesForContent(aFrame->GetContent(), InsertionKind::Async);
11178 return true;
11181 // A ruby-related frame that's getting new children.
11182 // The situation for ruby is complex, especially when interacting with
11183 // spaces. It contains these two special cases apart from tables:
11184 // 1) There are effectively three types of white spaces in ruby frames
11185 // we handle differently: leading/tailing/inter-level space,
11186 // inter-base/inter-annotation space, and inter-segment space.
11187 // These three types of spaces can be converted to each other when
11188 // their sibling changes.
11189 // 2) The first effective child of a ruby frame must always be a ruby
11190 // base container. It should be created or destroyed accordingly.
11191 if (IsRubyPseudo(aFrame) || frameType == LayoutFrameType::Ruby ||
11192 RubyUtils::IsRubyContainerBox(frameType)) {
11193 // We want to optimize it better, and avoid reframing as much as
11194 // possible. But given the cases above, and the fact that a ruby
11195 // usually won't be very large, it should be fine to reframe it.
11196 TRACE("Ruby");
11197 RecreateFramesForContent(aFrame->GetContent(), InsertionKind::Async);
11198 return true;
11201 // Reframe the multi-column container whenever elements insert/append
11202 // into it because we need to reconstruct column-span split.
11203 if (aFrame->IsColumnSetWrapperFrame()) {
11204 TRACE("Multi-column");
11205 RecreateFramesForContent(aFrame->GetContent(), InsertionKind::Async);
11206 return true;
11209 return false;
11211 #undef TRACE
11214 bool nsCSSFrameConstructor::WipeContainingBlock(
11215 nsFrameConstructorState& aState, nsIFrame* aContainingBlock,
11216 nsIFrame* aFrame, FrameConstructionItemList& aItems, bool aIsAppend,
11217 nsIFrame* aPrevSibling) {
11218 #define TRACE(reason) \
11219 PROFILER_MARKER("WipeContainingBlock: " reason, LAYOUT, {}, Tracing, \
11220 "Layout");
11222 if (aItems.IsEmpty()) {
11223 return false;
11226 // Before we go and append the frames, we must check for several
11227 // special situations.
11229 if (aFrame->GetContent() == mDocument->GetRootElement()) {
11230 // Situation #1 is when we insert content that becomes the canonical body
11231 // element, and its used WritingMode is different from the root element's
11232 // used WritingMode.
11233 // We need to reframe the root element so that the root element's frames has
11234 // the correct writing-mode propagated from body element. (See
11235 // nsCSSFrameConstructor::ConstructDocElementFrame.)
11237 // Bug 1594297: When inserting a new <body>, we may need to reframe the old
11238 // <body> which has a "overflow" value other than simple "visible". But it's
11239 // tricky, see bug 1593752.
11240 nsIContent* bodyElement = mDocument->GetBodyElement();
11241 for (FCItemIterator iter(aItems); !iter.IsDone(); iter.Next()) {
11242 const WritingMode bodyWM(iter.item().mComputedStyle);
11243 if (iter.item().mContent == bodyElement &&
11244 bodyWM != aFrame->GetWritingMode()) {
11245 TRACE("Root");
11246 RecreateFramesForContent(mDocument->GetRootElement(),
11247 InsertionKind::Async);
11248 return true;
11253 nsIFrame* nextSibling = ::GetInsertNextSibling(aFrame, aPrevSibling);
11255 // Situation #2 is a flex / grid container frame into which we're inserting
11256 // new inline non-replaced children, adjacent to an existing anonymous flex or
11257 // grid item.
11258 if (aFrame->IsFlexOrGridContainer()) {
11259 FCItemIterator iter(aItems);
11261 // Check if we're adding to-be-wrapped content right *after* an existing
11262 // anonymous flex or grid item (which would need to absorb this content).
11263 const bool isLegacyWebKitBox = IsFlexContainerForLegacyWebKitBox(aFrame);
11264 if (aPrevSibling && IsAnonymousItem(aPrevSibling) &&
11265 iter.item().NeedsAnonFlexOrGridItem(aState, isLegacyWebKitBox)) {
11266 TRACE("Inserting inline after anon flex or grid item");
11267 RecreateFramesForContent(aFrame->GetContent(), InsertionKind::Async);
11268 return true;
11271 // Check if we're adding to-be-wrapped content right *before* an existing
11272 // anonymous flex or grid item (which would need to absorb this content).
11273 if (nextSibling && IsAnonymousItem(nextSibling)) {
11274 // Jump to the last entry in the list
11275 iter.SetToEnd();
11276 iter.Prev();
11277 if (iter.item().NeedsAnonFlexOrGridItem(aState, isLegacyWebKitBox)) {
11278 TRACE("Inserting inline before anon flex or grid item");
11279 RecreateFramesForContent(aFrame->GetContent(), InsertionKind::Async);
11280 return true;
11285 // Situation #3 is an anonymous flex or grid item that's getting new children
11286 // who don't want to be wrapped.
11287 if (IsAnonymousItem(aFrame)) {
11288 AssertAnonymousFlexOrGridItemParent(aFrame, aFrame->GetParent());
11290 // We need to push a null float containing block to be sure that
11291 // "NeedsAnonFlexOrGridItem" will know we're not honoring floats for this
11292 // inserted content. (In particular, this is necessary in order for
11293 // its "GetGeometricParent" call to return the correct result.)
11294 // We're not honoring floats on this content because it has the
11295 // _flex/grid container_ as its parent in the content tree.
11296 nsFrameConstructorSaveState floatSaveState;
11297 aState.PushFloatContainingBlock(nullptr, floatSaveState);
11299 FCItemIterator iter(aItems);
11300 // Skip over things that _do_ need an anonymous flex item, because
11301 // they're perfectly happy to go here -- they won't cause a reframe.
11302 nsIFrame* containerFrame = aFrame->GetParent();
11303 const bool isLegacyWebKitBox =
11304 IsFlexContainerForLegacyWebKitBox(containerFrame);
11305 if (!iter.SkipItemsThatNeedAnonFlexOrGridItem(aState, isLegacyWebKitBox)) {
11306 // We hit something that _doesn't_ need an anonymous flex item!
11307 // Rebuild the flex container to bust it out.
11308 TRACE("Inserting non-inlines inside anon flex or grid item");
11309 RecreateFramesForContent(containerFrame->GetContent(),
11310 InsertionKind::Async);
11311 return true;
11314 // If we get here, then everything in |aItems| needs to be wrapped in
11315 // an anonymous flex or grid item. That's where it's already going - good!
11318 // Situation #4 is a case when table pseudo-frames don't work out right
11319 ParentType parentType = GetParentType(aFrame);
11320 // If all the kids want a parent of the type that aFrame is, then we're all
11321 // set to go. Indeed, there won't be any table pseudo-frames created between
11322 // aFrame and the kids, so those won't need to be merged with any table
11323 // pseudo-frames that might already be kids of aFrame. If aFrame itself is a
11324 // table pseudo-frame, then all the kids in this list would have wanted a
11325 // frame of that type wrapping them anyway, so putting them inside it is ok.
11326 if (!aItems.AllWantParentType(parentType)) {
11327 // Don't give up yet. If parentType is not eTypeBlock and the parent is
11328 // not a generated content frame, then try filtering whitespace out of the
11329 // list.
11330 if (parentType != eTypeBlock && !aFrame->IsGeneratedContentFrame()) {
11331 // For leading whitespace followed by a kid that wants our parent type,
11332 // there are four cases:
11333 // 1) We have a previous sibling which is not a table pseudo. That means
11334 // that previous sibling wanted a (non-block) parent of the type we're
11335 // looking at. Then the whitespace comes between two table-internal
11336 // elements, so should be collapsed out.
11337 // 2) We have a previous sibling which is a table pseudo. It might have
11338 // kids who want this whitespace, so we need to reframe.
11339 // 3) We have no previous sibling and our parent frame is not a table
11340 // pseudo. That means that we'll be at the beginning of our actual
11341 // non-block-type parent, and the whitespace is OK to collapse out.
11342 // If something is ever inserted before us, it'll find our own parent
11343 // as its parent and if it's something that would care about the
11344 // whitespace it'll want a block parent, so it'll trigger a reframe at
11345 // that point.
11346 // 4) We have no previous sibling and our parent frame is a table pseudo.
11347 // Need to reframe.
11348 // All that is predicated on finding the correct previous sibling. We
11349 // might have to walk backwards along continuations from aFrame to do so.
11351 // It's always OK to drop whitespace between any two items that want a
11352 // parent of type parentType.
11354 // For trailing whitespace preceded by a kid that wants our parent type,
11355 // there are four cases:
11356 // 1) We have a next sibling which is not a table pseudo. That means
11357 // that next sibling wanted a (non-block) parent of the type we're
11358 // looking at. Then the whitespace comes between two table-internal
11359 // elements, so should be collapsed out.
11360 // 2) We have a next sibling which is a table pseudo. It might have
11361 // kids who want this whitespace, so we need to reframe.
11362 // 3) We have no next sibling and our parent frame is not a table
11363 // pseudo. That means that we'll be at the end of our actual
11364 // non-block-type parent, and the whitespace is OK to collapse out.
11365 // If something is ever inserted after us, it'll find our own parent
11366 // as its parent and if it's something that would care about the
11367 // whitespace it'll want a block parent, so it'll trigger a reframe at
11368 // that point.
11369 // 4) We have no next sibling and our parent frame is a table pseudo.
11370 // Need to reframe.
11371 // All that is predicated on finding the correct next sibling. We might
11372 // have to walk forward along continuations from aFrame to do so. That
11373 // said, in the case when nextSibling is null at this point and aIsAppend
11374 // is true, we know we're in case 3. Furthermore, in that case we don't
11375 // even have to worry about the table pseudo situation; we know our
11376 // parent is not a table pseudo there.
11377 FCItemIterator iter(aItems);
11378 FCItemIterator start(iter);
11379 do {
11380 if (iter.SkipItemsWantingParentType(parentType)) {
11381 break;
11384 // iter points to an item that wants a different parent. If it's not
11385 // whitespace, we're done; no more point scanning the list.
11386 if (!iter.item().IsWhitespace(aState)) {
11387 break;
11390 if (iter == start) {
11391 // Leading whitespace. How to handle this depends on our
11392 // previous sibling and aFrame. See the long comment above.
11393 nsIFrame* prevSibling = aPrevSibling;
11394 if (!prevSibling) {
11395 // Try to find one after all
11396 nsIFrame* parentPrevCont = aFrame->GetPrevContinuation();
11397 while (parentPrevCont) {
11398 prevSibling = parentPrevCont->PrincipalChildList().LastChild();
11399 if (prevSibling) {
11400 break;
11402 parentPrevCont = parentPrevCont->GetPrevContinuation();
11405 if (prevSibling) {
11406 if (IsTablePseudo(prevSibling)) {
11407 // need to reframe
11408 break;
11410 } else if (IsTablePseudo(aFrame)) {
11411 // need to reframe
11412 break;
11416 FCItemIterator spaceEndIter(iter);
11417 // Advance spaceEndIter past any whitespace
11418 bool trailingSpaces = spaceEndIter.SkipWhitespace(aState);
11420 bool okToDrop;
11421 if (trailingSpaces) {
11422 // Trailing whitespace. How to handle this depeds on aIsAppend, our
11423 // next sibling and aFrame. See the long comment above.
11424 okToDrop = aIsAppend && !nextSibling;
11425 if (!okToDrop) {
11426 if (!nextSibling) {
11427 // Try to find one after all
11428 nsIFrame* parentNextCont = aFrame->GetNextContinuation();
11429 while (parentNextCont) {
11430 nextSibling = parentNextCont->PrincipalChildList().FirstChild();
11431 if (nextSibling) {
11432 break;
11434 parentNextCont = parentNextCont->GetNextContinuation();
11438 okToDrop = (nextSibling && !IsTablePseudo(nextSibling)) ||
11439 (!nextSibling && !IsTablePseudo(aFrame));
11441 #ifdef DEBUG
11442 else {
11443 NS_ASSERTION(!IsTablePseudo(aFrame), "How did that happen?");
11445 #endif
11446 } else {
11447 okToDrop = (spaceEndIter.item().DesiredParentType() == parentType);
11450 if (okToDrop) {
11451 iter.DeleteItemsTo(this, spaceEndIter);
11452 } else {
11453 // We're done: we don't want to drop the whitespace, and it has the
11454 // wrong parent type.
11455 break;
11458 // Now loop, since |iter| points to item right after the whitespace we
11459 // removed.
11460 } while (!iter.IsDone());
11463 // We might be able to figure out some sort of optimizations here, but they
11464 // would have to depend on having a correct aPrevSibling and a correct next
11465 // sibling. For example, we can probably avoid reframing if none of
11466 // aFrame, aPrevSibling, and next sibling are table pseudo-frames. But it
11467 // doesn't seem worth it to worry about that for now, especially since we
11468 // in fact do not have a reliable aPrevSibling, nor any next sibling, in
11469 // this method.
11471 // aItems might have changed, so recheck the parent type thing. In fact,
11472 // it might be empty, so recheck that too.
11473 if (aItems.IsEmpty()) {
11474 return false;
11477 if (!aItems.AllWantParentType(parentType)) {
11478 // Reframing aFrame->GetContent() is good enough, since the content of
11479 // table pseudo-frames is the ancestor content.
11480 TRACE("Pseudo-frames going wrong");
11481 RecreateFramesForContent(aFrame->GetContent(), InsertionKind::Async);
11482 return true;
11486 // Situation #5 is a frame in multicol subtree that's getting new children.
11487 if (aFrame->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR)) {
11488 bool anyColumnSpanItems = false;
11489 for (FCItemIterator iter(aItems); !iter.IsDone(); iter.Next()) {
11490 if (iter.item().mComputedStyle->StyleColumn()->IsColumnSpanStyle()) {
11491 anyColumnSpanItems = true;
11492 break;
11496 bool needsReframe =
11497 // 1. Insert / append any column-span children.
11498 anyColumnSpanItems ||
11499 // 2. GetInsertionPrevSibling() modifies insertion parent. If the prev
11500 // sibling is a column-span, aFrame ends up being the
11501 // column-span-wrapper.
11502 aFrame->Style()->GetPseudoType() ==
11503 PseudoStyleType::columnSpanWrapper ||
11504 // 3. Append into {ib} split container. There might be room for
11505 // optimization, but let's reframe for correctness...
11506 IsFramePartOfIBSplit(aFrame);
11508 if (needsReframe) {
11509 TRACE("Multi-column");
11510 RecreateFramesForContent(
11511 GetMultiColumnContainingBlockFor(aFrame)->GetContent(),
11512 InsertionKind::Async);
11513 return true;
11516 // If we get here, then we need further check for {ib} split to decide
11517 // whether to reframe. For example, appending a block into an empty inline
11518 // that is not part of an {ib} split, but should become an {ib} split.
11521 // A <fieldset> may need to pick up a new rendered legend from aItems.
11522 // We currently can't handle this case without recreating frames for
11523 // the fieldset.
11524 // XXXmats we should be able to optimize this when the fieldset doesn't
11525 // currently have a rendered legend. ContentRangeInserted needs to be fixed
11526 // to use the inner frame as the content insertion frame in that case.
11527 if (const auto* fieldset = GetFieldSetFrameFor(aFrame)) {
11528 // Check if any item is eligible to be a rendered legend.
11529 for (FCItemIterator iter(aItems); !iter.IsDone(); iter.Next()) {
11530 const auto& item = iter.item();
11531 if (!item.mContent->IsHTMLElement(nsGkAtoms::legend)) {
11532 continue;
11534 const auto* display = item.mComputedStyle->StyleDisplay();
11535 if (display->IsFloatingStyle() ||
11536 display->IsAbsolutelyPositionedStyle()) {
11537 continue;
11539 TRACE("Fieldset with rendered legend");
11540 RecreateFramesForContent(fieldset->GetContent(), InsertionKind::Async);
11541 return true;
11545 // Now we have several cases involving {ib} splits. Put them all in a
11546 // do/while with breaks to take us to the "go and reconstruct" code.
11547 do {
11548 if (IsInlineFrame(aFrame)) {
11549 if (aItems.AreAllItemsInline()) {
11550 // We can just put the kids in.
11551 return false;
11554 if (!IsFramePartOfIBSplit(aFrame)) {
11555 // Need to go ahead and reconstruct.
11556 break;
11559 // Now we're adding kids including some blocks to an inline part of an
11560 // {ib} split. If we plan to call AppendFrames, and don't have a next
11561 // sibling for the new frames, and our parent is the last continuation of
11562 // the last part of the {ib} split, and the same is true of all our
11563 // ancestor inlines (they have no following continuations and they're the
11564 // last part of their {ib} splits and we'd be adding to the end for all
11565 // of them), then AppendFrames will handle things for us. Bail out in
11566 // that case.
11567 if (aIsAppend && IsSafeToAppendToIBSplitInline(aFrame, nextSibling)) {
11568 return false;
11571 // Need to reconstruct.
11572 break;
11575 // Now we know we have a block parent. If it's not part of an
11576 // ib-split, we're all set.
11577 if (!IsFramePartOfIBSplit(aFrame)) {
11578 return false;
11581 // We're adding some kids to a block part of an {ib} split. If all the
11582 // kids are blocks, we don't need to reconstruct.
11583 if (aItems.AreAllItemsBlock()) {
11584 return false;
11587 // We might have some inline kids for this block. Just fall out of the
11588 // loop and reconstruct.
11589 } while (0);
11591 // If we don't have a containing block, start with aFrame and look for one.
11592 if (!aContainingBlock) {
11593 aContainingBlock = aFrame;
11596 // To find the right block to reframe, just walk up the tree until we find a
11597 // frame that is:
11598 // 1) Not part of an IB split
11599 // 2) Not a pseudo-frame
11600 // 3) Not an inline frame
11601 // We're guaranteed to find one, since ComputedStyle::ApplyStyleFixups
11602 // enforces that the root is display:none, display:table, or display:block.
11603 // Note that walking up "too far" is OK in terms of correctness, even if it
11604 // might be a little inefficient. This is why we walk out of all
11605 // pseudo-frames -- telling which ones are or are not OK to walk out of is
11606 // too hard (and I suspect that we do in fact need to walk out of all of
11607 // them).
11608 while (IsFramePartOfIBSplit(aContainingBlock) ||
11609 aContainingBlock->IsInlineOutside() ||
11610 aContainingBlock->Style()->IsPseudoOrAnonBox()) {
11611 aContainingBlock = aContainingBlock->GetParent();
11612 NS_ASSERTION(aContainingBlock,
11613 "Must have non-inline, non-ib-split, non-pseudo frame as "
11614 "root (or child of root, for a table root)!");
11617 // Tell parent of the containing block to reformulate the
11618 // entire block. This is painful and definitely not optimal
11619 // but it will *always* get the right answer.
11621 nsIContent* blockContent = aContainingBlock->GetContent();
11622 TRACE("IB splits");
11623 RecreateFramesForContent(blockContent, InsertionKind::Async);
11624 return true;
11625 #undef TRACE
11628 void nsCSSFrameConstructor::ReframeContainingBlock(nsIFrame* aFrame) {
11629 // XXXbz how exactly would we get here while isReflowing anyway? Should this
11630 // whole test be ifdef DEBUG?
11631 if (mPresShell->IsReflowLocked()) {
11632 // don't ReframeContainingBlock, this will result in a crash
11633 // if we remove a tree that's in reflow - see bug 121368 for testcase
11634 NS_ERROR(
11635 "Atemptted to nsCSSFrameConstructor::ReframeContainingBlock during a "
11636 "Reflow!!!");
11637 return;
11640 // Get the first "normal" ancestor of the target frame.
11641 nsIFrame* containingBlock = GetIBContainingBlockFor(aFrame);
11642 if (containingBlock) {
11643 // From here we look for the containing block in case the target
11644 // frame is already a block (which can happen when an inline frame
11645 // wraps some of its content in an anonymous block; see
11646 // ConstructInline)
11648 // NOTE: We used to get the FloatContainingBlock here, but it was often
11649 // wrong. GetIBContainingBlock works much better and provides the correct
11650 // container in all cases so GetFloatContainingBlock(aFrame) has been
11651 // removed
11653 // And get the containingBlock's content
11654 if (nsIContent* blockContent = containingBlock->GetContent()) {
11655 #ifdef DEBUG
11656 if (gNoisyContentUpdates) {
11657 printf(" ==> blockContent=%p\n", blockContent);
11659 #endif
11660 RecreateFramesForContent(blockContent, InsertionKind::Async);
11661 return;
11665 // If we get here, we're screwed!
11666 RecreateFramesForContent(mPresShell->GetDocument()->GetRootElement(),
11667 InsertionKind::Async);
11670 //////////////////////////////////////////////////////////
11671 // nsCSSFrameConstructor::FrameConstructionItem methods //
11672 //////////////////////////////////////////////////////////
11673 bool nsCSSFrameConstructor::FrameConstructionItem::IsWhitespace(
11674 nsFrameConstructorState& aState) const {
11675 MOZ_ASSERT(aState.mCreatingExtraFrames || !mContent->GetPrimaryFrame(),
11676 "How did that happen?");
11677 if (!mIsText) {
11678 return false;
11680 mContent->SetFlags(NS_CREATE_FRAME_IF_NON_WHITESPACE |
11681 NS_REFRAME_IF_WHITESPACE);
11682 return mContent->TextIsOnlyWhitespace();
11685 //////////////////////////////////////////////////////////////
11686 // nsCSSFrameConstructor::FrameConstructionItemList methods //
11687 //////////////////////////////////////////////////////////////
11688 void nsCSSFrameConstructor::FrameConstructionItemList::AdjustCountsForItem(
11689 FrameConstructionItem* aItem, int32_t aDelta) {
11690 MOZ_ASSERT(aDelta == 1 || aDelta == -1, "Unexpected delta");
11691 mItemCount += aDelta;
11692 if (aItem->mIsAllInline) {
11693 mInlineCount += aDelta;
11695 if (aItem->mIsBlock) {
11696 mBlockCount += aDelta;
11698 mDesiredParentCounts[aItem->DesiredParentType()] += aDelta;
11701 ////////////////////////////////////////////////////////////////////////
11702 // nsCSSFrameConstructor::FrameConstructionItemList::Iterator methods //
11703 ////////////////////////////////////////////////////////////////////////
11704 inline bool nsCSSFrameConstructor::FrameConstructionItemList::Iterator::
11705 SkipItemsWantingParentType(ParentType aParentType) {
11706 MOZ_ASSERT(!IsDone(), "Shouldn't be done yet");
11707 while (item().DesiredParentType() == aParentType) {
11708 Next();
11709 if (IsDone()) {
11710 return true;
11713 return false;
11716 inline bool nsCSSFrameConstructor::FrameConstructionItemList::Iterator::
11717 SkipItemsNotWantingParentType(ParentType aParentType) {
11718 MOZ_ASSERT(!IsDone(), "Shouldn't be done yet");
11719 while (item().DesiredParentType() != aParentType) {
11720 Next();
11721 if (IsDone()) {
11722 return true;
11725 return false;
11728 // Note: we implement -webkit-{inline-}box using nsFlexContainerFrame, but we
11729 // use different rules for what gets wrapped in an anonymous flex item.
11730 bool nsCSSFrameConstructor::FrameConstructionItem::NeedsAnonFlexOrGridItem(
11731 const nsFrameConstructorState& aState, bool aIsLegacyWebKitBox) {
11732 if (mFCData->mBits & FCDATA_IS_LINE_PARTICIPANT) {
11733 // This will be an inline non-replaced box.
11734 return true;
11737 if (aIsLegacyWebKitBox) {
11738 if (mComputedStyle->StyleDisplay()->IsInlineOutsideStyle()) {
11739 // In an emulated legacy box, all inline-level content gets wrapped in an
11740 // anonymous flex item.
11741 return true;
11743 if (mIsPopup ||
11744 (!(mFCData->mBits & FCDATA_DISALLOW_OUT_OF_FLOW) &&
11745 aState.GetGeometricParent(*mComputedStyle->StyleDisplay(), nullptr))) {
11746 // We're abspos or fixedpos (or a XUL popup), which means we'll spawn a
11747 // placeholder which (because our container is an emulated legacy box)
11748 // we'll need to wrap in an anonymous flex item. So, we just treat
11749 // _this_ frame as if _it_ needs to be wrapped in an anonymous flex item,
11750 // and then when we spawn the placeholder, it'll end up in the right
11751 // spot.
11752 return true;
11756 return false;
11759 inline bool nsCSSFrameConstructor::FrameConstructionItemList::Iterator::
11760 SkipItemsThatNeedAnonFlexOrGridItem(const nsFrameConstructorState& aState,
11761 bool aIsLegacyWebKitBox) {
11762 MOZ_ASSERT(!IsDone(), "Shouldn't be done yet");
11763 while (item().NeedsAnonFlexOrGridItem(aState, aIsLegacyWebKitBox)) {
11764 Next();
11765 if (IsDone()) {
11766 return true;
11769 return false;
11772 inline bool nsCSSFrameConstructor::FrameConstructionItemList::Iterator::
11773 SkipItemsThatDontNeedAnonFlexOrGridItem(
11774 const nsFrameConstructorState& aState, bool aIsLegacyWebKitBox) {
11775 MOZ_ASSERT(!IsDone(), "Shouldn't be done yet");
11776 while (!(item().NeedsAnonFlexOrGridItem(aState, aIsLegacyWebKitBox))) {
11777 Next();
11778 if (IsDone()) {
11779 return true;
11782 return false;
11785 inline bool nsCSSFrameConstructor::FrameConstructionItemList::Iterator::
11786 SkipItemsNotWantingRubyParent() {
11787 MOZ_ASSERT(!IsDone(), "Shouldn't be done yet");
11788 while (!IsRubyParentType(item().DesiredParentType())) {
11789 Next();
11790 if (IsDone()) {
11791 return true;
11794 return false;
11797 inline bool
11798 nsCSSFrameConstructor::FrameConstructionItemList::Iterator::SkipWhitespace(
11799 nsFrameConstructorState& aState) {
11800 MOZ_ASSERT(!IsDone(), "Shouldn't be done yet");
11801 MOZ_ASSERT(item().IsWhitespace(aState), "Not pointing to whitespace?");
11802 do {
11803 Next();
11804 if (IsDone()) {
11805 return true;
11807 } while (item().IsWhitespace(aState));
11809 return false;
11812 void nsCSSFrameConstructor::FrameConstructionItemList::Iterator::
11813 AppendItemToList(FrameConstructionItemList& aTargetList) {
11814 NS_ASSERTION(&aTargetList != &mList, "Unexpected call");
11815 MOZ_ASSERT(!IsDone(), "should not be done");
11817 FrameConstructionItem* item = mCurrent;
11818 Next();
11819 item->remove();
11820 aTargetList.mItems.insertBack(item);
11822 mList.AdjustCountsForItem(item, -1);
11823 aTargetList.AdjustCountsForItem(item, 1);
11826 void nsCSSFrameConstructor::FrameConstructionItemList::Iterator::
11827 AppendItemsToList(nsCSSFrameConstructor* aFCtor, const Iterator& aEnd,
11828 FrameConstructionItemList& aTargetList) {
11829 NS_ASSERTION(&aTargetList != &mList, "Unexpected call");
11830 MOZ_ASSERT(&mList == &aEnd.mList, "End iterator for some other list?");
11832 // We can't just move our guts to the other list if it already has
11833 // some information or if we're not moving our entire list.
11834 if (!AtStart() || !aEnd.IsDone() || !aTargetList.IsEmpty()) {
11835 do {
11836 AppendItemToList(aTargetList);
11837 } while (*this != aEnd);
11838 return;
11841 // Move our entire list of items into the empty target list.
11842 aTargetList.mItems = std::move(mList.mItems);
11844 // Copy over the various counters
11845 aTargetList.mInlineCount = mList.mInlineCount;
11846 aTargetList.mBlockCount = mList.mBlockCount;
11847 aTargetList.mItemCount = mList.mItemCount;
11848 memcpy(aTargetList.mDesiredParentCounts, mList.mDesiredParentCounts,
11849 sizeof(aTargetList.mDesiredParentCounts));
11851 // reset mList
11852 mList.Reset(aFCtor);
11854 // Point ourselves to aEnd, as advertised
11855 SetToEnd();
11856 MOZ_ASSERT(*this == aEnd, "How did that happen?");
11859 void nsCSSFrameConstructor::FrameConstructionItemList::Iterator::InsertItem(
11860 FrameConstructionItem* aItem) {
11861 if (IsDone()) {
11862 mList.mItems.insertBack(aItem);
11863 } else {
11864 // Just insert the item before us. There's no magic here.
11865 mCurrent->setPrevious(aItem);
11867 mList.AdjustCountsForItem(aItem, 1);
11869 MOZ_ASSERT(aItem->getNext() == mCurrent, "How did that happen?");
11872 void nsCSSFrameConstructor::FrameConstructionItemList::Iterator::DeleteItemsTo(
11873 nsCSSFrameConstructor* aFCtor, const Iterator& aEnd) {
11874 MOZ_ASSERT(&mList == &aEnd.mList, "End iterator for some other list?");
11875 MOZ_ASSERT(*this != aEnd, "Shouldn't be at aEnd yet");
11877 do {
11878 NS_ASSERTION(!IsDone(), "Ran off end of list?");
11879 FrameConstructionItem* item = mCurrent;
11880 Next();
11881 item->remove();
11882 mList.AdjustCountsForItem(item, -1);
11883 item->Delete(aFCtor);
11884 } while (*this != aEnd);
11887 void nsCSSFrameConstructor::QuotesDirty() {
11888 mQuotesDirty = true;
11889 mPresShell->SetNeedLayoutFlush();
11892 void nsCSSFrameConstructor::CountersDirty() {
11893 mCountersDirty = true;
11894 mPresShell->SetNeedLayoutFlush();
11897 void* nsCSSFrameConstructor::AllocateFCItem() {
11898 void* item;
11899 if (mFirstFreeFCItem) {
11900 item = mFirstFreeFCItem;
11901 mFirstFreeFCItem = mFirstFreeFCItem->mNext;
11902 } else {
11903 item = mFCItemPool.Allocate(sizeof(FrameConstructionItem));
11905 ++mFCItemsInUse;
11906 return item;
11909 void nsCSSFrameConstructor::FreeFCItem(FrameConstructionItem* aItem) {
11910 MOZ_ASSERT(mFCItemsInUse != 0);
11911 if (--mFCItemsInUse == 0) {
11912 // The arena is now unused - clear it but retain one chunk.
11913 mFirstFreeFCItem = nullptr;
11914 mFCItemPool.Clear();
11915 } else {
11916 // Prepend it to the list of free items.
11917 FreeFCItemLink* item = reinterpret_cast<FreeFCItemLink*>(aItem);
11918 item->mNext = mFirstFreeFCItem;
11919 mFirstFreeFCItem = item;
11923 void nsCSSFrameConstructor::AddSizeOfIncludingThis(
11924 nsWindowSizes& aSizes) const {
11925 if (nsIFrame* rootFrame = GetRootFrame()) {
11926 rootFrame->AddSizeOfExcludingThisForTree(aSizes);
11927 if (RetainedDisplayListBuilder* builder =
11928 rootFrame->GetProperty(RetainedDisplayListBuilder::Cached())) {
11929 builder->AddSizeOfIncludingThis(aSizes);
11933 // This must be done after measuring from the frame tree, since frame
11934 // manager will measure sizes of staled computed values and style
11935 // structs, which only make sense after we know what are being used.
11936 nsFrameManager::AddSizeOfIncludingThis(aSizes);
11938 // Measurement of the following members may be added later if DMD finds it
11939 // is worthwhile:
11940 // - mFCItemPool
11941 // - mContainStyleScopeManager