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/. */
6 
7 /*
8  * construction of a frame tree that is nearly isomorphic to the content
9  * tree and updating of that tree in response to dynamic changes
10  */
11 
12 #include "nsCSSFrameConstructor.h"
13 
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/HTMLDetailsElement.h"
23 #include "mozilla/dom/HTMLSelectElement.h"
24 #include "mozilla/dom/HTMLSharedListElement.h"
25 #include "mozilla/dom/HTMLSummaryElement.h"
26 #include "mozilla/EventStates.h"
27 #include "mozilla/Likely.h"
28 #include "mozilla/LinkedList.h"
29 #include "mozilla/MemoryReporting.h"
30 #include "mozilla/PresShell.h"
31 #include "mozilla/PresShellInlines.h"
32 #include "mozilla/ServoBindings.h"
33 #include "mozilla/ServoStyleSetInlines.h"
34 #include "mozilla/StaticPrefs_layout.h"
35 #include "mozilla/StaticPrefs_mathml.h"
36 #include "mozilla/Unused.h"
37 #include "RetainedDisplayListBuilder.h"
38 #include "nsAbsoluteContainingBlock.h"
39 #include "nsCSSPseudoElements.h"
40 #include "nsAtom.h"
41 #include "nsIFrameInlines.h"
42 #include "nsGkAtoms.h"
43 #include "nsPresContext.h"
44 #include "mozilla/dom/Document.h"
45 #include "mozilla/dom/DocumentInlines.h"
46 #include "nsTableFrame.h"
47 #include "nsTableColFrame.h"
48 #include "nsTableRowFrame.h"
49 #include "nsTableCellFrame.h"
50 #include "nsHTMLParts.h"
51 #include "nsUnicharUtils.h"
52 #include "nsViewManager.h"
53 #include "nsStyleConsts.h"
54 #ifdef MOZ_XUL
55 #  include "nsXULElement.h"
56 #endif  // MOZ_XUL
57 #include "nsContainerFrame.h"
58 #include "nsNameSpaceManager.h"
59 #include "nsComboboxControlFrame.h"
60 #include "nsListControlFrame.h"
61 #include "nsPlaceholderFrame.h"
62 #include "nsTableRowGroupFrame.h"
63 #include "nsIFormControl.h"
64 #include "nsCSSAnonBoxes.h"
65 #include "nsTextFragment.h"
66 #include "nsIAnonymousContentCreator.h"
67 #include "nsContentUtils.h"
68 #include "nsIScriptError.h"
69 #ifdef XP_MACOSX
70 #  include "nsIDocShell.h"
71 #endif
72 #include "ChildIterator.h"
73 #include "nsError.h"
74 #include "nsLayoutUtils.h"
75 #include "nsBoxFrame.h"
76 #include "nsBoxLayout.h"
77 #include "nsFlexContainerFrame.h"
78 #include "nsGridContainerFrame.h"
79 #include "RubyUtils.h"
80 #include "nsRubyFrame.h"
81 #include "nsRubyBaseFrame.h"
82 #include "nsRubyBaseContainerFrame.h"
83 #include "nsRubyTextFrame.h"
84 #include "nsRubyTextContainerFrame.h"
85 #include "nsImageFrame.h"
86 #include "nsIObjectLoadingContent.h"
87 #include "nsTArray.h"
88 #include "mozilla/dom/CharacterData.h"
89 #include "mozilla/dom/Element.h"
90 #include "mozilla/dom/ElementInlines.h"
91 #include "mozilla/dom/HTMLInputElement.h"
92 #include "nsAutoLayoutPhase.h"
93 #include "nsStyleStructInlines.h"
94 #include "nsPageContentFrame.h"
95 #include "mozilla/RestyleManager.h"
96 #include "StickyScrollContainer.h"
97 #include "nsFieldSetFrame.h"
98 #include "nsInlineFrame.h"
99 #include "nsBlockFrame.h"
100 #include "nsCanvasFrame.h"
101 #include "nsFirstLetterFrame.h"
102 #include "nsGfxScrollFrame.h"
103 #include "nsPageFrame.h"
104 #include "nsPageSequenceFrame.h"
105 #include "nsTableWrapperFrame.h"
106 #include "nsIScrollableFrame.h"
107 #include "nsBackdropFrame.h"
108 #include "nsTransitionManager.h"
109 #include "DetailsFrame.h"
110 
111 #ifdef MOZ_XUL
112 #  include "nsIPopupContainer.h"
113 #endif
114 #ifdef ACCESSIBILITY
115 #  include "nsAccessibilityService.h"
116 #endif
117 
118 #undef NOISY_FIRST_LETTER
119 
120 #include "nsMathMLParts.h"
121 #include "mozilla/dom/SVGTests.h"
122 #include "nsSVGUtils.h"
123 
124 #include "nsRefreshDriver.h"
125 #include "nsTextNode.h"
126 #include "ActiveLayerTracker.h"
127 
128 using namespace mozilla;
129 using namespace mozilla::dom;
130 
131 // An alias for convenience.
132 static const nsIFrame::ChildListID kPrincipalList = nsIFrame::kPrincipalList;
133 
134 nsIFrame* NS_NewHTMLCanvasFrame(PresShell* aPresShell, ComputedStyle* aStyle);
135 
136 nsIFrame* NS_NewHTMLVideoFrame(PresShell* aPresShell, ComputedStyle* aStyle);
137 
138 nsContainerFrame* NS_NewSVGOuterSVGFrame(PresShell* aPresShell,
139                                          ComputedStyle* aStyle);
140 nsContainerFrame* NS_NewSVGOuterSVGAnonChildFrame(PresShell* aPresShell,
141                                                   ComputedStyle* aStyle);
142 nsIFrame* NS_NewSVGInnerSVGFrame(PresShell* aPresShell, ComputedStyle* aStyle);
143 nsIFrame* NS_NewSVGGeometryFrame(PresShell* aPresShell, ComputedStyle* aStyle);
144 nsIFrame* NS_NewSVGGFrame(PresShell* aPresShell, ComputedStyle* aStyle);
145 nsIFrame* NS_NewSVGGenericContainerFrame(PresShell* aPresShell,
146                                          ComputedStyle* aStyle);
147 nsContainerFrame* NS_NewSVGForeignObjectFrame(PresShell* aPresShell,
148                                               ComputedStyle* aStyle);
149 nsIFrame* NS_NewSVGAFrame(PresShell* aPresShell, ComputedStyle* aStyle);
150 nsIFrame* NS_NewSVGSwitchFrame(PresShell* aPresShell, ComputedStyle* aStyle);
151 nsIFrame* NS_NewSVGSymbolFrame(PresShell* aPresShell, ComputedStyle* aStyle);
152 nsIFrame* NS_NewSVGTextFrame(PresShell* aPresShell, ComputedStyle* aStyle);
153 nsIFrame* NS_NewSVGContainerFrame(PresShell* aPresShell, ComputedStyle* aStyle);
154 nsIFrame* NS_NewSVGUseFrame(PresShell* aPresShell, ComputedStyle* aStyle);
155 nsIFrame* NS_NewSVGViewFrame(PresShell* aPresShell, ComputedStyle* aStyle);
156 extern nsIFrame* NS_NewSVGLinearGradientFrame(PresShell* aPresShell,
157                                               ComputedStyle* aStyle);
158 extern nsIFrame* NS_NewSVGRadialGradientFrame(PresShell* aPresShell,
159                                               ComputedStyle* aStyle);
160 extern nsIFrame* NS_NewSVGStopFrame(PresShell* aPresShell,
161                                     ComputedStyle* aStyle);
162 nsContainerFrame* NS_NewSVGMarkerFrame(PresShell* aPresShell,
163                                        ComputedStyle* aStyle);
164 nsContainerFrame* NS_NewSVGMarkerAnonChildFrame(PresShell* aPresShell,
165                                                 ComputedStyle* aStyle);
166 extern nsIFrame* NS_NewSVGImageFrame(PresShell* aPresShell,
167                                      ComputedStyle* aStyle);
168 nsIFrame* NS_NewSVGClipPathFrame(PresShell* aPresShell, ComputedStyle* aStyle);
169 nsIFrame* NS_NewSVGFilterFrame(PresShell* aPresShell, ComputedStyle* aStyle);
170 nsIFrame* NS_NewSVGPatternFrame(PresShell* aPresShell, ComputedStyle* aStyle);
171 nsIFrame* NS_NewSVGMaskFrame(PresShell* aPresShell, ComputedStyle* aStyle);
172 nsIFrame* NS_NewSVGFEContainerFrame(PresShell* aPresShell,
173                                     ComputedStyle* aStyle);
174 nsIFrame* NS_NewSVGFELeafFrame(PresShell* aPresShell, ComputedStyle* aStyle);
175 nsIFrame* NS_NewSVGFEImageFrame(PresShell* aPresShell, ComputedStyle* aStyle);
176 nsIFrame* NS_NewSVGFEUnstyledLeafFrame(PresShell* aPresShell,
177                                        ComputedStyle* aStyle);
178 
179 #include "mozilla/dom/NodeInfo.h"
180 #include "prenv.h"
181 #include "nsNodeInfoManager.h"
182 #include "nsContentCreatorFunctions.h"
183 
184 #ifdef DEBUG
185 // Set the environment variable GECKO_FRAMECTOR_DEBUG_FLAGS to one or
186 // more of the following flags (comma separated) for handy debug
187 // output.
188 static bool gNoisyContentUpdates = false;
189 static bool gReallyNoisyContentUpdates = false;
190 static bool gNoisyInlineConstruction = false;
191 
192 struct FrameCtorDebugFlags {
193   const char* name;
194   bool* on;
195 };
196 
197 static FrameCtorDebugFlags gFlags[] = {
198     {"content-updates", &gNoisyContentUpdates},
199     {"really-noisy-content-updates", &gReallyNoisyContentUpdates},
200     {"noisy-inline", &gNoisyInlineConstruction}};
201 
202 #  define NUM_DEBUG_FLAGS (sizeof(gFlags) / sizeof(gFlags[0]))
203 #endif
204 
205 #ifdef MOZ_XUL
206 #  include "nsMenuFrame.h"
207 #  include "nsPopupSetFrame.h"
208 #  include "nsTreeColFrame.h"
209 #  include "nsXULLabelFrame.h"
210 
211 //------------------------------------------------------------------
212 
213 nsContainerFrame* NS_NewRootBoxFrame(PresShell* aPresShell,
214                                      ComputedStyle* aStyle);
215 
216 nsContainerFrame* NS_NewDocElementBoxFrame(PresShell* aPresShell,
217                                            ComputedStyle* aStyle);
218 
219 nsIFrame* NS_NewDeckFrame(PresShell* aPresShell, ComputedStyle* aStyle);
220 
221 nsIFrame* NS_NewLeafBoxFrame(PresShell* aPresShell, ComputedStyle* aStyle);
222 
223 nsIFrame* NS_NewStackFrame(PresShell* aPresShell, ComputedStyle* aStyle);
224 
225 nsIFrame* NS_NewRangeFrame(PresShell* aPresShell, ComputedStyle* aStyle);
226 
227 nsIFrame* NS_NewImageBoxFrame(PresShell* aPresShell, ComputedStyle* aStyle);
228 
229 nsIFrame* NS_NewTextBoxFrame(PresShell* aPresShell, ComputedStyle* aStyle);
230 
231 nsIFrame* NS_NewGroupBoxFrame(PresShell* aPresShell, ComputedStyle* aStyle);
232 
233 nsIFrame* NS_NewButtonBoxFrame(PresShell* aPresShell, ComputedStyle* aStyle);
234 
235 nsIFrame* NS_NewSplitterFrame(PresShell* aPresShell, ComputedStyle* aStyle);
236 
237 nsIFrame* NS_NewMenuPopupFrame(PresShell* aPresShell, ComputedStyle* aStyle);
238 
239 nsIFrame* NS_NewPopupSetFrame(PresShell* aPresShell, ComputedStyle* aStyle);
240 
241 nsIFrame* NS_NewMenuFrame(PresShell* aPresShell, ComputedStyle* aStyle,
242                           uint32_t aFlags);
243 
244 nsIFrame* NS_NewMenuBarFrame(PresShell* aPresShell, ComputedStyle* aStyle);
245 
246 nsIFrame* NS_NewTreeBodyFrame(PresShell* aPresShell, ComputedStyle* aStyle);
247 
248 // grid
249 nsresult NS_NewGridLayout2(nsBoxLayout** aNewLayout);
250 nsIFrame* NS_NewGridRowLeafFrame(PresShell* aPresShell, ComputedStyle* aStyle);
251 nsIFrame* NS_NewGridRowGroupFrame(PresShell* aPresShell, ComputedStyle* aStyle);
252 
253 // end grid
254 
255 nsIFrame* NS_NewTitleBarFrame(PresShell* aPresShell, ComputedStyle* aStyle);
256 
257 nsIFrame* NS_NewResizerFrame(PresShell* aPresShell, ComputedStyle* aStyle);
258 
259 #endif
260 
261 nsHTMLScrollFrame* NS_NewHTMLScrollFrame(PresShell* aPresShell,
262                                          ComputedStyle* aStyle, bool aIsRoot);
263 
264 nsXULScrollFrame* NS_NewXULScrollFrame(PresShell* aPresShell,
265                                        ComputedStyle* aStyle, bool aIsRoot,
266                                        bool aClipAllDescendants);
267 
268 nsIFrame* NS_NewSliderFrame(PresShell* aPresShell, ComputedStyle* aStyle);
269 
270 nsIFrame* NS_NewScrollbarFrame(PresShell* aPresShell, ComputedStyle* aStyle);
271 
272 nsIFrame* NS_NewScrollbarButtonFrame(PresShell* aPresShell,
273                                      ComputedStyle* aStyle);
274 
275 nsIFrame* NS_NewImageFrameForContentProperty(PresShell*, ComputedStyle*);
276 
277 nsIFrame* NS_NewImageFrameForGeneratedContentIndex(PresShell*, ComputedStyle*);
278 
279 // Returns true if aFrame is an anonymous flex/grid item.
IsAnonymousFlexOrGridItem(const nsIFrame * aFrame)280 static inline bool IsAnonymousFlexOrGridItem(const nsIFrame* aFrame) {
281   auto pseudoType = aFrame->Style()->GetPseudoType();
282   return pseudoType == PseudoStyleType::anonymousFlexItem ||
283          pseudoType == PseudoStyleType::anonymousGridItem;
284 }
285 
286 // Returns true IFF the given nsIFrame is a nsFlexContainerFrame and
287 // represents a -webkit-{inline-}box or -moz-{inline-}box container.
IsFlexContainerForLegacyBox(const nsIFrame * aFrame)288 static inline bool IsFlexContainerForLegacyBox(const nsIFrame* aFrame) {
289   return aFrame->IsFlexContainerFrame() &&
290          aFrame->HasAnyStateBits(NS_STATE_FLEX_IS_EMULATING_LEGACY_BOX);
291 }
292 
293 #if DEBUG
AssertAnonymousFlexOrGridItemParent(const nsIFrame * aChild,const nsIFrame * aParent)294 static void AssertAnonymousFlexOrGridItemParent(const nsIFrame* aChild,
295                                                 const nsIFrame* aParent) {
296   MOZ_ASSERT(IsAnonymousFlexOrGridItem(aChild),
297              "expected an anonymous flex or grid item child frame");
298   MOZ_ASSERT(aParent, "expected a parent frame");
299   auto pseudoType = aChild->Style()->GetPseudoType();
300   if (pseudoType == PseudoStyleType::anonymousFlexItem) {
301     MOZ_ASSERT(aParent->IsFlexContainerFrame(),
302                "anonymous flex items should only exist as children "
303                "of flex container frames");
304   } else {
305     MOZ_ASSERT(aParent->IsGridContainerFrame(),
306                "anonymous grid items should only exist as children "
307                "of grid container frames");
308   }
309 }
310 #else
311 #  define AssertAnonymousFlexOrGridItemParent(x, y) PR_BEGIN_MACRO PR_END_MACRO
312 #endif
313 
314 #define FCDATA_DECL(_flags, _func) \
315   { _flags, {(FrameCreationFunc)_func}, nullptr, PseudoStyleType::NotPseudo }
316 #define FCDATA_WITH_WRAPPING_BLOCK(_flags, _func, _anon_box) \
317   {                                                          \
318     _flags | FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS,       \
319         {(FrameCreationFunc)_func}, nullptr, _anon_box       \
320   }
321 #define SIMPLE_FCDATA(_func) FCDATA_DECL(0, _func)
322 #define FULL_CTOR_FCDATA(_flags, _func)                  \
323   {                                                      \
324     _flags | FCDATA_FUNC_IS_FULL_CTOR, {nullptr}, _func, \
325         PseudoStyleType::NotPseudo                       \
326   }
327 
328 /**
329  * True if aFrame is an actual inline frame in the sense of non-replaced
330  * display:inline CSS boxes.  In other words, it can be affected by {ib}
331  * splitting and can contain first-letter frames.  Basically, this is either an
332  * inline frame (positioned or otherwise) or an line frame (this last because
333  * it can contain first-letter and because inserting blocks in the middle of it
334  * needs to terminate it).
335  */
IsInlineFrame(const nsIFrame * aFrame)336 static bool IsInlineFrame(const nsIFrame* aFrame) {
337   return aFrame->IsFrameOfType(nsIFrame::eLineParticipant);
338 }
339 
340 /**
341  * True for display: contents elements.
342  */
IsDisplayContents(const Element * aElement)343 static inline bool IsDisplayContents(const Element* aElement) {
344   return aElement->IsDisplayContents();
345 }
346 
IsDisplayContents(const nsIContent * aContent)347 static inline bool IsDisplayContents(const nsIContent* aContent) {
348   return aContent->IsElement() && IsDisplayContents(aContent->AsElement());
349 }
350 
351 /**
352  * True if aFrame is an instance of an SVG frame class or is an inline/block
353  * frame being used for SVG text.
354  */
IsFrameForSVG(const nsIFrame * aFrame)355 static bool IsFrameForSVG(const nsIFrame* aFrame) {
356   return aFrame->IsFrameOfType(nsIFrame::eSVG) ||
357          nsSVGUtils::IsInSVGTextSubtree(aFrame);
358 }
359 
IsLastContinuationForColumnContent(const nsIFrame * aFrame)360 static bool IsLastContinuationForColumnContent(const nsIFrame* aFrame) {
361   MOZ_ASSERT(aFrame);
362   return aFrame->Style()->GetPseudoType() == PseudoStyleType::columnContent &&
363          !aFrame->GetNextContinuation();
364 }
365 
366 /**
367  * Returns true iff aFrame explicitly prevents its descendants from floating
368  * (at least, down to the level of descendants which themselves are
369  * float-containing blocks -- those will manage the floating status of any
370  * lower-level descendents inside them, of course).
371  */
ShouldSuppressFloatingOfDescendants(nsIFrame * aFrame)372 static bool ShouldSuppressFloatingOfDescendants(nsIFrame* aFrame) {
373   return aFrame->IsFlexOrGridContainer() || aFrame->IsXULBoxFrame() ||
374          aFrame->IsFrameOfType(nsIFrame::eMathML);
375 }
376 
377 // Return true if column-span descendants should be suppressed under aFrame's
378 // subtree (until a multi-column container re-establishing a block formatting
379 // context). Basically, this is testing whether aFrame establishes a new block
380 // formatting context or not.
ShouldSuppressColumnSpanDescendants(nsIFrame * aFrame)381 static bool ShouldSuppressColumnSpanDescendants(nsIFrame* aFrame) {
382   if (aFrame->Style()->GetPseudoType() == PseudoStyleType::columnContent) {
383     // Never suppress column-span under ::-moz-column-content frames.
384     return false;
385   }
386 
387   if (aFrame->IsInlineFrame()) {
388     // Allow inline frames to have column-span block children.
389     return false;
390   }
391 
392   if (!aFrame->IsBlockFrameOrSubclass() ||
393       aFrame->HasAnyStateBits(NS_BLOCK_FLOAT_MGR | NS_FRAME_OUT_OF_FLOW)) {
394     // Need to suppress column-span under a different block formatting
395     // context or an out-of-flow frame.
396     //
397     // For example, the children of a column-span never need to be further
398     // processed even if there is a nested column-span child. Because a
399     // column-span always creates its own block formatting context, a nested
400     // column-span child won't be in the same block formatting context with the
401     // nearest multi-column ancestor. This is the same case as if the
402     // column-span is outside of a multi-column hierarchy.
403     return true;
404   }
405 
406   return false;
407 }
408 
409 /**
410  * If any children require a block parent, return the first such child.
411  * Otherwise return null.
412  */
AnyKidsNeedBlockParent(nsIFrame * aFrameList)413 static nsIContent* AnyKidsNeedBlockParent(nsIFrame* aFrameList) {
414   for (nsIFrame* k = aFrameList; k; k = k->GetNextSibling()) {
415     // Line participants, such as text and inline frames, can't be
416     // directly inside a XUL box; they must be wrapped in an
417     // intermediate block.
418     if (k->IsFrameOfType(nsIFrame::eLineParticipant)) {
419       return k->GetContent();
420     }
421   }
422   return nullptr;
423 }
424 
425 // Reparent a frame into a wrapper frame that is a child of its old parent.
ReparentFrame(RestyleManager * aRestyleManager,nsContainerFrame * aNewParentFrame,nsIFrame * aFrame,bool aForceStyleReparent)426 static void ReparentFrame(RestyleManager* aRestyleManager,
427                           nsContainerFrame* aNewParentFrame, nsIFrame* aFrame,
428                           bool aForceStyleReparent) {
429   aFrame->SetParent(aNewParentFrame);
430   // We reparent frames for two reasons: to put them inside ::first-line, and to
431   // put them inside some wrapper anonymous boxes.
432   if (aForceStyleReparent) {
433     aRestyleManager->ReparentComputedStyleForFirstLine(aFrame);
434   }
435 }
436 
ReparentFrames(nsCSSFrameConstructor * aFrameConstructor,nsContainerFrame * aNewParentFrame,const nsFrameList & aFrameList,bool aForceStyleReparent)437 static void ReparentFrames(nsCSSFrameConstructor* aFrameConstructor,
438                            nsContainerFrame* aNewParentFrame,
439                            const nsFrameList& aFrameList,
440                            bool aForceStyleReparent) {
441   RestyleManager* restyleManager = aFrameConstructor->RestyleManager();
442   for (nsIFrame* f : aFrameList) {
443     ReparentFrame(restyleManager, aNewParentFrame, f, aForceStyleReparent);
444   }
445 }
446 
447 //----------------------------------------------------------------------
448 //
449 // When inline frames get weird and have block frames in them, we
450 // annotate them to help us respond to incremental content changes
451 // more easily.
452 
IsFramePartOfIBSplit(nsIFrame * aFrame)453 static inline bool IsFramePartOfIBSplit(nsIFrame* aFrame) {
454   bool result = (aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) != 0;
455   MOZ_ASSERT(!result || static_cast<nsBlockFrame*>(do_QueryFrame(aFrame)) ||
456                  static_cast<nsInlineFrame*>(do_QueryFrame(aFrame)),
457              "only block/inline frames can have NS_FRAME_PART_OF_IBSPLIT");
458   return result;
459 }
460 
GetIBSplitSibling(nsIFrame * aFrame)461 static nsContainerFrame* GetIBSplitSibling(nsIFrame* aFrame) {
462   MOZ_ASSERT(IsFramePartOfIBSplit(aFrame), "Shouldn't call this");
463 
464   // We only store the "ib-split sibling" annotation with the first
465   // frame in the continuation chain. Walk back to find that frame now.
466   return aFrame->FirstContinuation()->GetProperty(nsIFrame::IBSplitSibling());
467 }
468 
GetIBSplitPrevSibling(nsIFrame * aFrame)469 static nsContainerFrame* GetIBSplitPrevSibling(nsIFrame* aFrame) {
470   MOZ_ASSERT(IsFramePartOfIBSplit(aFrame), "Shouldn't call this");
471 
472   // We only store the ib-split sibling annotation with the first
473   // frame in the continuation chain. Walk back to find that frame now.
474   return aFrame->FirstContinuation()->GetProperty(
475       nsIFrame::IBSplitPrevSibling());
476 }
477 
GetLastIBSplitSibling(nsIFrame * aFrame)478 static nsContainerFrame* GetLastIBSplitSibling(nsIFrame* aFrame) {
479   for (nsIFrame *frame = aFrame, *next;; frame = next) {
480     next = GetIBSplitSibling(frame);
481     if (!next) {
482       return static_cast<nsContainerFrame*>(frame);
483     }
484   }
485   MOZ_ASSERT_UNREACHABLE("unreachable code");
486   return nullptr;
487 }
488 
SetFrameIsIBSplit(nsContainerFrame * aFrame,nsContainerFrame * aIBSplitSibling)489 static void SetFrameIsIBSplit(nsContainerFrame* aFrame,
490                               nsContainerFrame* aIBSplitSibling) {
491   MOZ_ASSERT(aFrame, "bad args!");
492 
493   // We should be the only continuation
494   NS_ASSERTION(!aFrame->GetPrevContinuation(),
495                "assigning ib-split sibling to other than first continuation!");
496   NS_ASSERTION(!aFrame->GetNextContinuation() ||
497                    IsFramePartOfIBSplit(aFrame->GetNextContinuation()),
498                "should have no non-ib-split continuations here");
499 
500   // Mark the frame as ib-split.
501   aFrame->AddStateBits(NS_FRAME_PART_OF_IBSPLIT);
502 
503   if (aIBSplitSibling) {
504     NS_ASSERTION(!aIBSplitSibling->GetPrevContinuation(),
505                  "assigning something other than the first continuation as the "
506                  "ib-split sibling");
507 
508     // Store the ib-split sibling (if we were given one) with the
509     // first frame in the flow.
510     aFrame->SetProperty(nsIFrame::IBSplitSibling(), aIBSplitSibling);
511     aIBSplitSibling->SetProperty(nsIFrame::IBSplitPrevSibling(), aFrame);
512   }
513 }
514 
GetIBContainingBlockFor(nsIFrame * aFrame)515 static nsIFrame* GetIBContainingBlockFor(nsIFrame* aFrame) {
516   MOZ_ASSERT(
517       IsFramePartOfIBSplit(aFrame),
518       "GetIBContainingBlockFor() should only be called on known IB frames");
519 
520   // Get the first "normal" ancestor of the target frame.
521   nsIFrame* parentFrame;
522   do {
523     parentFrame = aFrame->GetParent();
524 
525     if (!parentFrame) {
526       NS_ERROR("no unsplit block frame in IB hierarchy");
527       return aFrame;
528     }
529 
530     // Note that we ignore non-ib-split frames which have a pseudo on their
531     // ComputedStyle -- they're not the frames we're looking for!  In
532     // particular, they may be hiding a real parent that _is_ in an ib-split.
533     if (!IsFramePartOfIBSplit(parentFrame) &&
534         !parentFrame->Style()->IsPseudoOrAnonBox())
535       break;
536 
537     aFrame = parentFrame;
538   } while (1);
539 
540   // post-conditions
541   NS_ASSERTION(parentFrame,
542                "no normal ancestor found for ib-split frame "
543                "in GetIBContainingBlockFor");
544   NS_ASSERTION(parentFrame != aFrame,
545                "parentFrame is actually the child frame - bogus reslt");
546 
547   return parentFrame;
548 }
549 
550 // Find the multicol containing block suitable for reframing.
551 //
552 // Note: this function may not return a ColumnSetWrapperFrame. For example, if
553 // the multicol containing block has "overflow:scroll" style, HTMLScrollFrame is
554 // returned because ColumnSetWrapperFrame is the scrolled frame which has the
555 // -moz-scrolled-content pseudo style. We may walk up "too far", but in terms of
556 // correctness of reframing, it's OK.
GetMultiColumnContainingBlockFor(nsIFrame * aFrame)557 static nsContainerFrame* GetMultiColumnContainingBlockFor(nsIFrame* aFrame) {
558   MOZ_ASSERT(aFrame->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR),
559              "Should only be called if the frame has a multi-column ancestor!");
560 
561   nsContainerFrame* current = aFrame->GetParent();
562   while (current &&
563          (current->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR) ||
564           current->Style()->IsPseudoOrAnonBox())) {
565     current = current->GetParent();
566   }
567 
568   MOZ_ASSERT(current,
569              "No multicol containing block in a valid column hierarchy?");
570 
571   return current;
572 }
573 
574 // This is a bit slow, but sometimes we need it.
ParentIsWrapperAnonBox(nsIFrame * aParent)575 static bool ParentIsWrapperAnonBox(nsIFrame* aParent) {
576   nsIFrame* maybeAnonBox = aParent;
577   if (maybeAnonBox->Style()->GetPseudoType() == PseudoStyleType::cellContent) {
578     // The thing that would maybe be a wrapper anon box is the cell.
579     maybeAnonBox = maybeAnonBox->GetParent();
580   }
581   return maybeAnonBox->Style()->IsWrapperAnonBox();
582 }
583 
584 //----------------------------------------------------------------------
585 
586 // Block/inline frame construction logic. We maintain a few invariants here:
587 //
588 // 1. Block frames contain block and inline frames.
589 //
590 // 2. Inline frames only contain inline frames. If an inline parent has a block
591 // child then the block child is migrated upward until it lands in a block
592 // parent (the inline frames containing block is where it will end up).
593 
SetInitialSingleChild(nsContainerFrame * aParent,nsIFrame * aFrame)594 inline void SetInitialSingleChild(nsContainerFrame* aParent, nsIFrame* aFrame) {
595   MOZ_ASSERT(!aFrame->GetNextSibling(), "Should be using a frame list");
596   nsFrameList temp(aFrame, aFrame);
597   aParent->SetInitialChildList(kPrincipalList, temp);
598 }
599 
600 // -----------------------------------------------------------
601 
602 // Structure used when constructing formatting object trees. Contains
603 // state information needed for absolutely positioned elements
604 namespace mozilla {
605 struct AbsoluteFrameList : public nsFrameList {
606   // containing block for absolutely positioned elements
607   nsContainerFrame* containingBlock;
608 
AbsoluteFrameListmozilla::AbsoluteFrameList609   explicit AbsoluteFrameList(nsContainerFrame* aContainingBlock)
610       : containingBlock(aContainingBlock) {}
611 
612 #ifdef DEBUG
613   // XXXbz Does this need a debug-only assignment operator that nulls out the
614   // childList in the AbsoluteFrameList we're copying?  Introducing a difference
615   // between debug and non-debug behavior seems bad, so I guess not...
~AbsoluteFrameListmozilla::AbsoluteFrameList616   ~AbsoluteFrameList() {
617     NS_ASSERTION(!FirstChild(),
618                  "Dangling child list.  Someone forgot to insert it?");
619   }
620 #endif
621 };
622 }  // namespace mozilla
623 
624 // -----------------------------------------------------------
625 
626 // Structure for saving the existing state when pushing/poping containing
627 // blocks. The destructor restores the state to its previous state
628 class MOZ_STACK_CLASS nsFrameConstructorSaveState {
629  public:
630   typedef nsIFrame::ChildListID ChildListID;
631   nsFrameConstructorSaveState();
632   ~nsFrameConstructorSaveState();
633 
634  private:
635   AbsoluteFrameList* mList;      // pointer to struct whose data we save/restore
636   AbsoluteFrameList mSavedList;  // copy of original data
637 
638   // The name of the child list in which our frames would belong
639   ChildListID mChildListID;
640   nsFrameConstructorState* mState;
641 
642   // State used only when we're saving the abs-pos state for a transformed
643   // element.
644   AbsoluteFrameList mSavedFixedList;
645 
646   bool mSavedFixedPosIsAbsPos;
647 
648   friend class nsFrameConstructorState;
649 };
650 
651 // Structure used for maintaining state information during the
652 // frame construction process
653 class MOZ_STACK_CLASS nsFrameConstructorState {
654  public:
655   typedef nsIFrame::ChildListID ChildListID;
656 
657   nsPresContext* mPresContext;
658   PresShell* mPresShell;
659   nsFrameManager* mFrameManager;
660 
661 #ifdef MOZ_XUL
662   // Frames destined for the kPopupList.
663   AbsoluteFrameList mPopupList;
664 #endif
665 
666   // Containing block information for out-of-flow frames.
667   AbsoluteFrameList mFixedList;
668   AbsoluteFrameList mAbsoluteList;
669   AbsoluteFrameList mFloatedList;
670   // The containing block of a frame in the top layer is defined by the
671   // spec: fixed-positioned frames are children of the viewport frame,
672   // and absolutely-positioned frames are children of the initial
673   // containing block. They would not be caught by any other containing
674   // block, e.g. frames with transform or filter.
675   AbsoluteFrameList mTopLayerFixedList;
676   AbsoluteFrameList mTopLayerAbsoluteList;
677 
678   nsCOMPtr<nsILayoutHistoryState> mFrameState;
679   // These bits will be added to the state bits of any frame we construct
680   // using this state.
681   nsFrameState mAdditionalStateBits;
682 
683   // When working with the transform and filter properties, we want to hook
684   // the abs-pos and fixed-pos lists together, since such
685   // elements are fixed-pos containing blocks.  This flag determines
686   // whether or not we want to wire the fixed-pos and abs-pos lists
687   // together.
688   bool mFixedPosIsAbsPos;
689 
690   // A boolean to indicate whether we have a "pending" popupgroup.  That is, we
691   // have already created the FrameConstructionItem for the root popupgroup but
692   // we have not yet created the relevant frame.
693   bool mHavePendingPopupgroup;
694 
695   // If false (which is the default) then call SetPrimaryFrame() as needed
696   // during frame construction.  If true, don't make any SetPrimaryFrame()
697   // calls, except for generated content which doesn't have a primary frame
698   // yet.  The mCreatingExtraFrames == true mode is meant to be used for
699   // construction of random "extra" frames for elements via normal frame
700   // construction APIs (e.g. replication of things across pages in paginated
701   // mode).
702   bool mCreatingExtraFrames;
703 
704   nsTArray<RefPtr<nsIContent>> mGeneratedContentWithInitializer;
705 
706   // Constructor
707   // Use the passed-in history state.
708   nsFrameConstructorState(
709       PresShell* aPresShell, nsContainerFrame* aFixedContainingBlock,
710       nsContainerFrame* aAbsoluteContainingBlock,
711       nsContainerFrame* aFloatContainingBlock,
712       already_AddRefed<nsILayoutHistoryState> aHistoryState);
713   // Get the history state from the pres context's pres shell.
714   nsFrameConstructorState(PresShell* aPresShell,
715                           nsContainerFrame* aFixedContainingBlock,
716                           nsContainerFrame* aAbsoluteContainingBlock,
717                           nsContainerFrame* aFloatContainingBlock);
718 
719   ~nsFrameConstructorState();
720 
721   // Process the frame insertions for all the out-of-flow nsAbsoluteItems.
722   void ProcessFrameInsertionsForAllLists();
723 
724   // Function to push the existing absolute containing block state and
725   // create a new scope. Code that uses this function should get matching
726   // logic in GetAbsoluteContainingBlock.
727   // Also makes aNewAbsoluteContainingBlock the containing block for
728   // fixed-pos elements if necessary.
729   // aPositionedFrame is the frame whose style actually makes
730   // aNewAbsoluteContainingBlock a containing block. E.g. for a scrollable
731   // element aPositionedFrame is the element's primary frame and
732   // aNewAbsoluteContainingBlock is the scrolled frame.
733   void PushAbsoluteContainingBlock(
734       nsContainerFrame* aNewAbsoluteContainingBlock, nsIFrame* aPositionedFrame,
735       nsFrameConstructorSaveState& aSaveState);
736 
737   // Function to push the existing float containing block state and
738   // create a new scope. Code that uses this function should get matching
739   // logic in GetFloatContainingBlock.
740   // Pushing a null float containing block forbids any frames from being
741   // floated until a new float containing block is pushed.
742   // XXX we should get rid of null float containing blocks and teach the
743   // various frame classes to deal with floats instead.
744   void PushFloatContainingBlock(nsContainerFrame* aNewFloatContainingBlock,
745                                 nsFrameConstructorSaveState& aSaveState);
746 
747   // Function to return the proper geometric parent for a frame with display
748   // struct given by aStyleDisplay and parent's frame given by
749   // aContentParentFrame.
750   nsContainerFrame* GetGeometricParent(
751       const nsStyleDisplay& aStyleDisplay,
752       nsContainerFrame* aContentParentFrame) const;
753 
754   // Collect absolute frames in mAbsoluteList which are proper descendants
755   // of aNewParent, and reparent them to aNewParent.
756   //
757   // Note: This function does something unusual that moves absolute items
758   // after their frames are constructed under a column hierarchy which has
759   // column-span elements. Do not use this if you're not dealing with
760   // columns.
761   void ReparentAbsoluteItems(nsContainerFrame* aNewParent);
762 
763   /**
764    * Function to add a new frame to the right frame list.  This MUST be called
765    * on frames before their children have been processed if the frames might
766    * conceivably be out-of-flow; otherwise cleanup in error cases won't work
767    * right.  Also, this MUST be called on frames after they have been
768    * initialized.
769    * @param aNewFrame the frame to add
770    * @param aFrameList the list to add in-flow frames to
771    * @param aContent the content pointer for aNewFrame
772    * @param aParentFrame the parent frame for the content if it were in-flow
773    * @param aCanBePositioned pass false if the frame isn't allowed to be
774    *        positioned
775    * @param aCanBeFloated pass false if the frame isn't allowed to be
776    *        floated
777    * @param aIsOutOfFlowPopup pass true if the frame is an out-of-flow popup
778    *        (XUL-only)
779    */
780   void AddChild(nsIFrame* aNewFrame, nsFrameList& aFrameList,
781                 nsIContent* aContent, nsContainerFrame* aParentFrame,
782                 bool aCanBePositioned = true, bool aCanBeFloated = true,
783                 bool aIsOutOfFlowPopup = false, bool aInsertAfter = false,
784                 nsIFrame* aInsertAfterFrame = nullptr);
785 
786   /**
787    * Function to return the fixed-pos element list.  Normally this will just
788    * hand back the fixed-pos element list, but in case we're dealing with a
789    * transformed element that's acting as an abs-pos and fixed-pos container,
790    * we'll hand back the abs-pos list.  Callers should use this function if they
791    * want to get the list acting as the fixed-pos item parent.
792    */
GetFixedList()793   AbsoluteFrameList& GetFixedList() {
794     return mFixedPosIsAbsPos ? mAbsoluteList : mFixedList;
795   }
GetFixedList() const796   const AbsoluteFrameList& GetFixedList() const {
797     return mFixedPosIsAbsPos ? mAbsoluteList : mFixedList;
798   }
799 
800  protected:
801   friend class nsFrameConstructorSaveState;
802 
803   /**
804    * ProcessFrameInsertions takes the frames in aFrameList and adds them as
805    * kids to the aChildListID child list of |aFrameList.containingBlock|.
806    */
807   void ProcessFrameInsertions(AbsoluteFrameList& aFrameList,
808                               ChildListID aChildListID);
809 
810   /**
811    * GetOutOfFlowFrameList selects the out-of-flow frame list the new
812    * frame should be added to. If the frame shouldn't be added to any
813    * out-of-flow list, it returns nullptr. The corresponding type of
814    * placeholder is also returned via the aPlaceholderType parameter
815    * if this method doesn't return nullptr. The caller should check
816    * whether the returned list really has a containing block.
817    */
818   AbsoluteFrameList* GetOutOfFlowFrameList(nsIFrame* aNewFrame,
819                                            bool aCanBePositioned,
820                                            bool aCanBeFloated,
821                                            bool aIsOutOfFlowPopup,
822                                            nsFrameState* aPlaceholderType);
823 
824   void ConstructBackdropFrameFor(nsIContent* aContent, nsIFrame* aFrame);
825 };
826 
nsFrameConstructorState(PresShell * aPresShell,nsContainerFrame * aFixedContainingBlock,nsContainerFrame * aAbsoluteContainingBlock,nsContainerFrame * aFloatContainingBlock,already_AddRefed<nsILayoutHistoryState> aHistoryState)827 nsFrameConstructorState::nsFrameConstructorState(
828     PresShell* aPresShell, nsContainerFrame* aFixedContainingBlock,
829     nsContainerFrame* aAbsoluteContainingBlock,
830     nsContainerFrame* aFloatContainingBlock,
831     already_AddRefed<nsILayoutHistoryState> aHistoryState)
832     : mPresContext(aPresShell->GetPresContext()),
833       mPresShell(aPresShell),
834       mFrameManager(aPresShell->FrameConstructor()),
835 #ifdef MOZ_XUL
836       mPopupList(nullptr),
837 #endif
838       mFixedList(aFixedContainingBlock),
839       mAbsoluteList(aAbsoluteContainingBlock),
840       mFloatedList(aFloatContainingBlock),
841       mTopLayerFixedList(
842           static_cast<nsContainerFrame*>(mFrameManager->GetRootFrame())),
843       mTopLayerAbsoluteList(
844           aPresShell->FrameConstructor()->GetDocElementContainingBlock()),
845       // See PushAbsoluteContaningBlock below
846       mFrameState(aHistoryState),
847       mAdditionalStateBits(nsFrameState(0)),
848       // If the fixed-pos containing block is equal to the abs-pos containing
849       // block, use the abs-pos containing block's abs-pos list for fixed-pos
850       // frames.
851       mFixedPosIsAbsPos(aFixedContainingBlock == aAbsoluteContainingBlock),
852       mHavePendingPopupgroup(false),
853       mCreatingExtraFrames(false) {
854 #ifdef MOZ_XUL
855   nsIPopupContainer* popupContainer =
856       nsIPopupContainer::GetPopupContainer(aPresShell);
857   if (popupContainer) {
858     mPopupList.containingBlock = popupContainer->GetPopupSetFrame();
859   }
860 #endif
861   MOZ_COUNT_CTOR(nsFrameConstructorState);
862 }
863 
nsFrameConstructorState(PresShell * aPresShell,nsContainerFrame * aFixedContainingBlock,nsContainerFrame * aAbsoluteContainingBlock,nsContainerFrame * aFloatContainingBlock)864 nsFrameConstructorState::nsFrameConstructorState(
865     PresShell* aPresShell, nsContainerFrame* aFixedContainingBlock,
866     nsContainerFrame* aAbsoluteContainingBlock,
867     nsContainerFrame* aFloatContainingBlock)
868     : nsFrameConstructorState(
869           aPresShell, aFixedContainingBlock, aAbsoluteContainingBlock,
870           aFloatContainingBlock,
871           aPresShell->GetDocument()->GetLayoutHistoryState()) {}
872 
~nsFrameConstructorState()873 nsFrameConstructorState::~nsFrameConstructorState() {
874   MOZ_COUNT_DTOR(nsFrameConstructorState);
875   ProcessFrameInsertionsForAllLists();
876   for (auto& content : Reversed(mGeneratedContentWithInitializer)) {
877     content->RemoveProperty(nsGkAtoms::genConInitializerProperty);
878   }
879 }
880 
ProcessFrameInsertionsForAllLists()881 void nsFrameConstructorState::ProcessFrameInsertionsForAllLists() {
882   ProcessFrameInsertions(mTopLayerFixedList, nsIFrame::kFixedList);
883   ProcessFrameInsertions(mTopLayerAbsoluteList, nsIFrame::kAbsoluteList);
884   ProcessFrameInsertions(mFloatedList, nsIFrame::kFloatList);
885   ProcessFrameInsertions(mAbsoluteList, nsIFrame::kAbsoluteList);
886   ProcessFrameInsertions(mFixedList, nsIFrame::kFixedList);
887 #ifdef MOZ_XUL
888   ProcessFrameInsertions(mPopupList, nsIFrame::kPopupList);
889 #endif
890 }
891 
PushAbsoluteContainingBlock(nsContainerFrame * aNewAbsoluteContainingBlock,nsIFrame * aPositionedFrame,nsFrameConstructorSaveState & aSaveState)892 void nsFrameConstructorState::PushAbsoluteContainingBlock(
893     nsContainerFrame* aNewAbsoluteContainingBlock, nsIFrame* aPositionedFrame,
894     nsFrameConstructorSaveState& aSaveState) {
895   aSaveState.mList = &mAbsoluteList;
896   aSaveState.mSavedList = mAbsoluteList;
897   aSaveState.mChildListID = nsIFrame::kAbsoluteList;
898   aSaveState.mState = this;
899   aSaveState.mSavedFixedPosIsAbsPos = mFixedPosIsAbsPos;
900 
901   if (mFixedPosIsAbsPos) {
902     // Since we're going to replace mAbsoluteList, we need to save it into
903     // mFixedList now (and save the current value of mFixedList).
904     aSaveState.mSavedFixedList = mFixedList;
905     mFixedList = mAbsoluteList;
906   }
907 
908   mAbsoluteList = AbsoluteFrameList(aNewAbsoluteContainingBlock);
909 
910   /* See if we're wiring the fixed-pos and abs-pos lists together.  This happens
911    * iff we're a transformed element.
912    */
913   mFixedPosIsAbsPos =
914       aPositionedFrame && aPositionedFrame->IsFixedPosContainingBlock();
915 
916   if (aNewAbsoluteContainingBlock) {
917     aNewAbsoluteContainingBlock->MarkAsAbsoluteContainingBlock();
918   }
919 }
920 
PushFloatContainingBlock(nsContainerFrame * aNewFloatContainingBlock,nsFrameConstructorSaveState & aSaveState)921 void nsFrameConstructorState::PushFloatContainingBlock(
922     nsContainerFrame* aNewFloatContainingBlock,
923     nsFrameConstructorSaveState& aSaveState) {
924   MOZ_ASSERT(!aNewFloatContainingBlock ||
925                  aNewFloatContainingBlock->IsFloatContainingBlock(),
926              "Please push a real float containing block!");
927   NS_ASSERTION(
928       !aNewFloatContainingBlock ||
929           !ShouldSuppressFloatingOfDescendants(aNewFloatContainingBlock),
930       "We should not push a frame that is supposed to _suppress_ "
931       "floats as a float containing block!");
932   aSaveState.mList = &mFloatedList;
933   aSaveState.mSavedList = mFloatedList;
934   aSaveState.mChildListID = nsIFrame::kFloatList;
935   aSaveState.mState = this;
936   mFloatedList = AbsoluteFrameList(aNewFloatContainingBlock);
937 }
938 
GetGeometricParent(const nsStyleDisplay & aStyleDisplay,nsContainerFrame * aContentParentFrame) const939 nsContainerFrame* nsFrameConstructorState::GetGeometricParent(
940     const nsStyleDisplay& aStyleDisplay,
941     nsContainerFrame* aContentParentFrame) const {
942   // If there is no container for a fixed, absolute, or floating root
943   // frame, we will ignore the positioning.  This hack is originally
944   // brought to you by the letter T: tables, since other roots don't
945   // even call into this code.  See bug 178855.
946   //
947   // XXX Disabling positioning in this case is a hack.  If one was so inclined,
948   // one could support this either by (1) inserting a dummy block between the
949   // table and the canvas or (2) teaching the canvas how to reflow positioned
950   // elements. (1) has the usual problems when multiple frames share the same
951   // content (notice all the special cases in this file dealing with inner
952   // tables and table wrappers which share the same content). (2) requires some
953   // work and possible factoring.
954   //
955   // XXXbz couldn't we just force position to "static" on roots and
956   // float to "none"?  That's OK per CSS 2.1, as far as I can tell.
957 
958   if (aContentParentFrame &&
959       nsSVGUtils::IsInSVGTextSubtree(aContentParentFrame)) {
960     return aContentParentFrame;
961   }
962 
963   if (aStyleDisplay.IsFloatingStyle() && mFloatedList.containingBlock) {
964     NS_ASSERTION(!aStyleDisplay.IsAbsolutelyPositionedStyle(),
965                  "Absolutely positioned _and_ floating?");
966     return mFloatedList.containingBlock;
967   }
968 
969   if (aStyleDisplay.mTopLayer != StyleTopLayer::None) {
970     MOZ_ASSERT(aStyleDisplay.mTopLayer == StyleTopLayer::Top,
971                "-moz-top-layer should be either none or top");
972     MOZ_ASSERT(aStyleDisplay.IsAbsolutelyPositionedStyle(),
973                "Top layer items should always be absolutely positioned");
974     if (aStyleDisplay.mPosition == StylePositionProperty::Fixed) {
975       MOZ_ASSERT(mTopLayerFixedList.containingBlock, "No root frame?");
976       return mTopLayerFixedList.containingBlock;
977     }
978     MOZ_ASSERT(aStyleDisplay.mPosition == StylePositionProperty::Absolute);
979     MOZ_ASSERT(mTopLayerAbsoluteList.containingBlock);
980     return mTopLayerAbsoluteList.containingBlock;
981   }
982 
983   if (aStyleDisplay.mPosition == StylePositionProperty::Absolute &&
984       mAbsoluteList.containingBlock) {
985     return mAbsoluteList.containingBlock;
986   }
987 
988   if (aStyleDisplay.mPosition == StylePositionProperty::Fixed &&
989       GetFixedList().containingBlock) {
990     return GetFixedList().containingBlock;
991   }
992 
993   return aContentParentFrame;
994 }
995 
ReparentAbsoluteItems(nsContainerFrame * aNewParent)996 void nsFrameConstructorState::ReparentAbsoluteItems(
997     nsContainerFrame* aNewParent) {
998   // Bug 1491727: This function might not conform to the spec. See
999   // https://github.com/w3c/csswg-drafts/issues/1894.
1000 
1001   MOZ_ASSERT(aNewParent->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR),
1002              "Restrict the usage under column hierarchy.");
1003 
1004   nsFrameList newAbsoluteItems;
1005 
1006   nsIFrame* current = mAbsoluteList.FirstChild();
1007   while (current) {
1008     nsIFrame* placeholder = current->GetPlaceholderFrame();
1009 
1010     if (nsLayoutUtils::IsProperAncestorFrame(aNewParent, placeholder)) {
1011       nsIFrame* next = current->GetNextSibling();
1012       mAbsoluteList.RemoveFrame(current);
1013       newAbsoluteItems.AppendFrame(aNewParent, current);
1014       current = next;
1015     } else {
1016       current = current->GetNextSibling();
1017     }
1018   }
1019 
1020   if (newAbsoluteItems.NotEmpty()) {
1021     // ~nsFrameConstructorSaveState() will move newAbsoluteItems to
1022     // aNewParent's absolute child list.
1023     nsFrameConstructorSaveState absoluteSaveState;
1024 
1025     // It doesn't matter whether aNewParent has position style or not. Caller
1026     // won't call us if we can't have absolute children.
1027     PushAbsoluteContainingBlock(aNewParent, aNewParent, absoluteSaveState);
1028     mAbsoluteList.SetFrames(newAbsoluteItems);
1029   }
1030 }
1031 
GetOutOfFlowFrameList(nsIFrame * aNewFrame,bool aCanBePositioned,bool aCanBeFloated,bool aIsOutOfFlowPopup,nsFrameState * aPlaceholderType)1032 AbsoluteFrameList* nsFrameConstructorState::GetOutOfFlowFrameList(
1033     nsIFrame* aNewFrame, bool aCanBePositioned, bool aCanBeFloated,
1034     bool aIsOutOfFlowPopup, nsFrameState* aPlaceholderType) {
1035 #ifdef MOZ_XUL
1036   if (MOZ_UNLIKELY(aIsOutOfFlowPopup)) {
1037     MOZ_ASSERT(mPopupList.containingBlock, "Must have a popup set frame!");
1038     *aPlaceholderType = PLACEHOLDER_FOR_POPUP;
1039     return &mPopupList;
1040   }
1041 #endif  // MOZ_XUL
1042   if (aCanBeFloated && aNewFrame->IsFloating()) {
1043     *aPlaceholderType = PLACEHOLDER_FOR_FLOAT;
1044     return &mFloatedList;
1045   }
1046 
1047   if (aCanBePositioned) {
1048     const nsStyleDisplay* disp = aNewFrame->StyleDisplay();
1049     if (disp->mTopLayer != StyleTopLayer::None) {
1050       *aPlaceholderType = PLACEHOLDER_FOR_TOPLAYER;
1051       if (disp->mPosition == StylePositionProperty::Fixed) {
1052         *aPlaceholderType |= PLACEHOLDER_FOR_FIXEDPOS;
1053         return &mTopLayerFixedList;
1054       }
1055       *aPlaceholderType |= PLACEHOLDER_FOR_ABSPOS;
1056       return &mTopLayerAbsoluteList;
1057     }
1058     if (disp->mPosition == StylePositionProperty::Absolute) {
1059       *aPlaceholderType = PLACEHOLDER_FOR_ABSPOS;
1060       return &mAbsoluteList;
1061     }
1062     if (disp->mPosition == StylePositionProperty::Fixed) {
1063       *aPlaceholderType = PLACEHOLDER_FOR_FIXEDPOS;
1064       return &GetFixedList();
1065     }
1066   }
1067   return nullptr;
1068 }
1069 
ConstructBackdropFrameFor(nsIContent * aContent,nsIFrame * aFrame)1070 void nsFrameConstructorState::ConstructBackdropFrameFor(nsIContent* aContent,
1071                                                         nsIFrame* aFrame) {
1072   MOZ_ASSERT(aFrame->StyleDisplay()->mTopLayer == StyleTopLayer::Top);
1073   nsContainerFrame* frame = do_QueryFrame(aFrame);
1074   if (!frame) {
1075     NS_WARNING("Cannot create backdrop frame for non-container frame");
1076     return;
1077   }
1078 
1079   RefPtr<ComputedStyle> style =
1080       mPresShell->StyleSet()->ResolvePseudoElementStyle(
1081           *aContent->AsElement(), PseudoStyleType::backdrop,
1082           /* aParentStyle */ nullptr);
1083   MOZ_ASSERT(style->StyleDisplay()->mTopLayer == StyleTopLayer::Top);
1084   nsContainerFrame* parentFrame =
1085       GetGeometricParent(*style->StyleDisplay(), nullptr);
1086 
1087   nsBackdropFrame* backdropFrame =
1088       new (mPresShell) nsBackdropFrame(style, mPresShell->GetPresContext());
1089   backdropFrame->Init(aContent, parentFrame, nullptr);
1090 
1091   nsFrameState placeholderType;
1092   AbsoluteFrameList* frameList =
1093       GetOutOfFlowFrameList(backdropFrame, true, true, false, &placeholderType);
1094   MOZ_ASSERT(placeholderType & PLACEHOLDER_FOR_TOPLAYER);
1095 
1096   nsIFrame* placeholder = nsCSSFrameConstructor::CreatePlaceholderFrameFor(
1097       mPresShell, aContent, backdropFrame, frame, nullptr, placeholderType);
1098   nsFrameList temp(placeholder, placeholder);
1099   frame->SetInitialChildList(nsIFrame::kBackdropList, temp);
1100 
1101   frameList->AppendFrame(nullptr, backdropFrame);
1102 }
1103 
AddChild(nsIFrame * aNewFrame,nsFrameList & aFrameList,nsIContent * aContent,nsContainerFrame * aParentFrame,bool aCanBePositioned,bool aCanBeFloated,bool aIsOutOfFlowPopup,bool aInsertAfter,nsIFrame * aInsertAfterFrame)1104 void nsFrameConstructorState::AddChild(
1105     nsIFrame* aNewFrame, nsFrameList& aFrameList, nsIContent* aContent,
1106     nsContainerFrame* aParentFrame, bool aCanBePositioned, bool aCanBeFloated,
1107     bool aIsOutOfFlowPopup, bool aInsertAfter, nsIFrame* aInsertAfterFrame) {
1108   MOZ_ASSERT(!aNewFrame->GetNextSibling(), "Shouldn't happen");
1109 
1110   nsFrameState placeholderType;
1111   AbsoluteFrameList* outOfFlowFrameList =
1112       GetOutOfFlowFrameList(aNewFrame, aCanBePositioned, aCanBeFloated,
1113                             aIsOutOfFlowPopup, &placeholderType);
1114 
1115   // The comments in GetGeometricParent regarding root table frames
1116   // all apply here, unfortunately. Thus, we need to check whether
1117   // the returned frame items really has containing block.
1118   nsFrameList* frameList;
1119   if (outOfFlowFrameList && outOfFlowFrameList->containingBlock) {
1120     MOZ_ASSERT(aNewFrame->GetParent() == outOfFlowFrameList->containingBlock,
1121                "Parent of the frame is not the containing block?");
1122     frameList = outOfFlowFrameList;
1123   } else {
1124     frameList = &aFrameList;
1125     placeholderType = nsFrameState(0);
1126   }
1127 
1128   if (placeholderType) {
1129     NS_ASSERTION(frameList != &aFrameList,
1130                  "Putting frame in-flow _and_ want a placeholder?");
1131     nsIFrame* placeholderFrame =
1132         nsCSSFrameConstructor::CreatePlaceholderFrameFor(
1133             mPresShell, aContent, aNewFrame, aParentFrame, nullptr,
1134             placeholderType);
1135 
1136     placeholderFrame->AddStateBits(mAdditionalStateBits);
1137     // Add the placeholder frame to the flow
1138     aFrameList.AppendFrame(nullptr, placeholderFrame);
1139 
1140     if (placeholderType & PLACEHOLDER_FOR_TOPLAYER) {
1141       ConstructBackdropFrameFor(aContent, aNewFrame);
1142     }
1143   }
1144 #ifdef DEBUG
1145   else {
1146     NS_ASSERTION(aNewFrame->GetParent() == aParentFrame,
1147                  "In-flow frame has wrong parent");
1148   }
1149 #endif
1150 
1151   if (aInsertAfter) {
1152     frameList->InsertFrame(nullptr, aInsertAfterFrame, aNewFrame);
1153   } else {
1154     frameList->AppendFrame(nullptr, aNewFrame);
1155   }
1156 }
1157 
1158 // Some of this function's callers recurse 1000 levels deep in crashtests. On
1159 // platforms where stack limits are low, we can't afford to incorporate this
1160 // function's `AutoTArray`s into its callers' stack frames, so disable inlining.
ProcessFrameInsertions(AbsoluteFrameList & aFrameList,ChildListID aChildListID)1161 MOZ_NEVER_INLINE void nsFrameConstructorState::ProcessFrameInsertions(
1162     AbsoluteFrameList& aFrameList, ChildListID aChildListID) {
1163 #define NS_NONXUL_LIST_TEST                                                  \
1164   (&aFrameList == &mFloatedList && aChildListID == nsIFrame::kFloatList) ||  \
1165       ((&aFrameList == &mAbsoluteList ||                                     \
1166         &aFrameList == &mTopLayerAbsoluteList) &&                            \
1167        aChildListID == nsIFrame::kAbsoluteList) ||                           \
1168       ((&aFrameList == &mFixedList || &aFrameList == &mTopLayerFixedList) && \
1169        aChildListID == nsIFrame::kFixedList)
1170 #ifdef MOZ_XUL
1171   MOZ_ASSERT(NS_NONXUL_LIST_TEST || (&aFrameList == &mPopupList &&
1172                                      aChildListID == nsIFrame::kPopupList),
1173              "Unexpected aFrameList/aChildListID combination");
1174 #else
1175   MOZ_ASSERT(NS_NONXUL_LIST_TEST,
1176              "Unexpected aFrameList/aChildListID combination");
1177 #endif
1178 
1179   if (aFrameList.IsEmpty()) {
1180     return;
1181   }
1182 
1183   nsContainerFrame* containingBlock = aFrameList.containingBlock;
1184 
1185   NS_ASSERTION(containingBlock, "Child list without containing block?");
1186 
1187   if (aChildListID == nsIFrame::kFixedList) {
1188     // Put this frame on the transformed-frame's abs-pos list instead, if
1189     // it has abs-pos children instead of fixed-pos children.
1190     aChildListID = containingBlock->GetAbsoluteListID();
1191   }
1192 
1193   // Insert the frames hanging out in aItems.  We can use SetInitialChildList()
1194   // if the containing block hasn't been reflowed yet (so NS_FRAME_FIRST_REFLOW
1195   // is set) and doesn't have any frames in the aChildListID child list yet.
1196   const nsFrameList& childList = containingBlock->GetChildList(aChildListID);
1197   if (childList.IsEmpty() &&
1198       (containingBlock->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
1199     // If we're injecting absolutely positioned frames, inject them on the
1200     // absolute containing block
1201     if (aChildListID == containingBlock->GetAbsoluteListID()) {
1202       containingBlock->GetAbsoluteContainingBlock()->SetInitialChildList(
1203           containingBlock, aChildListID, aFrameList);
1204     } else {
1205       containingBlock->SetInitialChildList(aChildListID, aFrameList);
1206     }
1207   } else if (aChildListID == nsIFrame::kFixedList ||
1208              aChildListID == nsIFrame::kAbsoluteList) {
1209     // The order is not important for abs-pos/fixed-pos frame list, just
1210     // append the frame items to the list directly.
1211     mFrameManager->AppendFrames(containingBlock, aChildListID, aFrameList);
1212   } else {
1213     // Note that whether the frame construction context is doing an append or
1214     // not is not helpful here, since it could be appending to some frame in
1215     // the middle of the document, which means we're not necessarily
1216     // appending to the children of the containing block.
1217     //
1218     // We need to make sure the 'append to the end of document' case is fast.
1219     // So first test the last child of the containing block
1220     nsIFrame* lastChild = childList.LastChild();
1221 
1222     // CompareTreePosition uses placeholder hierarchy for out of flow frames,
1223     // so this will make out-of-flows respect the ordering of placeholders,
1224     // which is great because it takes care of anonymous content.
1225     nsIFrame* firstNewFrame = aFrameList.FirstChild();
1226 
1227     // Cache the ancestor chain so that we can reuse it if needed.
1228     AutoTArray<nsIFrame*, 20> firstNewFrameAncestors;
1229     nsIFrame* notCommonAncestor = nullptr;
1230     if (lastChild) {
1231       notCommonAncestor = nsLayoutUtils::FillAncestors(
1232           firstNewFrame, containingBlock, &firstNewFrameAncestors);
1233     }
1234 
1235     if (!lastChild || nsLayoutUtils::CompareTreePosition(
1236                           lastChild, firstNewFrame, firstNewFrameAncestors,
1237                           notCommonAncestor ? containingBlock : nullptr) < 0) {
1238       // no lastChild, or lastChild comes before the new children, so just
1239       // append
1240       mFrameManager->AppendFrames(containingBlock, aChildListID, aFrameList);
1241     } else {
1242       // Try the other children. First collect them to an array so that a
1243       // reasonable fast binary search can be used to find the insertion point.
1244       AutoTArray<nsIFrame*, 128> children;
1245       for (nsIFrame* f = childList.FirstChild(); f != lastChild;
1246            f = f->GetNextSibling()) {
1247         children.AppendElement(f);
1248       }
1249 
1250       nsIFrame* insertionPoint = nullptr;
1251       int32_t imin = 0;
1252       int32_t max = children.Length();
1253       while (max > imin) {
1254         int32_t imid = imin + ((max - imin) / 2);
1255         nsIFrame* f = children[imid];
1256         int32_t compare = nsLayoutUtils::CompareTreePosition(
1257             f, firstNewFrame, firstNewFrameAncestors,
1258             notCommonAncestor ? containingBlock : nullptr);
1259         if (compare > 0) {
1260           // f is after the new frame.
1261           max = imid;
1262           insertionPoint = imid > 0 ? children[imid - 1] : nullptr;
1263         } else if (compare < 0) {
1264           // f is before the new frame.
1265           imin = imid + 1;
1266           insertionPoint = f;
1267         } else {
1268           // This is for the old behavior. Should be removed once it is
1269           // guaranteed that CompareTreePosition can't return 0!
1270           // See bug 928645.
1271           NS_WARNING("Something odd happening???");
1272           insertionPoint = nullptr;
1273           for (uint32_t i = 0; i < children.Length(); ++i) {
1274             nsIFrame* f = children[i];
1275             if (nsLayoutUtils::CompareTreePosition(
1276                     f, firstNewFrame, firstNewFrameAncestors,
1277                     notCommonAncestor ? containingBlock : nullptr) > 0) {
1278               break;
1279             }
1280             insertionPoint = f;
1281           }
1282           break;
1283         }
1284       }
1285       mFrameManager->InsertFrames(containingBlock, aChildListID, insertionPoint,
1286                                   aFrameList);
1287     }
1288   }
1289 
1290   MOZ_ASSERT(aFrameList.IsEmpty(), "How did that happen?");
1291 }
1292 
nsFrameConstructorSaveState()1293 nsFrameConstructorSaveState::nsFrameConstructorSaveState()
1294     : mList(nullptr),
1295       mSavedList(nullptr),
1296       mChildListID(kPrincipalList),
1297       mState(nullptr),
1298       mSavedFixedList(nullptr),
1299       mSavedFixedPosIsAbsPos(false) {}
1300 
~nsFrameConstructorSaveState()1301 nsFrameConstructorSaveState::~nsFrameConstructorSaveState() {
1302   // Restore the state
1303   if (mList) {
1304     NS_ASSERTION(mState, "Can't have mList set without having a state!");
1305     mState->ProcessFrameInsertions(*mList, mChildListID);
1306     *mList = mSavedList;
1307 #ifdef DEBUG
1308     // We've transferred the child list, so drop the pointer we held to it.
1309     // Note that this only matters for the assert in ~AbsoluteFrameList.
1310     mSavedList.Clear();
1311 #endif
1312     if (mList == &mState->mAbsoluteList) {
1313       mState->mFixedPosIsAbsPos = mSavedFixedPosIsAbsPos;
1314       if (mSavedFixedPosIsAbsPos) {
1315         // mAbsoluteList was moved to mFixedList, so move mFixedList back
1316         // and repair the old mFixedList now.
1317         mState->mAbsoluteList = mState->mFixedList;
1318         mState->mFixedList = mSavedFixedList;
1319 #ifdef DEBUG
1320         mSavedFixedList.Clear();
1321 #endif
1322       }
1323     }
1324     NS_ASSERTION(!mList->LastChild() || !mList->LastChild()->GetNextSibling(),
1325                  "Something corrupted our list");
1326   }
1327 }
1328 
1329 /**
1330  * Moves aFrameList from aOldParent to aNewParent.  This updates the parent
1331  * pointer of the frames in the list, and reparents their views as needed.
1332  * nsFrame::SetParent sets the NS_FRAME_HAS_VIEW bit on aNewParent and its
1333  * ancestors as needed. Then it sets the list as the initial child list
1334  * on aNewParent, unless aNewParent either already has kids or has been
1335  * reflowed; in that case it appends the new frames.  Note that this
1336  * method differs from ReparentFrames in that it doesn't change the kids'
1337  * style.
1338  */
1339 // XXXbz Since this is only used for {ib} splits, could we just copy the view
1340 // bits from aOldParent to aNewParent and then use the
1341 // nsFrameList::ApplySetParent?  That would still leave us doing two passes
1342 // over the list, of course; if we really wanted to we could factor out the
1343 // relevant part of ReparentFrameViewList, I suppose...  Or just get rid of
1344 // views, which would make most of this function go away.
MoveChildrenTo(nsIFrame * aOldParent,nsContainerFrame * aNewParent,nsFrameList & aFrameList)1345 static void MoveChildrenTo(nsIFrame* aOldParent, nsContainerFrame* aNewParent,
1346                            nsFrameList& aFrameList) {
1347   bool sameGrandParent = aOldParent->GetParent() == aNewParent->GetParent();
1348 
1349   if (aNewParent->HasView() || aOldParent->HasView() || !sameGrandParent) {
1350     // Move the frames into the new view
1351     nsContainerFrame::ReparentFrameViewList(aFrameList, aOldParent, aNewParent);
1352   }
1353 
1354   for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
1355     e.get()->SetParent(aNewParent);
1356   }
1357 
1358   if (aNewParent->PrincipalChildList().IsEmpty() &&
1359       (aNewParent->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
1360     aNewParent->SetInitialChildList(kPrincipalList, aFrameList);
1361   } else {
1362     aNewParent->AppendFrames(kPrincipalList, aFrameList);
1363   }
1364 }
1365 
ShouldCreateImageFrameForContent(const Element & aElement,ComputedStyle & aStyle)1366 static bool ShouldCreateImageFrameForContent(const Element& aElement,
1367                                              ComputedStyle& aStyle) {
1368   if (aElement.IsRootOfNativeAnonymousSubtree()) {
1369     return false;
1370   }
1371   auto& content = aStyle.StyleContent()->mContent;
1372   if (!content.IsItems()) {
1373     return false;
1374   }
1375   Span<const StyleContentItem> items = content.AsItems().AsSpan();
1376   return items.Length() == 1 && items[0].IsUrl();
1377 }
1378 
1379 //----------------------------------------------------------------------
1380 
nsCSSFrameConstructor(Document * aDocument,PresShell * aPresShell)1381 nsCSSFrameConstructor::nsCSSFrameConstructor(Document* aDocument,
1382                                              PresShell* aPresShell)
1383     : nsFrameManager(aPresShell),
1384       mDocument(aDocument),
1385       mRootElementFrame(nullptr),
1386       mRootElementStyleFrame(nullptr),
1387       mDocElementContainingBlock(nullptr),
1388       mPageSequenceFrame(nullptr),
1389       mFirstFreeFCItem(nullptr),
1390       mFCItemsInUse(0),
1391       mCurrentDepth(0),
1392       mQuotesDirty(false),
1393       mCountersDirty(false),
1394       mIsDestroyingFrameTree(false),
1395       mHasRootAbsPosContainingBlock(false),
1396       mAlwaysCreateFramesForIgnorableWhitespace(false) {
1397 #ifdef DEBUG
1398   static bool gFirstTime = true;
1399   if (gFirstTime) {
1400     gFirstTime = false;
1401     char* flags = PR_GetEnv("GECKO_FRAMECTOR_DEBUG_FLAGS");
1402     if (flags) {
1403       bool error = false;
1404       for (;;) {
1405         char* comma = PL_strchr(flags, ',');
1406         if (comma) *comma = '\0';
1407 
1408         bool found = false;
1409         FrameCtorDebugFlags* flag = gFlags;
1410         FrameCtorDebugFlags* limit = gFlags + NUM_DEBUG_FLAGS;
1411         while (flag < limit) {
1412           if (PL_strcasecmp(flag->name, flags) == 0) {
1413             *(flag->on) = true;
1414             printf("nsCSSFrameConstructor: setting %s debug flag on\n",
1415                    flag->name);
1416             found = true;
1417             break;
1418           }
1419           ++flag;
1420         }
1421 
1422         if (!found) error = true;
1423 
1424         if (!comma) break;
1425 
1426         *comma = ',';
1427         flags = comma + 1;
1428       }
1429 
1430       if (error) {
1431         printf("Here are the available GECKO_FRAMECTOR_DEBUG_FLAGS:\n");
1432         FrameCtorDebugFlags* flag = gFlags;
1433         FrameCtorDebugFlags* limit = gFlags + NUM_DEBUG_FLAGS;
1434         while (flag < limit) {
1435           printf("  %s\n", flag->name);
1436           ++flag;
1437         }
1438         printf(
1439             "Note: GECKO_FRAMECTOR_DEBUG_FLAGS is a comma separated list of "
1440             "flag\n");
1441         printf("names (no whitespace)\n");
1442       }
1443     }
1444   }
1445 #endif
1446 }
1447 
NotifyDestroyingFrame(nsIFrame * aFrame)1448 void nsCSSFrameConstructor::NotifyDestroyingFrame(nsIFrame* aFrame) {
1449   if (aFrame->GetStateBits() & NS_FRAME_GENERATED_CONTENT) {
1450     if (mQuoteList.DestroyNodesFor(aFrame)) QuotesDirty();
1451   }
1452 
1453   if (aFrame->HasAnyStateBits(NS_FRAME_HAS_CSS_COUNTER_STYLE) &&
1454       mCounterManager.DestroyNodesFor(aFrame)) {
1455     // Technically we don't need to update anything if we destroyed only
1456     // USE nodes.  However, this is unlikely to happen in the real world
1457     // since USE nodes generally go along with INCREMENT nodes.
1458     CountersDirty();
1459   }
1460 
1461   RestyleManager()->NotifyDestroyingFrame(aFrame);
1462 }
1463 
1464 struct nsGenConInitializer {
1465   UniquePtr<nsGenConNode> mNode;
1466   nsGenConList* mList;
1467   void (nsCSSFrameConstructor::*mDirtyAll)();
1468 
nsGenConInitializernsGenConInitializer1469   nsGenConInitializer(UniquePtr<nsGenConNode> aNode, nsGenConList* aList,
1470                       void (nsCSSFrameConstructor::*aDirtyAll)())
1471       : mNode(std::move(aNode)), mList(aList), mDirtyAll(aDirtyAll) {}
1472 };
1473 
CreateGenConTextNode(nsFrameConstructorState & aState,const nsString & aString,UniquePtr<nsGenConInitializer> aInitializer)1474 already_AddRefed<nsIContent> nsCSSFrameConstructor::CreateGenConTextNode(
1475     nsFrameConstructorState& aState, const nsString& aString,
1476     UniquePtr<nsGenConInitializer> aInitializer) {
1477   RefPtr<nsTextNode> content = new (mDocument->NodeInfoManager())
1478       nsTextNode(mDocument->NodeInfoManager());
1479   content->SetText(aString, false);
1480   if (aInitializer) {
1481     aInitializer->mNode->mText = content;
1482     content->SetProperty(nsGkAtoms::genConInitializerProperty,
1483                          aInitializer.release(),
1484                          nsINode::DeleteProperty<nsGenConInitializer>);
1485     aState.mGeneratedContentWithInitializer.AppendElement(content);
1486   }
1487   return content.forget();
1488 }
1489 
CreateGeneratedContent(nsFrameConstructorState & aState,const Element & aOriginatingElement,ComputedStyle & aPseudoStyle,uint32_t aContentIndex)1490 already_AddRefed<nsIContent> nsCSSFrameConstructor::CreateGeneratedContent(
1491     nsFrameConstructorState& aState, const Element& aOriginatingElement,
1492     ComputedStyle& aPseudoStyle, uint32_t aContentIndex) {
1493   using Type = StyleContentItem::Tag;
1494   // Get the content value
1495   const auto& item = aPseudoStyle.StyleContent()->ContentAt(aContentIndex);
1496   const Type type = item.tag;
1497 
1498   switch (type) {
1499     case Type::Url:
1500       return GeneratedImageContent::Create(*mDocument, aContentIndex);
1501 
1502     case Type::String:
1503       return CreateGenConTextNode(
1504           aState, NS_ConvertUTF8toUTF16(item.AsString().AsString()), nullptr);
1505 
1506     case Type::Attr: {
1507       const auto& attr = item.AsAttr();
1508       RefPtr<nsAtom> attrName = attr.attribute.AsAtom();
1509       int32_t attrNameSpace = kNameSpaceID_None;
1510       RefPtr<nsAtom> ns = attr.namespace_url.AsAtom();
1511       if (!ns->IsEmpty()) {
1512         nsresult rv = nsContentUtils::NameSpaceManager()->RegisterNameSpace(
1513             ns.forget(), attrNameSpace);
1514         NS_ENSURE_SUCCESS(rv, nullptr);
1515       }
1516 
1517       if (mDocument->IsHTMLDocument() && aOriginatingElement.IsHTMLElement()) {
1518         ToLowerCaseASCII(attrName);
1519       }
1520 
1521       nsCOMPtr<nsIContent> content;
1522       NS_NewAttributeContent(mDocument->NodeInfoManager(), attrNameSpace,
1523                              attrName, getter_AddRefs(content));
1524       return content.forget();
1525     }
1526 
1527     case Type::Counter:
1528     case Type::Counters: {
1529       RefPtr<nsAtom> name;
1530       CounterStylePtr ptr;
1531       nsString separator;
1532       if (type == Type::Counter) {
1533         auto& counter = item.AsCounter();
1534         name = counter._0.AsAtom();
1535         ptr = CounterStylePtr::FromStyle(counter._1);
1536       } else {
1537         auto& counters = item.AsCounters();
1538         name = counters._0.AsAtom();
1539         separator = NS_ConvertUTF8toUTF16(counters._1.AsString());
1540         ptr = CounterStylePtr::FromStyle(counters._2);
1541       }
1542 
1543       nsCounterList* counterList = mCounterManager.CounterListFor(name);
1544       auto node = MakeUnique<nsCounterUseNode>(
1545           std::move(ptr), std::move(separator), aContentIndex,
1546           /* aAllCounters = */ type == Type::Counters);
1547 
1548       auto initializer = MakeUnique<nsGenConInitializer>(
1549           std::move(node), counterList, &nsCSSFrameConstructor::CountersDirty);
1550       return CreateGenConTextNode(aState, EmptyString(),
1551                                   std::move(initializer));
1552     }
1553     case Type::OpenQuote:
1554     case Type::CloseQuote:
1555     case Type::NoOpenQuote:
1556     case Type::NoCloseQuote: {
1557       auto node = MakeUnique<nsQuoteNode>(type, aContentIndex);
1558       auto initializer = MakeUnique<nsGenConInitializer>(
1559           std::move(node), &mQuoteList, &nsCSSFrameConstructor::QuotesDirty);
1560       return CreateGenConTextNode(aState, EmptyString(),
1561                                   std::move(initializer));
1562     }
1563 
1564     case Type::MozAltContent: {
1565       // Use the "alt" attribute; if that fails and the node is an HTML
1566       // <input>, try the value attribute and then fall back to some default
1567       // localized text we have.
1568       // XXX what if the 'alt' attribute is added later, how will we
1569       // detect that and do the right thing here?
1570       if (aOriginatingElement.HasAttr(kNameSpaceID_None, nsGkAtoms::alt)) {
1571         nsCOMPtr<nsIContent> content;
1572         NS_NewAttributeContent(mDocument->NodeInfoManager(), kNameSpaceID_None,
1573                                nsGkAtoms::alt, getter_AddRefs(content));
1574         return content.forget();
1575       }
1576 
1577       if (aOriginatingElement.IsHTMLElement(nsGkAtoms::input)) {
1578         if (aOriginatingElement.HasAttr(kNameSpaceID_None, nsGkAtoms::value)) {
1579           nsCOMPtr<nsIContent> content;
1580           NS_NewAttributeContent(mDocument->NodeInfoManager(),
1581                                  kNameSpaceID_None, nsGkAtoms::value,
1582                                  getter_AddRefs(content));
1583           return content.forget();
1584         }
1585 
1586         nsAutoString temp;
1587         nsContentUtils::GetMaybeLocalizedString(
1588             nsContentUtils::eFORMS_PROPERTIES, "Submit", mDocument, temp);
1589         return CreateGenConTextNode(aState, temp, nullptr);
1590       }
1591 
1592       break;
1593     }
1594   }
1595 
1596   return nullptr;
1597 }
1598 
1599 /*
1600  * aParentFrame - the frame that should be the parent of the generated
1601  *   content.  This is the frame for the corresponding content node,
1602  *   which must not be a leaf frame.
1603  *
1604  * Any items created are added to aItems.
1605  *
1606  * We create an XML element (tag _moz_generated_content_before/after/marker)
1607  * representing the pseudoelement. We create a DOM node for each 'content'
1608  * item and make those nodes the children of the XML element. Then we create
1609  * a frame subtree for the XML element as if it were a regular child of
1610  * aParentFrame/aParentContent, giving the XML element the ::before, ::after
1611  * or ::marker style.
1612  */
CreateGeneratedContentItem(nsFrameConstructorState & aState,nsContainerFrame * aParentFrame,Element & aOriginatingElement,ComputedStyle & aStyle,PseudoStyleType aPseudoElement,FrameConstructionItemList & aItems)1613 void nsCSSFrameConstructor::CreateGeneratedContentItem(
1614     nsFrameConstructorState& aState, nsContainerFrame* aParentFrame,
1615     Element& aOriginatingElement, ComputedStyle& aStyle,
1616     PseudoStyleType aPseudoElement, FrameConstructionItemList& aItems) {
1617   MOZ_ASSERT(aPseudoElement == PseudoStyleType::before ||
1618                  aPseudoElement == PseudoStyleType::after ||
1619                  aPseudoElement == PseudoStyleType::marker,
1620              "unexpected aPseudoElement");
1621 
1622   if (aParentFrame && (aParentFrame->IsHTMLVideoFrame() ||
1623                        aParentFrame->IsDateTimeControlFrame())) {
1624     // Video frames and date time control frames may not be leafs when backed by
1625     // an UA widget, but we still don't want to expose generated content.
1626     return;
1627   }
1628 
1629   ServoStyleSet* styleSet = mPresShell->StyleSet();
1630 
1631   // Probe for the existence of the pseudo-element
1632   RefPtr<ComputedStyle> pseudoStyle = styleSet->ProbePseudoElementStyle(
1633       aOriginatingElement, aPseudoElement, &aStyle);
1634   if (!pseudoStyle) {
1635     return;
1636   }
1637 
1638   nsAtom* elemName = nullptr;
1639   nsAtom* property = nullptr;
1640   switch (aPseudoElement) {
1641     case PseudoStyleType::before:
1642       elemName = nsGkAtoms::mozgeneratedcontentbefore;
1643       property = nsGkAtoms::beforePseudoProperty;
1644       break;
1645     case PseudoStyleType::after:
1646       elemName = nsGkAtoms::mozgeneratedcontentafter;
1647       property = nsGkAtoms::afterPseudoProperty;
1648       break;
1649     case PseudoStyleType::marker:
1650       // We want to get a marker style even if we match no rules, but we still
1651       // want to check the result of GeneratedContentPseudoExists.
1652       elemName = nsGkAtoms::mozgeneratedcontentmarker;
1653       property = nsGkAtoms::markerPseudoProperty;
1654       break;
1655     default:
1656       MOZ_ASSERT_UNREACHABLE("unexpected aPseudoElement");
1657   }
1658 
1659   // |ProbePseudoStyleFor| checked the 'display' property and the
1660   // |ContentCount()| of the 'content' property for us.
1661   RefPtr<NodeInfo> nodeInfo = mDocument->NodeInfoManager()->GetNodeInfo(
1662       elemName, nullptr, kNameSpaceID_None, nsINode::ELEMENT_NODE);
1663   RefPtr<Element> container;
1664   nsresult rv = NS_NewXMLElement(getter_AddRefs(container), nodeInfo.forget());
1665   if (NS_FAILED(rv)) {
1666     return;
1667   }
1668 
1669   // Cleared when the pseudo is unbound from the tree, so no need to store a
1670   // strong reference, nor a destructor.
1671   aOriginatingElement.SetProperty(property, container.get());
1672 
1673   container->SetIsNativeAnonymousRoot();
1674   container->SetPseudoElementType(aPseudoElement);
1675 
1676   BindContext context(aOriginatingElement, BindContext::ForNativeAnonymous);
1677   rv = container->BindToTree(context, aOriginatingElement);
1678   if (NS_FAILED(rv)) {
1679     container->UnbindFromTree();
1680     return;
1681   }
1682 
1683   // Servo has already eagerly computed the style for the container, so we can
1684   // just stick the style on the element and avoid an additional traversal.
1685   //
1686   // We don't do this for pseudos that may trigger animations or transitions,
1687   // since those need to be kicked off by the traversal machinery.
1688   //
1689   // Note that when a pseudo-element animates, we flag the originating element,
1690   // so we check that flag, but we could also a more expensive (but exhaustive)
1691   // check using EffectSet::GetEffectSet, for example.
1692   if (!Servo_ComputedValues_SpecifiesAnimationsOrTransitions(pseudoStyle) &&
1693       !aOriginatingElement.MayHaveAnimations()) {
1694     Servo_SetExplicitStyle(container, pseudoStyle);
1695   } else {
1696     // If animations are involved, we avoid the SetExplicitStyle optimization
1697     // above. We need to grab style with animations from the pseudo element and
1698     // replace old one.
1699     mPresShell->StyleSet()->StyleNewSubtree(container);
1700     pseudoStyle = ServoStyleSet::ResolveServoStyle(*container);
1701   }
1702 
1703   uint32_t contentCount = pseudoStyle->StyleContent()->ContentCount();
1704   for (uint32_t contentIndex = 0; contentIndex < contentCount; contentIndex++) {
1705     nsCOMPtr<nsIContent> content = CreateGeneratedContent(
1706         aState, aOriginatingElement, *pseudoStyle, contentIndex);
1707     if (!content) {
1708       continue;
1709     }
1710     // We don't strictly have to set NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE
1711     // here; it would get set under AppendChildTo.  But AppendChildTo might
1712     // think that we're going from not being anonymous to being anonymous and
1713     // do some extra work; setting the flag here avoids that.
1714     content->SetFlags(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE);
1715     container->AppendChildTo(content, false);
1716     if (auto* element = Element::FromNode(content)) {
1717       // If we created any children elements, Servo needs to traverse them, but
1718       // the root is already set up.
1719       mPresShell->StyleSet()->StyleNewSubtree(element);
1720     }
1721   }
1722 
1723   AddFrameConstructionItemsInternal(aState, container, aParentFrame, true,
1724                                     pseudoStyle, {ItemFlag::IsGeneratedContent},
1725                                     aItems);
1726 }
1727 
1728 /****************************************************
1729  **  BEGIN TABLE SECTION
1730  ****************************************************/
1731 
1732 // The term pseudo frame is being used instead of anonymous frame, since
1733 // anonymous frame has been used elsewhere to refer to frames that have
1734 // generated content
1735 
1736 // Return whether the given frame is a table pseudo-frame. Note that
1737 // cell-content and table-outer frames have pseudo-types, but are always
1738 // created, even for non-anonymous cells and tables respectively.  So for those
1739 // we have to examine the cell or table frame to see whether it's a pseudo
1740 // frame. In particular, a lone table caption will have a table wrapper as its
1741 // parent, but will also trigger construction of an empty inner table, which
1742 // will be the one we can examine to see whether the wrapper was a pseudo-frame.
IsTablePseudo(nsIFrame * aFrame)1743 static bool IsTablePseudo(nsIFrame* aFrame) {
1744   auto pseudoType = aFrame->Style()->GetPseudoType();
1745   return pseudoType != PseudoStyleType::NotPseudo &&
1746          (pseudoType == PseudoStyleType::table ||
1747           pseudoType == PseudoStyleType::inlineTable ||
1748           pseudoType == PseudoStyleType::tableColGroup ||
1749           pseudoType == PseudoStyleType::tableRowGroup ||
1750           pseudoType == PseudoStyleType::tableRow ||
1751           pseudoType == PseudoStyleType::tableCell ||
1752           (pseudoType == PseudoStyleType::cellContent &&
1753            aFrame->GetParent()->Style()->GetPseudoType() ==
1754                PseudoStyleType::tableCell) ||
1755           (pseudoType == PseudoStyleType::tableWrapper &&
1756            (aFrame->PrincipalChildList()
1757                     .FirstChild()
1758                     ->Style()
1759                     ->GetPseudoType() == PseudoStyleType::table ||
1760             aFrame->PrincipalChildList()
1761                     .FirstChild()
1762                     ->Style()
1763                     ->GetPseudoType() == PseudoStyleType::inlineTable)));
1764 }
1765 
IsRubyPseudo(nsIFrame * aFrame)1766 static bool IsRubyPseudo(nsIFrame* aFrame) {
1767   return RubyUtils::IsRubyPseudo(aFrame->Style()->GetPseudoType());
1768 }
1769 
IsTableOrRubyPseudo(nsIFrame * aFrame)1770 static bool IsTableOrRubyPseudo(nsIFrame* aFrame) {
1771   return IsTablePseudo(aFrame) || IsRubyPseudo(aFrame);
1772 }
1773 
1774 /* static */
GetParentType(LayoutFrameType aFrameType)1775 nsCSSFrameConstructor::ParentType nsCSSFrameConstructor::GetParentType(
1776     LayoutFrameType aFrameType) {
1777   if (aFrameType == LayoutFrameType::Table) {
1778     return eTypeTable;
1779   }
1780   if (aFrameType == LayoutFrameType::TableRowGroup) {
1781     return eTypeRowGroup;
1782   }
1783   if (aFrameType == LayoutFrameType::TableRow) {
1784     return eTypeRow;
1785   }
1786   if (aFrameType == LayoutFrameType::TableColGroup) {
1787     return eTypeColGroup;
1788   }
1789   if (aFrameType == LayoutFrameType::RubyBaseContainer) {
1790     return eTypeRubyBaseContainer;
1791   }
1792   if (aFrameType == LayoutFrameType::RubyTextContainer) {
1793     return eTypeRubyTextContainer;
1794   }
1795   if (aFrameType == LayoutFrameType::Ruby) {
1796     return eTypeRuby;
1797   }
1798 
1799   return eTypeBlock;
1800 }
1801 
1802 // Pull all the captions present in aItems out into aCaptions.
PullOutCaptionFrames(nsFrameList & aList,nsFrameList & aCaptions)1803 static void PullOutCaptionFrames(nsFrameList& aList, nsFrameList& aCaptions) {
1804   nsIFrame* child = aList.FirstChild();
1805   while (child) {
1806     nsIFrame* nextSibling = child->GetNextSibling();
1807     if (child->StyleDisplay()->mDisplay == StyleDisplay::TableCaption) {
1808       aList.RemoveFrame(child);
1809       aCaptions.AppendFrame(nullptr, child);
1810     }
1811     child = nextSibling;
1812   }
1813 }
1814 
1815 // Construct the outer, inner table frames and the children frames for the
1816 // table.
1817 // XXX Page break frames for pseudo table frames are not constructed to avoid
1818 // the risk associated with revising the pseudo frame mechanism. The long term
1819 // solution of having frames handle page-break-before/after will solve the
1820 // problem.
ConstructTable(nsFrameConstructorState & aState,FrameConstructionItem & aItem,nsContainerFrame * aParentFrame,const nsStyleDisplay * aDisplay,nsFrameList & aFrameList)1821 nsIFrame* nsCSSFrameConstructor::ConstructTable(nsFrameConstructorState& aState,
1822                                                 FrameConstructionItem& aItem,
1823                                                 nsContainerFrame* aParentFrame,
1824                                                 const nsStyleDisplay* aDisplay,
1825                                                 nsFrameList& aFrameList) {
1826   MOZ_ASSERT(aDisplay->mDisplay == StyleDisplay::Table ||
1827                  aDisplay->mDisplay == StyleDisplay::InlineTable,
1828              "Unexpected call");
1829 
1830   nsIContent* const content = aItem.mContent;
1831   ComputedStyle* const computedStyle = aItem.mComputedStyle;
1832   const bool isMathMLContent = content->IsMathMLElement();
1833 
1834   // create the pseudo SC for the table wrapper as a child of the inner SC
1835   RefPtr<ComputedStyle> outerComputedStyle;
1836   outerComputedStyle =
1837       mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
1838           PseudoStyleType::tableWrapper, computedStyle);
1839 
1840   // Create the table wrapper frame which holds the caption and inner table
1841   // frame
1842   nsContainerFrame* newFrame;
1843   if (isMathMLContent)
1844     newFrame = NS_NewMathMLmtableOuterFrame(mPresShell, outerComputedStyle);
1845   else
1846     newFrame = NS_NewTableWrapperFrame(mPresShell, outerComputedStyle);
1847 
1848   nsContainerFrame* geometricParent = aState.GetGeometricParent(
1849       *outerComputedStyle->StyleDisplay(), aParentFrame);
1850 
1851   // Init the table wrapper frame
1852   InitAndRestoreFrame(aState, content, geometricParent, newFrame);
1853 
1854   // Create the inner table frame
1855   nsContainerFrame* innerFrame;
1856   if (isMathMLContent)
1857     innerFrame = NS_NewMathMLmtableFrame(mPresShell, computedStyle);
1858   else
1859     innerFrame = NS_NewTableFrame(mPresShell, computedStyle);
1860 
1861   InitAndRestoreFrame(aState, content, newFrame, innerFrame);
1862   innerFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
1863 
1864   // Put the newly created frames into the right child list
1865   SetInitialSingleChild(newFrame, innerFrame);
1866 
1867   aState.AddChild(newFrame, aFrameList, content, aParentFrame);
1868 
1869   if (!mRootElementFrame) {
1870     // The frame we're constructing will be the root element frame.
1871     SetRootElementFrameAndConstructCanvasAnonContent(newFrame, aState,
1872                                                      aFrameList);
1873   }
1874 
1875   nsFrameList childList;
1876 
1877   // Process children
1878   nsFrameConstructorSaveState absoluteSaveState;
1879   const nsStyleDisplay* display = outerComputedStyle->StyleDisplay();
1880 
1881   // Mark the table frame as an absolute container if needed
1882   newFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
1883   if (display->IsAbsPosContainingBlock(newFrame)) {
1884     aState.PushAbsoluteContainingBlock(newFrame, newFrame, absoluteSaveState);
1885   }
1886   if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) {
1887     ConstructFramesFromItemList(
1888         aState, aItem.mChildItems, innerFrame,
1889         aItem.mFCData->mBits & FCDATA_IS_WRAPPER_ANON_BOX, childList);
1890   } else {
1891     ProcessChildren(aState, content, computedStyle, innerFrame, true, childList,
1892                     false);
1893   }
1894 
1895   nsFrameList captionList;
1896   PullOutCaptionFrames(childList, captionList);
1897 
1898   // Set the inner table frame's initial primary list
1899   innerFrame->SetInitialChildList(kPrincipalList, childList);
1900 
1901   // Set the table wrapper frame's secondary childlist lists
1902   if (captionList.NotEmpty()) {
1903     captionList.ApplySetParent(newFrame);
1904     newFrame->SetInitialChildList(nsIFrame::kCaptionList, captionList);
1905   }
1906 
1907   return newFrame;
1908 }
1909 
MakeTablePartAbsoluteContainingBlockIfNeeded(nsFrameConstructorState & aState,const nsStyleDisplay * aDisplay,nsFrameConstructorSaveState & aAbsSaveState,nsContainerFrame * aFrame)1910 static void MakeTablePartAbsoluteContainingBlockIfNeeded(
1911     nsFrameConstructorState& aState, const nsStyleDisplay* aDisplay,
1912     nsFrameConstructorSaveState& aAbsSaveState, nsContainerFrame* aFrame) {
1913   // If we're positioned, then we need to become an absolute containing block
1914   // for any absolutely positioned children and register for post-reflow fixup.
1915   //
1916   // Note that usually if a frame type can be an absolute containing block, we
1917   // always set NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN, whether it actually is or
1918   // not. However, in this case flag serves the additional purpose of indicating
1919   // that the frame was registered with its table frame. This allows us to avoid
1920   // the overhead of unregistering the frame in most cases.
1921   if (aDisplay->IsAbsPosContainingBlock(aFrame)) {
1922     aFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
1923     aState.PushAbsoluteContainingBlock(aFrame, aFrame, aAbsSaveState);
1924     nsTableFrame::RegisterPositionedTablePart(aFrame);
1925   }
1926 }
1927 
ConstructTableRowOrRowGroup(nsFrameConstructorState & aState,FrameConstructionItem & aItem,nsContainerFrame * aParentFrame,const nsStyleDisplay * aDisplay,nsFrameList & aFrameList)1928 nsIFrame* nsCSSFrameConstructor::ConstructTableRowOrRowGroup(
1929     nsFrameConstructorState& aState, FrameConstructionItem& aItem,
1930     nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay,
1931     nsFrameList& aFrameList) {
1932   MOZ_ASSERT(aDisplay->mDisplay == StyleDisplay::TableRow ||
1933                  aDisplay->mDisplay == StyleDisplay::TableRowGroup ||
1934                  aDisplay->mDisplay == StyleDisplay::TableFooterGroup ||
1935                  aDisplay->mDisplay == StyleDisplay::TableHeaderGroup,
1936              "Not a row or row group");
1937   MOZ_ASSERT(aItem.mComputedStyle->StyleDisplay() == aDisplay,
1938              "Display style doesn't match style");
1939   nsIContent* const content = aItem.mContent;
1940   ComputedStyle* const computedStyle = aItem.mComputedStyle;
1941 
1942   nsContainerFrame* newFrame;
1943   if (aDisplay->mDisplay == StyleDisplay::TableRow) {
1944     if (content->IsMathMLElement())
1945       newFrame = NS_NewMathMLmtrFrame(mPresShell, computedStyle);
1946     else
1947       newFrame = NS_NewTableRowFrame(mPresShell, computedStyle);
1948   } else {
1949     newFrame = NS_NewTableRowGroupFrame(mPresShell, computedStyle);
1950   }
1951 
1952   InitAndRestoreFrame(aState, content, aParentFrame, newFrame);
1953 
1954   nsFrameConstructorSaveState absoluteSaveState;
1955   MakeTablePartAbsoluteContainingBlockIfNeeded(aState, aDisplay,
1956                                                absoluteSaveState, newFrame);
1957 
1958   nsFrameList childList;
1959   if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) {
1960     ConstructFramesFromItemList(
1961         aState, aItem.mChildItems, newFrame,
1962         aItem.mFCData->mBits & FCDATA_IS_WRAPPER_ANON_BOX, childList);
1963   } else {
1964     ProcessChildren(aState, content, computedStyle, newFrame, true, childList,
1965                     false);
1966   }
1967 
1968   newFrame->SetInitialChildList(kPrincipalList, childList);
1969   aFrameList.AppendFrame(nullptr, newFrame);
1970   return newFrame;
1971 }
1972 
ConstructTableCol(nsFrameConstructorState & aState,FrameConstructionItem & aItem,nsContainerFrame * aParentFrame,const nsStyleDisplay * aStyleDisplay,nsFrameList & aFrameList)1973 nsIFrame* nsCSSFrameConstructor::ConstructTableCol(
1974     nsFrameConstructorState& aState, FrameConstructionItem& aItem,
1975     nsContainerFrame* aParentFrame, const nsStyleDisplay* aStyleDisplay,
1976     nsFrameList& aFrameList) {
1977   nsIContent* const content = aItem.mContent;
1978   ComputedStyle* const computedStyle = aItem.mComputedStyle;
1979 
1980   nsTableColFrame* colFrame = NS_NewTableColFrame(mPresShell, computedStyle);
1981   InitAndRestoreFrame(aState, content, aParentFrame, colFrame);
1982 
1983   NS_ASSERTION(colFrame->Style() == computedStyle, "Unexpected style");
1984 
1985   aFrameList.AppendFrame(nullptr, colFrame);
1986 
1987   // construct additional col frames if the col frame has a span > 1
1988   int32_t span = colFrame->GetSpan();
1989   for (int32_t spanX = 1; spanX < span; spanX++) {
1990     nsTableColFrame* newCol = NS_NewTableColFrame(mPresShell, computedStyle);
1991     InitAndRestoreFrame(aState, content, aParentFrame, newCol, false);
1992     aFrameList.LastChild()->SetNextContinuation(newCol);
1993     newCol->SetPrevContinuation(aFrameList.LastChild());
1994     aFrameList.AppendFrame(nullptr, newCol);
1995     newCol->SetColType(eColAnonymousCol);
1996   }
1997 
1998   return colFrame;
1999 }
2000 
ConstructTableCell(nsFrameConstructorState & aState,FrameConstructionItem & aItem,nsContainerFrame * aParentFrame,const nsStyleDisplay * aDisplay,nsFrameList & aFrameList)2001 nsIFrame* nsCSSFrameConstructor::ConstructTableCell(
2002     nsFrameConstructorState& aState, FrameConstructionItem& aItem,
2003     nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay,
2004     nsFrameList& aFrameList) {
2005   MOZ_ASSERT(aDisplay->mDisplay == StyleDisplay::TableCell, "Unexpected call");
2006 
2007   nsIContent* const content = aItem.mContent;
2008   ComputedStyle* const computedStyle = aItem.mComputedStyle;
2009   const bool isMathMLContent = content->IsMathMLElement();
2010 
2011   nsTableFrame* tableFrame =
2012       static_cast<nsTableRowFrame*>(aParentFrame)->GetTableFrame();
2013   nsContainerFrame* newFrame;
2014   // <mtable> is border separate in mathml.css and the MathML code doesn't
2015   // implement border collapse. For those users who style <mtable> with border
2016   // collapse, give them the default non-MathML table frames that understand
2017   // border collapse. This won't break us because MathML table frames are all
2018   // subclasses of the default table code, and so we can freely mix <mtable>
2019   // with <mtr> or <tr>, <mtd> or <td>. What will happen is just that non-MathML
2020   // frames won't understand MathML attributes and will therefore miss the
2021   // special handling that the MathML code does.
2022   if (isMathMLContent && !tableFrame->IsBorderCollapse()) {
2023     newFrame = NS_NewMathMLmtdFrame(mPresShell, computedStyle, tableFrame);
2024   } else {
2025     // Warning: If you change this and add a wrapper frame around table cell
2026     // frames, make sure Bug 368554 doesn't regress!
2027     // See IsInAutoWidthTableCellForQuirk() in nsImageFrame.cpp.
2028     newFrame = NS_NewTableCellFrame(mPresShell, computedStyle, tableFrame);
2029   }
2030 
2031   // Initialize the table cell frame
2032   InitAndRestoreFrame(aState, content, aParentFrame, newFrame);
2033   newFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
2034 
2035   // Resolve pseudo style and initialize the body cell frame
2036   RefPtr<ComputedStyle> innerPseudoStyle;
2037   innerPseudoStyle = mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
2038       PseudoStyleType::cellContent, computedStyle);
2039 
2040   // Create a block frame that will format the cell's content
2041   bool isBlock;
2042   nsContainerFrame* cellInnerFrame;
2043   if (isMathMLContent) {
2044     cellInnerFrame = NS_NewMathMLmtdInnerFrame(mPresShell, innerPseudoStyle);
2045     isBlock = false;
2046   } else {
2047     cellInnerFrame = NS_NewBlockFormattingContext(mPresShell, innerPseudoStyle);
2048     isBlock = true;
2049   }
2050 
2051   InitAndRestoreFrame(aState, content, newFrame, cellInnerFrame);
2052 
2053   nsFrameConstructorSaveState absoluteSaveState;
2054   MakeTablePartAbsoluteContainingBlockIfNeeded(aState, aDisplay,
2055                                                absoluteSaveState, newFrame);
2056 
2057   nsFrameList childList;
2058   if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) {
2059     // Need to push ourselves as a float containing block.
2060     // XXXbz it might be nice to work on getting the parent
2061     // FrameConstructionItem down into ProcessChildren and just making use of
2062     // the push there, but that's a bit of work.
2063     nsFrameConstructorSaveState floatSaveState;
2064     if (!isBlock) { /* MathML case */
2065       aState.PushFloatContainingBlock(nullptr, floatSaveState);
2066     } else {
2067       aState.PushFloatContainingBlock(cellInnerFrame, floatSaveState);
2068     }
2069 
2070     ConstructFramesFromItemList(
2071         aState, aItem.mChildItems, cellInnerFrame,
2072         aItem.mFCData->mBits & FCDATA_IS_WRAPPER_ANON_BOX, childList);
2073   } else {
2074     // Process the child content
2075     ProcessChildren(aState, content, computedStyle, cellInnerFrame, true,
2076                     childList, isBlock);
2077   }
2078 
2079   cellInnerFrame->SetInitialChildList(kPrincipalList, childList);
2080   SetInitialSingleChild(newFrame, cellInnerFrame);
2081   aFrameList.AppendFrame(nullptr, newFrame);
2082   return newFrame;
2083 }
2084 
NeedFrameFor(const nsFrameConstructorState & aState,nsContainerFrame * aParentFrame,nsIContent * aChildContent)2085 static inline bool NeedFrameFor(const nsFrameConstructorState& aState,
2086                                 nsContainerFrame* aParentFrame,
2087                                 nsIContent* aChildContent) {
2088   // XXX the GetContent() != aChildContent check is needed due to bug 135040.
2089   // Remove it once that's fixed.
2090   MOZ_ASSERT(
2091       !aChildContent->GetPrimaryFrame() || aState.mCreatingExtraFrames ||
2092           aChildContent->GetPrimaryFrame()->GetContent() != aChildContent,
2093       "Why did we get called?");
2094 
2095   // don't create a whitespace frame if aParentFrame doesn't want it.
2096   // always create frames for children in generated content. counter(),
2097   // quotes, and attr() content can easily change dynamically and we don't
2098   // want to be reconstructing frames. It's not even clear that these
2099   // should be considered ignorable just because they evaluate to
2100   // whitespace.
2101 
2102   // We could handle all this in CreateNeededPseudoContainers or some other
2103   // place after we build our frame construction items, but that would involve
2104   // creating frame construction items for whitespace kids that ignores
2105   // white-space, where we know we'll be dropping them all anyway, and involve
2106   // an extra walk down the frame construction item list.
2107   auto excludesIgnorableWhitespace = [](nsIFrame* aParentFrame) {
2108     return aParentFrame->IsFrameOfType(nsIFrame::eXULBox) ||
2109            aParentFrame->IsFrameOfType(nsIFrame::eMathML);
2110   };
2111   if (!aParentFrame || !excludesIgnorableWhitespace(aParentFrame) ||
2112       aParentFrame->IsGeneratedContentFrame() || !aChildContent->IsText()) {
2113     return true;
2114   }
2115 
2116   aChildContent->SetFlags(NS_CREATE_FRAME_IF_NON_WHITESPACE |
2117                           NS_REFRAME_IF_WHITESPACE);
2118   return !aChildContent->TextIsOnlyWhitespace();
2119 }
2120 
2121 /***********************************************
2122  * END TABLE SECTION
2123  ***********************************************/
2124 
SetRootElementFrameAndConstructCanvasAnonContent(nsContainerFrame * aRootElementFrame,nsFrameConstructorState & aState,nsFrameList & aFrameList)2125 void nsCSSFrameConstructor::SetRootElementFrameAndConstructCanvasAnonContent(
2126     nsContainerFrame* aRootElementFrame, nsFrameConstructorState& aState,
2127     nsFrameList& aFrameList) {
2128   MOZ_DIAGNOSTIC_ASSERT(!mRootElementFrame);
2129   mRootElementFrame = aRootElementFrame;
2130   if (mDocElementContainingBlock->IsCanvasFrame()) {
2131     // NOTE(emilio): This is in the reverse order compared to normal anonymous
2132     // children. We usually generate anonymous kids first, then non-anonymous,
2133     // but we generate the doc element frame the other way around. This is fine
2134     // either way, but generating anonymous children in a different order
2135     // requires changing nsCanvasFrame (and a whole lot of other potentially
2136     // unknown code) to look at the last child to find the root frame rather
2137     // than the first child.
2138     ConstructAnonymousContentForCanvas(aState, mDocElementContainingBlock,
2139                                        aRootElementFrame->GetContent(),
2140                                        aFrameList);
2141   }
2142 }
2143 
ConstructDocElementFrame(Element * aDocElement)2144 nsIFrame* nsCSSFrameConstructor::ConstructDocElementFrame(
2145     Element* aDocElement) {
2146   MOZ_ASSERT(GetRootFrame(),
2147              "No viewport?  Someone forgot to call ConstructRootFrame!");
2148   MOZ_ASSERT(!mDocElementContainingBlock,
2149              "Shouldn't have a doc element containing block here");
2150 
2151   // Resolve a new style for the viewport since it may be affected by a new root
2152   // element style (e.g. a propagated 'direction').
2153   //
2154   // @see ComputedStyle::ApplyStyleFixups
2155   {
2156     RefPtr<ComputedStyle> sc =
2157         mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
2158             PseudoStyleType::viewport, nullptr);
2159     GetRootFrame()->SetComputedStyleWithoutNotification(sc);
2160   }
2161 
2162   // Ensure the document element is styled at this point.
2163   if (!aDocElement->HasServoData()) {
2164     mPresShell->StyleSet()->StyleNewSubtree(aDocElement);
2165   }
2166 
2167   // Make sure to call UpdateViewportScrollStylesOverride before
2168   // SetUpDocElementContainingBlock, since it sets up our scrollbar state
2169   // properly.
2170   DebugOnly<nsIContent*> propagatedScrollFrom;
2171   if (nsPresContext* presContext = mPresShell->GetPresContext()) {
2172     propagatedScrollFrom = presContext->UpdateViewportScrollStylesOverride();
2173   }
2174 
2175   SetUpDocElementContainingBlock(aDocElement);
2176 
2177   // This has the side-effect of getting `mFrameTreeState` from our docshell.
2178   //
2179   // FIXME(emilio): There may be a more sensible time to do this.
2180   if (!mFrameTreeState) {
2181     mPresShell->CaptureHistoryState(getter_AddRefs(mFrameTreeState));
2182   }
2183 
2184   NS_ASSERTION(mDocElementContainingBlock, "Should have parent by now");
2185   nsFrameConstructorState state(
2186       mPresShell,
2187       GetAbsoluteContainingBlock(mDocElementContainingBlock, FIXED_POS),
2188       nullptr, nullptr, do_AddRef(mFrameTreeState));
2189 
2190   RefPtr<ComputedStyle> computedStyle =
2191       ServoStyleSet::ResolveServoStyle(*aDocElement);
2192 
2193   const nsStyleDisplay* display = computedStyle->StyleDisplay();
2194 
2195   // --------- IF SCROLLABLE WRAP IN SCROLLFRAME --------
2196 
2197   NS_ASSERTION(!display->IsScrollableOverflow() ||
2198                    state.mPresContext->IsPaginated() ||
2199                    propagatedScrollFrom == aDocElement,
2200                "Scrollbars should have been propagated to the viewport");
2201 
2202   if (MOZ_UNLIKELY(display->mDisplay == StyleDisplay::None)) {
2203     return nullptr;
2204   }
2205 
2206   if (mDocElementContainingBlock->IsCanvasFrame()) {
2207     // This implements "The Principal Writing Mode".
2208     // https://drafts.csswg.org/css-writing-modes-3/#principal-flow
2209     //
2210     // If there's a <body> element in an HTML document, its writing-mode,
2211     // direction, and text-orientation override the root element's used value.
2212     //
2213     // We need to copy <body>'s WritingMode to mDocElementContainingBlock before
2214     // construct mRootElementFrame so that anonymous internal frames such as
2215     // <html> with table style can copy their parent frame's mWritingMode in
2216     // nsFrame::Init().
2217     MOZ_ASSERT(!mRootElementFrame,
2218                "We need to copy <body>'s principal writing-mode before "
2219                "constructing mRootElementFrame.");
2220 
2221     const WritingMode docElementWM(computedStyle);
2222     Element* body = mDocument->GetBodyElement();
2223     if (body) {
2224       RefPtr<ComputedStyle> bodyStyle = ResolveComputedStyle(body);
2225       const WritingMode bodyWM(bodyStyle);
2226 
2227       if (bodyWM != docElementWM) {
2228         nsContentUtils::ReportToConsole(
2229             nsIScriptError::warningFlag, NS_LITERAL_CSTRING("Layout"),
2230             mDocument, nsContentUtils::eLAYOUT_PROPERTIES,
2231             "PrincipalWritingModePropagationWarning");
2232       }
2233 
2234       mDocElementContainingBlock->PropagateWritingModeToSelfAndAncestors(
2235           bodyWM);
2236     } else {
2237       mDocElementContainingBlock->PropagateWritingModeToSelfAndAncestors(
2238           docElementWM);
2239     }
2240   }
2241 
2242   nsFrameConstructorSaveState docElementContainingBlockAbsoluteSaveState;
2243   if (mHasRootAbsPosContainingBlock) {
2244     // Push the absolute containing block now so we can absolutely position
2245     // the root element
2246     mDocElementContainingBlock->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
2247     state.PushAbsoluteContainingBlock(
2248         mDocElementContainingBlock, mDocElementContainingBlock,
2249         docElementContainingBlockAbsoluteSaveState);
2250   }
2251 
2252   // The rules from CSS 2.1, section 9.2.4, have already been applied
2253   // by the style system, so we can assume that display->mDisplay is
2254   // either NONE, BLOCK, or TABLE.
2255 
2256   // contentFrame is the primary frame for the root element. frameList contains
2257   // the children of the initial containing block.
2258   //
2259   // The first of those frames is usually `contentFrame`, but it can be
2260   // different, in particular if the root frame is positioned, in which case
2261   // contentFrame is the out-of-flow frame and frameList.FirstChild() is the
2262   // placeholder.
2263   //
2264   // The rest of the frames in frameList are the anonymous content of the canvas
2265   // frame.
2266   nsContainerFrame* contentFrame;
2267   nsFrameList frameList;
2268   bool processChildren = false;
2269 
2270   nsFrameConstructorSaveState absoluteSaveState;
2271 
2272   // Check whether we need to build a XUL box or SVG root frame
2273 #ifdef MOZ_XUL
2274   if (aDocElement->IsXULElement()) {
2275     contentFrame = NS_NewDocElementBoxFrame(mPresShell, computedStyle);
2276     InitAndRestoreFrame(state, aDocElement, mDocElementContainingBlock,
2277                         contentFrame);
2278     frameList = {contentFrame, contentFrame};
2279     processChildren = true;
2280   } else
2281 #endif
2282       if (aDocElement->IsSVGElement()) {
2283     if (!aDocElement->IsSVGElement(nsGkAtoms::svg)) {
2284       return nullptr;
2285     }
2286     // We're going to call the right function ourselves, so no need to give a
2287     // function to this FrameConstructionData.
2288 
2289     // XXXbz on the other hand, if we converted this whole function to
2290     // FrameConstructionData/Item, then we'd need the right function
2291     // here... but would probably be able to get away with less code in this
2292     // function in general.
2293     static const FrameConstructionData rootSVGData = FCDATA_DECL(0, nullptr);
2294     AutoFrameConstructionItem item(this, &rootSVGData, aDocElement,
2295                                    do_AddRef(computedStyle), true);
2296 
2297     contentFrame = static_cast<nsContainerFrame*>(ConstructOuterSVG(
2298         state, item, mDocElementContainingBlock, display, frameList));
2299   } else if (display->mDisplay == StyleDisplay::Flex ||
2300              display->mDisplay == StyleDisplay::WebkitBox ||
2301              display->mDisplay == StyleDisplay::Grid ||
2302              (StaticPrefs::layout_css_emulate_moz_box_with_flex() &&
2303               display->mDisplay == StyleDisplay::MozBox)) {
2304     auto func = display->mDisplay == StyleDisplay::Grid
2305                     ? NS_NewGridContainerFrame
2306                     : NS_NewFlexContainerFrame;
2307     contentFrame = func(mPresShell, computedStyle);
2308     InitAndRestoreFrame(
2309         state, aDocElement,
2310         state.GetGeometricParent(*display, mDocElementContainingBlock),
2311         contentFrame);
2312     state.AddChild(contentFrame, frameList, aDocElement,
2313                    mDocElementContainingBlock);
2314     processChildren = true;
2315 
2316     contentFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
2317     if (display->IsAbsPosContainingBlock(contentFrame)) {
2318       state.PushAbsoluteContainingBlock(contentFrame, contentFrame,
2319                                         absoluteSaveState);
2320     }
2321   } else if (display->mDisplay == StyleDisplay::Table) {
2322     // We're going to call the right function ourselves, so no need to give a
2323     // function to this FrameConstructionData.
2324 
2325     // XXXbz on the other hand, if we converted this whole function to
2326     // FrameConstructionData/Item, then we'd need the right function
2327     // here... but would probably be able to get away with less code in this
2328     // function in general.
2329     static const FrameConstructionData rootTableData = FCDATA_DECL(0, nullptr);
2330     AutoFrameConstructionItem item(this, &rootTableData, aDocElement,
2331                                    do_AddRef(computedStyle), true);
2332 
2333     // if the document is a table then just populate it.
2334     contentFrame = static_cast<nsContainerFrame*>(ConstructTable(
2335         state, item, mDocElementContainingBlock, display, frameList));
2336   } else if (display->DisplayInside() == StyleDisplayInside::Ruby) {
2337     static const FrameConstructionData data =
2338         FULL_CTOR_FCDATA(0, &nsCSSFrameConstructor::ConstructBlockRubyFrame);
2339     AutoFrameConstructionItem item(this, &data, aDocElement,
2340                                    do_AddRef(computedStyle), true);
2341     contentFrame = static_cast<nsContainerFrame*>(ConstructBlockRubyFrame(
2342         state, item,
2343         state.GetGeometricParent(*display, mDocElementContainingBlock), display,
2344         frameList));
2345   } else {
2346     MOZ_ASSERT(display->mDisplay == StyleDisplay::Block ||
2347                    display->mDisplay == StyleDisplay::FlowRoot,
2348                "Unhandled display type for root element");
2349     contentFrame = NS_NewBlockFormattingContext(mPresShell, computedStyle);
2350     ConstructBlock(
2351         state, aDocElement,
2352         state.GetGeometricParent(*display, mDocElementContainingBlock),
2353         mDocElementContainingBlock, computedStyle, &contentFrame, frameList,
2354         display->IsAbsPosContainingBlock(contentFrame) ? contentFrame
2355                                                        : nullptr);
2356   }
2357 
2358   MOZ_ASSERT(frameList.FirstChild());
2359   MOZ_ASSERT(frameList.FirstChild()->GetContent() == aDocElement);
2360   MOZ_ASSERT(contentFrame);
2361 
2362   MOZ_ASSERT(
2363       processChildren ? !mRootElementFrame : mRootElementFrame == contentFrame,
2364       "unexpected mRootElementFrame");
2365   if (processChildren) {
2366     SetRootElementFrameAndConstructCanvasAnonContent(contentFrame, state,
2367                                                      frameList);
2368   }
2369 
2370   // Figure out which frame has the main style for the document element,
2371   // assigning it to mRootElementStyleFrame.
2372   // Backgrounds should be propagated from that frame to the viewport.
2373   contentFrame->GetParentComputedStyle(&mRootElementStyleFrame);
2374   bool isChild = mRootElementStyleFrame &&
2375                  mRootElementStyleFrame->GetParent() == contentFrame;
2376   if (!isChild) {
2377     mRootElementStyleFrame = mRootElementFrame;
2378   }
2379 
2380   if (processChildren) {
2381     // Still need to process the child content
2382     nsFrameList childList;
2383 
2384     NS_ASSERTION(!contentFrame->IsBlockFrameOrSubclass() &&
2385                      !contentFrame->IsFrameOfType(nsIFrame::eSVG),
2386                  "Only XUL frames should reach here");
2387     ProcessChildren(state, aDocElement, computedStyle, contentFrame, true,
2388                     childList, false);
2389 
2390     // Set the initial child lists
2391     contentFrame->SetInitialChildList(kPrincipalList, childList);
2392   }
2393 
2394   nsIFrame* newFrame = frameList.FirstChild();
2395   // set the primary frame
2396   aDocElement->SetPrimaryFrame(contentFrame);
2397   mDocElementContainingBlock->AppendFrames(kPrincipalList, frameList);
2398 
2399   MOZ_ASSERT(!state.mHavePendingPopupgroup,
2400              "Should have proccessed pending popup group by now");
2401 
2402   return newFrame;
2403 }
2404 
ConstructRootFrame()2405 nsIFrame* nsCSSFrameConstructor::ConstructRootFrame() {
2406   AUTO_PROFILER_LABEL("nsCSSFrameConstructor::ConstructRootFrame",
2407                       LAYOUT_FrameConstruction);
2408   AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
2409 
2410   ServoStyleSet* styleSet = mPresShell->StyleSet();
2411 
2412   // --------- BUILD VIEWPORT -----------
2413   RefPtr<ComputedStyle> viewportPseudoStyle =
2414       styleSet->ResolveInheritingAnonymousBoxStyle(PseudoStyleType::viewport,
2415                                                    nullptr);
2416   ViewportFrame* viewportFrame =
2417       NS_NewViewportFrame(mPresShell, viewportPseudoStyle);
2418 
2419   // XXXbz do we _have_ to pass a null content pointer to that frame?
2420   // Would it really kill us to pass in the root element or something?
2421   // What would that break?
2422   viewportFrame->Init(nullptr, nullptr, nullptr);
2423 
2424   viewportFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
2425 
2426   // Bind the viewport frame to the root view
2427   nsView* rootView = mPresShell->GetViewManager()->GetRootView();
2428   viewportFrame->SetView(rootView);
2429 
2430   viewportFrame->SyncFrameViewProperties(rootView);
2431   nsContainerFrame::SyncWindowProperties(mPresShell->GetPresContext(),
2432                                          viewportFrame, rootView, nullptr,
2433                                          nsContainerFrame::SET_ASYNC);
2434 
2435   // Make it an absolute container for fixed-pos elements
2436   viewportFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
2437   viewportFrame->MarkAsAbsoluteContainingBlock();
2438 
2439   return viewportFrame;
2440 }
2441 
SetUpDocElementContainingBlock(nsIContent * aDocElement)2442 void nsCSSFrameConstructor::SetUpDocElementContainingBlock(
2443     nsIContent* aDocElement) {
2444   MOZ_ASSERT(aDocElement, "No element?");
2445   MOZ_ASSERT(!aDocElement->GetParent(), "Not root content?");
2446   MOZ_ASSERT(aDocElement->GetUncomposedDoc(), "Not in a document?");
2447   MOZ_ASSERT(aDocElement->GetUncomposedDoc()->GetRootElement() == aDocElement,
2448              "Not the root of the document?");
2449 
2450   /*
2451     how the root frame hierarchy should look
2452 
2453   Galley presentation, non-XUL, with scrolling:
2454 
2455       ViewportFrame [fixed-cb]
2456         nsHTMLScrollFrame
2457           nsCanvasFrame [abs-cb]
2458             root element frame (nsBlockFrame, nsSVGOuterSVGFrame,
2459                                 nsTableWrapperFrame, nsPlaceholderFrame)
2460 
2461   Galley presentation, XUL
2462 
2463       ViewportFrame [fixed-cb]
2464         nsRootBoxFrame
2465           root element frame (nsDocElementBoxFrame)
2466 
2467   Print presentation, non-XUL
2468 
2469       ViewportFrame
2470         nsPageSequenceFrame
2471           nsPageFrame
2472             nsPageContentFrame [fixed-cb]
2473               nsCanvasFrame [abs-cb]
2474                 root element frame (nsBlockFrame, nsSVGOuterSVGFrame,
2475                                     nsTableWrapperFrame, nsPlaceholderFrame)
2476 
2477   Print-preview presentation, non-XUL
2478 
2479       ViewportFrame
2480         nsHTMLScrollFrame
2481           nsPageSequenceFrame
2482             nsPageFrame
2483               nsPageContentFrame [fixed-cb]
2484                 nsCanvasFrame [abs-cb]
2485                   root element frame (nsBlockFrame, nsSVGOuterSVGFrame,
2486                                       nsTableWrapperFrame, nsPlaceholderFrame)
2487 
2488   Print/print preview of XUL is not supported.
2489   [fixed-cb]: the default containing block for fixed-pos content
2490   [abs-cb]: the default containing block for abs-pos content
2491 
2492   Meaning of nsCSSFrameConstructor fields:
2493     mRootElementFrame is "root element frame".  This is the primary frame for
2494       the root element.
2495     mDocElementContainingBlock is the parent of mRootElementFrame
2496       (i.e. nsCanvasFrame or nsRootBoxFrame)
2497     mPageSequenceFrame is the nsPageSequenceFrame, or null if there isn't
2498       one
2499   */
2500 
2501   // --------- CREATE ROOT FRAME -------
2502 
2503   // Create the root frame. The document element's frame is a child of the
2504   // root frame.
2505   //
2506   // The root frame serves two purposes:
2507   // - reserves space for any margins needed for the document element's frame
2508   // - renders the document element's background. This ensures the background
2509   //   covers the entire canvas as specified by the CSS2 spec
2510 
2511   nsPresContext* presContext = mPresShell->GetPresContext();
2512   bool isPaginated = presContext->IsRootPaginatedDocument();
2513   nsContainerFrame* viewportFrame =
2514       static_cast<nsContainerFrame*>(GetRootFrame());
2515   ComputedStyle* viewportPseudoStyle = viewportFrame->Style();
2516 
2517   nsContainerFrame* rootFrame = nullptr;
2518   PseudoStyleType rootPseudo;
2519 
2520   if (!isPaginated) {
2521 #ifdef MOZ_XUL
2522     if (aDocElement->IsXULElement()) {
2523       // pass a temporary stylecontext, the correct one will be set later
2524       rootFrame = NS_NewRootBoxFrame(mPresShell, viewportPseudoStyle);
2525     } else
2526 #endif
2527     {
2528       // pass a temporary stylecontext, the correct one will be set later
2529       rootFrame = NS_NewCanvasFrame(mPresShell, viewportPseudoStyle);
2530       mHasRootAbsPosContainingBlock = true;
2531     }
2532 
2533     rootPseudo = PseudoStyleType::canvas;
2534     mDocElementContainingBlock = rootFrame;
2535   } else {
2536     // Create a page sequence frame
2537     rootFrame = mPageSequenceFrame =
2538         NS_NewPageSequenceFrame(mPresShell, viewportPseudoStyle);
2539     rootPseudo = PseudoStyleType::pageSequence;
2540     rootFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
2541   }
2542 
2543   // --------- IF SCROLLABLE WRAP IN SCROLLFRAME --------
2544 
2545   // If the device supports scrolling (e.g., in galley mode on the screen and
2546   // for print-preview, but not when printing), then create a scroll frame that
2547   // will act as the scrolling mechanism for the viewport.
2548   // XXX Do we even need a viewport when printing to a printer?
2549 
2550   bool isHTML = aDocElement->IsHTMLElement();
2551   bool isXUL = false;
2552 
2553   if (!isHTML) {
2554     isXUL = aDocElement->IsXULElement();
2555   }
2556 
2557   // Never create scrollbars for XUL documents or top level XHTML documents that
2558   // disable scrolling.
2559   bool isScrollable = true;
2560   if (isPaginated) {
2561     isScrollable = presContext->HasPaginatedScrolling();
2562   } else if (isXUL) {
2563     isScrollable = false;
2564   } else if (nsContentUtils::IsInChromeDocshell(aDocElement->OwnerDoc()) &&
2565              aDocElement->AsElement()->AttrValueIs(
2566                  kNameSpaceID_None, nsGkAtoms::scrolling, nsGkAtoms::_false,
2567                  eCaseMatters)) {
2568     isScrollable = false;
2569   }
2570 
2571   // We no longer need to do overflow propagation here. It's taken care of
2572   // when we construct frames for the element whose overflow might be
2573   // propagated
2574   NS_ASSERTION(!isScrollable || !isXUL,
2575                "XUL documents should never be scrollable - see above");
2576 
2577   nsContainerFrame* newFrame = rootFrame;
2578   RefPtr<ComputedStyle> rootPseudoStyle;
2579   // we must create a state because if the scrollbars are GFX it needs the
2580   // state to build the scrollbar frames.
2581   nsFrameConstructorState state(mPresShell, nullptr, nullptr, nullptr);
2582 
2583   // Start off with the viewport as parent; we'll adjust it as needed.
2584   nsContainerFrame* parentFrame = viewportFrame;
2585 
2586   ServoStyleSet* styleSet = mPresShell->StyleSet();
2587   // If paginated, make sure we don't put scrollbars in
2588   if (!isScrollable) {
2589     rootPseudoStyle = styleSet->ResolveInheritingAnonymousBoxStyle(
2590         rootPseudo, viewportPseudoStyle);
2591   } else {
2592     if (rootPseudo == PseudoStyleType::canvas) {
2593       rootPseudo = PseudoStyleType::scrolledCanvas;
2594     } else {
2595       NS_ASSERTION(rootPseudo == PseudoStyleType::pageSequence,
2596                    "Unknown root pseudo");
2597       rootPseudo = PseudoStyleType::scrolledPageSequence;
2598     }
2599 
2600     // Build the frame. We give it the content we are wrapping which is the
2601     // document element, the root frame, the parent view port frame, and we
2602     // should get back the new frame and the scrollable view if one was
2603     // created.
2604 
2605     // resolve a context for the scrollframe
2606     RefPtr<ComputedStyle> computedStyle =
2607         styleSet->ResolveInheritingAnonymousBoxStyle(
2608             PseudoStyleType::viewportScroll, viewportPseudoStyle);
2609 
2610     // Note that the viewport scrollframe is always built with
2611     // overflow:auto style. This forces the scroll frame to create
2612     // anonymous content for both scrollbars. This is necessary even
2613     // if the HTML or BODY elements are overriding the viewport
2614     // scroll style to 'hidden' --- dynamic style changes might put
2615     // scrollbars back on the viewport and we don't want to have to
2616     // reframe the viewport to create the scrollbar content.
2617     newFrame = nullptr;
2618     rootPseudoStyle =
2619         BeginBuildingScrollFrame(state, aDocElement, computedStyle,
2620                                  viewportFrame, rootPseudo, true, newFrame);
2621     parentFrame = newFrame;
2622   }
2623 
2624   rootFrame->SetComputedStyleWithoutNotification(rootPseudoStyle);
2625   rootFrame->Init(aDocElement, parentFrame, nullptr);
2626 
2627   if (isScrollable) {
2628     FinishBuildingScrollFrame(parentFrame, rootFrame);
2629   }
2630 
2631   if (isPaginated) {
2632     // Create the first page
2633     // Set the initial child lists
2634     nsContainerFrame* canvasFrame;
2635     nsContainerFrame* pageFrame =
2636         ConstructPageFrame(mPresShell, rootFrame, nullptr, canvasFrame);
2637     pageFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
2638     SetInitialSingleChild(rootFrame, pageFrame);
2639 
2640     // The eventual parent of the document element frame.
2641     // XXX should this be set for every new page (in ConstructPageFrame)?
2642     mDocElementContainingBlock = canvasFrame;
2643     mHasRootAbsPosContainingBlock = true;
2644   }
2645 
2646   if (viewportFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) {
2647     SetInitialSingleChild(viewportFrame, newFrame);
2648   } else {
2649     nsFrameList newFrameList(newFrame, newFrame);
2650     viewportFrame->AppendFrames(kPrincipalList, newFrameList);
2651   }
2652 }
2653 
ConstructAnonymousContentForCanvas(nsFrameConstructorState & aState,nsContainerFrame * aFrame,nsIContent * aDocElement,nsFrameList & aFrameList)2654 void nsCSSFrameConstructor::ConstructAnonymousContentForCanvas(
2655     nsFrameConstructorState& aState, nsContainerFrame* aFrame,
2656     nsIContent* aDocElement, nsFrameList& aFrameList) {
2657   NS_ASSERTION(aFrame->IsCanvasFrame(), "aFrame should be canvas frame!");
2658   MOZ_ASSERT(mRootElementFrame->GetContent() == aDocElement);
2659 
2660   AutoTArray<nsIAnonymousContentCreator::ContentInfo, 4> anonymousItems;
2661   GetAnonymousContent(aDocElement, aFrame, anonymousItems);
2662   if (anonymousItems.IsEmpty()) {
2663     return;
2664   }
2665 
2666   AutoFrameConstructionItemList itemsToConstruct(this);
2667   AddFCItemsForAnonymousContent(aState, aFrame, anonymousItems,
2668                                 itemsToConstruct);
2669 
2670   ConstructFramesFromItemList(aState, itemsToConstruct, aFrame,
2671                               /* aParentIsWrapperAnonBox = */ false,
2672                               aFrameList);
2673 }
2674 
ConstructPageFrame(PresShell * aPresShell,nsContainerFrame * aParentFrame,nsIFrame * aPrevPageFrame,nsContainerFrame * & aCanvasFrame)2675 nsContainerFrame* nsCSSFrameConstructor::ConstructPageFrame(
2676     PresShell* aPresShell, nsContainerFrame* aParentFrame,
2677     nsIFrame* aPrevPageFrame, nsContainerFrame*& aCanvasFrame) {
2678   ComputedStyle* parentComputedStyle = aParentFrame->Style();
2679   ServoStyleSet* styleSet = aPresShell->StyleSet();
2680 
2681   RefPtr<ComputedStyle> pagePseudoStyle;
2682   pagePseudoStyle = styleSet->ResolveInheritingAnonymousBoxStyle(
2683       PseudoStyleType::page, parentComputedStyle);
2684 
2685   nsContainerFrame* pageFrame = NS_NewPageFrame(aPresShell, pagePseudoStyle);
2686 
2687   // Initialize the page frame and force it to have a view. This makes printing
2688   // of the pages easier and faster.
2689   pageFrame->Init(nullptr, aParentFrame, aPrevPageFrame);
2690 
2691   RefPtr<ComputedStyle> pageContentPseudoStyle;
2692   pageContentPseudoStyle = styleSet->ResolveInheritingAnonymousBoxStyle(
2693       PseudoStyleType::pageContent, pagePseudoStyle);
2694 
2695   nsContainerFrame* pageContentFrame =
2696       NS_NewPageContentFrame(aPresShell, pageContentPseudoStyle);
2697 
2698   // Initialize the page content frame and force it to have a view. Also make it
2699   // the containing block for fixed elements which are repeated on every page.
2700   nsIFrame* prevPageContentFrame = nullptr;
2701   if (aPrevPageFrame) {
2702     prevPageContentFrame = aPrevPageFrame->PrincipalChildList().FirstChild();
2703     NS_ASSERTION(prevPageContentFrame, "missing page content frame");
2704   }
2705   pageContentFrame->Init(nullptr, pageFrame, prevPageContentFrame);
2706   if (!prevPageContentFrame) {
2707     pageContentFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
2708   }
2709   SetInitialSingleChild(pageFrame, pageContentFrame);
2710   // Make it an absolute container for fixed-pos elements
2711   pageContentFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
2712   pageContentFrame->MarkAsAbsoluteContainingBlock();
2713 
2714   RefPtr<ComputedStyle> canvasPseudoStyle;
2715   canvasPseudoStyle = styleSet->ResolveInheritingAnonymousBoxStyle(
2716       PseudoStyleType::canvas, pageContentPseudoStyle);
2717 
2718   aCanvasFrame = NS_NewCanvasFrame(aPresShell, canvasPseudoStyle);
2719 
2720   nsIFrame* prevCanvasFrame = nullptr;
2721   if (prevPageContentFrame) {
2722     prevCanvasFrame = prevPageContentFrame->PrincipalChildList().FirstChild();
2723     NS_ASSERTION(prevCanvasFrame, "missing canvas frame");
2724   }
2725   aCanvasFrame->Init(nullptr, pageContentFrame, prevCanvasFrame);
2726   SetInitialSingleChild(pageContentFrame, aCanvasFrame);
2727   return pageFrame;
2728 }
2729 
2730 /* static */
CreatePlaceholderFrameFor(PresShell * aPresShell,nsIContent * aContent,nsIFrame * aFrame,nsContainerFrame * aParentFrame,nsIFrame * aPrevInFlow,nsFrameState aTypeBit)2731 nsIFrame* nsCSSFrameConstructor::CreatePlaceholderFrameFor(
2732     PresShell* aPresShell, nsIContent* aContent, nsIFrame* aFrame,
2733     nsContainerFrame* aParentFrame, nsIFrame* aPrevInFlow,
2734     nsFrameState aTypeBit) {
2735   RefPtr<ComputedStyle> placeholderStyle =
2736       aPresShell->StyleSet()->ResolveStyleForPlaceholder();
2737 
2738   // The placeholder frame gets a pseudo style.
2739   nsPlaceholderFrame* placeholderFrame =
2740       NS_NewPlaceholderFrame(aPresShell, placeholderStyle, aTypeBit);
2741 
2742   placeholderFrame->Init(aContent, aParentFrame, aPrevInFlow);
2743 
2744   // Associate the placeholder/out-of-flow with each other.
2745   placeholderFrame->SetOutOfFlowFrame(aFrame);
2746   aFrame->SetProperty(nsIFrame::PlaceholderFrameProperty(), placeholderFrame);
2747 
2748   aFrame->AddStateBits(NS_FRAME_OUT_OF_FLOW);
2749 
2750   return placeholderFrame;
2751 }
2752 
2753 // Clears any lazy bits set in the range [aStartContent, aEndContent).  If
2754 // aEndContent is null, that means to clear bits in all siblings starting with
2755 // aStartContent.  aStartContent must not be null unless aEndContent is also
2756 // null.  We do this so that when new children are inserted under elements whose
2757 // frame is a leaf the new children don't cause us to try to construct frames
2758 // for the existing children again.
ClearLazyBits(nsIContent * aStartContent,nsIContent * aEndContent)2759 static inline void ClearLazyBits(nsIContent* aStartContent,
2760                                  nsIContent* aEndContent) {
2761   MOZ_ASSERT(aStartContent || !aEndContent,
2762              "Must have start child if we have an end child");
2763 
2764   for (nsIContent* cur = aStartContent; cur != aEndContent;
2765        cur = cur->GetNextSibling()) {
2766     cur->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
2767   }
2768 }
2769 
ConstructSelectFrame(nsFrameConstructorState & aState,FrameConstructionItem & aItem,nsContainerFrame * aParentFrame,const nsStyleDisplay * aStyleDisplay,nsFrameList & aFrameList)2770 nsIFrame* nsCSSFrameConstructor::ConstructSelectFrame(
2771     nsFrameConstructorState& aState, FrameConstructionItem& aItem,
2772     nsContainerFrame* aParentFrame, const nsStyleDisplay* aStyleDisplay,
2773     nsFrameList& aFrameList) {
2774   nsIContent* const content = aItem.mContent;
2775   ComputedStyle* const computedStyle = aItem.mComputedStyle;
2776 
2777   // Construct a frame-based listbox or combobox
2778   dom::HTMLSelectElement* sel = dom::HTMLSelectElement::FromNode(content);
2779   MOZ_ASSERT(sel);
2780   if (sel->IsCombobox()) {
2781     // Construct a frame-based combo box.
2782     // The frame-based combo box is built out of three parts. A display area, a
2783     // button and a dropdown list. The display area and button are created
2784     // through anonymous content. The drop-down list's frame is created
2785     // explicitly. The combobox frame shares its content with the drop-down
2786     // list.
2787     nsFrameState flags = NS_BLOCK_FLOAT_MGR;
2788     nsComboboxControlFrame* comboboxFrame =
2789         NS_NewComboboxControlFrame(mPresShell, computedStyle, flags);
2790 
2791     // Save the history state so we don't restore during construction
2792     // since the complete tree is required before we restore.
2793     nsILayoutHistoryState* historyState = aState.mFrameState;
2794     aState.mFrameState = nullptr;
2795     // Initialize the combobox frame
2796     InitAndRestoreFrame(aState, content,
2797                         aState.GetGeometricParent(*aStyleDisplay, aParentFrame),
2798                         comboboxFrame);
2799 
2800     comboboxFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
2801 
2802     aState.AddChild(comboboxFrame, aFrameList, content, aParentFrame);
2803 
2804     // Resolve pseudo element style for the dropdown list
2805     RefPtr<ComputedStyle> listStyle;
2806     listStyle = mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
2807         PseudoStyleType::dropDownList, computedStyle);
2808 
2809     // Create a listbox
2810     nsContainerFrame* listFrame = NS_NewListControlFrame(mPresShell, listStyle);
2811 
2812     // Notify the listbox that it is being used as a dropdown list.
2813     nsListControlFrame* listControlFrame = do_QueryFrame(listFrame);
2814     if (listControlFrame) {
2815       listControlFrame->SetComboboxFrame(comboboxFrame);
2816     }
2817     // Notify combobox that it should use the listbox as it's popup
2818     comboboxFrame->SetDropDown(listFrame);
2819 
2820     NS_ASSERTION(!listFrame->IsAbsPosContainingBlock(),
2821                  "Ended up with positioned dropdown list somehow.");
2822     NS_ASSERTION(!listFrame->IsFloating(),
2823                  "Ended up with floating dropdown list somehow.");
2824 
2825     // child frames of combobox frame
2826     nsFrameList childList;
2827 
2828     // Initialize the scroll frame positioned. Note that it is NOT
2829     // initialized as absolutely positioned.
2830     nsContainerFrame* scrolledFrame =
2831         NS_NewSelectsAreaFrame(mPresShell, computedStyle, flags);
2832 
2833     InitializeSelectFrame(aState, listFrame, scrolledFrame, content,
2834                           comboboxFrame, listStyle, true, childList);
2835 
2836     NS_ASSERTION(listFrame->GetView(), "ListFrame's view is nullptr");
2837 
2838     // Create display and button frames from the combobox's anonymous content.
2839     // The anonymous content is appended to existing anonymous content for this
2840     // element (the scrollbars).
2841     //
2842     // nsComboboxControlFrame needs special frame creation behavior for its
2843     // first piece of anonymous content, which means that we can't take the
2844     // normal ProcessChildren path.
2845     AutoTArray<nsIAnonymousContentCreator::ContentInfo, 2> newAnonymousItems;
2846     DebugOnly<nsresult> rv =
2847         GetAnonymousContent(content, comboboxFrame, newAnonymousItems);
2848     MOZ_ASSERT(NS_SUCCEEDED(rv));
2849     MOZ_ASSERT(newAnonymousItems.Length() == 2);
2850 
2851     // Manually create a frame for the special NAC.
2852     MOZ_ASSERT(newAnonymousItems[0].mContent ==
2853                comboboxFrame->GetDisplayNode());
2854     newAnonymousItems.RemoveElementAt(0);
2855     nsIFrame* customFrame = comboboxFrame->CreateFrameForDisplayNode();
2856     MOZ_ASSERT(customFrame);
2857     childList.AppendFrame(nullptr, customFrame);
2858 
2859     // The other piece of NAC can take the normal path.
2860     AutoFrameConstructionItemList fcItems(this);
2861     AddFCItemsForAnonymousContent(aState, comboboxFrame, newAnonymousItems,
2862                                   fcItems);
2863     ConstructFramesFromItemList(aState, fcItems, comboboxFrame,
2864                                 /* aParentIsWrapperAnonBox = */ false,
2865                                 childList);
2866 
2867     comboboxFrame->SetInitialChildList(kPrincipalList, childList);
2868 
2869     // Initialize the additional popup child list which contains the
2870     // dropdown list frame.
2871     nsFrameList popupList;
2872     popupList.AppendFrame(nullptr, listFrame);
2873     comboboxFrame->SetInitialChildList(nsIFrame::kSelectPopupList, popupList);
2874 
2875     aState.mFrameState = historyState;
2876     if (aState.mFrameState) {
2877       // Restore frame state for the entire subtree of |comboboxFrame|.
2878       RestoreFrameState(comboboxFrame, aState.mFrameState);
2879     }
2880     return comboboxFrame;
2881   }
2882 
2883   // Listbox, not combobox
2884   nsContainerFrame* listFrame =
2885       NS_NewListControlFrame(mPresShell, computedStyle);
2886 
2887   nsContainerFrame* scrolledFrame =
2888       NS_NewSelectsAreaFrame(mPresShell, computedStyle, NS_BLOCK_FLOAT_MGR);
2889 
2890   // ******* this code stolen from Initialze ScrollFrame ********
2891   // please adjust this code to use BuildScrollFrame.
2892 
2893   InitializeSelectFrame(aState, listFrame, scrolledFrame, content, aParentFrame,
2894                         computedStyle, false, aFrameList);
2895 
2896   return listFrame;
2897 }
2898 
2899 /**
2900  * Used to be InitializeScrollFrame but now it's only used for the select tag
2901  * But the select tag should really be fixed to use GFX scrollbars that can
2902  * be create with BuildScrollFrame.
2903  */
InitializeSelectFrame(nsFrameConstructorState & aState,nsContainerFrame * scrollFrame,nsContainerFrame * scrolledFrame,nsIContent * aContent,nsContainerFrame * aParentFrame,ComputedStyle * aComputedStyle,bool aBuildCombobox,nsFrameList & aFrameList)2904 void nsCSSFrameConstructor::InitializeSelectFrame(
2905     nsFrameConstructorState& aState, nsContainerFrame* scrollFrame,
2906     nsContainerFrame* scrolledFrame, nsIContent* aContent,
2907     nsContainerFrame* aParentFrame, ComputedStyle* aComputedStyle,
2908     bool aBuildCombobox, nsFrameList& aFrameList) {
2909   // Initialize it
2910   nsContainerFrame* geometricParent =
2911       aState.GetGeometricParent(*aComputedStyle->StyleDisplay(), aParentFrame);
2912 
2913   // We don't call InitAndRestoreFrame for scrollFrame because we can only
2914   // restore the frame state after its parts have been created (in particular,
2915   // the scrollable view). So we have to split Init and Restore.
2916 
2917   scrollFrame->Init(aContent, geometricParent, nullptr);
2918 
2919   if (!aBuildCombobox) {
2920     aState.AddChild(scrollFrame, aFrameList, aContent, aParentFrame);
2921   }
2922 
2923   BuildScrollFrame(aState, aContent, aComputedStyle, scrolledFrame,
2924                    geometricParent, scrollFrame);
2925 
2926   if (aState.mFrameState) {
2927     // Restore frame state for the scroll frame
2928     RestoreFrameStateFor(scrollFrame, aState.mFrameState);
2929   }
2930 
2931   // Process children
2932   nsFrameList childList;
2933 
2934   ProcessChildren(aState, aContent, aComputedStyle, scrolledFrame, false,
2935                   childList, false);
2936 
2937   // Set the scrolled frame's initial child lists
2938   scrolledFrame->SetInitialChildList(kPrincipalList, childList);
2939 }
2940 
ConstructFieldSetFrame(nsFrameConstructorState & aState,FrameConstructionItem & aItem,nsContainerFrame * aParentFrame,const nsStyleDisplay * aStyleDisplay,nsFrameList & aFrameList)2941 nsIFrame* nsCSSFrameConstructor::ConstructFieldSetFrame(
2942     nsFrameConstructorState& aState, FrameConstructionItem& aItem,
2943     nsContainerFrame* aParentFrame, const nsStyleDisplay* aStyleDisplay,
2944     nsFrameList& aFrameList) {
2945   nsIContent* const content = aItem.mContent;
2946   ComputedStyle* const computedStyle = aItem.mComputedStyle;
2947 
2948   nsContainerFrame* fieldsetFrame =
2949       NS_NewFieldSetFrame(mPresShell, computedStyle);
2950 
2951   // Initialize it
2952   InitAndRestoreFrame(aState, content,
2953                       aState.GetGeometricParent(*aStyleDisplay, aParentFrame),
2954                       fieldsetFrame);
2955 
2956   fieldsetFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
2957 
2958   // Resolve style and initialize the frame
2959   RefPtr<ComputedStyle> fieldsetContentStyle;
2960   fieldsetContentStyle =
2961       mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
2962           PseudoStyleType::fieldsetContent, computedStyle);
2963 
2964   const nsStyleDisplay* fieldsetContentDisplay =
2965       fieldsetContentStyle->StyleDisplay();
2966   bool isScrollable = fieldsetContentDisplay->IsScrollableOverflow();
2967   nsContainerFrame* scrollFrame = nullptr;
2968   if (isScrollable) {
2969     fieldsetContentStyle = BeginBuildingScrollFrame(
2970         aState, content, fieldsetContentStyle, fieldsetFrame,
2971         PseudoStyleType::scrolledContent, false, scrollFrame);
2972   }
2973 
2974   nsContainerFrame* absPosContainer = nullptr;
2975   if (fieldsetFrame->IsAbsPosContainingBlock()) {
2976     absPosContainer = fieldsetFrame;
2977   }
2978 
2979   // Create the inner ::-moz-fieldset-content frame.
2980   nsContainerFrame* contentFrameTop;
2981   nsContainerFrame* contentFrame;
2982   auto parent = scrollFrame ? scrollFrame : fieldsetFrame;
2983   MOZ_ASSERT(fieldsetContentDisplay->DisplayOutside() ==
2984              StyleDisplayOutside::Block);
2985   switch (fieldsetContentDisplay->DisplayInside()) {
2986     case StyleDisplayInside::Flex:
2987       contentFrame = NS_NewFlexContainerFrame(mPresShell, fieldsetContentStyle);
2988       InitAndRestoreFrame(aState, content, parent, contentFrame);
2989       contentFrameTop = contentFrame;
2990       break;
2991     case StyleDisplayInside::Grid:
2992       contentFrame = NS_NewGridContainerFrame(mPresShell, fieldsetContentStyle);
2993       InitAndRestoreFrame(aState, content, parent, contentFrame);
2994       contentFrameTop = contentFrame;
2995       break;
2996     default: {
2997       MOZ_ASSERT(fieldsetContentDisplay->mDisplay == StyleDisplay::Block,
2998                  "bug in StyleAdjuster::adjust_for_fieldset_content?");
2999 
3000       contentFrame =
3001           NS_NewBlockFormattingContext(mPresShell, fieldsetContentStyle);
3002       if (fieldsetContentStyle->StyleColumn()->IsColumnContainerStyle()) {
3003         contentFrameTop = BeginBuildingColumns(
3004             aState, content, parent, contentFrame, fieldsetContentStyle);
3005         if (absPosContainer) {
3006           absPosContainer = contentFrameTop;
3007         }
3008       } else {
3009         // No need to create column container. Initialize content frame.
3010         InitAndRestoreFrame(aState, content, parent, contentFrame);
3011         contentFrameTop = contentFrame;
3012       }
3013 
3014       break;
3015     }
3016   }
3017 
3018   aState.AddChild(fieldsetFrame, aFrameList, content, aParentFrame);
3019 
3020   // Process children
3021   nsFrameConstructorSaveState absoluteSaveState;
3022   nsFrameList childList;
3023 
3024   contentFrameTop->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
3025   if (absPosContainer) {
3026     aState.PushAbsoluteContainingBlock(contentFrameTop, absPosContainer,
3027                                        absoluteSaveState);
3028   }
3029 
3030   ProcessChildren(aState, content, computedStyle, contentFrame, true, childList,
3031                   true);
3032 
3033   nsFrameList fieldsetKids;
3034   fieldsetKids.AppendFrame(nullptr,
3035                            scrollFrame ? scrollFrame : contentFrameTop);
3036 
3037   for (nsFrameList::Enumerator e(childList); !e.AtEnd(); e.Next()) {
3038     nsIFrame* child = e.get();
3039     nsContainerFrame* cif = child->GetContentInsertionFrame();
3040     if (cif && cif->IsLegendFrame()) {
3041       // We want the legend to be the first frame in the fieldset child list.
3042       // That way the EventStateManager will do the right thing when tabbing
3043       // from a selection point within the legend (bug 236071), which is
3044       // used for implementing legend access keys (bug 81481).
3045       // GetAdjustedParentFrame() below depends on this frame order.
3046       childList.RemoveFrame(child);
3047       // Make sure to reparent the legend so it has the fieldset as the parent.
3048       fieldsetKids.InsertFrame(fieldsetFrame, nullptr, child);
3049       // Legend is no longer in the multicol container. Remove the bit.
3050       child->RemoveStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR);
3051       if (scrollFrame) {
3052         StickyScrollContainer::NotifyReparentedFrameAcrossScrollFrameBoundary(
3053             child, contentFrame);
3054       }
3055       break;
3056     }
3057   }
3058 
3059   if (!MayNeedToCreateColumnSpanSiblings(contentFrame, childList)) {
3060     // Set the inner frame's initial child lists.
3061     contentFrame->SetInitialChildList(kPrincipalList, childList);
3062   } else {
3063     // Extract any initial non-column-span kids, and put them in inner frame's
3064     // child list.
3065     nsFrameList initialNonColumnSpanKids =
3066         childList.Split([](nsIFrame* f) { return f->IsColumnSpan(); });
3067     contentFrame->SetInitialChildList(kPrincipalList, initialNonColumnSpanKids);
3068 
3069     if (childList.NotEmpty()) {
3070       nsFrameList columnSpanSiblings = CreateColumnSpanSiblings(
3071           aState, contentFrame, childList,
3072           // Column content should never be a absolute/fixed positioned
3073           // containing block. Pass nullptr as aPositionedFrame.
3074           nullptr);
3075       FinishBuildingColumns(aState, contentFrameTop, contentFrame,
3076                             columnSpanSiblings);
3077     }
3078   }
3079 
3080   if (isScrollable) {
3081     FinishBuildingScrollFrame(scrollFrame, contentFrameTop);
3082   }
3083 
3084   // Set the outer frame's initial child list
3085   fieldsetFrame->SetInitialChildList(kPrincipalList, fieldsetKids);
3086 
3087   // Our new frame returned is the outer frame, which is the fieldset frame.
3088   return fieldsetFrame;
3089 }
3090 
ConstructDetailsFrame(nsFrameConstructorState & aState,FrameConstructionItem & aItem,nsContainerFrame * aParentFrame,const nsStyleDisplay * aStyleDisplay,nsFrameList & aFrameList)3091 nsIFrame* nsCSSFrameConstructor::ConstructDetailsFrame(
3092     nsFrameConstructorState& aState, FrameConstructionItem& aItem,
3093     nsContainerFrame* aParentFrame, const nsStyleDisplay* aStyleDisplay,
3094     nsFrameList& aFrameList) {
3095   if (!aStyleDisplay->IsScrollableOverflow()) {
3096     return ConstructNonScrollableBlockWithConstructor(
3097         aState, aItem, aParentFrame, aStyleDisplay, aFrameList,
3098         NS_NewDetailsFrame);
3099   }
3100 
3101   // Build a scroll frame to wrap details frame if necessary.
3102   return ConstructScrollableBlockWithConstructor(aState, aItem, aParentFrame,
3103                                                  aStyleDisplay, aFrameList,
3104                                                  NS_NewDetailsFrame);
3105 }
3106 
ConstructBlockRubyFrame(nsFrameConstructorState & aState,FrameConstructionItem & aItem,nsContainerFrame * aParentFrame,const nsStyleDisplay * aStyleDisplay,nsFrameList & aFrameList)3107 nsIFrame* nsCSSFrameConstructor::ConstructBlockRubyFrame(
3108     nsFrameConstructorState& aState, FrameConstructionItem& aItem,
3109     nsContainerFrame* aParentFrame, const nsStyleDisplay* aStyleDisplay,
3110     nsFrameList& aFrameList) {
3111   nsIContent* const content = aItem.mContent;
3112   ComputedStyle* const computedStyle = aItem.mComputedStyle;
3113 
3114   nsBlockFrame* blockFrame = NS_NewBlockFrame(mPresShell, computedStyle);
3115   nsContainerFrame* newFrame = blockFrame;
3116   nsContainerFrame* geometricParent =
3117       aState.GetGeometricParent(*aStyleDisplay, aParentFrame);
3118   if ((aItem.mFCData->mBits & FCDATA_MAY_NEED_SCROLLFRAME) &&
3119       aStyleDisplay->IsScrollableOverflow()) {
3120     nsContainerFrame* scrollframe = nullptr;
3121     BuildScrollFrame(aState, content, computedStyle, blockFrame,
3122                      geometricParent, scrollframe);
3123     newFrame = scrollframe;
3124   } else {
3125     InitAndRestoreFrame(aState, content, geometricParent, blockFrame);
3126   }
3127 
3128   RefPtr<ComputedStyle> rubyStyle =
3129       mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
3130           PseudoStyleType::blockRubyContent, computedStyle);
3131   nsContainerFrame* rubyFrame = NS_NewRubyFrame(mPresShell, rubyStyle);
3132   InitAndRestoreFrame(aState, content, blockFrame, rubyFrame);
3133   SetInitialSingleChild(blockFrame, rubyFrame);
3134   blockFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
3135 
3136   aState.AddChild(newFrame, aFrameList, content, aParentFrame);
3137 
3138   if (!mRootElementFrame) {
3139     // The frame we're constructing will be the root element frame.
3140     SetRootElementFrameAndConstructCanvasAnonContent(newFrame, aState,
3141                                                      aFrameList);
3142   }
3143 
3144   nsFrameConstructorSaveState absoluteSaveState;
3145   blockFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
3146   if (aStyleDisplay->IsAbsPosContainingBlock(newFrame)) {
3147     aState.PushAbsoluteContainingBlock(blockFrame, blockFrame,
3148                                        absoluteSaveState);
3149   }
3150   nsFrameConstructorSaveState floatSaveState;
3151   if (blockFrame->IsFloatContainingBlock()) {
3152     aState.PushFloatContainingBlock(blockFrame, floatSaveState);
3153   }
3154 
3155   nsFrameList childList;
3156   ProcessChildren(aState, content, rubyStyle, rubyFrame, true, childList, false,
3157                   nullptr);
3158   rubyFrame->SetInitialChildList(kPrincipalList, childList);
3159 
3160   return newFrame;
3161 }
3162 
FindAncestorWithGeneratedContentPseudo(nsIFrame * aFrame)3163 static nsIFrame* FindAncestorWithGeneratedContentPseudo(nsIFrame* aFrame) {
3164   for (nsIFrame* f = aFrame->GetParent(); f; f = f->GetParent()) {
3165     NS_ASSERTION(f->IsGeneratedContentFrame(),
3166                  "should not have exited generated content");
3167     auto pseudo = f->Style()->GetPseudoType();
3168     if (pseudo == PseudoStyleType::before || pseudo == PseudoStyleType::after ||
3169         pseudo == PseudoStyleType::marker)
3170       return f;
3171   }
3172   return nullptr;
3173 }
3174 
3175 /* static */
3176 const nsCSSFrameConstructor::FrameConstructionData*
FindTextData(const Text & aTextContent,nsIFrame * aParentFrame)3177 nsCSSFrameConstructor::FindTextData(const Text& aTextContent,
3178                                     nsIFrame* aParentFrame) {
3179   if (aParentFrame && IsFrameForSVG(aParentFrame)) {
3180     nsIFrame* ancestorFrame =
3181         nsSVGUtils::GetFirstNonAAncestorFrame(aParentFrame);
3182     if (!ancestorFrame || !nsSVGUtils::IsInSVGTextSubtree(ancestorFrame)) {
3183       return nullptr;
3184     }
3185 
3186     // FIXME(bug 1588477) Don't render stuff in display: contents / Shadow DOM
3187     // subtrees, because TextCorrespondenceRecorder in the SVG text code doesn't
3188     // really know how to deal with it. This kinda sucks. :(
3189     if (aParentFrame->GetContent() != aTextContent.GetParent()) {
3190       return nullptr;
3191     }
3192 
3193     static const FrameConstructionData sSVGTextData = FCDATA_DECL(
3194         FCDATA_IS_LINE_PARTICIPANT | FCDATA_IS_SVG_TEXT, NS_NewTextFrame);
3195     return &sSVGTextData;
3196   }
3197 
3198   static const FrameConstructionData sTextData =
3199       FCDATA_DECL(FCDATA_IS_LINE_PARTICIPANT, NS_NewTextFrame);
3200   return &sTextData;
3201 }
3202 
ConstructTextFrame(const FrameConstructionData * aData,nsFrameConstructorState & aState,nsIContent * aContent,nsContainerFrame * aParentFrame,ComputedStyle * aComputedStyle,nsFrameList & aFrameList)3203 void nsCSSFrameConstructor::ConstructTextFrame(
3204     const FrameConstructionData* aData, nsFrameConstructorState& aState,
3205     nsIContent* aContent, nsContainerFrame* aParentFrame,
3206     ComputedStyle* aComputedStyle, nsFrameList& aFrameList) {
3207   MOZ_ASSERT(aData, "Must have frame construction data");
3208 
3209   nsIFrame* newFrame =
3210       (*aData->mFunc.mCreationFunc)(mPresShell, aComputedStyle);
3211 
3212   InitAndRestoreFrame(aState, aContent, aParentFrame, newFrame);
3213 
3214   // We never need to create a view for a text frame.
3215 
3216   if (newFrame->IsGeneratedContentFrame()) {
3217     UniquePtr<nsGenConInitializer> initializer(
3218         static_cast<nsGenConInitializer*>(
3219             aContent->TakeProperty(nsGkAtoms::genConInitializerProperty)));
3220     if (initializer) {
3221       if (initializer->mNode.release()->InitTextFrame(
3222               initializer->mList,
3223               FindAncestorWithGeneratedContentPseudo(newFrame), newFrame)) {
3224         (this->*(initializer->mDirtyAll))();
3225       }
3226     }
3227   }
3228 
3229   // Add the newly constructed frame to the flow
3230   aFrameList.AppendFrame(nullptr, newFrame);
3231 
3232   if (!aState.mCreatingExtraFrames) aContent->SetPrimaryFrame(newFrame);
3233 }
3234 
3235 /* static */
3236 const nsCSSFrameConstructor::FrameConstructionData*
FindDataByInt(int32_t aInt,const Element & aElement,ComputedStyle & aComputedStyle,const FrameConstructionDataByInt * aDataPtr,uint32_t aDataLength)3237 nsCSSFrameConstructor::FindDataByInt(int32_t aInt, const Element& aElement,
3238                                      ComputedStyle& aComputedStyle,
3239                                      const FrameConstructionDataByInt* aDataPtr,
3240                                      uint32_t aDataLength) {
3241   for (const FrameConstructionDataByInt *curData = aDataPtr,
3242                                         *endData = aDataPtr + aDataLength;
3243        curData != endData; ++curData) {
3244     if (curData->mInt == aInt) {
3245       const FrameConstructionData* data = &curData->mData;
3246       if (data->mBits & FCDATA_FUNC_IS_DATA_GETTER) {
3247         return data->mFunc.mDataGetter(aElement, aComputedStyle);
3248       }
3249 
3250       return data;
3251     }
3252   }
3253 
3254   return nullptr;
3255 }
3256 
3257 /* static */
3258 const nsCSSFrameConstructor::FrameConstructionData*
FindDataByTag(const Element & aElement,ComputedStyle & aStyle,const FrameConstructionDataByTag * aDataPtr,uint32_t aDataLength)3259 nsCSSFrameConstructor::FindDataByTag(const Element& aElement,
3260                                      ComputedStyle& aStyle,
3261                                      const FrameConstructionDataByTag* aDataPtr,
3262                                      uint32_t aDataLength) {
3263   const nsAtom* tag = aElement.NodeInfo()->NameAtom();
3264   for (const FrameConstructionDataByTag *curData = aDataPtr,
3265                                         *endData = aDataPtr + aDataLength;
3266        curData != endData; ++curData) {
3267     if (curData->mTag == tag) {
3268       const FrameConstructionData* data = &curData->mData;
3269       if (data->mBits & FCDATA_FUNC_IS_DATA_GETTER) {
3270         return data->mFunc.mDataGetter(aElement, aStyle);
3271       }
3272 
3273       return data;
3274     }
3275   }
3276 
3277   return nullptr;
3278 }
3279 
3280 #define SUPPRESS_FCDATA() FCDATA_DECL(FCDATA_SUPPRESS_FRAME, nullptr)
3281 #define SIMPLE_INT_CREATE(_int, _func) \
3282   { _int, SIMPLE_FCDATA(_func) }
3283 #define SIMPLE_INT_CHAIN(_int, _func) \
3284   { _int, FCDATA_DECL(FCDATA_FUNC_IS_DATA_GETTER, _func) }
3285 #define COMPLEX_INT_CREATE(_int, _func) \
3286   { _int, FULL_CTOR_FCDATA(0, _func) }
3287 
3288 #define SIMPLE_TAG_CREATE(_tag, _func) \
3289   { nsGkAtoms::_tag, SIMPLE_FCDATA(_func) }
3290 #define SIMPLE_TAG_CHAIN(_tag, _func) \
3291   { nsGkAtoms::_tag, FCDATA_DECL(FCDATA_FUNC_IS_DATA_GETTER, _func) }
3292 #define COMPLEX_TAG_CREATE(_tag, _func) \
3293   { nsGkAtoms::_tag, FULL_CTOR_FCDATA(0, _func) }
3294 
IsFrameForFieldSet(nsIFrame * aFrame)3295 static bool IsFrameForFieldSet(nsIFrame* aFrame) {
3296   auto pseudo = aFrame->Style()->GetPseudoType();
3297   if (pseudo == PseudoStyleType::fieldsetContent ||
3298       pseudo == PseudoStyleType::scrolledContent ||
3299       pseudo == PseudoStyleType::columnSet ||
3300       pseudo == PseudoStyleType::columnContent) {
3301     return IsFrameForFieldSet(aFrame->GetParent());
3302   }
3303   return aFrame->IsFieldSetFrame();
3304 }
3305 
3306 /* static */
3307 const nsCSSFrameConstructor::FrameConstructionData*
FindHTMLData(const Element & aElement,nsIFrame * aParentFrame,ComputedStyle & aStyle)3308 nsCSSFrameConstructor::FindHTMLData(const Element& aElement,
3309                                     nsIFrame* aParentFrame,
3310                                     ComputedStyle& aStyle) {
3311   MOZ_ASSERT(aElement.IsHTMLElement());
3312 
3313   nsAtom* tag = aElement.NodeInfo()->NameAtom();
3314   NS_ASSERTION(!aParentFrame ||
3315                    aParentFrame->Style()->GetPseudoType() !=
3316                        PseudoStyleType::fieldsetContent ||
3317                    aParentFrame->GetParent()->IsFieldSetFrame(),
3318                "Unexpected parent for fieldset content anon box");
3319   if (tag == nsGkAtoms::legend &&
3320       (!aParentFrame || !IsFrameForFieldSet(aParentFrame) ||
3321        aStyle.StyleDisplay()->IsFloatingStyle() ||
3322        aStyle.StyleDisplay()->IsAbsolutelyPositionedStyle())) {
3323     // <legend> is only special inside fieldset, we only check the frame tree
3324     // parent because the content tree parent may not be a <fieldset> due to
3325     // display:contents, or Shadow DOM. For floated or absolutely positioned
3326     // legends we want to construct by display type and not do special legend
3327     // stuff.
3328     return nullptr;
3329   }
3330 
3331   static const FrameConstructionDataByTag sHTMLData[] = {
3332       SIMPLE_TAG_CHAIN(img, nsCSSFrameConstructor::FindImgData),
3333       SIMPLE_TAG_CHAIN(mozgeneratedcontentimage,
3334                        nsCSSFrameConstructor::FindGeneratedImageData),
3335       {nsGkAtoms::br,
3336        FCDATA_DECL(FCDATA_IS_LINE_PARTICIPANT | FCDATA_IS_LINE_BREAK,
3337                    NS_NewBRFrame)},
3338       SIMPLE_TAG_CREATE(wbr, NS_NewWBRFrame),
3339       SIMPLE_TAG_CHAIN(input, nsCSSFrameConstructor::FindInputData),
3340       SIMPLE_TAG_CREATE(textarea, NS_NewTextControlFrame),
3341       COMPLEX_TAG_CREATE(select, &nsCSSFrameConstructor::ConstructSelectFrame),
3342       SIMPLE_TAG_CHAIN(object, nsCSSFrameConstructor::FindObjectData),
3343       SIMPLE_TAG_CHAIN(embed, nsCSSFrameConstructor::FindObjectData),
3344       COMPLEX_TAG_CREATE(fieldset,
3345                          &nsCSSFrameConstructor::ConstructFieldSetFrame),
3346       {nsGkAtoms::legend,
3347        FCDATA_DECL(FCDATA_ALLOW_BLOCK_STYLES | FCDATA_MAY_NEED_SCROLLFRAME,
3348                    NS_NewLegendFrame)},
3349       SIMPLE_TAG_CREATE(frameset, NS_NewHTMLFramesetFrame),
3350       SIMPLE_TAG_CREATE(iframe, NS_NewSubDocumentFrame),
3351       {nsGkAtoms::button,
3352        FCDATA_WITH_WRAPPING_BLOCK(
3353            FCDATA_ALLOW_BLOCK_STYLES | FCDATA_ALLOW_GRID_FLEX_COLUMN,
3354            NS_NewHTMLButtonControlFrame, PseudoStyleType::buttonContent)},
3355       SIMPLE_TAG_CHAIN(canvas, nsCSSFrameConstructor::FindCanvasData),
3356       SIMPLE_TAG_CREATE(video, NS_NewHTMLVideoFrame),
3357       SIMPLE_TAG_CREATE(audio, NS_NewHTMLVideoFrame),
3358       SIMPLE_TAG_CREATE(progress, NS_NewProgressFrame),
3359       SIMPLE_TAG_CREATE(meter, NS_NewMeterFrame),
3360       COMPLEX_TAG_CREATE(details,
3361                          &nsCSSFrameConstructor::ConstructDetailsFrame)};
3362 
3363   return FindDataByTag(aElement, aStyle, sHTMLData, ArrayLength(sHTMLData));
3364 }
3365 
3366 /* static */
3367 const nsCSSFrameConstructor::FrameConstructionData*
FindGeneratedImageData(const Element & aElement,ComputedStyle &)3368 nsCSSFrameConstructor::FindGeneratedImageData(const Element& aElement,
3369                                               ComputedStyle&) {
3370   if (!aElement.IsInNativeAnonymousSubtree()) {
3371     return nullptr;
3372   }
3373 
3374   static const FrameConstructionData sImgData =
3375       SIMPLE_FCDATA(NS_NewImageFrameForGeneratedContentIndex);
3376   return &sImgData;
3377 }
3378 
3379 /* static */
3380 const nsCSSFrameConstructor::FrameConstructionData*
FindImgData(const Element & aElement,ComputedStyle & aStyle)3381 nsCSSFrameConstructor::FindImgData(const Element& aElement,
3382                                    ComputedStyle& aStyle) {
3383   if (!nsImageFrame::ShouldCreateImageFrameFor(aElement, aStyle)) {
3384     return nullptr;
3385   }
3386 
3387   static const FrameConstructionData sImgData = SIMPLE_FCDATA(NS_NewImageFrame);
3388   return &sImgData;
3389 }
3390 
3391 /* static */
3392 const nsCSSFrameConstructor::FrameConstructionData*
FindImgControlData(const Element & aElement,ComputedStyle & aStyle)3393 nsCSSFrameConstructor::FindImgControlData(const Element& aElement,
3394                                           ComputedStyle& aStyle) {
3395   if (!nsImageFrame::ShouldCreateImageFrameFor(aElement, aStyle)) {
3396     return nullptr;
3397   }
3398 
3399   static const FrameConstructionData sImgControlData =
3400       SIMPLE_FCDATA(NS_NewImageControlFrame);
3401   return &sImgControlData;
3402 }
3403 
3404 /* static */
3405 const nsCSSFrameConstructor::FrameConstructionData*
FindInputData(const Element & aElement,ComputedStyle & aStyle)3406 nsCSSFrameConstructor::FindInputData(const Element& aElement,
3407                                      ComputedStyle& aStyle) {
3408   static const FrameConstructionDataByInt sInputData[] = {
3409       SIMPLE_INT_CREATE(NS_FORM_INPUT_CHECKBOX, NS_NewCheckboxRadioFrame),
3410       SIMPLE_INT_CREATE(NS_FORM_INPUT_RADIO, NS_NewCheckboxRadioFrame),
3411       SIMPLE_INT_CREATE(NS_FORM_INPUT_FILE, NS_NewFileControlFrame),
3412       SIMPLE_INT_CHAIN(NS_FORM_INPUT_IMAGE,
3413                        nsCSSFrameConstructor::FindImgControlData),
3414       SIMPLE_INT_CREATE(NS_FORM_INPUT_EMAIL, NS_NewTextControlFrame),
3415       SIMPLE_INT_CREATE(NS_FORM_INPUT_SEARCH, NS_NewTextControlFrame),
3416       SIMPLE_INT_CREATE(NS_FORM_INPUT_TEXT, NS_NewTextControlFrame),
3417       SIMPLE_INT_CREATE(NS_FORM_INPUT_TEL, NS_NewTextControlFrame),
3418       SIMPLE_INT_CREATE(NS_FORM_INPUT_URL, NS_NewTextControlFrame),
3419       SIMPLE_INT_CREATE(NS_FORM_INPUT_RANGE, NS_NewRangeFrame),
3420       SIMPLE_INT_CREATE(NS_FORM_INPUT_PASSWORD, NS_NewTextControlFrame),
3421       {NS_FORM_INPUT_COLOR,
3422        FCDATA_WITH_WRAPPING_BLOCK(0, NS_NewColorControlFrame,
3423                                   PseudoStyleType::buttonContent)},
3424       // TODO: this is temporary until a frame is written: bug 635240.
3425       SIMPLE_INT_CREATE(NS_FORM_INPUT_NUMBER, NS_NewNumberControlFrame),
3426       SIMPLE_INT_CREATE(NS_FORM_INPUT_TIME, NS_NewDateTimeControlFrame),
3427       SIMPLE_INT_CREATE(NS_FORM_INPUT_DATE, NS_NewDateTimeControlFrame),
3428       // TODO: this is temporary until a frame is written: bug 888320
3429       SIMPLE_INT_CREATE(NS_FORM_INPUT_MONTH, NS_NewTextControlFrame),
3430       // TODO: this is temporary until a frame is written: bug 888320
3431       SIMPLE_INT_CREATE(NS_FORM_INPUT_WEEK, NS_NewTextControlFrame),
3432       // TODO: this is temporary until a frame is written: bug 888320
3433       SIMPLE_INT_CREATE(NS_FORM_INPUT_DATETIME_LOCAL, NS_NewTextControlFrame),
3434       {NS_FORM_INPUT_SUBMIT,
3435        FCDATA_WITH_WRAPPING_BLOCK(0, NS_NewGfxButtonControlFrame,
3436                                   PseudoStyleType::buttonContent)},
3437       {NS_FORM_INPUT_RESET,
3438        FCDATA_WITH_WRAPPING_BLOCK(0, NS_NewGfxButtonControlFrame,
3439                                   PseudoStyleType::buttonContent)},
3440       {NS_FORM_INPUT_BUTTON,
3441        FCDATA_WITH_WRAPPING_BLOCK(0, NS_NewGfxButtonControlFrame,
3442                                   PseudoStyleType::buttonContent)}
3443       // Keeping hidden inputs out of here on purpose for so they get frames by
3444       // display (in practice, none).
3445   };
3446 
3447   auto controlType = HTMLInputElement::FromNode(aElement)->ControlType();
3448 
3449   // radio and checkbox inputs with appearance:none should be constructed
3450   // by display type.  (Note that we're not checking that appearance is
3451   // not (respectively) StyleAppearance::Radio and StyleAppearance::Checkbox.)
3452   if ((controlType == NS_FORM_INPUT_CHECKBOX ||
3453        controlType == NS_FORM_INPUT_RADIO) &&
3454       !aStyle.StyleDisplay()->HasAppearance()) {
3455     return nullptr;
3456   }
3457 
3458   return FindDataByInt(controlType, aElement, aStyle, sInputData,
3459                        ArrayLength(sInputData));
3460 }
3461 
3462 /* static */
3463 const nsCSSFrameConstructor::FrameConstructionData*
FindObjectData(const Element & aElement,ComputedStyle & aStyle)3464 nsCSSFrameConstructor::FindObjectData(const Element& aElement,
3465                                       ComputedStyle& aStyle) {
3466   // GetDisplayedType isn't necessarily nsIObjectLoadingContent::TYPE_NULL for
3467   // cases when the object is broken/suppressed/etc (e.g. a broken image), but
3468   // we want to treat those cases as TYPE_NULL
3469   uint32_t type;
3470   if (aElement.State().HasAtLeastOneOfStates(NS_EVENT_STATE_BROKEN |
3471                                              NS_EVENT_STATE_USERDISABLED |
3472                                              NS_EVENT_STATE_SUPPRESSED)) {
3473     type = nsIObjectLoadingContent::TYPE_NULL;
3474   } else {
3475     nsCOMPtr<nsIObjectLoadingContent> objContent =
3476         do_QueryInterface(const_cast<Element*>(&aElement));
3477     NS_ASSERTION(objContent,
3478                  "embed and object must implement "
3479                  "nsIObjectLoadingContent!");
3480 
3481     objContent->GetDisplayedType(&type);
3482   }
3483 
3484   static const FrameConstructionDataByInt sObjectData[] = {
3485       SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_LOADING,
3486                         NS_NewEmptyFrame),
3487       SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_PLUGIN,
3488                         NS_NewObjectFrame),
3489       SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_IMAGE, NS_NewImageFrame),
3490       SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_DOCUMENT,
3491                         NS_NewSubDocumentFrame),
3492       // Fake plugin handlers load as documents
3493       SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_FAKE_PLUGIN,
3494                         NS_NewSubDocumentFrame)
3495       // Nothing for TYPE_NULL so we'll construct frames by display there
3496   };
3497 
3498   return FindDataByInt((int32_t)type, aElement, aStyle, sObjectData,
3499                        ArrayLength(sObjectData));
3500 }
3501 
3502 /* static */
3503 const nsCSSFrameConstructor::FrameConstructionData*
FindCanvasData(const Element & aElement,ComputedStyle & aStyle)3504 nsCSSFrameConstructor::FindCanvasData(const Element& aElement,
3505                                       ComputedStyle& aStyle) {
3506   // We want to check whether script is enabled on the document that
3507   // could be painting to the canvas.  That's the owner document of
3508   // the canvas, except when the owner document is a static document,
3509   // in which case it's the original document it was cloned from.
3510   Document* doc = aElement.OwnerDoc();
3511   if (doc->IsStaticDocument()) {
3512     doc = doc->GetOriginalDocument();
3513   }
3514   if (!doc->IsScriptEnabled()) {
3515     return nullptr;
3516   }
3517 
3518   static const FrameConstructionData sCanvasData = FCDATA_WITH_WRAPPING_BLOCK(
3519       0, NS_NewHTMLCanvasFrame, PseudoStyleType::htmlCanvasContent);
3520   return &sCanvasData;
3521 }
3522 
ConstructFrameFromItemInternal(FrameConstructionItem & aItem,nsFrameConstructorState & aState,nsContainerFrame * aParentFrame,nsFrameList & aFrameList)3523 void nsCSSFrameConstructor::ConstructFrameFromItemInternal(
3524     FrameConstructionItem& aItem, nsFrameConstructorState& aState,
3525     nsContainerFrame* aParentFrame, nsFrameList& aFrameList) {
3526   const FrameConstructionData* data = aItem.mFCData;
3527   NS_ASSERTION(data, "Must have frame construction data");
3528 
3529   uint32_t bits = data->mBits;
3530 
3531   NS_ASSERTION(!(bits & FCDATA_FUNC_IS_DATA_GETTER),
3532                "Should have dealt with this inside the data finder");
3533 
3534   // Some sets of bits are not compatible with each other
3535 #define CHECK_ONLY_ONE_BIT(_bit1, _bit2)           \
3536   NS_ASSERTION(!(bits & _bit1) || !(bits & _bit2), \
3537                "Only one of these bits should be set")
3538   CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR,
3539                      FCDATA_FORCE_NULL_ABSPOS_CONTAINER);
3540   CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_WRAP_KIDS_IN_BLOCKS);
3541   CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_IS_POPUP);
3542   CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_SKIP_ABSPOS_PUSH);
3543   CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR,
3544                      FCDATA_DISALLOW_GENERATED_CONTENT);
3545   CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_ALLOW_BLOCK_STYLES);
3546   CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR,
3547                      FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS);
3548   CHECK_ONLY_ONE_BIT(FCDATA_WRAP_KIDS_IN_BLOCKS,
3549                      FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS);
3550 #undef CHECK_ONLY_ONE_BIT
3551   NS_ASSERTION(!(bits & FCDATA_FORCED_NON_SCROLLABLE_BLOCK) ||
3552                    ((bits & FCDATA_FUNC_IS_FULL_CTOR) &&
3553                     data->mFullConstructor ==
3554                         &nsCSSFrameConstructor::ConstructNonScrollableBlock),
3555                "Unexpected FCDATA_FORCED_NON_SCROLLABLE_BLOCK flag");
3556   MOZ_ASSERT(
3557       !(bits & FCDATA_IS_WRAPPER_ANON_BOX) || (bits & FCDATA_USE_CHILD_ITEMS),
3558       "Wrapper anon boxes should always have FCDATA_USE_CHILD_ITEMS");
3559   MOZ_ASSERT(!(bits & FCDATA_ALLOW_GRID_FLEX_COLUMN) ||
3560                  (bits & FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS),
3561              "Need the block wrapper bit to create grid/flex/column.");
3562 
3563   // Don't create a subdocument frame for iframes if we're creating extra frames
3564   if (aState.mCreatingExtraFrames &&
3565       aItem.mContent->IsHTMLElement(nsGkAtoms::iframe)) {
3566     return;
3567   }
3568 
3569   nsIContent* const content = aItem.mContent;
3570   nsIFrame* newFrame;
3571   nsIFrame* primaryFrame;
3572   ComputedStyle* const computedStyle = aItem.mComputedStyle;
3573   const nsStyleDisplay* display = computedStyle->StyleDisplay();
3574   if (bits & FCDATA_FUNC_IS_FULL_CTOR) {
3575     newFrame = (this->*(data->mFullConstructor))(aState, aItem, aParentFrame,
3576                                                  display, aFrameList);
3577     MOZ_ASSERT(newFrame, "Full constructor failed");
3578     primaryFrame = newFrame;
3579   } else {
3580     newFrame = (*data->mFunc.mCreationFunc)(mPresShell, computedStyle);
3581 
3582     bool allowOutOfFlow = !(bits & FCDATA_DISALLOW_OUT_OF_FLOW);
3583     bool isPopup = aItem.mIsPopup;
3584     NS_ASSERTION(
3585         !isPopup || (aState.mPopupList.containingBlock &&
3586                      aState.mPopupList.containingBlock->IsPopupSetFrame()),
3587         "Should have a containing block here!");
3588 
3589     nsContainerFrame* geometricParent =
3590         isPopup ? aState.mPopupList.containingBlock
3591                 : (allowOutOfFlow
3592                        ? aState.GetGeometricParent(*display, aParentFrame)
3593                        : aParentFrame);
3594 
3595     // Must init frameToAddToList to null, since it's inout
3596     nsIFrame* frameToAddToList = nullptr;
3597     if ((bits & FCDATA_MAY_NEED_SCROLLFRAME) &&
3598         display->IsScrollableOverflow()) {
3599       nsContainerFrame* scrollframe = nullptr;
3600       BuildScrollFrame(aState, content, computedStyle, newFrame,
3601                        geometricParent, scrollframe);
3602       frameToAddToList = scrollframe;
3603     } else {
3604       InitAndRestoreFrame(aState, content, geometricParent, newFrame);
3605       frameToAddToList = newFrame;
3606     }
3607 
3608     // Use frameToAddToList as the primary frame.  In the non-scrollframe case
3609     // they're equal, but in the scrollframe case newFrame is the scrolled
3610     // frame, while frameToAddToList is the scrollframe (and should be the
3611     // primary frame).
3612     primaryFrame = frameToAddToList;
3613 
3614     // If we need to create a block formatting context to wrap our
3615     // kids, do it now.
3616     nsIFrame* maybeAbsoluteContainingBlockStyleFrame = primaryFrame;
3617     nsIFrame* maybeAbsoluteContainingBlock = newFrame;
3618     nsIFrame* possiblyLeafFrame = newFrame;
3619     nsContainerFrame* outerFrame = nullptr;
3620     if (bits & FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS) {
3621       RefPtr<ComputedStyle> outerStyle =
3622           mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
3623               data->mAnonBoxPseudo, computedStyle);
3624 #ifdef DEBUG
3625       nsContainerFrame* containerFrame = do_QueryFrame(newFrame);
3626       MOZ_ASSERT(containerFrame);
3627 #endif
3628       nsContainerFrame* container = static_cast<nsContainerFrame*>(newFrame);
3629       nsContainerFrame* innerFrame;
3630       if (bits & FCDATA_ALLOW_GRID_FLEX_COLUMN) {
3631         switch (display->DisplayInside()) {
3632           case StyleDisplayInside::Flex:
3633             outerFrame = NS_NewFlexContainerFrame(mPresShell, outerStyle);
3634             InitAndRestoreFrame(aState, content, container, outerFrame);
3635             innerFrame = outerFrame;
3636             break;
3637           case StyleDisplayInside::Grid:
3638             outerFrame = NS_NewGridContainerFrame(mPresShell, outerStyle);
3639             InitAndRestoreFrame(aState, content, container, outerFrame);
3640             innerFrame = outerFrame;
3641             break;
3642           default: {
3643             innerFrame = NS_NewBlockFormattingContext(mPresShell, outerStyle);
3644             if (outerStyle->StyleColumn()->IsColumnContainerStyle()) {
3645               outerFrame = BeginBuildingColumns(aState, content, container,
3646                                                 innerFrame, outerStyle);
3647             } else {
3648               // No need to create column container. Initialize innerFrame.
3649               InitAndRestoreFrame(aState, content, container, innerFrame);
3650               outerFrame = innerFrame;
3651             }
3652             break;
3653           }
3654         }
3655       } else {
3656         innerFrame = NS_NewBlockFormattingContext(mPresShell, outerStyle);
3657         InitAndRestoreFrame(aState, content, container, innerFrame);
3658         outerFrame = innerFrame;
3659       }
3660 
3661       SetInitialSingleChild(container, outerFrame);
3662 
3663       container->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
3664 
3665       // Now figure out whether newFrame or outerFrame should be the
3666       // absolute container.
3667       auto outerDisplay = outerStyle->StyleDisplay();
3668       if (outerDisplay->IsAbsPosContainingBlock(outerFrame)) {
3669         maybeAbsoluteContainingBlock = outerFrame;
3670         maybeAbsoluteContainingBlockStyleFrame = outerFrame;
3671         innerFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
3672       }
3673 
3674       // Our kids should go into the innerFrame.
3675       newFrame = innerFrame;
3676     }
3677 
3678     aState.AddChild(frameToAddToList, aFrameList, content, aParentFrame,
3679                     allowOutOfFlow, allowOutOfFlow, isPopup);
3680 
3681     nsContainerFrame* newFrameAsContainer = do_QueryFrame(newFrame);
3682     if (newFrameAsContainer) {
3683 #ifdef MOZ_XUL
3684       // Icky XUL stuff, sadly
3685 
3686       if (aItem.mIsRootPopupgroup) {
3687         NS_ASSERTION(nsIPopupContainer::GetPopupContainer(mPresShell) &&
3688                          nsIPopupContainer::GetPopupContainer(mPresShell)
3689                                  ->GetPopupSetFrame() == newFrame,
3690                      "Unexpected PopupSetFrame");
3691         aState.mPopupList.containingBlock = newFrameAsContainer;
3692         aState.mHavePendingPopupgroup = false;
3693       }
3694 #endif /* MOZ_XUL */
3695 
3696       // Process the child content if requested
3697       nsFrameList childList;
3698       nsFrameConstructorSaveState absoluteSaveState;
3699 
3700       if (bits & FCDATA_FORCE_NULL_ABSPOS_CONTAINER) {
3701         aState.PushAbsoluteContainingBlock(nullptr, nullptr, absoluteSaveState);
3702       } else if (!(bits & FCDATA_SKIP_ABSPOS_PUSH)) {
3703         maybeAbsoluteContainingBlock->AddStateBits(
3704             NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
3705         if (maybeAbsoluteContainingBlockStyleFrame->IsAbsPosContainingBlock()) {
3706           auto* cf =
3707               static_cast<nsContainerFrame*>(maybeAbsoluteContainingBlock);
3708           aState.PushAbsoluteContainingBlock(
3709               cf, maybeAbsoluteContainingBlockStyleFrame, absoluteSaveState);
3710         }
3711       }
3712 
3713       if (bits & FCDATA_USE_CHILD_ITEMS) {
3714         nsFrameConstructorSaveState floatSaveState;
3715 
3716         if (ShouldSuppressFloatingOfDescendants(newFrame)) {
3717           aState.PushFloatContainingBlock(nullptr, floatSaveState);
3718         } else if (newFrame->IsFloatContainingBlock()) {
3719           aState.PushFloatContainingBlock(newFrameAsContainer, floatSaveState);
3720         }
3721         ConstructFramesFromItemList(
3722             aState, aItem.mChildItems, newFrameAsContainer,
3723             bits & FCDATA_IS_WRAPPER_ANON_BOX, childList);
3724       } else {
3725         // Process the child frames.
3726         ProcessChildren(aState, content, computedStyle, newFrameAsContainer,
3727                         !(bits & FCDATA_DISALLOW_GENERATED_CONTENT), childList,
3728                         (bits & FCDATA_ALLOW_BLOCK_STYLES) != 0,
3729                         possiblyLeafFrame);
3730       }
3731 
3732       if (bits & FCDATA_WRAP_KIDS_IN_BLOCKS) {
3733         nsFrameList newList;
3734         nsFrameList currentBlockList;
3735         nsIFrame* f;
3736         while ((f = childList.FirstChild()) != nullptr) {
3737           bool wrapFrame = IsInlineFrame(f) || IsFramePartOfIBSplit(f);
3738           if (!wrapFrame) {
3739             FlushAccumulatedBlock(aState, content, newFrameAsContainer,
3740                                   currentBlockList, newList);
3741           }
3742 
3743           childList.RemoveFrame(f);
3744           if (wrapFrame) {
3745             currentBlockList.AppendFrame(nullptr, f);
3746           } else {
3747             newList.AppendFrame(nullptr, f);
3748           }
3749         }
3750         FlushAccumulatedBlock(aState, content, newFrameAsContainer,
3751                               currentBlockList, newList);
3752 
3753         if (childList.NotEmpty()) {
3754           // an error must have occurred, delete unprocessed frames
3755           childList.DestroyFrames();
3756         }
3757 
3758         childList = newList;
3759       }
3760 
3761       if (!(bits & FCDATA_ALLOW_GRID_FLEX_COLUMN) ||
3762           !MayNeedToCreateColumnSpanSiblings(newFrameAsContainer, childList)) {
3763         // Set the frame's initial child list. Note that MathML depends on this
3764         // being called even if childList is empty!
3765         newFrameAsContainer->SetInitialChildList(kPrincipalList, childList);
3766       } else {
3767         // Extract any initial non-column-span kids, and put them in inner
3768         // frame's child list.
3769         nsFrameList initialNonColumnSpanKids =
3770             childList.Split([](nsIFrame* f) { return f->IsColumnSpan(); });
3771         newFrameAsContainer->SetInitialChildList(kPrincipalList,
3772                                                  initialNonColumnSpanKids);
3773 
3774         if (childList.NotEmpty()) {
3775           nsFrameList columnSpanSiblings = CreateColumnSpanSiblings(
3776               aState, newFrameAsContainer, childList,
3777               // Column content should never be a absolute/fixed positioned
3778               // containing block. Pass nullptr as aPositionedFrame.
3779               nullptr);
3780 
3781           MOZ_ASSERT(outerFrame,
3782                      "outerFrame should be non-null if multi-column container "
3783                      "is created.");
3784           FinishBuildingColumns(aState, outerFrame, newFrameAsContainer,
3785                                 columnSpanSiblings);
3786         }
3787       }
3788     }
3789   }
3790 
3791   if (computedStyle->GetPseudoType() == PseudoStyleType::marker &&
3792       newFrame->IsBulletFrame()) {
3793     MOZ_ASSERT(!computedStyle->StyleContent()->ContentCount());
3794     auto* node = new nsCounterUseNode(nsCounterUseNode::ForLegacyBullet);
3795     auto* list = mCounterManager.CounterListFor(nsGkAtoms::list_item);
3796     if (node->InitBullet(list, newFrame)) {
3797       CountersDirty();
3798     }
3799   }
3800 
3801   NS_ASSERTION(newFrame->IsFrameOfType(nsIFrame::eLineParticipant) ==
3802                    ((bits & FCDATA_IS_LINE_PARTICIPANT) != 0),
3803                "Incorrectly set FCDATA_IS_LINE_PARTICIPANT bits");
3804 
3805   // Even if mCreatingExtraFrames is set, we may need to SetPrimaryFrame for
3806   // generated content that doesn't have one yet.  Note that we have to examine
3807   // the frame bit, because by this point mIsGeneratedContent has been cleared
3808   // on aItem.
3809   if ((!aState.mCreatingExtraFrames ||
3810        (aItem.mContent->IsRootOfNativeAnonymousSubtree() &&
3811         !aItem.mContent->GetPrimaryFrame())) &&
3812       !(bits & FCDATA_SKIP_FRAMESET)) {
3813     aItem.mContent->SetPrimaryFrame(primaryFrame);
3814     ActiveLayerTracker::TransferActivityToFrame(aItem.mContent, primaryFrame);
3815   }
3816 }
3817 
GatherSubtreeElements(Element * aElement,nsTArray<Element * > & aElements)3818 static void GatherSubtreeElements(Element* aElement,
3819                                   nsTArray<Element*>& aElements) {
3820   aElements.AppendElement(aElement);
3821   StyleChildrenIterator iter(aElement);
3822   for (nsIContent* c = iter.GetNextChild(); c; c = iter.GetNextChild()) {
3823     if (!c->IsElement()) {
3824       continue;
3825     }
3826     GatherSubtreeElements(c->AsElement(), aElements);
3827   }
3828 }
3829 
GetAnonymousContent(nsIContent * aParent,nsIFrame * aParentFrame,nsTArray<nsIAnonymousContentCreator::ContentInfo> & aContent)3830 nsresult nsCSSFrameConstructor::GetAnonymousContent(
3831     nsIContent* aParent, nsIFrame* aParentFrame,
3832     nsTArray<nsIAnonymousContentCreator::ContentInfo>& aContent) {
3833   nsIAnonymousContentCreator* creator = do_QueryFrame(aParentFrame);
3834   if (!creator) {
3835     return NS_OK;
3836   }
3837 
3838   nsresult rv = creator->CreateAnonymousContent(aContent);
3839   if (NS_FAILED(rv)) {
3840     // CreateAnonymousContent failed, e.g. because the page has a <use> loop.
3841     return rv;
3842   }
3843 
3844   MOZ_ASSERT(aParent->IsElement());
3845   for (const auto& info : aContent) {
3846     // get our child's content and set its parent to our content
3847     nsIContent* content = info.mContent;
3848     content->SetIsNativeAnonymousRoot();
3849 
3850     BindContext context(*aParent->AsElement(), BindContext::ForNativeAnonymous);
3851     rv = content->BindToTree(context, *aParent);
3852 
3853     if (NS_FAILED(rv)) {
3854       content->UnbindFromTree();
3855       return rv;
3856     }
3857   }
3858 
3859   // Some situations where we don't cache anonymous content styles:
3860   //
3861   // * when visibility or pointer-events is anything other than the initial
3862   //   value; we rely on visibility and pointer-events inheriting into anonymous
3863   //   content, but don't bother adding this state to the AnonymousContentKey,
3864   //   since it's not so common. Note that on android scrollbars always have
3865   //   pointer-events: none so we don't need to check for that.
3866   //
3867   // * when the medium is anything other than screen; some UA style sheet rules
3868   //   apply in e.g. print medium, and will give different results from the
3869   //   cached styles
3870   bool allowStyleCaching =
3871       StaticPrefs::layout_css_cached_scrollbar_styles_enabled() &&
3872       aParentFrame->StyleVisibility()->mVisible == StyleVisibility::Visible &&
3873 #ifndef ANDROID
3874       aParentFrame->StyleUI()->mPointerEvents == StylePointerEvents::Auto &&
3875 #endif
3876       mPresShell->GetPresContext()->Medium() == nsGkAtoms::screen;
3877 
3878   // Compute styles for the anonymous content tree.
3879   ServoStyleSet* styleSet = mPresShell->StyleSet();
3880   for (auto& info : aContent) {
3881     Element* e = Element::FromNode(info.mContent);
3882     if (!e) {
3883       continue;
3884     }
3885 
3886     if (info.mKey == AnonymousContentKey::None || !allowStyleCaching) {
3887       // Most NAC subtrees do not use caching of computed styles.  Just go
3888       // ahead and eagerly style the subtree.
3889       styleSet->StyleNewSubtree(e);
3890       continue;
3891     }
3892 
3893     // We have a NAC subtree for which we can use cached styles.
3894     AutoTArray<RefPtr<ComputedStyle>, 2> cachedStyles;
3895     AutoTArray<Element*, 2> elements;
3896 
3897     GatherSubtreeElements(e, elements);
3898     styleSet->GetCachedAnonymousContentStyles(info.mKey, cachedStyles);
3899 
3900     if (cachedStyles.IsEmpty()) {
3901       // We haven't stored cached styles for this kind of NAC subtree yet.
3902       // Eagerly compute those styles, then cache them for later.
3903       styleSet->StyleNewSubtree(e);
3904       for (Element* e : elements) {
3905         if (e->HasServoData()) {
3906           cachedStyles.AppendElement(ServoStyleSet::ResolveServoStyle(*e));
3907         } else {
3908           cachedStyles.AppendElement(nullptr);
3909         }
3910       }
3911       styleSet->PutCachedAnonymousContentStyles(info.mKey,
3912                                                 std::move(cachedStyles));
3913       continue;
3914     }
3915 
3916     // We previously stored cached styles for this kind of NAC subtree.
3917     // Iterate over them and set them on the subtree's elements.
3918     MOZ_ASSERT(cachedStyles.Length() == elements.Length(),
3919                "should always produce the same size NAC subtree");
3920     for (size_t i = 0, len = cachedStyles.Length(); i != len; ++i) {
3921       if (cachedStyles[i]) {
3922 #ifdef DEBUG
3923         // Assert that our cached style is the same as one we could compute.
3924         RefPtr<ComputedStyle> cs = styleSet->ResolveStyleLazily(*elements[i]);
3925         MOZ_ASSERT(
3926             cachedStyles[i]->EqualForCachedAnonymousContentStyle(*cs),
3927             "cached anonymous content styles should be identical to those we "
3928             "would compute normally");
3929 #  ifdef ANDROID
3930         MOZ_ASSERT(cs->StyleUI()->mPointerEvents == StylePointerEvents::None);
3931 #  endif
3932 #endif
3933         Servo_SetExplicitStyle(elements[i], cachedStyles[i]);
3934       }
3935     }
3936   }
3937 
3938   return NS_OK;
3939 }
3940 
IsXULDisplayType(const nsStyleDisplay * aDisplay)3941 static bool IsXULDisplayType(const nsStyleDisplay* aDisplay) {
3942   // -moz-{inline-}box is XUL, unless we're emulating it with flexbox.
3943   if (!StaticPrefs::layout_css_emulate_moz_box_with_flex() &&
3944       aDisplay->DisplayInside() == StyleDisplayInside::MozBox) {
3945     return true;
3946   }
3947 
3948 #ifdef MOZ_XUL
3949   return (aDisplay->mDisplay == StyleDisplay::MozGrid ||
3950           aDisplay->mDisplay == StyleDisplay::MozStack ||
3951           aDisplay->mDisplay == StyleDisplay::MozGridGroup ||
3952           aDisplay->mDisplay == StyleDisplay::MozGridLine ||
3953           aDisplay->mDisplay == StyleDisplay::MozDeck ||
3954           aDisplay->mDisplay == StyleDisplay::MozPopup);
3955 #else
3956   return false;
3957 #endif
3958 }
3959 
3960 // XUL frames are not allowed to be out of flow.
3961 #define SIMPLE_XUL_FCDATA(_func) \
3962   FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_SKIP_ABSPOS_PUSH, _func)
3963 #define SCROLLABLE_XUL_FCDATA(_func)                                  \
3964   FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_SKIP_ABSPOS_PUSH | \
3965                   FCDATA_MAY_NEED_SCROLLFRAME,                        \
3966               _func)
3967 // .. but we allow some XUL frames to be _containers_ for out-of-flow content
3968 // (This is the same as SCROLLABLE_XUL_FCDATA, but w/o FCDATA_SKIP_ABSPOS_PUSH)
3969 #define SCROLLABLE_ABSPOS_CONTAINER_XUL_FCDATA(_func) \
3970   FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_MAY_NEED_SCROLLFRAME, _func)
3971 
3972 #define SIMPLE_XUL_CREATE(_tag, _func) \
3973   { nsGkAtoms::_tag, SIMPLE_XUL_FCDATA(_func) }
3974 #define SCROLLABLE_XUL_CREATE(_tag, _func) \
3975   { nsGkAtoms::_tag, SCROLLABLE_XUL_FCDATA(_func) }
3976 
NS_NewGridBoxFrame(PresShell * aPresShell,ComputedStyle * aComputedStyle)3977 static nsIFrame* NS_NewGridBoxFrame(PresShell* aPresShell,
3978                                     ComputedStyle* aComputedStyle) {
3979   nsCOMPtr<nsBoxLayout> layout;
3980   NS_NewGridLayout2(getter_AddRefs(layout));
3981   return NS_NewBoxFrame(aPresShell, aComputedStyle, false, layout);
3982 }
3983 
3984 /* static */
3985 const nsCSSFrameConstructor::FrameConstructionData*
FindXULTagData(const Element & aElement,ComputedStyle & aStyle)3986 nsCSSFrameConstructor::FindXULTagData(const Element& aElement,
3987                                       ComputedStyle& aStyle) {
3988   MOZ_ASSERT(aElement.IsXULElement());
3989 
3990   static const FrameConstructionDataByTag sXULTagData[] = {
3991 #ifdef MOZ_XUL
3992       SCROLLABLE_XUL_CREATE(thumb, NS_NewButtonBoxFrame),
3993       SCROLLABLE_XUL_CREATE(checkbox, NS_NewButtonBoxFrame),
3994       SCROLLABLE_XUL_CREATE(radio, NS_NewButtonBoxFrame),
3995       SCROLLABLE_XUL_CREATE(titlebar, NS_NewTitleBarFrame),
3996       SCROLLABLE_XUL_CREATE(resizer, NS_NewResizerFrame),
3997       SCROLLABLE_XUL_CREATE(toolbarpaletteitem, NS_NewBoxFrame),
3998       SCROLLABLE_XUL_CREATE(treecolpicker, NS_NewButtonBoxFrame),
3999       SIMPLE_XUL_CREATE(image, NS_NewImageBoxFrame),
4000       SIMPLE_XUL_CREATE(spacer, NS_NewLeafBoxFrame),
4001       SIMPLE_XUL_CREATE(treechildren, NS_NewTreeBodyFrame),
4002       SIMPLE_XUL_CREATE(treecol, NS_NewTreeColFrame),
4003       SIMPLE_TAG_CHAIN(button, nsCSSFrameConstructor::FindXULButtonData),
4004       SIMPLE_TAG_CHAIN(toolbarbutton, nsCSSFrameConstructor::FindXULButtonData),
4005       SIMPLE_TAG_CHAIN(label, nsCSSFrameConstructor::FindXULLabelData),
4006       SIMPLE_TAG_CHAIN(description,
4007                        nsCSSFrameConstructor::FindXULDescriptionData),
4008       SIMPLE_XUL_CREATE(menu, NS_NewMenuFrame),
4009       SIMPLE_XUL_CREATE(menubutton, NS_NewMenuFrame),
4010       SIMPLE_XUL_CREATE(menulist, NS_NewMenuFrame),
4011       SIMPLE_XUL_CREATE(menuitem, NS_NewMenuItemFrame),
4012 #  ifdef XP_MACOSX
4013       SIMPLE_TAG_CHAIN(menubar, nsCSSFrameConstructor::FindXULMenubarData),
4014 #  else
4015       SIMPLE_XUL_CREATE(menubar, NS_NewMenuBarFrame),
4016 #  endif /* XP_MACOSX */
4017       SIMPLE_TAG_CHAIN(popupgroup, nsCSSFrameConstructor::FindPopupGroupData),
4018       SIMPLE_XUL_CREATE(iframe, NS_NewSubDocumentFrame),
4019       SIMPLE_XUL_CREATE(editor, NS_NewSubDocumentFrame),
4020       SIMPLE_XUL_CREATE(browser, NS_NewSubDocumentFrame),
4021       SIMPLE_XUL_CREATE(splitter, NS_NewSplitterFrame),
4022 #endif /* MOZ_XUL */
4023       SIMPLE_XUL_CREATE(slider, NS_NewSliderFrame),
4024       SIMPLE_XUL_CREATE(scrollbar, NS_NewScrollbarFrame),
4025       SIMPLE_XUL_CREATE(scrollbarbutton, NS_NewScrollbarButtonFrame)};
4026 
4027   return FindDataByTag(aElement, aStyle, sXULTagData, ArrayLength(sXULTagData));
4028 }
4029 
4030 #ifdef MOZ_XUL
4031 /* static */
4032 const nsCSSFrameConstructor::FrameConstructionData*
FindPopupGroupData(const Element & aElement,ComputedStyle &)4033 nsCSSFrameConstructor::FindPopupGroupData(const Element& aElement,
4034                                           ComputedStyle&) {
4035   if (!aElement.IsRootOfNativeAnonymousSubtree()) {
4036     return nullptr;
4037   }
4038 
4039   static const FrameConstructionData sPopupSetData =
4040       SIMPLE_XUL_FCDATA(NS_NewPopupSetFrame);
4041   return &sPopupSetData;
4042 }
4043 
4044 /* static */
4045 const nsCSSFrameConstructor::FrameConstructionData
4046     nsCSSFrameConstructor::sXULTextBoxData =
4047         SIMPLE_XUL_FCDATA(NS_NewTextBoxFrame);
4048 
4049 /* static */
4050 const nsCSSFrameConstructor::FrameConstructionData*
FindXULButtonData(const Element & aElement,ComputedStyle &)4051 nsCSSFrameConstructor::FindXULButtonData(const Element& aElement,
4052                                          ComputedStyle&) {
4053   static const FrameConstructionData sXULMenuData =
4054       SIMPLE_XUL_FCDATA(NS_NewMenuFrame);
4055   if (aElement.AttrValueIs(kNameSpaceID_None, nsGkAtoms::type, nsGkAtoms::menu,
4056                            eCaseMatters)) {
4057     return &sXULMenuData;
4058   }
4059 
4060 #  ifdef MOZ_THUNDERBIRD
4061   if (aElement.AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
4062                            NS_LITERAL_STRING("menu-button"), eCaseMatters)) {
4063     return &sXULMenuData;
4064   }
4065 #  endif
4066 
4067   static const FrameConstructionData sXULButtonData =
4068       SCROLLABLE_XUL_FCDATA(NS_NewButtonBoxFrame);
4069   return &sXULButtonData;
4070 }
4071 
4072 /* static */
4073 const nsCSSFrameConstructor::FrameConstructionData*
FindXULLabelData(const Element & aElement,ComputedStyle &)4074 nsCSSFrameConstructor::FindXULLabelData(const Element& aElement,
4075                                         ComputedStyle&) {
4076   if (aElement.HasAttr(kNameSpaceID_None, nsGkAtoms::value)) {
4077     return &sXULTextBoxData;
4078   }
4079 
4080   static const FrameConstructionData sLabelData =
4081       SIMPLE_XUL_FCDATA(NS_NewXULLabelFrame);
4082   return &sLabelData;
4083 }
4084 
NS_NewXULDescriptionFrame(PresShell * aPresShell,ComputedStyle * aContext)4085 static nsIFrame* NS_NewXULDescriptionFrame(PresShell* aPresShell,
4086                                            ComputedStyle* aContext) {
4087   // XXXbz do we really need to set up the block formatting context root? If the
4088   // parent is not a block we'll get it anyway, and if it is, do we want it?
4089   return NS_NewBlockFormattingContext(aPresShell, aContext);
4090 }
4091 
4092 /* static */
4093 const nsCSSFrameConstructor::FrameConstructionData*
FindXULDescriptionData(const Element & aElement,ComputedStyle &)4094 nsCSSFrameConstructor::FindXULDescriptionData(const Element& aElement,
4095                                               ComputedStyle&) {
4096   if (aElement.HasAttr(kNameSpaceID_None, nsGkAtoms::value)) {
4097     return &sXULTextBoxData;
4098   }
4099 
4100   static const FrameConstructionData sDescriptionData =
4101       SIMPLE_XUL_FCDATA(NS_NewXULDescriptionFrame);
4102   return &sDescriptionData;
4103 }
4104 
4105 #  ifdef XP_MACOSX
4106 /* static */
4107 const nsCSSFrameConstructor::FrameConstructionData*
FindXULMenubarData(const Element & aElement,ComputedStyle &)4108 nsCSSFrameConstructor::FindXULMenubarData(const Element& aElement,
4109                                           ComputedStyle&) {
4110   if (aElement.OwnerDoc()->IsInChromeDocShell()) {
4111     BrowsingContext* bc = aElement.OwnerDoc()->GetBrowsingContext();
4112     bool isRoot = bc && !bc->GetParent();
4113     if (isRoot) {
4114       // This is the root.  Suppress the menubar, since on Mac
4115       // window menus are not attached to the window.
4116       static const FrameConstructionData sSuppressData = SUPPRESS_FCDATA();
4117       return &sSuppressData;
4118     }
4119   }
4120 
4121   static const FrameConstructionData sMenubarData =
4122       SIMPLE_XUL_FCDATA(NS_NewMenuBarFrame);
4123   return &sMenubarData;
4124 }
4125 #  endif /* XP_MACOSX */
4126 
4127 #endif /* MOZ_XUL */
4128 
BeginBuildingScrollFrame(nsFrameConstructorState & aState,nsIContent * aContent,ComputedStyle * aContentStyle,nsContainerFrame * aParentFrame,PseudoStyleType aScrolledPseudo,bool aIsRoot,nsContainerFrame * & aNewFrame)4129 already_AddRefed<ComputedStyle> nsCSSFrameConstructor::BeginBuildingScrollFrame(
4130     nsFrameConstructorState& aState, nsIContent* aContent,
4131     ComputedStyle* aContentStyle, nsContainerFrame* aParentFrame,
4132     PseudoStyleType aScrolledPseudo, bool aIsRoot,
4133     nsContainerFrame*& aNewFrame) {
4134   nsContainerFrame* gfxScrollFrame = aNewFrame;
4135 
4136   nsFrameList anonymousList;
4137 
4138   RefPtr<ComputedStyle> contentStyle = aContentStyle;
4139 
4140   if (!gfxScrollFrame) {
4141     // Build a XULScrollFrame when the child is a box, otherwise an
4142     // HTMLScrollFrame
4143     // XXXbz this is the lone remaining consumer of IsXULDisplayType.
4144     // I wonder whether we can eliminate that somehow.
4145     const nsStyleDisplay* displayStyle = aContentStyle->StyleDisplay();
4146     if (IsXULDisplayType(displayStyle)) {
4147       gfxScrollFrame = NS_NewXULScrollFrame(
4148           mPresShell, contentStyle, aIsRoot,
4149           displayStyle->mDisplay == StyleDisplay::MozStack);
4150     } else {
4151       gfxScrollFrame = NS_NewHTMLScrollFrame(mPresShell, contentStyle, aIsRoot);
4152     }
4153 
4154     InitAndRestoreFrame(aState, aContent, aParentFrame, gfxScrollFrame);
4155   }
4156 
4157   MOZ_ASSERT(gfxScrollFrame);
4158 
4159   // if there are any anonymous children for the scroll frame, create
4160   // frames for them.
4161   //
4162   // We can't take the normal ProcessChildren path, because the NAC needs to
4163   // be parented to the scrollframe, and everything else needs to be parented
4164   // to the scrolledframe.
4165   AutoTArray<nsIAnonymousContentCreator::ContentInfo, 4> scrollNAC;
4166   DebugOnly<nsresult> rv =
4167       GetAnonymousContent(aContent, gfxScrollFrame, scrollNAC);
4168   MOZ_ASSERT(NS_SUCCEEDED(rv));
4169   if (scrollNAC.Length() > 0) {
4170     AutoFrameConstructionItemList items(this);
4171     AddFCItemsForAnonymousContent(aState, gfxScrollFrame, scrollNAC, items);
4172     ConstructFramesFromItemList(aState, items, gfxScrollFrame,
4173                                 /* aParentIsWrapperAnonBox = */ false,
4174                                 anonymousList);
4175   }
4176 
4177   aNewFrame = gfxScrollFrame;
4178   gfxScrollFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
4179 
4180   // we used the style that was passed in. So resolve another one.
4181   ServoStyleSet* styleSet = mPresShell->StyleSet();
4182   RefPtr<ComputedStyle> scrolledChildStyle =
4183       styleSet->ResolveInheritingAnonymousBoxStyle(aScrolledPseudo,
4184                                                    contentStyle);
4185 
4186   gfxScrollFrame->SetInitialChildList(kPrincipalList, anonymousList);
4187 
4188   return scrolledChildStyle.forget();
4189 }
4190 
FinishBuildingScrollFrame(nsContainerFrame * aScrollFrame,nsIFrame * aScrolledFrame)4191 void nsCSSFrameConstructor::FinishBuildingScrollFrame(
4192     nsContainerFrame* aScrollFrame, nsIFrame* aScrolledFrame) {
4193   nsFrameList scrolled(aScrolledFrame, aScrolledFrame);
4194   aScrollFrame->AppendFrames(kPrincipalList, scrolled);
4195 }
4196 
4197 /**
4198  * Called to wrap a gfx scrollframe around a frame. The hierarchy will look like
4199  * this
4200  *
4201  * ------- for gfx scrollbars ------
4202  *
4203  *
4204  *            ScrollFrame
4205  *                 ^
4206  *                 |
4207  *               Frame (scrolled frame you passed in)
4208  *
4209  *
4210  * -----------------------------------
4211  * LEGEND:
4212  *
4213  * ScrollFrame: This is a frame that manages gfx cross platform frame based
4214  * scrollbars.
4215  *
4216  * @param aContent the content node of the child to wrap.
4217  *
4218  * @param aScrolledFrame The frame of the content to wrap. This should not be
4219  * Initialized. This method will initialize it with a scrolled pseudo and no
4220  * nsIContent. The content will be attached to the scrollframe returned.
4221  *
4222  * @param aContentStyle the style that has already been resolved for the content
4223  * being passed in.
4224  *
4225  * @param aParentFrame   The parent to attach the scroll frame to
4226  *
4227  * @param aNewFrame The new scrollframe or gfx scrollframe that we create. It
4228  * will contain the scrolled frame you passed in. (returned) If this is not
4229  * null,  we'll just use it
4230  *
4231  * @param aScrolledContentStyle the style that was resolved for the scrolled
4232  * frame. (returned)
4233  */
BuildScrollFrame(nsFrameConstructorState & aState,nsIContent * aContent,ComputedStyle * aContentStyle,nsIFrame * aScrolledFrame,nsContainerFrame * aParentFrame,nsContainerFrame * & aNewFrame)4234 void nsCSSFrameConstructor::BuildScrollFrame(nsFrameConstructorState& aState,
4235                                              nsIContent* aContent,
4236                                              ComputedStyle* aContentStyle,
4237                                              nsIFrame* aScrolledFrame,
4238                                              nsContainerFrame* aParentFrame,
4239                                              nsContainerFrame*& aNewFrame) {
4240   RefPtr<ComputedStyle> scrolledContentStyle = BeginBuildingScrollFrame(
4241       aState, aContent, aContentStyle, aParentFrame,
4242       PseudoStyleType::scrolledContent, false, aNewFrame);
4243 
4244   aScrolledFrame->SetComputedStyleWithoutNotification(scrolledContentStyle);
4245   InitAndRestoreFrame(aState, aContent, aNewFrame, aScrolledFrame);
4246 
4247   FinishBuildingScrollFrame(aNewFrame, aScrolledFrame);
4248 }
4249 
4250 const nsCSSFrameConstructor::FrameConstructionData*
FindDisplayData(const nsStyleDisplay & aDisplay,const Element & aElement)4251 nsCSSFrameConstructor::FindDisplayData(const nsStyleDisplay& aDisplay,
4252                                        const Element& aElement) {
4253   static_assert(eParentTypeCount < (1 << (32 - FCDATA_PARENT_TYPE_OFFSET)),
4254                 "Check eParentTypeCount should not overflow");
4255 
4256   // The style system ensures that floated and positioned frames are
4257   // block-level.
4258   NS_ASSERTION(
4259       !(aDisplay.IsFloatingStyle() || aDisplay.IsAbsolutelyPositionedStyle()) ||
4260           aDisplay.IsBlockOutsideStyle() || IsXULDisplayType(&aDisplay),
4261       "Style system did not apply CSS2.1 section 9.7 fixups");
4262 
4263   // If this is "body", try propagating its scroll style to the viewport
4264   // Note that we need to do this even if the body is NOT scrollable;
4265   // it might have dynamically changed from scrollable to not scrollable,
4266   // and that might need to be propagated.
4267   // XXXbz is this the right place to do this?  If this code moves,
4268   // make this function static.
4269   bool propagatedScrollToViewport = false;
4270   if (aElement.IsHTMLElement(nsGkAtoms::body)) {
4271     if (nsPresContext* presContext = mPresShell->GetPresContext()) {
4272       propagatedScrollToViewport =
4273           presContext->UpdateViewportScrollStylesOverride() == &aElement;
4274       MOZ_ASSERT(!propagatedScrollToViewport ||
4275                      !mPresShell->GetPresContext()->IsPaginated(),
4276                  "Shouldn't propagate scroll in paginated contexts");
4277     }
4278   }
4279 
4280   switch (aDisplay.DisplayInside()) {
4281     case StyleDisplayInside::Flow:
4282     case StyleDisplayInside::FlowRoot: {
4283       if (aDisplay.IsInlineFlow()) {
4284         static const FrameConstructionData data =
4285             FULL_CTOR_FCDATA(FCDATA_IS_INLINE | FCDATA_IS_LINE_PARTICIPANT,
4286                              &nsCSSFrameConstructor::ConstructInline);
4287         return &data;
4288       }
4289 
4290       // If the frame is a block-level frame and is scrollable, then wrap it in
4291       // a scroll frame.  Except we don't want to do that for paginated contexts
4292       // for frames that are block-outside and aren't frames for native
4293       // anonymous stuff.
4294       // XXX Ignore tables for the time being (except caption)
4295       const uint32_t kCaptionCtorFlags =
4296           FCDATA_IS_TABLE_PART | FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable);
4297       bool caption = aDisplay.mDisplay == StyleDisplay::TableCaption;
4298       bool suppressScrollFrame = false;
4299       bool needScrollFrame =
4300           aDisplay.IsScrollableOverflow() && !propagatedScrollToViewport;
4301       if (needScrollFrame) {
4302         suppressScrollFrame = mPresShell->GetPresContext()->IsPaginated() &&
4303                               aDisplay.IsBlockOutsideStyle() &&
4304                               !aElement.IsInNativeAnonymousSubtree();
4305         if (!suppressScrollFrame) {
4306           static const FrameConstructionData sScrollableBlockData[2] = {
4307               FULL_CTOR_FCDATA(
4308                   0, &nsCSSFrameConstructor::ConstructScrollableBlock),
4309               FULL_CTOR_FCDATA(
4310                   kCaptionCtorFlags,
4311                   &nsCSSFrameConstructor::ConstructScrollableBlock)};
4312           return &sScrollableBlockData[caption];
4313         }
4314 
4315         // If the scrollable frame would have propagated its scrolling to the
4316         // viewport, we still want to construct a regular block rather than a
4317         // scrollframe so that it paginates correctly, but we don't want to set
4318         // the bit on the block that tells it to clip at paint time.
4319         if (mPresShell->GetPresContext()->ElementWouldPropagateScrollStyles(
4320                 aElement)) {
4321           suppressScrollFrame = false;
4322         }
4323       }
4324 
4325       // Handle various non-scrollable blocks.
4326       static const FrameConstructionData sNonScrollableBlockData[2][2] = {
4327           {FULL_CTOR_FCDATA(
4328                0, &nsCSSFrameConstructor::ConstructNonScrollableBlock),
4329            FULL_CTOR_FCDATA(
4330                kCaptionCtorFlags,
4331                &nsCSSFrameConstructor::ConstructNonScrollableBlock)},
4332           {FULL_CTOR_FCDATA(
4333                FCDATA_FORCED_NON_SCROLLABLE_BLOCK,
4334                &nsCSSFrameConstructor::ConstructNonScrollableBlock),
4335            FULL_CTOR_FCDATA(
4336                FCDATA_FORCED_NON_SCROLLABLE_BLOCK | kCaptionCtorFlags,
4337                &nsCSSFrameConstructor::ConstructNonScrollableBlock)}};
4338       return &sNonScrollableBlockData[suppressScrollFrame][caption];
4339     }
4340     case StyleDisplayInside::Table: {
4341       static const FrameConstructionData data =
4342           FULL_CTOR_FCDATA(0, &nsCSSFrameConstructor::ConstructTable);
4343       return &data;
4344     }
4345     // NOTE: In the unlikely event that we add another table-part here that
4346     // has a desired-parent-type (& hence triggers table fixup), we'll need to
4347     // also update the flexbox chunk in ComputedStyle::ApplyStyleFixups().
4348     case StyleDisplayInside::TableRowGroup: {
4349       static const FrameConstructionData data = FULL_CTOR_FCDATA(
4350           FCDATA_IS_TABLE_PART | FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
4351           &nsCSSFrameConstructor::ConstructTableRowOrRowGroup);
4352       return &data;
4353     }
4354     case StyleDisplayInside::TableColumn: {
4355       static const FrameConstructionData data = FULL_CTOR_FCDATA(
4356           FCDATA_IS_TABLE_PART |
4357               FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeColGroup),
4358           &nsCSSFrameConstructor::ConstructTableCol);
4359       return &data;
4360     }
4361     case StyleDisplayInside::TableColumnGroup: {
4362       static const FrameConstructionData data =
4363           FCDATA_DECL(FCDATA_IS_TABLE_PART | FCDATA_DISALLOW_OUT_OF_FLOW |
4364                           FCDATA_SKIP_ABSPOS_PUSH |
4365                           FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
4366                       NS_NewTableColGroupFrame);
4367       return &data;
4368     }
4369     case StyleDisplayInside::TableHeaderGroup: {
4370       static const FrameConstructionData data = FULL_CTOR_FCDATA(
4371           FCDATA_IS_TABLE_PART | FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
4372           &nsCSSFrameConstructor::ConstructTableRowOrRowGroup);
4373       return &data;
4374     }
4375     case StyleDisplayInside::TableFooterGroup: {
4376       static const FrameConstructionData data = FULL_CTOR_FCDATA(
4377           FCDATA_IS_TABLE_PART | FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
4378           &nsCSSFrameConstructor::ConstructTableRowOrRowGroup);
4379       return &data;
4380     }
4381     case StyleDisplayInside::TableRow: {
4382       static const FrameConstructionData data = FULL_CTOR_FCDATA(
4383           FCDATA_IS_TABLE_PART |
4384               FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRowGroup),
4385           &nsCSSFrameConstructor::ConstructTableRowOrRowGroup);
4386       return &data;
4387     }
4388     case StyleDisplayInside::TableCell: {
4389       static const FrameConstructionData data = FULL_CTOR_FCDATA(
4390           FCDATA_IS_TABLE_PART | FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRow),
4391           &nsCSSFrameConstructor::ConstructTableCell);
4392       return &data;
4393     }
4394     case StyleDisplayInside::MozBox: {
4395       if (!aElement.IsInNativeAnonymousSubtree() &&
4396           aElement.OwnerDoc()->IsContentDocument()) {
4397         aElement.OwnerDoc()->WarnOnceAbout(Document::eMozBoxOrInlineBoxDisplay);
4398       }
4399 
4400       // If we're emulating -moz-box with flexbox, then treat it as non-XUL and
4401       // fall through (except for scrollcorners which have to be XUL becuase
4402       // their parent reflows them with BoxReflow() which means they have to get
4403       // actual-XUL frames).
4404       if (!StaticPrefs::layout_css_emulate_moz_box_with_flex() ||
4405           aElement.IsXULElement(nsGkAtoms::scrollcorner)) {
4406         static const FrameConstructionData data =
4407             SCROLLABLE_ABSPOS_CONTAINER_XUL_FCDATA(NS_NewBoxFrame);
4408         return &data;
4409       }
4410       [[fallthrough]];
4411     }
4412     case StyleDisplayInside::Flex:
4413     case StyleDisplayInside::WebkitBox: {
4414       static const FrameConstructionData nonScrollableData =
4415           FCDATA_DECL(0, NS_NewFlexContainerFrame);
4416       static const FrameConstructionData data =
4417           FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewFlexContainerFrame);
4418       return MOZ_UNLIKELY(propagatedScrollToViewport) ? &nonScrollableData
4419                                                       : &data;
4420     }
4421     case StyleDisplayInside::Grid: {
4422       static const FrameConstructionData nonScrollableData =
4423           FCDATA_DECL(0, NS_NewGridContainerFrame);
4424       static const FrameConstructionData data =
4425           FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewGridContainerFrame);
4426       return MOZ_UNLIKELY(propagatedScrollToViewport) ? &nonScrollableData
4427                                                       : &data;
4428     }
4429     case StyleDisplayInside::Ruby: {
4430       static const FrameConstructionData data[] = {
4431           FULL_CTOR_FCDATA(FCDATA_MAY_NEED_SCROLLFRAME,
4432                            &nsCSSFrameConstructor::ConstructBlockRubyFrame),
4433           FCDATA_DECL(FCDATA_IS_LINE_PARTICIPANT, NS_NewRubyFrame),
4434       };
4435       bool isInline = aDisplay.DisplayOutside() == StyleDisplayOutside::Inline;
4436       return &data[isInline];
4437     }
4438     case StyleDisplayInside::RubyBase: {
4439       static const FrameConstructionData data = FCDATA_DECL(
4440           FCDATA_IS_LINE_PARTICIPANT |
4441               FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRubyBaseContainer),
4442           NS_NewRubyBaseFrame);
4443       return &data;
4444     }
4445     case StyleDisplayInside::RubyBaseContainer: {
4446       static const FrameConstructionData data =
4447           FCDATA_DECL(FCDATA_IS_LINE_PARTICIPANT |
4448                           FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRuby),
4449                       NS_NewRubyBaseContainerFrame);
4450       return &data;
4451     }
4452     case StyleDisplayInside::RubyText: {
4453       static const FrameConstructionData data = FCDATA_DECL(
4454           FCDATA_IS_LINE_PARTICIPANT |
4455               FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRubyTextContainer),
4456           NS_NewRubyTextFrame);
4457       return &data;
4458     }
4459     case StyleDisplayInside::RubyTextContainer: {
4460       static const FrameConstructionData data =
4461           FCDATA_DECL(FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRuby),
4462                       NS_NewRubyTextContainerFrame);
4463       return &data;
4464     }
4465 #ifdef MOZ_XUL
4466     case StyleDisplayInside::MozGrid: {
4467       static const FrameConstructionData data =
4468           SCROLLABLE_XUL_FCDATA(NS_NewGridBoxFrame);
4469       return &data;
4470     }
4471     case StyleDisplayInside::MozGridGroup: {
4472       static const FrameConstructionData data =
4473           SCROLLABLE_XUL_FCDATA(NS_NewGridRowGroupFrame);
4474       return &data;
4475     }
4476     case StyleDisplayInside::MozGridLine: {
4477       static const FrameConstructionData data =
4478           SCROLLABLE_XUL_FCDATA(NS_NewGridRowLeafFrame);
4479       return &data;
4480     }
4481     case StyleDisplayInside::MozStack: {
4482       static const FrameConstructionData data =
4483           SCROLLABLE_XUL_FCDATA(NS_NewStackFrame);
4484       return &data;
4485     }
4486     case StyleDisplayInside::MozDeck: {
4487       static const FrameConstructionData data =
4488           SIMPLE_XUL_FCDATA(NS_NewDeckFrame);
4489       return &data;
4490     }
4491     case StyleDisplayInside::MozPopup: {
4492       static const FrameConstructionData data =
4493           FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_IS_POPUP |
4494                           FCDATA_SKIP_ABSPOS_PUSH,
4495                       NS_NewMenuPopupFrame);
4496       return &data;
4497     }
4498 #endif /* MOZ_XUL */
4499     default:
4500       MOZ_ASSERT_UNREACHABLE("unknown 'display' value");
4501       return nullptr;
4502   }
4503 }
4504 
ConstructScrollableBlock(nsFrameConstructorState & aState,FrameConstructionItem & aItem,nsContainerFrame * aParentFrame,const nsStyleDisplay * aDisplay,nsFrameList & aFrameList)4505 nsIFrame* nsCSSFrameConstructor::ConstructScrollableBlock(
4506     nsFrameConstructorState& aState, FrameConstructionItem& aItem,
4507     nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay,
4508     nsFrameList& aFrameList) {
4509   return ConstructScrollableBlockWithConstructor(aState, aItem, aParentFrame,
4510                                                  aDisplay, aFrameList,
4511                                                  NS_NewBlockFormattingContext);
4512 }
4513 
ConstructScrollableBlockWithConstructor(nsFrameConstructorState & aState,FrameConstructionItem & aItem,nsContainerFrame * aParentFrame,const nsStyleDisplay * aDisplay,nsFrameList & aFrameList,BlockFrameCreationFunc aConstructor)4514 nsIFrame* nsCSSFrameConstructor::ConstructScrollableBlockWithConstructor(
4515     nsFrameConstructorState& aState, FrameConstructionItem& aItem,
4516     nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay,
4517     nsFrameList& aFrameList, BlockFrameCreationFunc aConstructor) {
4518   nsIContent* const content = aItem.mContent;
4519   ComputedStyle* const computedStyle = aItem.mComputedStyle;
4520 
4521   nsContainerFrame* newFrame = nullptr;
4522   RefPtr<ComputedStyle> scrolledContentStyle = BeginBuildingScrollFrame(
4523       aState, content, computedStyle,
4524       aState.GetGeometricParent(*aDisplay, aParentFrame),
4525       PseudoStyleType::scrolledContent, false, newFrame);
4526 
4527   // Create our block frame
4528   // pass a temporary stylecontext, the correct one will be set later
4529   nsContainerFrame* scrolledFrame = aConstructor(mPresShell, computedStyle);
4530 
4531   // Make sure to AddChild before we call ConstructBlock so that we
4532   // end up before our descendants in fixed-pos lists as needed.
4533   aState.AddChild(newFrame, aFrameList, content, aParentFrame);
4534 
4535   nsFrameList blockList;
4536   ConstructBlock(
4537       aState, content, newFrame, newFrame, scrolledContentStyle, &scrolledFrame,
4538       blockList,
4539       aDisplay->IsAbsPosContainingBlock(newFrame) ? newFrame : nullptr);
4540 
4541   MOZ_ASSERT(blockList.OnlyChild() == scrolledFrame,
4542              "Scrollframe's frameList should be exactly the scrolled frame!");
4543   FinishBuildingScrollFrame(newFrame, scrolledFrame);
4544 
4545   return newFrame;
4546 }
4547 
ConstructNonScrollableBlock(nsFrameConstructorState & aState,FrameConstructionItem & aItem,nsContainerFrame * aParentFrame,const nsStyleDisplay * aDisplay,nsFrameList & aFrameList)4548 nsIFrame* nsCSSFrameConstructor::ConstructNonScrollableBlock(
4549     nsFrameConstructorState& aState, FrameConstructionItem& aItem,
4550     nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay,
4551     nsFrameList& aFrameList) {
4552   return ConstructNonScrollableBlockWithConstructor(
4553       aState, aItem, aParentFrame, aDisplay, aFrameList, NS_NewBlockFrame);
4554 }
4555 
ConstructNonScrollableBlockWithConstructor(nsFrameConstructorState & aState,FrameConstructionItem & aItem,nsContainerFrame * aParentFrame,const nsStyleDisplay * aDisplay,nsFrameList & aFrameList,BlockFrameCreationFunc aConstructor)4556 nsIFrame* nsCSSFrameConstructor::ConstructNonScrollableBlockWithConstructor(
4557     nsFrameConstructorState& aState, FrameConstructionItem& aItem,
4558     nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay,
4559     nsFrameList& aFrameList, BlockFrameCreationFunc aConstructor) {
4560   ComputedStyle* const computedStyle = aItem.mComputedStyle;
4561 
4562   // We want a block formatting context root in paginated contexts for
4563   // every block that would be scrollable in a non-paginated context.
4564   // We mark our blocks with a bit here if this condition is true, so
4565   // we can check it later in nsFrame::ApplyPaginatedOverflowClipping.
4566   bool clipPaginatedOverflow =
4567       (aItem.mFCData->mBits & FCDATA_FORCED_NON_SCROLLABLE_BLOCK) != 0;
4568   nsFrameState flags = nsFrameState(0);
4569   if ((aDisplay->IsAbsolutelyPositionedStyle() || aDisplay->IsFloatingStyle() ||
4570        aDisplay->DisplayInside() == StyleDisplayInside::FlowRoot ||
4571        clipPaginatedOverflow) &&
4572       !nsSVGUtils::IsInSVGTextSubtree(aParentFrame)) {
4573     flags = NS_BLOCK_FORMATTING_CONTEXT_STATE_BITS;
4574     if (clipPaginatedOverflow) {
4575       flags |= NS_BLOCK_CLIP_PAGINATED_OVERFLOW;
4576     }
4577   }
4578 
4579   nsContainerFrame* newFrame = aConstructor(mPresShell, computedStyle);
4580   newFrame->AddStateBits(flags);
4581   ConstructBlock(
4582       aState, aItem.mContent,
4583       aState.GetGeometricParent(*aDisplay, aParentFrame), aParentFrame,
4584       computedStyle, &newFrame, aFrameList,
4585       aDisplay->IsAbsPosContainingBlock(newFrame) ? newFrame : nullptr);
4586   return newFrame;
4587 }
4588 
InitAndRestoreFrame(const nsFrameConstructorState & aState,nsIContent * aContent,nsContainerFrame * aParentFrame,nsIFrame * aNewFrame,bool aAllowCounters)4589 void nsCSSFrameConstructor::InitAndRestoreFrame(
4590     const nsFrameConstructorState& aState, nsIContent* aContent,
4591     nsContainerFrame* aParentFrame, nsIFrame* aNewFrame, bool aAllowCounters) {
4592   MOZ_ASSERT(aNewFrame, "Null frame cannot be initialized");
4593 
4594   // Initialize the frame
4595   aNewFrame->Init(aContent, aParentFrame, nullptr);
4596   aNewFrame->AddStateBits(aState.mAdditionalStateBits);
4597 
4598   if (aState.mFrameState) {
4599     // Restore frame state for just the newly created frame.
4600     RestoreFrameStateFor(aNewFrame, aState.mFrameState);
4601   }
4602 
4603   if (aAllowCounters && mCounterManager.AddCounterChanges(aNewFrame)) {
4604     CountersDirty();
4605   }
4606 }
4607 
ResolveComputedStyle(nsIContent * aContent)4608 already_AddRefed<ComputedStyle> nsCSSFrameConstructor::ResolveComputedStyle(
4609     nsIContent* aContent) {
4610   if (auto* element = Element::FromNode(aContent)) {
4611     return ServoStyleSet::ResolveServoStyle(*element);
4612   }
4613 
4614   MOZ_ASSERT(aContent->IsText(),
4615              "shouldn't waste time creating ComputedStyles for "
4616              "comments and processing instructions");
4617 
4618   Element* parent = aContent->GetFlattenedTreeParentElement();
4619   MOZ_ASSERT(parent, "Text out of the flattened tree?");
4620 
4621   // FIXME(emilio): The const_cast is unfortunate, but it's not worse than what
4622   // we did before.
4623   //
4624   // We could use ResolveServoStyle, but that would involve extra unnecessary
4625   // refcount traffic...
4626   auto* parentStyle =
4627       const_cast<ComputedStyle*>(Servo_Element_GetMaybeOutOfDateStyle(parent));
4628   MOZ_ASSERT(parentStyle,
4629              "How are we inserting text frames in an unstyled element?");
4630   return mPresShell->StyleSet()->ResolveStyleForText(aContent, parentStyle);
4631 }
4632 
4633 // MathML Mod - RBS
FlushAccumulatedBlock(nsFrameConstructorState & aState,nsIContent * aContent,nsContainerFrame * aParentFrame,nsFrameList & aBlockList,nsFrameList & aNewList)4634 void nsCSSFrameConstructor::FlushAccumulatedBlock(
4635     nsFrameConstructorState& aState, nsIContent* aContent,
4636     nsContainerFrame* aParentFrame, nsFrameList& aBlockList,
4637     nsFrameList& aNewList) {
4638   if (aBlockList.IsEmpty()) {
4639     // Nothing to do
4640     return;
4641   }
4642 
4643   auto anonPseudo = PseudoStyleType::mozMathMLAnonymousBlock;
4644 
4645   ComputedStyle* parentContext =
4646       nsFrame::CorrectStyleParentFrame(aParentFrame, anonPseudo)->Style();
4647   ServoStyleSet* styleSet = mPresShell->StyleSet();
4648   RefPtr<ComputedStyle> blockContext;
4649   blockContext =
4650       styleSet->ResolveInheritingAnonymousBoxStyle(anonPseudo, parentContext);
4651 
4652   // then, create a block frame that will wrap the child frames. Make it a
4653   // MathML frame so that Get(Absolute/Float)ContainingBlockFor know that this
4654   // is not a suitable block.
4655   nsContainerFrame* blockFrame =
4656       NS_NewMathMLmathBlockFrame(mPresShell, blockContext);
4657 
4658   InitAndRestoreFrame(aState, aContent, aParentFrame, blockFrame);
4659   ReparentFrames(this, blockFrame, aBlockList, false);
4660   // We have to walk over aBlockList before we hand it over to blockFrame.
4661   for (nsIFrame* f : aBlockList) {
4662     f->SetParentIsWrapperAnonBox();
4663   }
4664   // abs-pos and floats are disabled in MathML children so we don't have to
4665   // worry about messing up those.
4666   blockFrame->SetInitialChildList(kPrincipalList, aBlockList);
4667   NS_ASSERTION(aBlockList.IsEmpty(), "What happened?");
4668   aBlockList.Clear();
4669   aNewList.AppendFrame(nullptr, blockFrame);
4670 }
4671 
4672 // Only <math> elements can be floated or positioned.  All other MathML
4673 // should be in-flow.
4674 #define SIMPLE_MATHML_CREATE(_tag, _func)                                 \
4675   {                                                                       \
4676     nsGkAtoms::_tag, FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW |            \
4677                                      FCDATA_FORCE_NULL_ABSPOS_CONTAINER | \
4678                                      FCDATA_WRAP_KIDS_IN_BLOCKS,          \
4679                                  _func)                                   \
4680   }
4681 
4682 /* static */
4683 const nsCSSFrameConstructor::FrameConstructionData*
FindMathMLData(const Element & aElement,ComputedStyle & aStyle)4684 nsCSSFrameConstructor::FindMathMLData(const Element& aElement,
4685                                       ComputedStyle& aStyle) {
4686   MOZ_ASSERT(aElement.IsMathMLElement());
4687 
4688   nsAtom* tag = aElement.NodeInfo()->NameAtom();
4689 
4690   // Handle <math> specially, because it sometimes produces inlines
4691   if (tag == nsGkAtoms::math) {
4692     // The IsBlockOutsideStyle() check must match what
4693     // specified::Display::equivalent_block_display is checking for
4694     // already-block-outside things. Though the behavior here for the
4695     // display:table case is pretty weird...
4696     if (aStyle.StyleDisplay()->IsBlockOutsideStyle()) {
4697       static const FrameConstructionData sBlockMathData = FCDATA_DECL(
4698           FCDATA_FORCE_NULL_ABSPOS_CONTAINER | FCDATA_WRAP_KIDS_IN_BLOCKS,
4699           NS_NewMathMLmathBlockFrame);
4700       return &sBlockMathData;
4701     }
4702 
4703     static const FrameConstructionData sInlineMathData =
4704         FCDATA_DECL(FCDATA_FORCE_NULL_ABSPOS_CONTAINER |
4705                         FCDATA_IS_LINE_PARTICIPANT | FCDATA_WRAP_KIDS_IN_BLOCKS,
4706                     NS_NewMathMLmathInlineFrame);
4707     return &sInlineMathData;
4708   }
4709 
4710   if (!StaticPrefs::mathml_mfenced_element_disabled() &&
4711       tag == nsGkAtoms::mfenced_) {
4712     // These flags are the same as those of SIMPLE_MATHML_CREATE.
4713     static const FrameConstructionData sMathFencedData = FCDATA_DECL(
4714         FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_FORCE_NULL_ABSPOS_CONTAINER |
4715             FCDATA_WRAP_KIDS_IN_BLOCKS,
4716         NS_NewMathMLmfencedFrame);
4717     return &sMathFencedData;
4718   }
4719 
4720   static const FrameConstructionDataByTag sMathMLData[] = {
4721       SIMPLE_MATHML_CREATE(annotation_, NS_NewMathMLTokenFrame),
4722       SIMPLE_MATHML_CREATE(annotation_xml_, NS_NewMathMLmrowFrame),
4723       SIMPLE_MATHML_CREATE(mi_, NS_NewMathMLTokenFrame),
4724       SIMPLE_MATHML_CREATE(mn_, NS_NewMathMLTokenFrame),
4725       SIMPLE_MATHML_CREATE(ms_, NS_NewMathMLTokenFrame),
4726       SIMPLE_MATHML_CREATE(mtext_, NS_NewMathMLTokenFrame),
4727       SIMPLE_MATHML_CREATE(mo_, NS_NewMathMLmoFrame),
4728       SIMPLE_MATHML_CREATE(mfrac_, NS_NewMathMLmfracFrame),
4729       SIMPLE_MATHML_CREATE(msup_, NS_NewMathMLmmultiscriptsFrame),
4730       SIMPLE_MATHML_CREATE(msub_, NS_NewMathMLmmultiscriptsFrame),
4731       SIMPLE_MATHML_CREATE(msubsup_, NS_NewMathMLmmultiscriptsFrame),
4732       SIMPLE_MATHML_CREATE(munder_, NS_NewMathMLmunderoverFrame),
4733       SIMPLE_MATHML_CREATE(mover_, NS_NewMathMLmunderoverFrame),
4734       SIMPLE_MATHML_CREATE(munderover_, NS_NewMathMLmunderoverFrame),
4735       SIMPLE_MATHML_CREATE(mphantom_, NS_NewMathMLmrowFrame),
4736       SIMPLE_MATHML_CREATE(mpadded_, NS_NewMathMLmpaddedFrame),
4737       SIMPLE_MATHML_CREATE(mspace_, NS_NewMathMLmspaceFrame),
4738       SIMPLE_MATHML_CREATE(none, NS_NewMathMLmspaceFrame),
4739       SIMPLE_MATHML_CREATE(mprescripts_, NS_NewMathMLmspaceFrame),
4740       SIMPLE_MATHML_CREATE(mfenced_, NS_NewMathMLmrowFrame),
4741       SIMPLE_MATHML_CREATE(mmultiscripts_, NS_NewMathMLmmultiscriptsFrame),
4742       SIMPLE_MATHML_CREATE(mstyle_, NS_NewMathMLmrowFrame),
4743       SIMPLE_MATHML_CREATE(msqrt_, NS_NewMathMLmsqrtFrame),
4744       SIMPLE_MATHML_CREATE(mroot_, NS_NewMathMLmrootFrame),
4745       SIMPLE_MATHML_CREATE(maction_, NS_NewMathMLmactionFrame),
4746       SIMPLE_MATHML_CREATE(mrow_, NS_NewMathMLmrowFrame),
4747       SIMPLE_MATHML_CREATE(merror_, NS_NewMathMLmrowFrame),
4748       SIMPLE_MATHML_CREATE(menclose_, NS_NewMathMLmencloseFrame),
4749       SIMPLE_MATHML_CREATE(semantics_, NS_NewMathMLsemanticsFrame)};
4750 
4751   return FindDataByTag(aElement, aStyle, sMathMLData, ArrayLength(sMathMLData));
4752 }
4753 
ConstructFrameWithAnonymousChild(nsFrameConstructorState & aState,FrameConstructionItem & aItem,nsContainerFrame * aParentFrame,nsFrameList & aFrameList,ContainerFrameCreationFunc aConstructor,ContainerFrameCreationFunc aInnerConstructor,PseudoStyleType aInnerPseudo,bool aCandidateRootFrame)4754 nsContainerFrame* nsCSSFrameConstructor::ConstructFrameWithAnonymousChild(
4755     nsFrameConstructorState& aState, FrameConstructionItem& aItem,
4756     nsContainerFrame* aParentFrame, nsFrameList& aFrameList,
4757     ContainerFrameCreationFunc aConstructor,
4758     ContainerFrameCreationFunc aInnerConstructor, PseudoStyleType aInnerPseudo,
4759     bool aCandidateRootFrame) {
4760   nsIContent* const content = aItem.mContent;
4761   ComputedStyle* const computedStyle = aItem.mComputedStyle;
4762 
4763   // Create the outer frame:
4764   nsContainerFrame* newFrame = aConstructor(mPresShell, computedStyle);
4765 
4766   InitAndRestoreFrame(aState, content,
4767                       aCandidateRootFrame
4768                           ? aState.GetGeometricParent(
4769                                 *computedStyle->StyleDisplay(), aParentFrame)
4770                           : aParentFrame,
4771                       newFrame);
4772   newFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
4773 
4774   // Create the pseudo SC for the anonymous wrapper child as a child of the SC:
4775   RefPtr<ComputedStyle> scForAnon;
4776   scForAnon = mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
4777       aInnerPseudo, computedStyle);
4778 
4779   // Create the anonymous inner wrapper frame
4780   nsContainerFrame* innerFrame = aInnerConstructor(mPresShell, scForAnon);
4781 
4782   InitAndRestoreFrame(aState, content, newFrame, innerFrame);
4783 
4784   // Put the newly created frames into the right child list
4785   SetInitialSingleChild(newFrame, innerFrame);
4786 
4787   aState.AddChild(newFrame, aFrameList, content, aParentFrame,
4788                   aCandidateRootFrame, aCandidateRootFrame);
4789 
4790   if (!mRootElementFrame && aCandidateRootFrame) {
4791     // The frame we're constructing will be the root element frame.
4792     SetRootElementFrameAndConstructCanvasAnonContent(newFrame, aState,
4793                                                      aFrameList);
4794   }
4795 
4796   nsFrameList childList;
4797 
4798   // Process children
4799   if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) {
4800     ConstructFramesFromItemList(
4801         aState, aItem.mChildItems, innerFrame,
4802         aItem.mFCData->mBits & FCDATA_IS_WRAPPER_ANON_BOX, childList);
4803   } else {
4804     ProcessChildren(aState, content, computedStyle, innerFrame, true, childList,
4805                     false);
4806   }
4807 
4808   // Set the inner wrapper frame's initial primary list
4809   innerFrame->SetInitialChildList(kPrincipalList, childList);
4810 
4811   return newFrame;
4812 }
4813 
ConstructOuterSVG(nsFrameConstructorState & aState,FrameConstructionItem & aItem,nsContainerFrame * aParentFrame,const nsStyleDisplay * aDisplay,nsFrameList & aFrameList)4814 nsIFrame* nsCSSFrameConstructor::ConstructOuterSVG(
4815     nsFrameConstructorState& aState, FrameConstructionItem& aItem,
4816     nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay,
4817     nsFrameList& aFrameList) {
4818   return ConstructFrameWithAnonymousChild(
4819       aState, aItem, aParentFrame, aFrameList, NS_NewSVGOuterSVGFrame,
4820       NS_NewSVGOuterSVGAnonChildFrame, PseudoStyleType::mozSVGOuterSVGAnonChild,
4821       true);
4822 }
4823 
ConstructMarker(nsFrameConstructorState & aState,FrameConstructionItem & aItem,nsContainerFrame * aParentFrame,const nsStyleDisplay * aDisplay,nsFrameList & aFrameList)4824 nsIFrame* nsCSSFrameConstructor::ConstructMarker(
4825     nsFrameConstructorState& aState, FrameConstructionItem& aItem,
4826     nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay,
4827     nsFrameList& aFrameList) {
4828   return ConstructFrameWithAnonymousChild(
4829       aState, aItem, aParentFrame, aFrameList, NS_NewSVGMarkerFrame,
4830       NS_NewSVGMarkerAnonChildFrame, PseudoStyleType::mozSVGMarkerAnonChild,
4831       false);
4832 }
4833 
4834 // Only outer <svg> elements can be floated or positioned.  All other SVG
4835 // should be in-flow.
4836 #define SIMPLE_SVG_FCDATA(_func)                                      \
4837   FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_SKIP_ABSPOS_PUSH | \
4838                   FCDATA_DISALLOW_GENERATED_CONTENT,                  \
4839               _func)
4840 #define SIMPLE_SVG_CREATE(_tag, _func) \
4841   { nsGkAtoms::_tag, SIMPLE_SVG_FCDATA(_func) }
4842 
IsFilterPrimitiveChildTag(const nsAtom * aTag)4843 static bool IsFilterPrimitiveChildTag(const nsAtom* aTag) {
4844   return aTag == nsGkAtoms::feDistantLight || aTag == nsGkAtoms::fePointLight ||
4845          aTag == nsGkAtoms::feSpotLight || aTag == nsGkAtoms::feFuncR ||
4846          aTag == nsGkAtoms::feFuncG || aTag == nsGkAtoms::feFuncB ||
4847          aTag == nsGkAtoms::feFuncA || aTag == nsGkAtoms::feMergeNode;
4848 }
4849 
4850 /* static */
4851 const nsCSSFrameConstructor::FrameConstructionData*
FindSVGData(const Element & aElement,nsIFrame * aParentFrame,bool aIsWithinSVGText,bool aAllowsTextPathChild,ComputedStyle & aStyle)4852 nsCSSFrameConstructor::FindSVGData(const Element& aElement,
4853                                    nsIFrame* aParentFrame,
4854                                    bool aIsWithinSVGText,
4855                                    bool aAllowsTextPathChild,
4856                                    ComputedStyle& aStyle) {
4857   MOZ_ASSERT(aElement.IsSVGElement());
4858 
4859   static const FrameConstructionData sSuppressData = SUPPRESS_FCDATA();
4860   static const FrameConstructionData sContainerData =
4861       SIMPLE_SVG_FCDATA(NS_NewSVGContainerFrame);
4862 
4863   bool parentIsSVG = aIsWithinSVGText;
4864   nsIContent* parentContent =
4865       aParentFrame ? aParentFrame->GetContent() : nullptr;
4866 
4867   nsAtom* tag = aElement.NodeInfo()->NameAtom();
4868 
4869   // XXXbz should this really be based on the tag of the parent frame's content?
4870   // Should it not be based on the type of the parent frame (e.g. whether it's
4871   // an SVG frame)?
4872   if (parentContent) {
4873     // It's not clear whether the SVG spec intends to allow any SVG
4874     // content within svg:foreignObject at all (SVG 1.1, section
4875     // 23.2), but if it does, it better be svg:svg.  So given that
4876     // we're allowing it, treat it as a non-SVG parent.
4877     parentIsSVG =
4878         parentContent->IsSVGElement() &&
4879         parentContent->NodeInfo()->NameAtom() != nsGkAtoms::foreignObject;
4880   }
4881 
4882   if ((tag != nsGkAtoms::svg && !parentIsSVG) ||
4883       (tag == nsGkAtoms::desc || tag == nsGkAtoms::title ||
4884        tag == nsGkAtoms::metadata)) {
4885     // Sections 5.1 and G.4 of SVG 1.1 say that SVG elements other than
4886     // svg:svg not contained within svg:svg are incorrect, although they
4887     // don't seem to specify error handling.  Ignore them, since many of
4888     // our frame classes can't deal.  It *may* be that the document
4889     // should at that point be considered in error according to F.2, but
4890     // it's hard to tell.
4891     //
4892     // Style mutation can't change this situation, so don't bother
4893     // adding to the undisplayed content map.
4894     //
4895     // We don't currently handle any UI for desc/title/metadata
4896     return &sSuppressData;
4897   }
4898 
4899   // We don't need frames for animation elements
4900   if (aElement.IsNodeOfType(nsINode::eANIMATION)) {
4901     return &sSuppressData;
4902   }
4903 
4904   if (tag == nsGkAtoms::svg && !parentIsSVG) {
4905     // We need outer <svg> elements to have an nsSVGOuterSVGFrame regardless
4906     // of whether they fail conditional processing attributes, since various
4907     // SVG frames assume that one exists.  We handle the non-rendering
4908     // of failing outer <svg> element contents like <switch> statements,
4909     // and do the PassesConditionalProcessingTests call in
4910     // nsSVGOuterSVGFrame::Init.
4911     static const FrameConstructionData sOuterSVGData =
4912         FULL_CTOR_FCDATA(0, &nsCSSFrameConstructor::ConstructOuterSVG);
4913     return &sOuterSVGData;
4914   }
4915 
4916   if (tag == nsGkAtoms::marker) {
4917     static const FrameConstructionData sMarkerSVGData =
4918         FULL_CTOR_FCDATA(0, &nsCSSFrameConstructor::ConstructMarker);
4919     return &sMarkerSVGData;
4920   }
4921 
4922   nsCOMPtr<SVGTests> tests = do_QueryInterface(const_cast<Element*>(&aElement));
4923   if (tests && !tests->PassesConditionalProcessingTests()) {
4924     // Elements with failing conditional processing attributes never get
4925     // rendered.  Note that this is not where we select which frame in a
4926     // <switch> to render!  That happens in nsSVGSwitchFrame::PaintSVG.
4927     if (aIsWithinSVGText) {
4928       // SVGTextFrame doesn't handle conditional processing attributes,
4929       // so don't create frames for descendants of <text> with failing
4930       // attributes.  We need frames not to be created so that text layout
4931       // is correct.
4932       return &sSuppressData;
4933     }
4934     // If we're not inside <text>, create an nsSVGContainerFrame (which is a
4935     // frame that doesn't render) so that paint servers can still be referenced,
4936     // even if they live inside an element with failing conditional processing
4937     // attributes.
4938     return &sContainerData;
4939   }
4940 
4941   // Ensure that a stop frame is a child of a gradient and that gradients
4942   // can only have stop children.
4943   bool parentIsGradient =
4944       aParentFrame && (aParentFrame->IsSVGLinearGradientFrame() ||
4945                        aParentFrame->IsSVGRadialGradientFrame());
4946   bool stop = (tag == nsGkAtoms::stop);
4947   if ((parentIsGradient && !stop) || (!parentIsGradient && stop)) {
4948     return &sSuppressData;
4949   }
4950 
4951   // Prevent bad frame types being children of filters or parents of filter
4952   // primitives.  If aParentFrame is null, we know that the frame that will
4953   // be created will be an nsInlineFrame, so it can never be a filter.
4954   bool parentIsFilter = aParentFrame && aParentFrame->IsSVGFilterFrame();
4955   bool filterPrimitive = aElement.IsNodeOfType(nsINode::eFILTER);
4956   if ((parentIsFilter && !filterPrimitive) ||
4957       (!parentIsFilter && filterPrimitive)) {
4958     return &sSuppressData;
4959   }
4960 
4961   // Prevent bad frame types being children of filter primitives or parents of
4962   // filter primitive children.  If aParentFrame is null, we know that the frame
4963   // that will be created will be an nsInlineFrame, so it can never be a filter
4964   // primitive.
4965   bool parentIsFEContainerFrame =
4966       aParentFrame && aParentFrame->IsSVGFEContainerFrame();
4967   if ((parentIsFEContainerFrame && !IsFilterPrimitiveChildTag(tag)) ||
4968       (!parentIsFEContainerFrame && IsFilterPrimitiveChildTag(tag))) {
4969     return &sSuppressData;
4970   }
4971 
4972   // Special cases for text/tspan/textPath, because the kind of frame
4973   // they get depends on the parent frame.  We ignore 'a' elements when
4974   // determining the parent, however.
4975   if (aIsWithinSVGText) {
4976     // If aIsWithinSVGText is true, then we know that the "SVG text uses
4977     // CSS frames" pref was true when this SVG fragment was first constructed.
4978     //
4979     // FIXME(bug 1588477) Don't render stuff in display: contents / Shadow DOM
4980     // subtrees, because TextCorrespondenceRecorder in the SVG text code doesn't
4981     // really know how to deal with it. This kinda sucks. :(
4982     if (aParentFrame && aParentFrame->GetContent() != aElement.GetParent()) {
4983       return &sSuppressData;
4984     }
4985 
4986     // We don't use ConstructInline because we want different behavior
4987     // for generated content.
4988     static const FrameConstructionData sTSpanData = FCDATA_DECL(
4989         FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_SKIP_ABSPOS_PUSH |
4990             FCDATA_DISALLOW_GENERATED_CONTENT | FCDATA_IS_LINE_PARTICIPANT |
4991             FCDATA_IS_INLINE | FCDATA_USE_CHILD_ITEMS,
4992         NS_NewInlineFrame);
4993     if (tag == nsGkAtoms::textPath) {
4994       if (aAllowsTextPathChild) {
4995         return &sTSpanData;
4996       }
4997     } else if (tag == nsGkAtoms::tspan || tag == nsGkAtoms::a) {
4998       return &sTSpanData;
4999     }
5000     return &sSuppressData;
5001   } else if (tag == nsGkAtoms::tspan || tag == nsGkAtoms::textPath) {
5002     return &sSuppressData;
5003   }
5004 
5005   static const FrameConstructionDataByTag sSVGData[] = {
5006       SIMPLE_SVG_CREATE(svg, NS_NewSVGInnerSVGFrame),
5007       SIMPLE_SVG_CREATE(g, NS_NewSVGGFrame),
5008       SIMPLE_SVG_CREATE(svgSwitch, NS_NewSVGSwitchFrame),
5009       SIMPLE_SVG_CREATE(symbol, NS_NewSVGSymbolFrame),
5010       SIMPLE_SVG_CREATE(polygon, NS_NewSVGGeometryFrame),
5011       SIMPLE_SVG_CREATE(polyline, NS_NewSVGGeometryFrame),
5012       SIMPLE_SVG_CREATE(circle, NS_NewSVGGeometryFrame),
5013       SIMPLE_SVG_CREATE(ellipse, NS_NewSVGGeometryFrame),
5014       SIMPLE_SVG_CREATE(line, NS_NewSVGGeometryFrame),
5015       SIMPLE_SVG_CREATE(rect, NS_NewSVGGeometryFrame),
5016       SIMPLE_SVG_CREATE(path, NS_NewSVGGeometryFrame),
5017       SIMPLE_SVG_CREATE(defs, NS_NewSVGContainerFrame),
5018       SIMPLE_SVG_CREATE(generic_, NS_NewSVGGenericContainerFrame),
5019       {nsGkAtoms::text,
5020        FCDATA_WITH_WRAPPING_BLOCK(
5021            FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_ALLOW_BLOCK_STYLES,
5022            NS_NewSVGTextFrame, PseudoStyleType::mozSVGText)},
5023       {nsGkAtoms::foreignObject,
5024        FCDATA_WITH_WRAPPING_BLOCK(FCDATA_DISALLOW_OUT_OF_FLOW,
5025                                   NS_NewSVGForeignObjectFrame,
5026                                   PseudoStyleType::mozSVGForeignContent)},
5027       SIMPLE_SVG_CREATE(a, NS_NewSVGAFrame),
5028       SIMPLE_SVG_CREATE(linearGradient, NS_NewSVGLinearGradientFrame),
5029       SIMPLE_SVG_CREATE(radialGradient, NS_NewSVGRadialGradientFrame),
5030       SIMPLE_SVG_CREATE(stop, NS_NewSVGStopFrame),
5031       SIMPLE_SVG_CREATE(use, NS_NewSVGUseFrame),
5032       SIMPLE_SVG_CREATE(view, NS_NewSVGViewFrame),
5033       SIMPLE_SVG_CREATE(image, NS_NewSVGImageFrame),
5034       SIMPLE_SVG_CREATE(clipPath, NS_NewSVGClipPathFrame),
5035       SIMPLE_SVG_CREATE(filter, NS_NewSVGFilterFrame),
5036       SIMPLE_SVG_CREATE(pattern, NS_NewSVGPatternFrame),
5037       SIMPLE_SVG_CREATE(mask, NS_NewSVGMaskFrame),
5038       SIMPLE_SVG_CREATE(feDistantLight, NS_NewSVGFEUnstyledLeafFrame),
5039       SIMPLE_SVG_CREATE(fePointLight, NS_NewSVGFEUnstyledLeafFrame),
5040       SIMPLE_SVG_CREATE(feSpotLight, NS_NewSVGFEUnstyledLeafFrame),
5041       SIMPLE_SVG_CREATE(feBlend, NS_NewSVGFELeafFrame),
5042       SIMPLE_SVG_CREATE(feColorMatrix, NS_NewSVGFELeafFrame),
5043       SIMPLE_SVG_CREATE(feFuncR, NS_NewSVGFEUnstyledLeafFrame),
5044       SIMPLE_SVG_CREATE(feFuncG, NS_NewSVGFEUnstyledLeafFrame),
5045       SIMPLE_SVG_CREATE(feFuncB, NS_NewSVGFEUnstyledLeafFrame),
5046       SIMPLE_SVG_CREATE(feFuncA, NS_NewSVGFEUnstyledLeafFrame),
5047       SIMPLE_SVG_CREATE(feComposite, NS_NewSVGFELeafFrame),
5048       SIMPLE_SVG_CREATE(feComponentTransfer, NS_NewSVGFEContainerFrame),
5049       SIMPLE_SVG_CREATE(feConvolveMatrix, NS_NewSVGFELeafFrame),
5050       SIMPLE_SVG_CREATE(feDiffuseLighting, NS_NewSVGFEContainerFrame),
5051       SIMPLE_SVG_CREATE(feDisplacementMap, NS_NewSVGFELeafFrame),
5052       SIMPLE_SVG_CREATE(feDropShadow, NS_NewSVGFELeafFrame),
5053       SIMPLE_SVG_CREATE(feFlood, NS_NewSVGFELeafFrame),
5054       SIMPLE_SVG_CREATE(feGaussianBlur, NS_NewSVGFELeafFrame),
5055       SIMPLE_SVG_CREATE(feImage, NS_NewSVGFEImageFrame),
5056       SIMPLE_SVG_CREATE(feMerge, NS_NewSVGFEContainerFrame),
5057       SIMPLE_SVG_CREATE(feMergeNode, NS_NewSVGFEUnstyledLeafFrame),
5058       SIMPLE_SVG_CREATE(feMorphology, NS_NewSVGFELeafFrame),
5059       SIMPLE_SVG_CREATE(feOffset, NS_NewSVGFELeafFrame),
5060       SIMPLE_SVG_CREATE(feSpecularLighting, NS_NewSVGFEContainerFrame),
5061       SIMPLE_SVG_CREATE(feTile, NS_NewSVGFELeafFrame),
5062       SIMPLE_SVG_CREATE(feTurbulence, NS_NewSVGFELeafFrame)};
5063 
5064   const FrameConstructionData* data =
5065       FindDataByTag(aElement, aStyle, sSVGData, ArrayLength(sSVGData));
5066 
5067   if (!data) {
5068     data = &sContainerData;
5069   }
5070 
5071   return data;
5072 }
5073 
AddPageBreakItem(nsIContent * aContent,FrameConstructionItemList & aItems)5074 void nsCSSFrameConstructor::AddPageBreakItem(
5075     nsIContent* aContent, FrameConstructionItemList& aItems) {
5076   RefPtr<ComputedStyle> pseudoStyle =
5077       mPresShell->StyleSet()->ResolveNonInheritingAnonymousBoxStyle(
5078           PseudoStyleType::pageBreak);
5079 
5080   MOZ_ASSERT(pseudoStyle->StyleDisplay()->mDisplay == StyleDisplay::Block,
5081              "Unexpected display");
5082 
5083   static const FrameConstructionData sPageBreakData =
5084       FCDATA_DECL(FCDATA_SKIP_FRAMESET, NS_NewPageBreakFrame);
5085 
5086   aItems.AppendItem(this, &sPageBreakData, aContent, pseudoStyle.forget(),
5087                     true);
5088 }
5089 
ShouldCreateItemsForChild(nsFrameConstructorState & aState,nsIContent * aContent,nsContainerFrame * aParentFrame)5090 bool nsCSSFrameConstructor::ShouldCreateItemsForChild(
5091     nsFrameConstructorState& aState, nsIContent* aContent,
5092     nsContainerFrame* aParentFrame) {
5093   aContent->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
5094   // XXX the GetContent() != aContent check is needed due to bug 135040.
5095   // Remove it once that's fixed.
5096   if (aContent->GetPrimaryFrame() &&
5097       aContent->GetPrimaryFrame()->GetContent() == aContent &&
5098       !aState.mCreatingExtraFrames) {
5099     MOZ_ASSERT(false,
5100                "asked to create frame construction item for a node that "
5101                "already has a frame");
5102     return false;
5103   }
5104 
5105   // don't create a whitespace frame if aParent doesn't want it
5106   if (!NeedFrameFor(aState, aParentFrame, aContent)) {
5107     return false;
5108   }
5109 
5110   // never create frames for comments or PIs
5111   if (aContent->IsComment() || aContent->IsProcessingInstruction()) {
5112     return false;
5113   }
5114 
5115   return true;
5116 }
5117 
DoAddFrameConstructionItems(nsFrameConstructorState & aState,nsIContent * aContent,ComputedStyle * aComputedStyle,bool aSuppressWhiteSpaceOptimizations,nsContainerFrame * aParentFrame,FrameConstructionItemList & aItems,ItemFlags aFlags)5118 void nsCSSFrameConstructor::DoAddFrameConstructionItems(
5119     nsFrameConstructorState& aState, nsIContent* aContent,
5120     ComputedStyle* aComputedStyle, bool aSuppressWhiteSpaceOptimizations,
5121     nsContainerFrame* aParentFrame, FrameConstructionItemList& aItems,
5122     ItemFlags aFlags) {
5123   auto flags = aFlags + ItemFlag::AllowPageBreak;
5124   if (aParentFrame) {
5125     if (nsSVGUtils::IsInSVGTextSubtree(aParentFrame)) {
5126       flags += ItemFlag::IsWithinSVGText;
5127     }
5128     if (aParentFrame->IsBlockFrame() && aParentFrame->GetParent() &&
5129         aParentFrame->GetParent()->IsSVGTextFrame()) {
5130       flags += ItemFlag::AllowTextPathChild;
5131     }
5132   }
5133   AddFrameConstructionItemsInternal(aState, aContent, aParentFrame,
5134                                     aSuppressWhiteSpaceOptimizations,
5135                                     aComputedStyle, flags, aItems);
5136 }
5137 
AddFrameConstructionItems(nsFrameConstructorState & aState,nsIContent * aContent,bool aSuppressWhiteSpaceOptimizations,const InsertionPoint & aInsertion,FrameConstructionItemList & aItems,ItemFlags aFlags)5138 void nsCSSFrameConstructor::AddFrameConstructionItems(
5139     nsFrameConstructorState& aState, nsIContent* aContent,
5140     bool aSuppressWhiteSpaceOptimizations, const InsertionPoint& aInsertion,
5141     FrameConstructionItemList& aItems, ItemFlags aFlags) {
5142   nsContainerFrame* parentFrame = aInsertion.mParentFrame;
5143   if (!ShouldCreateItemsForChild(aState, aContent, parentFrame)) {
5144     return;
5145   }
5146   RefPtr<ComputedStyle> computedStyle = ResolveComputedStyle(aContent);
5147   DoAddFrameConstructionItems(aState, aContent, computedStyle,
5148                               aSuppressWhiteSpaceOptimizations, parentFrame,
5149                               aItems, aFlags);
5150 }
5151 
5152 // Whether we should suppress frames for a child under a <select> frame.
5153 //
5154 // Never create frames for non-option/optgroup kids of <select> and non-option
5155 // kids of <optgroup> inside a <select>.
ShouldSuppressFrameInSelect(const nsIContent * aParent,const nsIContent & aChild)5156 static bool ShouldSuppressFrameInSelect(const nsIContent* aParent,
5157                                         const nsIContent& aChild) {
5158   if (!aParent ||
5159       !aParent->IsAnyOfHTMLElements(nsGkAtoms::select, nsGkAtoms::optgroup,
5160                                     nsGkAtoms::option)) {
5161     return false;
5162   }
5163 
5164   // Options with labels have their label text added in ::before by forms.css.
5165   // Suppress frames for their child text.
5166   if (aParent->IsHTMLElement(nsGkAtoms::option) &&
5167       !aChild.IsRootOfNativeAnonymousSubtree()) {
5168     return aParent->AsElement()->HasNonEmptyAttr(nsGkAtoms::label);
5169   }
5170 
5171   // If we're in any display: contents subtree, just suppress the frame.
5172   //
5173   // We can't be regular NAC, since display: contents has no frame to generate
5174   // them off.
5175   if (aChild.GetParent() != aParent) {
5176     return true;
5177   }
5178 
5179   // Option is always fine.
5180   if (aChild.IsHTMLElement(nsGkAtoms::option)) {
5181     return false;
5182   }
5183 
5184   // <optgroup> is OK in <select> but not in <optgroup>.
5185   if (aChild.IsHTMLElement(nsGkAtoms::optgroup) &&
5186       aParent->IsHTMLElement(nsGkAtoms::select)) {
5187     return false;
5188   }
5189 
5190   // Allow native anonymous content no matter what.
5191   if (aChild.IsRootOfNativeAnonymousSubtree()) {
5192     return false;
5193   }
5194 
5195   return true;
5196 }
5197 
ShouldSuppressFrameInNonOpenDetails(const HTMLDetailsElement * aDetails,ComputedStyle * aComputedStyle,const nsIContent & aChild)5198 static bool ShouldSuppressFrameInNonOpenDetails(
5199     const HTMLDetailsElement* aDetails, ComputedStyle* aComputedStyle,
5200     const nsIContent& aChild) {
5201   if (!aDetails || aDetails->Open()) {
5202     return false;
5203   }
5204 
5205   if (aChild.GetParent() != aDetails) {
5206     return true;
5207   }
5208 
5209   auto* summary = HTMLSummaryElement::FromNode(aChild);
5210   if (summary && summary->IsMainSummary()) {
5211     return false;
5212   }
5213 
5214   // Don't suppress NAC, unless it's a ::before, inside ::marker, or ::after.
5215   if (aChild.IsRootOfNativeAnonymousSubtree() &&
5216       !(aChild.IsGeneratedContentContainerForMarker() &&
5217         aComputedStyle->StyleList()->mListStylePosition ==
5218             NS_STYLE_LIST_STYLE_POSITION_INSIDE) &&
5219       !aChild.IsGeneratedContentContainerForBefore() &&
5220       !aChild.IsGeneratedContentContainerForAfter()) {
5221     return false;
5222   }
5223 
5224   return true;
5225 }
5226 
5227 const nsCSSFrameConstructor::FrameConstructionData*
FindDataForContent(nsIContent & aContent,ComputedStyle & aStyle,nsIFrame * aParentFrame,ItemFlags aFlags)5228 nsCSSFrameConstructor::FindDataForContent(nsIContent& aContent,
5229                                           ComputedStyle& aStyle,
5230                                           nsIFrame* aParentFrame,
5231                                           ItemFlags aFlags) {
5232   MOZ_ASSERT(aStyle.StyleDisplay()->mDisplay != StyleDisplay::None &&
5233                  aStyle.StyleDisplay()->mDisplay != StyleDisplay::Contents,
5234              "These two special display values should be handled earlier");
5235 
5236   if (auto* text = Text::FromNode(aContent)) {
5237     return FindTextData(*text, aParentFrame);
5238   }
5239 
5240   return FindElementData(*aContent.AsElement(), aStyle, aParentFrame, aFlags);
5241 }
5242 
5243 const nsCSSFrameConstructor::FrameConstructionData*
FindElementData(const Element & aElement,ComputedStyle & aStyle,nsIFrame * aParentFrame,ItemFlags aFlags)5244 nsCSSFrameConstructor::FindElementData(const Element& aElement,
5245                                        ComputedStyle& aStyle,
5246                                        nsIFrame* aParentFrame,
5247                                        ItemFlags aFlags) {
5248   // Don't create frames for non-SVG element children of SVG elements.
5249   if (!aElement.IsSVGElement()) {
5250     if (aParentFrame && IsFrameForSVG(aParentFrame) &&
5251         !aParentFrame->IsSVGForeignObjectFrame()) {
5252       return nullptr;
5253     }
5254     if (aFlags.contains(ItemFlag::IsWithinSVGText)) {
5255       return nullptr;
5256     }
5257   }
5258 
5259   if (auto* data = FindElementTagData(aElement, aStyle, aParentFrame, aFlags)) {
5260     return data;
5261   }
5262 
5263   // Check for 'content: <image-url>' on the element (which makes us ignore
5264   // 'display' values other than 'none' or 'contents').
5265   if (ShouldCreateImageFrameForContent(aElement, aStyle)) {
5266     static const FrameConstructionData sImgData =
5267         SIMPLE_FCDATA(NS_NewImageFrameForContentProperty);
5268     return &sImgData;
5269   }
5270 
5271   const auto& display = *aStyle.StyleDisplay();
5272   return FindDisplayData(display, aElement);
5273 }
5274 
5275 const nsCSSFrameConstructor::FrameConstructionData*
FindElementTagData(const Element & aElement,ComputedStyle & aStyle,nsIFrame * aParentFrame,ItemFlags aFlags)5276 nsCSSFrameConstructor::FindElementTagData(const Element& aElement,
5277                                           ComputedStyle& aStyle,
5278                                           nsIFrame* aParentFrame,
5279                                           ItemFlags aFlags) {
5280   // A ::marker pseudo creates a nsBulletFrame, unless 'content' was set.
5281   if (aStyle.GetPseudoType() == PseudoStyleType::marker &&
5282       aStyle.StyleContent()->ContentCount() == 0) {
5283     static const FrameConstructionData data = FCDATA_DECL(
5284         FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_SKIP_ABSPOS_PUSH |
5285             FCDATA_DISALLOW_GENERATED_CONTENT | FCDATA_IS_LINE_PARTICIPANT |
5286             FCDATA_IS_INLINE | FCDATA_USE_CHILD_ITEMS,
5287         NS_NewBulletFrame);
5288     return &data;
5289   }
5290   switch (aElement.GetNameSpaceID()) {
5291     case kNameSpaceID_XHTML:
5292       return FindHTMLData(aElement, aParentFrame, aStyle);
5293     case kNameSpaceID_MathML:
5294       return FindMathMLData(aElement, aStyle);
5295     case kNameSpaceID_SVG:
5296       return FindSVGData(aElement, aParentFrame,
5297                          aFlags.contains(ItemFlag::IsWithinSVGText),
5298                          aFlags.contains(ItemFlag::AllowTextPathChild), aStyle);
5299     case kNameSpaceID_XUL:
5300       return FindXULTagData(aElement, aStyle);
5301     default:
5302       return nullptr;
5303   }
5304 }
5305 
AddFrameConstructionItemsInternal(nsFrameConstructorState & aState,nsIContent * aContent,nsContainerFrame * aParentFrame,bool aSuppressWhiteSpaceOptimizations,ComputedStyle * aComputedStyle,ItemFlags aFlags,FrameConstructionItemList & aItems)5306 void nsCSSFrameConstructor::AddFrameConstructionItemsInternal(
5307     nsFrameConstructorState& aState, nsIContent* aContent,
5308     nsContainerFrame* aParentFrame, bool aSuppressWhiteSpaceOptimizations,
5309     ComputedStyle* aComputedStyle, ItemFlags aFlags,
5310     FrameConstructionItemList& aItems) {
5311   MOZ_ASSERT(aContent->IsText() || aContent->IsElement(),
5312              "Shouldn't get anything else here!");
5313   MOZ_ASSERT(aContent->IsInComposedDoc());
5314   MOZ_ASSERT(!aContent->GetPrimaryFrame() || aState.mCreatingExtraFrames ||
5315              aContent->NodeInfo()->NameAtom() == nsGkAtoms::area);
5316 
5317   const bool withinSVGText = aFlags.contains(ItemFlag::IsWithinSVGText);
5318   const bool isGeneratedContent = aFlags.contains(ItemFlag::IsGeneratedContent);
5319   MOZ_ASSERT(!isGeneratedContent || aComputedStyle->IsPseudoElement(),
5320              "Generated content should be a pseudo-element");
5321 
5322   FrameConstructionItem* item = nullptr;
5323   auto cleanupGeneratedContent = mozilla::MakeScopeExit([&]() {
5324     if (isGeneratedContent && !item) {
5325       MOZ_ASSERT(!IsDisplayContents(aContent),
5326                  "This would need to change if we support display: contents "
5327                  "in generated content");
5328       aContent->UnbindFromTree();
5329     }
5330   });
5331 
5332   // 'display:none' elements never creates any frames at all.
5333   const nsStyleDisplay& display = *aComputedStyle->StyleDisplay();
5334   if (display.mDisplay == StyleDisplay::None) {
5335     return;
5336   }
5337 
5338   if (display.mDisplay == StyleDisplay::Contents) {
5339     // See the mDisplay fixup code in StyleAdjuster::adjust.
5340     MOZ_ASSERT(!aContent->AsElement()->IsRootOfNativeAnonymousSubtree(),
5341                "display:contents on anonymous content is unsupported");
5342 
5343     // FIXME(bug 1588477): <svg:text>'s TextNodeCorrespondenceRecorder has
5344     // trouble with everything that looks like display: contents.
5345     if (withinSVGText) {
5346       return;
5347     }
5348 
5349     CreateGeneratedContentItem(aState, aParentFrame, *aContent->AsElement(),
5350                                *aComputedStyle, PseudoStyleType::before,
5351                                aItems);
5352 
5353     FlattenedChildIterator iter(aContent);
5354     InsertionPoint insertion(aParentFrame, aContent);
5355     for (nsIContent* child = iter.GetNextChild(); child;
5356          child = iter.GetNextChild()) {
5357       AddFrameConstructionItems(aState, child, aSuppressWhiteSpaceOptimizations,
5358                                 insertion, aItems, aFlags);
5359     }
5360     aItems.SetParentHasNoShadowDOM(!iter.ShadowDOMInvolved());
5361 
5362     CreateGeneratedContentItem(aState, aParentFrame, *aContent->AsElement(),
5363                                *aComputedStyle, PseudoStyleType::after, aItems);
5364     return;
5365   }
5366 
5367   nsIContent* parent = aParentFrame ? aParentFrame->GetContent() : nullptr;
5368   if (ShouldSuppressFrameInSelect(parent, *aContent)) {
5369     return;
5370   }
5371 
5372   // When constructing a child of a non-open <details>, create only the frame
5373   // for the main <summary> element, and skip other elements.  This only applies
5374   // to things that are not roots of native anonymous subtrees (except for
5375   // ::before and ::after); we always want to create "internal" anonymous
5376   // content.
5377   auto* details = HTMLDetailsElement::FromNodeOrNull(parent);
5378   if (ShouldSuppressFrameInNonOpenDetails(details, aComputedStyle, *aContent)) {
5379     return;
5380   }
5381 
5382   const FrameConstructionData* data =
5383       FindDataForContent(*aContent, *aComputedStyle, aParentFrame, aFlags);
5384   if (!data || data->mBits & FCDATA_SUPPRESS_FRAME) {
5385     return;
5386   }
5387 
5388   bool isPopup = false;
5389 
5390 #ifdef MOZ_XUL
5391   if ((data->mBits & FCDATA_IS_POPUP) && (!aParentFrame ||  // Parent is inline
5392                                           !aParentFrame->IsMenuFrame())) {
5393     if (!aState.mPopupList.containingBlock && !aState.mHavePendingPopupgroup) {
5394       return;
5395     }
5396 
5397     isPopup = true;
5398   }
5399 #endif /* MOZ_XUL */
5400 
5401   uint32_t bits = data->mBits;
5402 
5403   // Inside colgroups, suppress everything except columns.
5404   if (aParentFrame && aParentFrame->IsTableColGroupFrame() &&
5405       (!(bits & FCDATA_IS_TABLE_PART) ||
5406        display.mDisplay != StyleDisplay::TableColumn)) {
5407     return;
5408   }
5409 
5410   bool canHavePageBreak =
5411       aFlags.contains(ItemFlag::AllowPageBreak) &&
5412       aState.mPresContext->IsPaginated() &&
5413       !display.IsAbsolutelyPositionedStyle() &&
5414       !(aParentFrame && aParentFrame->IsGridContainerFrame()) &&
5415       !(bits & FCDATA_IS_TABLE_PART) && !(bits & FCDATA_IS_SVG_TEXT);
5416 
5417   if (canHavePageBreak && display.BreakBefore()) {
5418     AddPageBreakItem(aContent, aItems);
5419   }
5420 
5421   if (details && details->Open()) {
5422     auto* summary = HTMLSummaryElement::FromNode(aContent);
5423     if (summary && summary->IsMainSummary()) {
5424       // If details is open, the main summary needs to be rendered as if it is
5425       // the first child, so add the item to the front of the item list.
5426       item = aItems.PrependItem(this, data, aContent, do_AddRef(aComputedStyle),
5427                                 aSuppressWhiteSpaceOptimizations);
5428     }
5429   }
5430 
5431   if (!item) {
5432     item = aItems.AppendItem(this, data, aContent, do_AddRef(aComputedStyle),
5433                              aSuppressWhiteSpaceOptimizations);
5434   }
5435   item->mIsText = !aContent->IsElement();
5436   item->mIsGeneratedContent = isGeneratedContent;
5437   if (isGeneratedContent) {
5438     // We need to keep this alive until the frame takes ownership.
5439     // This corresponds to the Release in ConstructFramesFromItem.
5440     item->mContent->AddRef();
5441   }
5442   item->mIsRootPopupgroup = aContent->IsRootOfNativeAnonymousSubtree() &&
5443                             aContent->IsXULElement(nsGkAtoms::popupgroup);
5444   if (item->mIsRootPopupgroup) {
5445     aState.mHavePendingPopupgroup = true;
5446   }
5447   item->mIsPopup = isPopup;
5448 
5449   if (canHavePageBreak && display.BreakAfter()) {
5450     AddPageBreakItem(aContent, aItems);
5451   }
5452 
5453   if (bits & FCDATA_IS_INLINE) {
5454     // To correctly set item->mIsAllInline we need to build up our child items
5455     // right now.
5456     BuildInlineChildItems(aState, *item,
5457                           aFlags.contains(ItemFlag::IsWithinSVGText),
5458                           aFlags.contains(ItemFlag::AllowTextPathChild));
5459     item->mIsBlock = false;
5460   } else {
5461     // Compute a boolean isInline which is guaranteed to be false for blocks
5462     // (but may also be false for some inlines).
5463     bool isInline =
5464         // Table-internal things are inline-outside if and only if they're kids
5465         // of inlines, since they'll trigger construction of inline-table
5466         // pseudos.
5467         ((bits & FCDATA_IS_TABLE_PART) &&
5468          (!aParentFrame ||  // No aParentFrame means inline
5469           aParentFrame->StyleDisplay()->IsInlineFlow())) ||
5470         // Things that are inline-outside but aren't inline frames are inline
5471         display.IsInlineOutsideStyle() ||
5472         // Popups that are certainly out of flow.
5473         isPopup;
5474 
5475     // Set mIsAllInline conservatively.  It just might be that even an inline
5476     // that has mIsAllInline false doesn't need an {ib} split.  So this is just
5477     // an optimization to keep from doing too much work in cases when we can
5478     // show that mIsAllInline is true..
5479     item->mIsAllInline =
5480         isInline ||
5481         // Figure out whether we're guaranteed this item will be out of flow.
5482         // This is not a precise test, since one of our ancestor inlines might
5483         // add an absolute containing block (if it's relatively positioned) when
5484         // there wasn't such a containing block before.  But it's conservative
5485         // in the sense that anything that will really end up as an in-flow
5486         // non-inline will test false here.  In other words, if this test is
5487         // true we're guaranteed to be inline; if it's false we don't know what
5488         // we'll end up as.
5489         //
5490         // If we make this test precise, we can remove some of the code dealing
5491         // with the imprecision in ConstructInline and adjust the comments on
5492         // mIsAllInline and mIsBlock in the header.
5493         (!(bits & FCDATA_DISALLOW_OUT_OF_FLOW) &&
5494          aState.GetGeometricParent(display, nullptr));
5495 
5496     // Set mIsBlock conservatively.  It's OK to set it false for some real
5497     // blocks, but not OK to set it true for things that aren't blocks.  Since
5498     // isOutOfFlow might be false even in cases when the frame will end up
5499     // out-of-flow, we can't use it here.  But we _can_ say that the frame will
5500     // for sure end up in-flow if it's not floated or absolutely positioned.
5501     item->mIsBlock = !isInline && !display.IsAbsolutelyPositionedStyle() &&
5502                      !display.IsFloatingStyle() && !(bits & FCDATA_IS_SVG_TEXT);
5503   }
5504 
5505   if (item->mIsAllInline) {
5506     aItems.InlineItemAdded();
5507   } else if (item->mIsBlock) {
5508     aItems.BlockItemAdded();
5509   }
5510 
5511   // Our item should be treated as a line participant if we have the relevant
5512   // bit and are going to be in-flow.  Note that this really only matters if
5513   // our ancestor is a box or some such, so the fact that we might have an
5514   // inline ancestor that might become a containing block is not relevant here.
5515   if ((bits & FCDATA_IS_LINE_PARTICIPANT) &&
5516       ((bits & FCDATA_DISALLOW_OUT_OF_FLOW) ||
5517        !aState.GetGeometricParent(display, nullptr))) {
5518     item->mIsLineParticipant = true;
5519     aItems.LineParticipantItemAdded();
5520   }
5521 }
5522 
5523 /**
5524  * Return true if the frame construction item pointed to by aIter will
5525  * create a frame adjacent to a line boundary in the frame tree, and that
5526  * line boundary is induced by a content node adjacent to the frame's
5527  * content node in the content tree. The latter condition is necessary so
5528  * that ContentAppended/ContentInserted/ContentRemoved can easily find any
5529  * text nodes that were suppressed here.
5530  */
AtLineBoundary(FCItemIterator & aIter)5531 bool nsCSSFrameConstructor::AtLineBoundary(FCItemIterator& aIter) {
5532   if (aIter.item().mSuppressWhiteSpaceOptimizations) {
5533     return false;
5534   }
5535 
5536   if (aIter.AtStart()) {
5537     if (aIter.List()->HasLineBoundaryAtStart() &&
5538         !aIter.item().mContent->GetPreviousSibling())
5539       return true;
5540   } else {
5541     FCItemIterator prev = aIter;
5542     prev.Prev();
5543     if (prev.item().IsLineBoundary() &&
5544         !prev.item().mSuppressWhiteSpaceOptimizations &&
5545         aIter.item().mContent->GetPreviousSibling() == prev.item().mContent)
5546       return true;
5547   }
5548 
5549   FCItemIterator next = aIter;
5550   next.Next();
5551   if (next.IsDone()) {
5552     if (aIter.List()->HasLineBoundaryAtEnd() &&
5553         !aIter.item().mContent->GetNextSibling())
5554       return true;
5555   } else {
5556     if (next.item().IsLineBoundary() &&
5557         !next.item().mSuppressWhiteSpaceOptimizations &&
5558         aIter.item().mContent->GetNextSibling() == next.item().mContent)
5559       return true;
5560   }
5561 
5562   return false;
5563 }
5564 
ConstructFramesFromItem(nsFrameConstructorState & aState,FCItemIterator & aIter,nsContainerFrame * aParentFrame,nsFrameList & aFrameList)5565 void nsCSSFrameConstructor::ConstructFramesFromItem(
5566     nsFrameConstructorState& aState, FCItemIterator& aIter,
5567     nsContainerFrame* aParentFrame, nsFrameList& aFrameList) {
5568   FrameConstructionItem& item = aIter.item();
5569   ComputedStyle* computedStyle = item.mComputedStyle;
5570 
5571   const auto* disp = computedStyle->StyleDisplay();
5572   MOZ_ASSERT(!disp->IsAbsolutelyPositionedStyle() ||
5573                  disp->DisplayInside() != StyleDisplayInside::MozBox,
5574              "This may be a frame that was previously blockified "
5575              "but isn't any longer! It probably needs explicit "
5576              "'display:block' to preserve behavior");
5577   Unused << disp;  // (unused in configs that define the assertion away)
5578 
5579   if (item.mIsText) {
5580     // If this is collapsible whitespace next to a line boundary,
5581     // don't create a frame. item.IsWhitespace() also sets the
5582     // NS_CREATE_FRAME_IF_NON_WHITESPACE flag in the text node. (If we
5583     // end up creating a frame, nsTextFrame::Init will clear the flag.)
5584     // We don't do this for generated content, because some generated
5585     // text content is empty text nodes that are about to be initialized.
5586     // (We check mAdditionalStateBits because only the generated content
5587     // container's frame construction item is marked with
5588     // mIsGeneratedContent, and we might not have an aParentFrame.)
5589     // We don't do it for content that may have Shadow DOM siblings / insertion
5590     // points, because they make it difficult to correctly create the frame due
5591     // to dynamic changes.
5592     // We don't do it for SVG text, since we might need to position and
5593     // measure the white space glyphs due to x/y/dx/dy attributes.
5594     if (AtLineBoundary(aIter) &&
5595         !computedStyle->StyleText()->WhiteSpaceOrNewlineIsSignificant() &&
5596         aIter.List()->ParentHasNoShadowDOM() &&
5597         !(aState.mAdditionalStateBits & NS_FRAME_GENERATED_CONTENT) &&
5598         (item.mFCData->mBits & FCDATA_IS_LINE_PARTICIPANT) &&
5599         !(item.mFCData->mBits & FCDATA_IS_SVG_TEXT) &&
5600         !mAlwaysCreateFramesForIgnorableWhitespace && item.IsWhitespace(aState))
5601       return;
5602 
5603     ConstructTextFrame(item.mFCData, aState, item.mContent, aParentFrame,
5604                        computedStyle, aFrameList);
5605     return;
5606   }
5607 
5608   AutoRestore<nsFrameState> savedStateBits(aState.mAdditionalStateBits);
5609   if (item.mIsGeneratedContent) {
5610     // Ensure that frames created here are all tagged with
5611     // NS_FRAME_GENERATED_CONTENT.
5612     aState.mAdditionalStateBits |= NS_FRAME_GENERATED_CONTENT;
5613   }
5614 
5615   // XXXbz maybe just inline ConstructFrameFromItemInternal here or something?
5616   ConstructFrameFromItemInternal(item, aState, aParentFrame, aFrameList);
5617 
5618   if (item.mIsGeneratedContent) {
5619     // This corresponds to the AddRef in AddFrameConstructionItemsInternal.
5620     // The frame owns the generated content now.
5621     item.mContent->Release();
5622 
5623     // Now that we've passed ownership of item.mContent to the frame, unset
5624     // our generated content flag so we don't release or unbind it ourselves.
5625     item.mIsGeneratedContent = false;
5626   }
5627 }
5628 
IsRootBoxFrame(nsIFrame * aFrame)5629 inline bool IsRootBoxFrame(nsIFrame* aFrame) { return (aFrame->IsRootFrame()); }
5630 
ReconstructDocElementHierarchy(InsertionKind aInsertionKind)5631 void nsCSSFrameConstructor::ReconstructDocElementHierarchy(
5632     InsertionKind aInsertionKind) {
5633   Element* rootElement = mDocument->GetRootElement();
5634   if (!rootElement) {
5635     /* nothing to do */
5636     return;
5637   }
5638   RecreateFramesForContent(rootElement, aInsertionKind);
5639 }
5640 
GetAbsoluteContainingBlock(nsIFrame * aFrame,ContainingBlockType aType)5641 nsContainerFrame* nsCSSFrameConstructor::GetAbsoluteContainingBlock(
5642     nsIFrame* aFrame, ContainingBlockType aType) {
5643   // Starting with aFrame, look for a frame that is absolutely positioned or
5644   // relatively positioned (and transformed, if aType is FIXED)
5645   for (nsIFrame* frame = aFrame; frame; frame = frame->GetParent()) {
5646     if (frame->IsFrameOfType(nsIFrame::eMathML)) {
5647       // If it's mathml, bail out -- no absolute positioning out from inside
5648       // mathml frames.  Note that we don't make this part of the loop
5649       // condition because of the stuff at the end of this method...
5650       return nullptr;
5651     }
5652 
5653     // Look for the ICB.
5654     if (aType == FIXED_POS) {
5655       LayoutFrameType t = frame->Type();
5656       if (t == LayoutFrameType::Viewport || t == LayoutFrameType::PageContent) {
5657         return static_cast<nsContainerFrame*>(frame);
5658       }
5659     }
5660 
5661     // If the frame is positioned, we will probably return it as the containing
5662     // block (see the exceptions below).  Otherwise, we'll start looking at the
5663     // parent frame, unless we're dealing with a scrollframe.
5664     // Scrollframes are special since they're not positioned, but their
5665     // scrolledframe might be.  So, we need to check this special case to return
5666     // the correct containing block (the scrolledframe) in that case.
5667     // If we're looking for a fixed-pos containing block and the frame is
5668     // not transformed, skip it.
5669     if (!frame->IsAbsPosContainingBlock() ||
5670         (aType == FIXED_POS && !frame->IsFixedPosContainingBlock())) {
5671       continue;
5672     }
5673     nsIFrame* absPosCBCandidate = frame;
5674     LayoutFrameType type = absPosCBCandidate->Type();
5675     if (type == LayoutFrameType::FieldSet) {
5676       absPosCBCandidate =
5677           static_cast<nsFieldSetFrame*>(absPosCBCandidate)->GetInner();
5678       if (!absPosCBCandidate) {
5679         continue;
5680       }
5681       type = absPosCBCandidate->Type();
5682     }
5683     if (type == LayoutFrameType::Scroll) {
5684       nsIScrollableFrame* scrollFrame = do_QueryFrame(absPosCBCandidate);
5685       absPosCBCandidate = scrollFrame->GetScrolledFrame();
5686       if (!absPosCBCandidate) {
5687         continue;
5688       }
5689       type = absPosCBCandidate->Type();
5690     }
5691     // Only first continuations can be containing blocks.
5692     absPosCBCandidate = absPosCBCandidate->FirstContinuation();
5693     // Is the frame really an absolute container?
5694     if (!absPosCBCandidate->IsAbsoluteContainer()) {
5695       continue;
5696     }
5697 
5698     // For tables, skip the inner frame and consider the table wrapper frame.
5699     if (type == LayoutFrameType::Table) {
5700       continue;
5701     }
5702     // For table wrapper frames, we can just return absPosCBCandidate.
5703     MOZ_ASSERT((nsContainerFrame*)do_QueryFrame(absPosCBCandidate),
5704                "abs.pos. containing block must be nsContainerFrame sub-class");
5705     return static_cast<nsContainerFrame*>(absPosCBCandidate);
5706   }
5707 
5708   MOZ_ASSERT(aType != FIXED_POS, "no ICB in this frame tree?");
5709 
5710   // It is possible for the search for the containing block to fail, because
5711   // no absolute container can be found in the parent chain.  In those cases,
5712   // we fall back to the document element's containing block.
5713   return mHasRootAbsPosContainingBlock ? mDocElementContainingBlock : nullptr;
5714 }
5715 
GetFloatContainingBlock(nsIFrame * aFrame)5716 nsContainerFrame* nsCSSFrameConstructor::GetFloatContainingBlock(
5717     nsIFrame* aFrame) {
5718   // Starting with aFrame, look for a frame that is a float containing block.
5719   // IF we hit a mathml frame, bail out; we don't allow floating out of mathml
5720   // frames, because they don't seem to be able to deal.
5721   // The logic here needs to match the logic in ProcessChildren()
5722   for (nsIFrame* containingBlock = aFrame;
5723        containingBlock && !ShouldSuppressFloatingOfDescendants(containingBlock);
5724        containingBlock = containingBlock->GetParent()) {
5725     if (containingBlock->IsFloatContainingBlock()) {
5726       MOZ_ASSERT((nsContainerFrame*)do_QueryFrame(containingBlock),
5727                  "float containing block must be nsContainerFrame sub-class");
5728       return static_cast<nsContainerFrame*>(containingBlock);
5729     }
5730   }
5731 
5732   // If we didn't find a containing block, then there just isn't
5733   // one.... return null
5734   return nullptr;
5735 }
5736 
5737 /**
5738  * This function will get the previous sibling to use for an append operation.
5739  *
5740  * It takes a parent frame (must not be null) and the next insertion sibling, if
5741  * the parent content is display: contents or has ::after content (may be null).
5742  */
FindAppendPrevSibling(nsIFrame * aParentFrame,nsIFrame * aNextSibling)5743 static nsIFrame* FindAppendPrevSibling(nsIFrame* aParentFrame,
5744                                        nsIFrame* aNextSibling) {
5745   aParentFrame->DrainSelfOverflowList();
5746 
5747   if (aNextSibling) {
5748     MOZ_ASSERT(
5749         aNextSibling->GetParent()->GetContentInsertionFrame() == aParentFrame,
5750         "Wrong parent");
5751     return aNextSibling->GetPrevSibling();
5752   }
5753 
5754   return aParentFrame->GetChildList(kPrincipalList).LastChild();
5755 }
5756 
5757 /**
5758  * Finds the right parent frame to append content to aParentFrame.
5759  *
5760  * Cannot return or receive null.
5761  */
ContinuationToAppendTo(nsContainerFrame * aParentFrame)5762 static nsContainerFrame* ContinuationToAppendTo(
5763     nsContainerFrame* aParentFrame) {
5764   MOZ_ASSERT(aParentFrame);
5765 
5766   if (IsFramePartOfIBSplit(aParentFrame)) {
5767     // If the frame we are manipulating is a ib-split frame (that is, one that's
5768     // been created as a result of a block-in-inline situation) then we need to
5769     // append to the last ib-split sibling, not to the frame itself.
5770     //
5771     // Always make sure to look at the last continuation of the frame for the
5772     // {ib} case, even if that continuation is empty.
5773     //
5774     // We don't do this for the non-ib-split-frame case, since in the other
5775     // cases appending to the last nonempty continuation is fine and in fact not
5776     // doing that can confuse code that doesn't know to pull kids from
5777     // continuations other than its next one.
5778     return static_cast<nsContainerFrame*>(
5779         GetLastIBSplitSibling(aParentFrame)->LastContinuation());
5780   }
5781 
5782   return nsLayoutUtils::LastContinuationWithChild(aParentFrame);
5783 }
5784 
5785 /**
5786  * This function will get the next sibling for a frame insert operation given
5787  * the parent and previous sibling.  aPrevSibling may be null.
5788  */
GetInsertNextSibling(nsIFrame * aParentFrame,nsIFrame * aPrevSibling)5789 static nsIFrame* GetInsertNextSibling(nsIFrame* aParentFrame,
5790                                       nsIFrame* aPrevSibling) {
5791   if (aPrevSibling) {
5792     return aPrevSibling->GetNextSibling();
5793   }
5794 
5795   return aParentFrame->PrincipalChildList().FirstChild();
5796 }
5797 
AppendFramesToParent(nsFrameConstructorState & aState,nsContainerFrame * aParentFrame,nsFrameList & aFrameList,nsIFrame * aPrevSibling,bool aIsRecursiveCall)5798 void nsCSSFrameConstructor::AppendFramesToParent(
5799     nsFrameConstructorState& aState, nsContainerFrame* aParentFrame,
5800     nsFrameList& aFrameList, nsIFrame* aPrevSibling, bool aIsRecursiveCall) {
5801   MOZ_ASSERT(
5802       !IsFramePartOfIBSplit(aParentFrame) || !GetIBSplitSibling(aParentFrame) ||
5803           !GetIBSplitSibling(aParentFrame)->PrincipalChildList().FirstChild(),
5804       "aParentFrame has a ib-split sibling with kids?");
5805   MOZ_ASSERT(!aPrevSibling || aPrevSibling->GetParent() == aParentFrame,
5806              "Parent and prevsibling don't match");
5807   MOZ_ASSERT(
5808       !aParentFrame->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR) ||
5809           !IsFramePartOfIBSplit(aParentFrame),
5810       "We should have wiped aParentFrame in WipeContainingBlock() "
5811       "if it's part of an IB split!");
5812 
5813   nsIFrame* nextSibling = ::GetInsertNextSibling(aParentFrame, aPrevSibling);
5814 
5815   NS_ASSERTION(nextSibling || !aParentFrame->GetNextContinuation() ||
5816                    !aParentFrame->GetNextContinuation()
5817                         ->PrincipalChildList()
5818                         .FirstChild() ||
5819                    aIsRecursiveCall,
5820                "aParentFrame has later continuations with kids?");
5821   NS_ASSERTION(
5822       nextSibling || !IsFramePartOfIBSplit(aParentFrame) ||
5823           (IsInlineFrame(aParentFrame) && !GetIBSplitSibling(aParentFrame) &&
5824            !aParentFrame->GetNextContinuation()) ||
5825           aIsRecursiveCall,
5826       "aParentFrame is not last?");
5827 
5828   // If we're inserting a list of frames at the end of the trailing inline
5829   // of an {ib} split, we may need to create additional {ib} siblings to parent
5830   // them.
5831   if (!nextSibling && IsFramePartOfIBSplit(aParentFrame)) {
5832     // When we get here, our frame list might start with a block.  If it does
5833     // so, and aParentFrame is an inline, and it and all its previous
5834     // continuations have no siblings, then put the initial blocks from the
5835     // frame list into the previous block of the {ib} split.  Note that we
5836     // didn't want to stop at the block part of the split when figuring out
5837     // initial parent, because that could screw up float parenting; it's easier
5838     // to do this little fixup here instead.
5839     if (aFrameList.NotEmpty() && aFrameList.FirstChild()->IsBlockOutside()) {
5840       // See whether our trailing inline is empty
5841       nsIFrame* firstContinuation = aParentFrame->FirstContinuation();
5842       if (firstContinuation->PrincipalChildList().IsEmpty()) {
5843         // Our trailing inline is empty.  Collect our starting blocks from
5844         // aFrameList, get the right parent frame for them, and put them in.
5845         nsFrameList blockKids =
5846             aFrameList.Split([](nsIFrame* f) { return !f->IsBlockOutside(); });
5847         NS_ASSERTION(blockKids.NotEmpty(), "No blocks?");
5848 
5849         nsContainerFrame* prevBlock = GetIBSplitPrevSibling(firstContinuation);
5850         prevBlock =
5851             static_cast<nsContainerFrame*>(prevBlock->LastContinuation());
5852         NS_ASSERTION(prevBlock, "Should have previous block here");
5853 
5854         MoveChildrenTo(aParentFrame, prevBlock, blockKids);
5855       }
5856     }
5857 
5858     // We want to put some of the frames into this inline frame.
5859     nsFrameList inlineKids =
5860         aFrameList.Split([](nsIFrame* f) { return f->IsBlockOutside(); });
5861 
5862     if (!inlineKids.IsEmpty()) {
5863       AppendFrames(aParentFrame, kPrincipalList, inlineKids);
5864     }
5865 
5866     if (!aFrameList.IsEmpty()) {
5867       nsFrameList ibSiblings;
5868       CreateIBSiblings(aState, aParentFrame,
5869                        aParentFrame->IsAbsPosContainingBlock(), aFrameList,
5870                        ibSiblings);
5871 
5872       // Make sure to trigger reflow of the inline that used to be our
5873       // last one and now isn't anymore, since its GetSkipSides() has
5874       // changed.
5875       mPresShell->FrameNeedsReflow(aParentFrame, IntrinsicDirty::TreeChange,
5876                                    NS_FRAME_HAS_DIRTY_CHILDREN);
5877 
5878       // Recurse so we create new ib siblings as needed for aParentFrame's
5879       // parent
5880       return AppendFramesToParent(aState, aParentFrame->GetParent(), ibSiblings,
5881                                   aParentFrame, true);
5882     }
5883     return;
5884   }
5885 
5886   // If we're appending a list of frames to the last continuations of a
5887   // ::-moz-column-content, we may need to create column-span siblings for them.
5888   if (!nextSibling && IsLastContinuationForColumnContent(aParentFrame)) {
5889     // Extract any initial non-column-span kids, and append them to
5890     // ::-moz-column-content's child list.
5891     nsFrameList initialNonColumnSpanKids =
5892         aFrameList.Split([](nsIFrame* f) { return f->IsColumnSpan(); });
5893     AppendFrames(aParentFrame, kPrincipalList, initialNonColumnSpanKids);
5894 
5895     if (aFrameList.IsEmpty()) {
5896       // No more kids to process (there weren't any column-span kids).
5897       return;
5898     }
5899 
5900     nsFrameList columnSpanSiblings = CreateColumnSpanSiblings(
5901         aState, aParentFrame, aFrameList,
5902         // Column content should never be a absolute/fixed positioned containing
5903         // block. Pass nullptr as aPositionedFrame.
5904         nullptr);
5905 
5906     nsContainerFrame* columnSetWrapper = aParentFrame->GetParent();
5907     while (!columnSetWrapper->IsColumnSetWrapperFrame()) {
5908       columnSetWrapper = columnSetWrapper->GetParent();
5909     }
5910     MOZ_ASSERT(columnSetWrapper,
5911                "No ColumnSetWrapperFrame ancestor for -moz-column-content?");
5912 
5913     FinishBuildingColumns(aState, columnSetWrapper, aParentFrame,
5914                           columnSpanSiblings);
5915 
5916     MOZ_ASSERT(columnSpanSiblings.IsEmpty(),
5917                "The column-span siblings should be moved to the proper place!");
5918     return;
5919   }
5920 
5921   // Insert the frames after our aPrevSibling
5922   InsertFrames(aParentFrame, kPrincipalList, aPrevSibling, aFrameList);
5923 }
5924 
5925 // This gets called to see if the frames corresponding to aSibling and aContent
5926 // should be siblings in the frame tree. Although (1) rows and cols, (2) row
5927 // groups and col groups, (3) row groups and captions, (4) legends and content
5928 // inside fieldsets, (5) popups and other kids of the menu are siblings from a
5929 // content perspective, they are not considered siblings in the frame tree.
IsValidSibling(nsIFrame * aSibling,nsIContent * aContent,Maybe<StyleDisplay> & aDisplay)5930 bool nsCSSFrameConstructor::IsValidSibling(nsIFrame* aSibling,
5931                                            nsIContent* aContent,
5932                                            Maybe<StyleDisplay>& aDisplay) {
5933   nsIFrame* parentFrame = aSibling->GetParent();
5934   LayoutFrameType parentType = parentFrame->Type();
5935 
5936   StyleDisplay siblingDisplay = aSibling->GetDisplay();
5937   if (StyleDisplay::TableColumnGroup == siblingDisplay ||
5938       StyleDisplay::TableColumn == siblingDisplay ||
5939       StyleDisplay::TableCaption == siblingDisplay ||
5940       StyleDisplay::TableHeaderGroup == siblingDisplay ||
5941       StyleDisplay::TableRowGroup == siblingDisplay ||
5942       StyleDisplay::TableFooterGroup == siblingDisplay ||
5943       LayoutFrameType::Menu == parentType) {
5944     // if we haven't already, resolve a style to find the display type of
5945     // aContent.
5946     if (aDisplay.isNothing()) {
5947       if (aContent->IsComment() || aContent->IsProcessingInstruction()) {
5948         // Comments and processing instructions never have frames, so we should
5949         // not try to generate styles for them.
5950         return false;
5951       }
5952       // FIXME(emilio): This is buggy some times, see bug 1424656.
5953       RefPtr<ComputedStyle> computedStyle = ResolveComputedStyle(aContent);
5954       const nsStyleDisplay* display = computedStyle->StyleDisplay();
5955       aDisplay.emplace(display->mDisplay);
5956     }
5957 
5958     StyleDisplay display = aDisplay.value();
5959     if (LayoutFrameType::Menu == parentType) {
5960       return (StyleDisplay::MozPopup == display) ==
5961              (StyleDisplay::MozPopup == siblingDisplay);
5962     }
5963     // To have decent performance we want to return false in cases in which
5964     // reordering the two siblings has no effect on display.  To ensure
5965     // correctness, we MUST return false in cases where the two siblings have
5966     // the same desired parent type and live on different display lists.
5967     // Specificaly, columns and column groups should only consider columns and
5968     // column groups as valid siblings.  Captions should only consider other
5969     // captions.  All other things should consider each other as valid
5970     // siblings.  The restriction in the |if| above on siblingDisplay is ok,
5971     // because for correctness the only part that really needs to happen is to
5972     // not consider captions, column groups, and row/header/footer groups
5973     // siblings of each other.  Treating a column or colgroup as a valid
5974     // sibling of a non-table-related frame will just mean we end up reframing.
5975     if ((siblingDisplay == StyleDisplay::TableCaption) !=
5976         (display == StyleDisplay::TableCaption)) {
5977       // One's a caption and the other is not.  Not valid siblings.
5978       return false;
5979     }
5980 
5981     if ((siblingDisplay == StyleDisplay::TableColumnGroup ||
5982          siblingDisplay == StyleDisplay::TableColumn) !=
5983         (display == StyleDisplay::TableColumnGroup ||
5984          display == StyleDisplay::TableColumn)) {
5985       // One's a column or column group and the other is not.  Not valid
5986       // siblings.
5987       return false;
5988     }
5989     // Fall through; it's possible that the display type was overridden and
5990     // a different sort of frame was constructed, so we may need to return false
5991     // below.
5992   }
5993 
5994   if (IsFrameForFieldSet(parentFrame)) {
5995     // Legends can be sibling of legends but not of other content in the
5996     // fieldset
5997     if (nsContainerFrame* cif = aSibling->GetContentInsertionFrame()) {
5998       aSibling = cif;
5999     }
6000     LayoutFrameType sibType = aSibling->Type();
6001     bool legendContent = aContent->IsHTMLElement(nsGkAtoms::legend);
6002 
6003     if ((legendContent && (LayoutFrameType::Legend != sibType)) ||
6004         (!legendContent && (LayoutFrameType::Legend == sibType)))
6005       return false;
6006   }
6007 
6008   return true;
6009 }
6010 
6011 // FIXME(emilio): If we ever kill IsValidSibling() we can simplify this quite a
6012 // bit (no need to pass aTargetContent or aTargetContentDisplay, and the
6013 // adjust() calls can be responsibility of the caller).
6014 template <nsCSSFrameConstructor::SiblingDirection aDirection>
FindSiblingInternal(FlattenedChildIterator & aIter,nsIContent * aTargetContent,Maybe<StyleDisplay> & aTargetContentDisplay)6015 nsIFrame* nsCSSFrameConstructor::FindSiblingInternal(
6016     FlattenedChildIterator& aIter, nsIContent* aTargetContent,
6017     Maybe<StyleDisplay>& aTargetContentDisplay) {
6018   auto adjust = [&](nsIFrame* aPotentialSiblingFrame) -> nsIFrame* {
6019     return AdjustSiblingFrame(aPotentialSiblingFrame, aTargetContent,
6020                               aTargetContentDisplay, aDirection);
6021   };
6022 
6023   auto nextDomSibling = [](FlattenedChildIterator& aIter) -> nsIContent* {
6024     return aDirection == SiblingDirection::Forward ? aIter.GetNextChild()
6025                                                    : aIter.GetPreviousChild();
6026   };
6027 
6028   auto getInsideMarkerFrame = [](const nsIContent* aContent) -> nsIFrame* {
6029     auto* marker = nsLayoutUtils::GetMarkerFrame(aContent);
6030     const bool isInsideMarker =
6031         marker && marker->GetInFlowParent()->StyleList()->mListStylePosition ==
6032                       NS_STYLE_LIST_STYLE_POSITION_INSIDE;
6033     return isInsideMarker ? marker : nullptr;
6034   };
6035 
6036   auto getNearPseudo = [&](const nsIContent* aContent) -> nsIFrame* {
6037     if (aDirection == SiblingDirection::Forward) {
6038       if (auto* marker = getInsideMarkerFrame(aContent)) {
6039         return marker;
6040       }
6041       return nsLayoutUtils::GetBeforeFrame(aContent);
6042     }
6043     return nsLayoutUtils::GetAfterFrame(aContent);
6044   };
6045 
6046   auto getFarPseudo = [&](const nsIContent* aContent) -> nsIFrame* {
6047     if (aDirection == SiblingDirection::Forward) {
6048       return nsLayoutUtils::GetAfterFrame(aContent);
6049     }
6050     if (auto* before = nsLayoutUtils::GetBeforeFrame(aContent)) {
6051       return before;
6052     }
6053     return getInsideMarkerFrame(aContent);
6054   };
6055 
6056   while (nsIContent* sibling = nextDomSibling(aIter)) {
6057     // NOTE(emilio): It's important to check GetPrimaryFrame() before
6058     // IsDisplayContents to get the correct insertion point when multiple
6059     // siblings go from display: non-none to display: contents.
6060     if (nsIFrame* primaryFrame = sibling->GetPrimaryFrame()) {
6061       // XXX the GetContent() == sibling check is needed due to bug 135040.
6062       // Remove it once that's fixed.
6063       if (primaryFrame->GetContent() == sibling) {
6064         if (nsIFrame* frame = adjust(primaryFrame)) {
6065           return frame;
6066         }
6067       }
6068     }
6069 
6070     if (IsDisplayContents(sibling)) {
6071       if (nsIFrame* frame = adjust(getNearPseudo(sibling))) {
6072         return frame;
6073       }
6074 
6075       const bool startFromBeginning = aDirection == SiblingDirection::Forward;
6076       FlattenedChildIterator iter(sibling, startFromBeginning);
6077       nsIFrame* sibling = FindSiblingInternal<aDirection>(
6078           iter, aTargetContent, aTargetContentDisplay);
6079       if (sibling) {
6080         return sibling;
6081       }
6082     }
6083   }
6084 
6085   return adjust(getFarPseudo(aIter.Parent()));
6086 }
6087 
AdjustSiblingFrame(nsIFrame * aSibling,nsIContent * aTargetContent,Maybe<StyleDisplay> & aTargetContentDisplay,SiblingDirection aDirection)6088 nsIFrame* nsCSSFrameConstructor::AdjustSiblingFrame(
6089     nsIFrame* aSibling, nsIContent* aTargetContent,
6090     Maybe<StyleDisplay>& aTargetContentDisplay, SiblingDirection aDirection) {
6091   if (!aSibling) {
6092     return nullptr;
6093   }
6094 
6095   if (aSibling->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
6096     aSibling = aSibling->GetPlaceholderFrame();
6097     MOZ_ASSERT(aSibling);
6098   }
6099 
6100   MOZ_ASSERT(!aSibling->GetPrevContinuation(), "How?");
6101   if (aDirection == SiblingDirection::Backward) {
6102     // The frame may be a ib-split frame (a split inline frame that contains a
6103     // block).  Get the last part of that split.
6104     if (IsFramePartOfIBSplit(aSibling)) {
6105       aSibling = GetLastIBSplitSibling(aSibling);
6106     }
6107 
6108     // The frame may have a continuation. If so, we want the last
6109     // non-overflow-container continuation as our previous sibling.
6110     aSibling = aSibling->GetTailContinuation();
6111   }
6112 
6113   if (!IsValidSibling(aSibling, aTargetContent, aTargetContentDisplay)) {
6114     return nullptr;
6115   }
6116 
6117   return aSibling;
6118 }
6119 
FindPreviousSibling(const FlattenedChildIterator & aIter,Maybe<StyleDisplay> & aTargetContentDisplay)6120 nsIFrame* nsCSSFrameConstructor::FindPreviousSibling(
6121     const FlattenedChildIterator& aIter,
6122     Maybe<StyleDisplay>& aTargetContentDisplay) {
6123   return FindSibling<SiblingDirection::Backward>(aIter, aTargetContentDisplay);
6124 }
6125 
FindNextSibling(const FlattenedChildIterator & aIter,Maybe<StyleDisplay> & aTargetContentDisplay)6126 nsIFrame* nsCSSFrameConstructor::FindNextSibling(
6127     const FlattenedChildIterator& aIter,
6128     Maybe<StyleDisplay>& aTargetContentDisplay) {
6129   return FindSibling<SiblingDirection::Forward>(aIter, aTargetContentDisplay);
6130 }
6131 
6132 template <nsCSSFrameConstructor::SiblingDirection aDirection>
FindSibling(const FlattenedChildIterator & aIter,Maybe<StyleDisplay> & aTargetContentDisplay)6133 nsIFrame* nsCSSFrameConstructor::FindSibling(
6134     const FlattenedChildIterator& aIter,
6135     Maybe<StyleDisplay>& aTargetContentDisplay) {
6136   nsIContent* targetContent = aIter.Get();
6137   FlattenedChildIterator siblingIter = aIter;
6138   nsIFrame* sibling = FindSiblingInternal<aDirection>(
6139       siblingIter, targetContent, aTargetContentDisplay);
6140   if (sibling) {
6141     return sibling;
6142   }
6143 
6144   // Our siblings (if any) do not have a frame to guide us. The frame for the
6145   // target content should be inserted whereever a frame for the container would
6146   // be inserted. This is needed when inserting into display: contents nodes.
6147   const nsIContent* current = aIter.Parent();
6148   while (IsDisplayContents(current)) {
6149     const nsIContent* parent = current->GetFlattenedTreeParent();
6150     MOZ_ASSERT(parent, "No display: contents on the root");
6151 
6152     FlattenedChildIterator iter(parent);
6153     iter.Seek(current);
6154     sibling = FindSiblingInternal<aDirection>(iter, targetContent,
6155                                               aTargetContentDisplay);
6156     if (sibling) {
6157       return sibling;
6158     }
6159 
6160     current = parent;
6161   }
6162 
6163   return nullptr;
6164 }
6165 
6166 // For fieldsets, returns the area frame, if the child is not a legend.
GetAdjustedParentFrame(nsContainerFrame * aParentFrame,nsIContent * aChildContent)6167 static nsContainerFrame* GetAdjustedParentFrame(nsContainerFrame* aParentFrame,
6168                                                 nsIContent* aChildContent) {
6169   MOZ_ASSERT(!aParentFrame->IsTableWrapperFrame(), "Shouldn't be happening!");
6170 
6171   nsContainerFrame* newParent = nullptr;
6172   if (aParentFrame->IsFieldSetFrame()) {
6173     // If the parent is a fieldSet, use the fieldSet's area frame as the
6174     // parent unless the new content is a legend.
6175     if (!aChildContent->IsHTMLElement(nsGkAtoms::legend)) {
6176       newParent = static_cast<nsFieldSetFrame*>(aParentFrame)->GetInner();
6177       if (newParent) {
6178         newParent = newParent->GetContentInsertionFrame();
6179       }
6180     }
6181   }
6182   return newParent ? newParent : aParentFrame;
6183 }
6184 
GetInsertionPrevSibling(InsertionPoint * aInsertion,nsIContent * aChild,bool * aIsAppend,bool * aIsRangeInsertSafe,nsIContent * aStartSkipChild,nsIContent * aEndSkipChild)6185 nsIFrame* nsCSSFrameConstructor::GetInsertionPrevSibling(
6186     InsertionPoint* aInsertion, nsIContent* aChild, bool* aIsAppend,
6187     bool* aIsRangeInsertSafe, nsIContent* aStartSkipChild,
6188     nsIContent* aEndSkipChild) {
6189   MOZ_ASSERT(aInsertion->mParentFrame, "Must have parent frame to start with");
6190 
6191   *aIsAppend = false;
6192 
6193   // Find the frame that precedes the insertion point.
6194   FlattenedChildIterator iter(aInsertion->mContainer);
6195   if (iter.ShadowDOMInvolved() || !aChild->IsRootOfNativeAnonymousSubtree()) {
6196     // The check for IsRootOfNativeAnonymousSubtree() is because editor is
6197     // severely broken and calls us directly for native anonymous
6198     // nodes that it creates.
6199     if (aStartSkipChild) {
6200       iter.Seek(aStartSkipChild);
6201     } else {
6202       iter.Seek(aChild);
6203     }
6204   } else {
6205     // Prime the iterator for the call to FindPreviousSibling.
6206     iter.GetNextChild();
6207     MOZ_ASSERT(aChild->GetProperty(nsGkAtoms::restylableAnonymousNode),
6208                "Someone passed native anonymous content directly into frame "
6209                "construction.  Stop doing that!");
6210   }
6211 
6212   // Note that FindPreviousSibling is passed the iterator by value, so that
6213   // the later usage of the iterator starts from the same place.
6214   Maybe<StyleDisplay> childDisplay;
6215   nsIFrame* prevSibling = FindPreviousSibling(iter, childDisplay);
6216 
6217   // Now, find the geometric parent so that we can handle
6218   // continuations properly. Use the prev sibling if we have it;
6219   // otherwise use the next sibling.
6220   if (prevSibling) {
6221     aInsertion->mParentFrame =
6222         prevSibling->GetParent()->GetContentInsertionFrame();
6223   } else {
6224     // If there is no previous sibling, then find the frame that follows
6225     //
6226     // FIXME(emilio): This is really complex and probably shouldn't be.
6227     if (aEndSkipChild) {
6228       iter.Seek(aEndSkipChild);
6229       iter.GetPreviousChild();
6230     }
6231     if (nsIFrame* nextSibling = FindNextSibling(iter, childDisplay)) {
6232       aInsertion->mParentFrame =
6233           nextSibling->GetParent()->GetContentInsertionFrame();
6234     } else {
6235       // No previous or next sibling, so treat this like an appended frame.
6236       *aIsAppend = true;
6237 
6238       // Deal with fieldsets.
6239       aInsertion->mParentFrame =
6240           ::GetAdjustedParentFrame(aInsertion->mParentFrame, aChild);
6241 
6242       aInsertion->mParentFrame =
6243           ::ContinuationToAppendTo(aInsertion->mParentFrame);
6244 
6245       prevSibling = ::FindAppendPrevSibling(aInsertion->mParentFrame, nullptr);
6246     }
6247   }
6248 
6249   *aIsRangeInsertSafe = childDisplay.isNothing();
6250   return prevSibling;
6251 }
6252 
GetContentInsertionFrameFor(nsIContent * aContent)6253 nsContainerFrame* nsCSSFrameConstructor::GetContentInsertionFrameFor(
6254     nsIContent* aContent) {
6255   nsIFrame* frame;
6256   while (!(frame = aContent->GetPrimaryFrame())) {
6257     if (!IsDisplayContents(aContent)) {
6258       return nullptr;
6259     }
6260 
6261     aContent = aContent->GetFlattenedTreeParent();
6262     if (!aContent) {
6263       return nullptr;
6264     }
6265   }
6266 
6267   // If the content of the frame is not the desired content then this is not
6268   // really a frame for the desired content.
6269   // XXX This check is needed due to bug 135040. Remove it once that's fixed.
6270   if (frame->GetContent() != aContent) {
6271     return nullptr;
6272   }
6273 
6274   nsContainerFrame* insertionFrame = frame->GetContentInsertionFrame();
6275 
6276   NS_ASSERTION(!insertionFrame || insertionFrame == frame || !frame->IsLeaf(),
6277                "The insertion frame is the primary frame or the primary frame "
6278                "isn't a leaf");
6279 
6280   return insertionFrame;
6281 }
6282 
IsSpecialFramesetChild(nsIContent * aContent)6283 static bool IsSpecialFramesetChild(nsIContent* aContent) {
6284   // IMPORTANT: This must match the conditions in nsHTMLFramesetFrame::Init.
6285   return aContent->IsAnyOfHTMLElements(nsGkAtoms::frameset, nsGkAtoms::frame);
6286 }
6287 
6288 static void InvalidateCanvasIfNeeded(PresShell* aPresShell, nsIContent* aNode);
6289 
AddTextItemIfNeeded(nsFrameConstructorState & aState,const InsertionPoint & aInsertion,nsIContent * aPossibleTextContent,FrameConstructionItemList & aItems)6290 void nsCSSFrameConstructor::AddTextItemIfNeeded(
6291     nsFrameConstructorState& aState, const InsertionPoint& aInsertion,
6292     nsIContent* aPossibleTextContent, FrameConstructionItemList& aItems) {
6293   MOZ_ASSERT(aPossibleTextContent, "Must have node");
6294   if (!aPossibleTextContent->IsText() ||
6295       !aPossibleTextContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE) ||
6296       aPossibleTextContent->HasFlag(NODE_NEEDS_FRAME)) {
6297     // Not text, or not suppressed due to being all-whitespace (if it were being
6298     // suppressed, it would have the NS_CREATE_FRAME_IF_NON_WHITESPACE flag), or
6299     // going to be reframed anyway.
6300     return;
6301   }
6302   MOZ_ASSERT(!aPossibleTextContent->GetPrimaryFrame(),
6303              "Text node has a frame and NS_CREATE_FRAME_IF_NON_WHITESPACE");
6304   AddFrameConstructionItems(aState, aPossibleTextContent, false, aInsertion,
6305                             aItems);
6306 }
6307 
ReframeTextIfNeeded(nsIContent * aContent)6308 void nsCSSFrameConstructor::ReframeTextIfNeeded(nsIContent* aContent) {
6309   if (!aContent->IsText() ||
6310       !aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE) ||
6311       aContent->HasFlag(NODE_NEEDS_FRAME)) {
6312     // Not text, or not suppressed due to being all-whitespace (if it were being
6313     // suppressed, it would have the NS_CREATE_FRAME_IF_NON_WHITESPACE flag), or
6314     // going to be reframed anyway.
6315     return;
6316   }
6317   MOZ_ASSERT(!aContent->GetPrimaryFrame(),
6318              "Text node has a frame and NS_CREATE_FRAME_IF_NON_WHITESPACE");
6319   ContentInserted(aContent, InsertionKind::Async);
6320 }
6321 
6322 #ifdef DEBUG
CheckBitsForLazyFrameConstruction(nsIContent * aParent)6323 void nsCSSFrameConstructor::CheckBitsForLazyFrameConstruction(
6324     nsIContent* aParent) {
6325   // If we hit a node with no primary frame, or the NODE_NEEDS_FRAME bit set
6326   // we want to assert, but leaf frames that process their own children and may
6327   // ignore anonymous children (eg framesets) make this complicated. So we set
6328   // these two booleans if we encounter these situations and unset them if we
6329   // hit a node with a leaf frame.
6330   //
6331   // It's fine if one of node without primary frame is in a display:none
6332   // subtree.
6333   //
6334   // Also, it's fine if one of the nodes without primary frame is a display:
6335   // contents node.
6336   bool noPrimaryFrame = false;
6337   bool needsFrameBitSet = false;
6338   nsIContent* content = aParent;
6339   while (content && !content->HasFlag(NODE_DESCENDANTS_NEED_FRAMES)) {
6340     if (content->GetPrimaryFrame() && content->GetPrimaryFrame()->IsLeaf()) {
6341       noPrimaryFrame = needsFrameBitSet = false;
6342     }
6343     if (!noPrimaryFrame && !content->GetPrimaryFrame()) {
6344       noPrimaryFrame = !IsDisplayContents(content);
6345     }
6346     if (!needsFrameBitSet && content->HasFlag(NODE_NEEDS_FRAME)) {
6347       needsFrameBitSet = true;
6348     }
6349 
6350     content = content->GetFlattenedTreeParent();
6351   }
6352   if (content && content->GetPrimaryFrame() &&
6353       content->GetPrimaryFrame()->IsLeaf()) {
6354     noPrimaryFrame = needsFrameBitSet = false;
6355   }
6356   MOZ_ASSERT(!noPrimaryFrame,
6357              "Ancestors of nodes with frames to be "
6358              "constructed lazily should have frames");
6359   MOZ_ASSERT(!needsFrameBitSet,
6360              "Ancestors of nodes with frames to be "
6361              "constructed lazily should not have NEEDS_FRAME bit set");
6362 }
6363 #endif
6364 
6365 // Returns true if this operation can be lazy, false if not.
6366 //
6367 // FIXME(emilio, bug 1410020): This function assumes that the flattened tree
6368 // parent of all the appended children is the same, which, afaict, is not
6369 // necessarily true.
ConstructLazily(Operation aOperation,nsIContent * aChild)6370 void nsCSSFrameConstructor::ConstructLazily(Operation aOperation,
6371                                             nsIContent* aChild) {
6372   MOZ_ASSERT(aChild->GetParent());
6373 
6374   // We can construct lazily; just need to set suitable bits in the content
6375   // tree.
6376   Element* parent = aChild->GetFlattenedTreeParentElement();
6377   if (!parent) {
6378     // Not part of the flat tree, nothing to do.
6379     return;
6380   }
6381 
6382   if (Servo_Element_IsDisplayNone(parent)) {
6383     // Nothing to do either.
6384     //
6385     // FIXME(emilio): This should be an assert, except for weird <frameset>
6386     // stuff that does its own frame construction. Such an assert would fire in
6387     // layout/style/crashtests/1411478.html, for example.
6388     return;
6389   }
6390 
6391   // Set NODE_NEEDS_FRAME on the new nodes.
6392   if (aOperation == CONTENTINSERT) {
6393     NS_ASSERTION(!aChild->GetPrimaryFrame() ||
6394                      aChild->GetPrimaryFrame()->GetContent() != aChild,
6395                  // XXX the aChild->GetPrimaryFrame()->GetContent() != aChild
6396                  // check is needed due to bug 135040. Remove it once that's
6397                  // fixed.
6398                  "setting NEEDS_FRAME on a node that already has a frame?");
6399     aChild->SetFlags(NODE_NEEDS_FRAME);
6400   } else {  // CONTENTAPPEND
6401     for (nsIContent* child = aChild; child; child = child->GetNextSibling()) {
6402       NS_ASSERTION(!child->GetPrimaryFrame() ||
6403                        child->GetPrimaryFrame()->GetContent() != child,
6404                    // XXX the child->GetPrimaryFrame()->GetContent() != child
6405                    // check is needed due to bug 135040. Remove it once that's
6406                    // fixed.
6407                    "setting NEEDS_FRAME on a node that already has a frame?");
6408       child->SetFlags(NODE_NEEDS_FRAME);
6409     }
6410   }
6411 
6412   CheckBitsForLazyFrameConstruction(parent);
6413   parent->NoteDescendantsNeedFramesForServo();
6414 }
6415 
IssueSingleInsertNofications(nsIContent * aStartChild,nsIContent * aEndChild,InsertionKind aInsertionKind)6416 void nsCSSFrameConstructor::IssueSingleInsertNofications(
6417     nsIContent* aStartChild, nsIContent* aEndChild,
6418     InsertionKind aInsertionKind) {
6419   for (nsIContent* child = aStartChild; child != aEndChild;
6420        child = child->GetNextSibling()) {
6421     MOZ_ASSERT(!child->GetPrimaryFrame());
6422 
6423     // Call ContentRangeInserted with this node.
6424     ContentRangeInserted(child, child->GetNextSibling(), aInsertionKind);
6425   }
6426 }
6427 
IsMultiple() const6428 bool nsCSSFrameConstructor::InsertionPoint::IsMultiple() const {
6429   // Fieldset frames have multiple normal flow child frame lists so handle it
6430   // the same as if it had multiple content insertion points.
6431   return mParentFrame && mParentFrame->IsFieldSetFrame();
6432 }
6433 
6434 nsCSSFrameConstructor::InsertionPoint
GetRangeInsertionPoint(nsIContent * aStartChild,nsIContent * aEndChild,InsertionKind aInsertionKind)6435 nsCSSFrameConstructor::GetRangeInsertionPoint(nsIContent* aStartChild,
6436                                               nsIContent* aEndChild,
6437                                               InsertionKind aInsertionKind) {
6438   MOZ_ASSERT(aStartChild);
6439   MOZ_ASSERT(aStartChild->GetParent());
6440 
6441   nsIContent* parent = aStartChild->GetParent();
6442 
6443   // If the children of the container may be distributed to different insertion
6444   // points, insert them separately and bail out, letting ContentInserted handle
6445   // the mess.
6446   if (parent->GetShadowRoot()) {
6447     IssueSingleInsertNofications(aStartChild, aEndChild, aInsertionKind);
6448     return {};
6449   }
6450 
6451 #ifdef DEBUG
6452   {
6453     nsIContent* expectedParent = aStartChild->GetFlattenedTreeParent();
6454     for (nsIContent* child = aStartChild->GetNextSibling(); child;
6455          child = child->GetNextSibling()) {
6456       MOZ_ASSERT(child->GetFlattenedTreeParent() == expectedParent);
6457     }
6458   }
6459 #endif
6460 
6461   // Now the flattened tree parent of all the siblings is the same, just use the
6462   // same insertion point and take the fast path, unless it's a multiple
6463   // insertion point.
6464   InsertionPoint ip = GetInsertionPoint(aStartChild);
6465   if (ip.IsMultiple()) {
6466     IssueSingleInsertNofications(aStartChild, aEndChild, aInsertionKind);
6467     return {};
6468   }
6469 
6470   return ip;
6471 }
6472 
MaybeRecreateForFrameset(nsIFrame * aParentFrame,nsIContent * aStartChild,nsIContent * aEndChild)6473 bool nsCSSFrameConstructor::MaybeRecreateForFrameset(nsIFrame* aParentFrame,
6474                                                      nsIContent* aStartChild,
6475                                                      nsIContent* aEndChild) {
6476   if (aParentFrame->IsFrameSetFrame()) {
6477     // Check whether we have any kids we care about.
6478     for (nsIContent* cur = aStartChild; cur != aEndChild;
6479          cur = cur->GetNextSibling()) {
6480       if (IsSpecialFramesetChild(cur)) {
6481         // Just reframe the parent, since framesets are weird like that.
6482         RecreateFramesForContent(aParentFrame->GetContent(),
6483                                  InsertionKind::Async);
6484         return true;
6485       }
6486     }
6487   }
6488   return false;
6489 }
6490 
LazilyStyleNewChildRange(nsIContent * aStartChild,nsIContent * aEndChild)6491 void nsCSSFrameConstructor::LazilyStyleNewChildRange(nsIContent* aStartChild,
6492                                                      nsIContent* aEndChild) {
6493   for (nsIContent* child = aStartChild; child != aEndChild;
6494        child = child->GetNextSibling()) {
6495     if (child->IsElement()) {
6496       child->AsElement()->NoteDirtyForServo();
6497     }
6498   }
6499 }
6500 
6501 #ifdef DEBUG
IsFlattenedTreeChild(nsIContent * aParent,nsIContent * aChild)6502 static bool IsFlattenedTreeChild(nsIContent* aParent, nsIContent* aChild) {
6503   FlattenedChildIterator iter(aParent);
6504   for (nsIContent* node = iter.GetNextChild(); node;
6505        node = iter.GetNextChild()) {
6506     if (node == aChild) {
6507       return true;
6508     }
6509   }
6510   return false;
6511 }
6512 #endif
6513 
StyleNewChildRange(nsIContent * aStartChild,nsIContent * aEndChild)6514 void nsCSSFrameConstructor::StyleNewChildRange(nsIContent* aStartChild,
6515                                                nsIContent* aEndChild) {
6516   ServoStyleSet* styleSet = mPresShell->StyleSet();
6517 
6518   for (nsIContent* child = aStartChild; child != aEndChild;
6519        child = child->GetNextSibling()) {
6520     if (!child->IsElement()) {
6521       continue;
6522     }
6523 
6524     Element* childElement = child->AsElement();
6525 
6526     // We only come in here from non-lazy frame construction, so the children
6527     // should be unstyled.
6528     MOZ_ASSERT(!childElement->HasServoData());
6529 
6530 #ifdef DEBUG
6531     {
6532       // Furthermore, all of them should have the same flattened tree parent
6533       // (GetRangeInsertionPoint ensures it). And that parent should be styled,
6534       // otherwise we would've never found an insertion point at all.
6535       Element* parent = childElement->GetFlattenedTreeParentElement();
6536       MOZ_ASSERT(parent);
6537       MOZ_ASSERT(parent->HasServoData());
6538       MOZ_ASSERT(
6539           IsFlattenedTreeChild(parent, child),
6540           "GetFlattenedTreeParent and ChildIterator don't agree, fix this!");
6541     }
6542 #endif
6543 
6544     styleSet->StyleNewSubtree(childElement);
6545   }
6546 }
6547 
FindNextSiblingForAppend(const InsertionPoint & aInsertion)6548 nsIFrame* nsCSSFrameConstructor::FindNextSiblingForAppend(
6549     const InsertionPoint& aInsertion) {
6550   auto SlowPath = [&]() -> nsIFrame* {
6551     FlattenedChildIterator iter(aInsertion.mContainer,
6552                                 /* aStartAtBeginning = */ false);
6553     iter.GetPreviousChild();  // Prime the iterator.
6554     Maybe<StyleDisplay> unused;
6555     return FindNextSibling(iter, unused);
6556   };
6557 
6558   if (!IsDisplayContents(aInsertion.mContainer) &&
6559       !nsLayoutUtils::GetAfterFrame(aInsertion.mContainer)) {
6560     MOZ_ASSERT(!SlowPath());
6561     return nullptr;
6562   }
6563 
6564   return SlowPath();
6565 }
6566 
ContentAppended(nsIContent * aFirstNewContent,InsertionKind aInsertionKind)6567 void nsCSSFrameConstructor::ContentAppended(nsIContent* aFirstNewContent,
6568                                             InsertionKind aInsertionKind) {
6569   MOZ_ASSERT(aInsertionKind == InsertionKind::Sync ||
6570              !RestyleManager()->IsInStyleRefresh());
6571 
6572   AUTO_PROFILER_LABEL("nsCSSFrameConstructor::ContentAppended",
6573                       LAYOUT_FrameConstruction);
6574   AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
6575 
6576 #ifdef DEBUG
6577   if (gNoisyContentUpdates) {
6578     printf(
6579         "nsCSSFrameConstructor::ContentAppended container=%p "
6580         "first-child=%p lazy=%d\n",
6581         aFirstNewContent->GetParent(), aFirstNewContent,
6582         aInsertionKind == InsertionKind::Async);
6583     if (gReallyNoisyContentUpdates && aFirstNewContent->GetParent()) {
6584       aFirstNewContent->GetParent()->List(stdout, 0);
6585     }
6586   }
6587 
6588   for (nsIContent* child = aFirstNewContent; child;
6589        child = child->GetNextSibling()) {
6590     // XXX the GetContent() != child check is needed due to bug 135040.
6591     // Remove it once that's fixed.
6592     MOZ_ASSERT(
6593         !child->GetPrimaryFrame() ||
6594             child->GetPrimaryFrame()->GetContent() != child,
6595         "asked to construct a frame for a node that already has a frame");
6596   }
6597 #endif
6598 
6599   LAYOUT_PHASE_TEMP_EXIT();
6600   InsertionPoint insertion =
6601       GetRangeInsertionPoint(aFirstNewContent, nullptr, aInsertionKind);
6602   nsContainerFrame*& parentFrame = insertion.mParentFrame;
6603   LAYOUT_PHASE_TEMP_REENTER();
6604   if (!parentFrame) {
6605     // We're punting on frame construction because there's no container frame.
6606     // The Servo-backed style system handles this case like the lazy frame
6607     // construction case, except when we're already constructing frames, in
6608     // which case we shouldn't need to do anything else.
6609     if (aInsertionKind == InsertionKind::Async) {
6610       LazilyStyleNewChildRange(aFirstNewContent, nullptr);
6611     }
6612     return;
6613   }
6614 
6615   if (aInsertionKind == InsertionKind::Async) {
6616     ConstructLazily(CONTENTAPPEND, aFirstNewContent);
6617     LazilyStyleNewChildRange(aFirstNewContent, nullptr);
6618     return;
6619   }
6620 
6621   LAYOUT_PHASE_TEMP_EXIT();
6622   if (MaybeRecreateForFrameset(parentFrame, aFirstNewContent, nullptr)) {
6623     LAYOUT_PHASE_TEMP_REENTER();
6624     return;
6625   }
6626   LAYOUT_PHASE_TEMP_REENTER();
6627 
6628   if (parentFrame->IsLeaf()) {
6629     // Nothing to do here; we shouldn't be constructing kids of leaves
6630     // Clear lazy bits so we don't try to construct again.
6631     ClearLazyBits(aFirstNewContent, nullptr);
6632     return;
6633   }
6634 
6635   LAYOUT_PHASE_TEMP_EXIT();
6636   if (WipeInsertionParent(parentFrame)) {
6637     LAYOUT_PHASE_TEMP_REENTER();
6638     return;
6639   }
6640   LAYOUT_PHASE_TEMP_REENTER();
6641 
6642 #ifdef DEBUG
6643   if (gNoisyContentUpdates && IsFramePartOfIBSplit(parentFrame)) {
6644     printf("nsCSSFrameConstructor::ContentAppended: parentFrame=");
6645     parentFrame->ListTag(stdout);
6646     printf(" is ib-split\n");
6647   }
6648 #endif
6649 
6650   // We should never get here with fieldsets, since they have
6651   // multiple insertion points.
6652   MOZ_ASSERT(!parentFrame->IsFieldSetFrame(),
6653              "Parent frame should not be fieldset!");
6654 
6655   nsIFrame* nextSibling = FindNextSiblingForAppend(insertion);
6656   if (nextSibling) {
6657     parentFrame = nextSibling->GetParent()->GetContentInsertionFrame();
6658   } else {
6659     parentFrame = ::ContinuationToAppendTo(parentFrame);
6660   }
6661 
6662   nsContainerFrame* containingBlock = GetFloatContainingBlock(parentFrame);
6663 
6664   // See if the containing block has :first-letter style applied.
6665   const bool haveFirstLetterStyle =
6666       containingBlock && HasFirstLetterStyle(containingBlock);
6667 
6668   const bool haveFirstLineStyle =
6669       containingBlock && ShouldHaveFirstLineStyle(containingBlock->GetContent(),
6670                                                   containingBlock->Style());
6671 
6672   if (haveFirstLetterStyle) {
6673     AutoWeakFrame wf(nextSibling);
6674 
6675     // Before we get going, remove the current letter frames
6676     RemoveLetterFrames(mPresShell, containingBlock);
6677 
6678     // Reget nextSibling, since we may have killed it.
6679     //
6680     // FIXME(emilio): This kinda sucks! :(
6681     if (nextSibling && !wf) {
6682       nextSibling = FindNextSiblingForAppend(insertion);
6683       if (nextSibling) {
6684         parentFrame = nextSibling->GetParent()->GetContentInsertionFrame();
6685         containingBlock = GetFloatContainingBlock(parentFrame);
6686       }
6687     }
6688   }
6689 
6690   // Create some new frames
6691   nsFrameConstructorState state(
6692       mPresShell, GetAbsoluteContainingBlock(parentFrame, FIXED_POS),
6693       GetAbsoluteContainingBlock(parentFrame, ABS_POS), containingBlock);
6694 
6695   LayoutFrameType frameType = parentFrame->Type();
6696 
6697   FlattenedChildIterator iter(insertion.mContainer);
6698   const bool haveNoShadowDOM =
6699       !iter.ShadowDOMInvolved() || !iter.GetNextChild();
6700 
6701   AutoFrameConstructionItemList items(this);
6702   if (aFirstNewContent->GetPreviousSibling() &&
6703       GetParentType(frameType) == eTypeBlock && haveNoShadowDOM) {
6704     // If there's a text node in the normal content list just before the new
6705     // items, and it has no frame, make a frame construction item for it. If it
6706     // doesn't need a frame, ConstructFramesFromItemList below won't give it
6707     // one.  No need to do all this if our parent type is not block, though,
6708     // since WipeContainingBlock already handles that situation.
6709     //
6710     // Because we're appending, we don't need to worry about any text
6711     // after the appended content; there can only be generated content
6712     // (and bare text nodes are not generated). Native anonymous content
6713     // generated by frames never participates in inline layout.
6714     AddTextItemIfNeeded(state, insertion,
6715                         aFirstNewContent->GetPreviousSibling(), items);
6716   }
6717   for (nsIContent* child = aFirstNewContent; child;
6718        child = child->GetNextSibling()) {
6719     AddFrameConstructionItems(state, child, false, insertion, items);
6720   }
6721 
6722   nsIFrame* prevSibling = ::FindAppendPrevSibling(parentFrame, nextSibling);
6723 
6724   // Perform special check for diddling around with the frames in
6725   // a ib-split inline frame.
6726   // If we're appending before :after content, then we're not really
6727   // appending, so let WipeContainingBlock know that.
6728   LAYOUT_PHASE_TEMP_EXIT();
6729   if (WipeContainingBlock(state, containingBlock, parentFrame, items, true,
6730                           prevSibling)) {
6731     LAYOUT_PHASE_TEMP_REENTER();
6732     return;
6733   }
6734   LAYOUT_PHASE_TEMP_REENTER();
6735 
6736   // If the parent is a block frame, and we're not in a special case
6737   // where frames can be moved around, determine if the list is for the
6738   // start or end of the block.
6739   if (parentFrame->IsBlockFrameOrSubclass() && !haveFirstLetterStyle &&
6740       !haveFirstLineStyle && !IsFramePartOfIBSplit(parentFrame)) {
6741     items.SetLineBoundaryAtStart(!prevSibling ||
6742                                  !prevSibling->IsInlineOutside() ||
6743                                  prevSibling->IsBrFrame());
6744     // :after content can't be <br> so no need to check it
6745     //
6746     // FIXME(emilio): A display: contents sibling could! Write a test-case and
6747     // fix.
6748     items.SetLineBoundaryAtEnd(!nextSibling || !nextSibling->IsInlineOutside());
6749   }
6750   // To suppress whitespace-only text frames, we have to verify that
6751   // our container's DOM child list matches its flattened tree child list.
6752   items.SetParentHasNoShadowDOM(haveNoShadowDOM);
6753 
6754   nsFrameList frameList;
6755   ConstructFramesFromItemList(state, items, parentFrame,
6756                               ParentIsWrapperAnonBox(parentFrame), frameList);
6757 
6758   for (nsIContent* child = aFirstNewContent; child;
6759        child = child->GetNextSibling()) {
6760     // Invalidate now instead of before the WipeContainingBlock call, just in
6761     // case we do wipe; in that case we don't need to do this walk at all.
6762     // XXXbz does that matter?  Would it make more sense to save some virtual
6763     // GetChildAt_Deprecated calls instead and do this during construction of
6764     // our FrameConstructionItemList?
6765     InvalidateCanvasIfNeeded(mPresShell, child);
6766   }
6767 
6768   // If the container is a table and a caption was appended, it needs to be put
6769   // in the table wrapper frame's additional child list.
6770   nsFrameList captionList;
6771   if (LayoutFrameType::Table == frameType) {
6772     // Pull out the captions.  Note that we don't want to do that as we go,
6773     // because processing a single caption can add a whole bunch of things to
6774     // the frame items due to pseudoframe processing.  So we'd have to pull
6775     // captions from a list anyway; might as well do that here.
6776     // XXXbz this is no longer true; we could pull captions directly out of the
6777     // FrameConstructionItemList now.
6778     PullOutCaptionFrames(frameList, captionList);
6779   }
6780 
6781   if (haveFirstLineStyle && parentFrame == containingBlock) {
6782     // It's possible that some of the new frames go into a
6783     // first-line frame. Look at them and see...
6784     AppendFirstLineFrames(state, containingBlock->GetContent(), containingBlock,
6785                           frameList);
6786     // That moved things into line frames as needed, reparenting their
6787     // styles.  Nothing else needs to be done.
6788   } else if (parentFrame->Style()->HasPseudoElementData()) {
6789     // parentFrame might be inside a ::first-line frame.  Check whether it is,
6790     // and if so fix up our styles.
6791     CheckForFirstLineInsertion(parentFrame, frameList);
6792     CheckForFirstLineInsertion(parentFrame, captionList);
6793   }
6794 
6795   // Notify the parent frame passing it the list of new frames
6796   // Append the flowed frames to the principal child list; captions
6797   // need special treatment
6798   if (captionList.NotEmpty()) {  // append the caption to the table wrapper
6799     NS_ASSERTION(LayoutFrameType::Table == frameType, "how did that happen?");
6800     nsContainerFrame* outerTable = parentFrame->GetParent();
6801     captionList.ApplySetParent(outerTable);
6802     AppendFrames(outerTable, nsIFrame::kCaptionList, captionList);
6803   }
6804 
6805   LAYOUT_PHASE_TEMP_EXIT();
6806   if (MaybeRecreateForColumnSpan(state, parentFrame, frameList, prevSibling)) {
6807     LAYOUT_PHASE_TEMP_REENTER();
6808     return;
6809   }
6810   LAYOUT_PHASE_TEMP_REENTER();
6811 
6812   if (frameList.NotEmpty()) {  // append the in-flow kids
6813     AppendFramesToParent(state, parentFrame, frameList, prevSibling);
6814   }
6815 
6816   // Recover first-letter frames
6817   if (haveFirstLetterStyle) {
6818     RecoverLetterFrames(containingBlock);
6819   }
6820 
6821 #ifdef DEBUG
6822   if (gReallyNoisyContentUpdates) {
6823     printf("nsCSSFrameConstructor::ContentAppended: resulting frame model:\n");
6824     parentFrame->List(stdout);
6825   }
6826 #endif
6827 
6828 #ifdef ACCESSIBILITY
6829   if (nsAccessibilityService* accService =
6830           PresShell::GetAccessibilityService()) {
6831     accService->ContentRangeInserted(mPresShell, aFirstNewContent, nullptr);
6832   }
6833 #endif
6834 }
6835 
ContentInserted(nsIContent * aChild,InsertionKind aInsertionKind)6836 void nsCSSFrameConstructor::ContentInserted(nsIContent* aChild,
6837                                             InsertionKind aInsertionKind) {
6838   ContentRangeInserted(aChild, aChild->GetNextSibling(), aInsertionKind);
6839 }
6840 
6841 // ContentRangeInserted handles creating frames for a range of nodes that
6842 // aren't at the end of their childlist. ContentRangeInserted isn't a real
6843 // content notification, but rather it handles regular ContentInserted calls
6844 // for a single node as well as the lazy construction of frames for a range of
6845 // nodes when called from CreateNeededFrames. For a range of nodes to be
6846 // suitable to have its frames constructed all at once they must meet the same
6847 // conditions that ContentAppended imposes (GetRangeInsertionPoint checks
6848 // these), plus more. Namely when finding the insertion prevsibling we must not
6849 // need to consult something specific to any one node in the range, so that the
6850 // insertion prevsibling would be the same for each node in the range. So we
6851 // pass the first node in the range to GetInsertionPrevSibling, and if
6852 // IsValidSibling (the only place GetInsertionPrevSibling might look at the
6853 // passed in node itself) needs to resolve style on the node we record this and
6854 // return that this range needs to be split up and inserted separately. Table
6855 // captions need extra attention as we need to determine where to insert them
6856 // in the caption list, while skipping any nodes in the range being inserted
6857 // (because when we treat the caption frames the other nodes have had their
6858 // frames constructed but not yet inserted into the frame tree).
ContentRangeInserted(nsIContent * aStartChild,nsIContent * aEndChild,InsertionKind aInsertionKind)6859 void nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aStartChild,
6860                                                  nsIContent* aEndChild,
6861                                                  InsertionKind aInsertionKind) {
6862   MOZ_ASSERT(aInsertionKind == InsertionKind::Sync ||
6863              !RestyleManager()->IsInStyleRefresh());
6864 
6865   AUTO_PROFILER_LABEL("nsCSSFrameConstructor::ContentRangeInserted",
6866                       LAYOUT_FrameConstruction);
6867   AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
6868 
6869   MOZ_ASSERT(aStartChild, "must always pass a child");
6870 
6871 #ifdef DEBUG
6872   if (gNoisyContentUpdates) {
6873     printf(
6874         "nsCSSFrameConstructor::ContentRangeInserted container=%p "
6875         "start-child=%p end-child=%p lazy=%d\n",
6876         aStartChild->GetParent(), aStartChild, aEndChild,
6877         aInsertionKind == InsertionKind::Async);
6878     if (gReallyNoisyContentUpdates) {
6879       if (aStartChild->GetParent()) {
6880         aStartChild->GetParent()->List(stdout, 0);
6881       } else {
6882         aStartChild->List(stdout, 0);
6883       }
6884     }
6885   }
6886 
6887   for (nsIContent* child = aStartChild; child != aEndChild;
6888        child = child->GetNextSibling()) {
6889     // XXX the GetContent() != child check is needed due to bug 135040.
6890     // Remove it once that's fixed.
6891     NS_ASSERTION(
6892         !child->GetPrimaryFrame() ||
6893             child->GetPrimaryFrame()->GetContent() != child,
6894         "asked to construct a frame for a node that already has a frame");
6895   }
6896 #endif
6897 
6898   bool isSingleInsert = (aStartChild->GetNextSibling() == aEndChild);
6899   NS_ASSERTION(isSingleInsert || aInsertionKind == InsertionKind::Sync,
6900                "range insert shouldn't be lazy");
6901   NS_ASSERTION(isSingleInsert || aEndChild,
6902                "range should not include all nodes after aStartChild");
6903 
6904   // If we have a null parent, then this must be the document element being
6905   // inserted, or some other child of the document in the DOM (might be a PI,
6906   // say).
6907   if (!aStartChild->GetParent()) {
6908     MOZ_ASSERT(isSingleInsert,
6909                "root node insertion should be a single insertion");
6910     Element* docElement = mDocument->GetRootElement();
6911 
6912     if (aStartChild != docElement) {
6913       // Not the root element; just bail out
6914       return;
6915     }
6916 
6917     MOZ_ASSERT(!mRootElementFrame, "root element frame already created");
6918 
6919     // Create frames for the document element and its child elements
6920     if (ConstructDocElementFrame(docElement)) {
6921       InvalidateCanvasIfNeeded(mPresShell, aStartChild);
6922 #ifdef DEBUG
6923       if (gReallyNoisyContentUpdates) {
6924         printf(
6925             "nsCSSFrameConstructor::ContentRangeInserted: resulting frame "
6926             "model:\n");
6927         mRootElementFrame->List(stdout);
6928       }
6929 #endif
6930     }
6931 
6932 #ifdef ACCESSIBILITY
6933     if (nsAccessibilityService* accService =
6934             PresShell::GetAccessibilityService()) {
6935       accService->ContentRangeInserted(mPresShell, aStartChild, aEndChild);
6936     }
6937 #endif
6938 
6939     return;
6940   }
6941 
6942   InsertionPoint insertion;
6943   if (isSingleInsert) {
6944     // See if we have a Shadow DOM insertion point. If so, then that's our real
6945     // parent frame; if not, then the frame hasn't been built yet and we just
6946     // bail.
6947     insertion = GetInsertionPoint(aStartChild);
6948   } else {
6949     // Get our insertion point. If we need to issue single ContentInserteds
6950     // GetRangeInsertionPoint will take care of that for us.
6951     LAYOUT_PHASE_TEMP_EXIT();
6952     insertion = GetRangeInsertionPoint(aStartChild, aEndChild, aInsertionKind);
6953     LAYOUT_PHASE_TEMP_REENTER();
6954   }
6955 
6956   if (!insertion.mParentFrame) {
6957     // We're punting on frame construction because there's no container frame.
6958     // The Servo-backed style system handles this case like the lazy frame
6959     // construction case, except when we're already constructing frames, in
6960     // which case we shouldn't need to do anything else.
6961     if (aInsertionKind == InsertionKind::Async) {
6962       LazilyStyleNewChildRange(aStartChild, aEndChild);
6963     }
6964     return;
6965   }
6966 
6967   if (aInsertionKind == InsertionKind::Async) {
6968     ConstructLazily(CONTENTINSERT, aStartChild);
6969     LazilyStyleNewChildRange(aStartChild, aEndChild);
6970     return;
6971   }
6972 
6973   bool isAppend, isRangeInsertSafe;
6974   nsIFrame* prevSibling = GetInsertionPrevSibling(
6975       &insertion, aStartChild, &isAppend, &isRangeInsertSafe);
6976 
6977   // check if range insert is safe
6978   if (!isSingleInsert && !isRangeInsertSafe) {
6979     // must fall back to a single ContertInserted for each child in the range
6980     LAYOUT_PHASE_TEMP_EXIT();
6981     IssueSingleInsertNofications(aStartChild, aEndChild, InsertionKind::Sync);
6982     LAYOUT_PHASE_TEMP_REENTER();
6983     return;
6984   }
6985 
6986   LayoutFrameType frameType = insertion.mParentFrame->Type();
6987   LAYOUT_PHASE_TEMP_EXIT();
6988   if (MaybeRecreateForFrameset(insertion.mParentFrame, aStartChild,
6989                                aEndChild)) {
6990     LAYOUT_PHASE_TEMP_REENTER();
6991     return;
6992   }
6993   LAYOUT_PHASE_TEMP_REENTER();
6994 
6995   // We should only get here with fieldsets when doing a single insert, because
6996   // fieldsets have multiple insertion points.
6997   NS_ASSERTION(isSingleInsert || frameType != LayoutFrameType::FieldSet,
6998                "Unexpected parent");
6999   if (IsFrameForFieldSet(insertion.mParentFrame) &&
7000       aStartChild->NodeInfo()->NameAtom() == nsGkAtoms::legend) {
7001     // Just reframe the parent, since figuring out whether this
7002     // should be the new legend and then handling it is too complex.
7003     // We could do a little better here --- check if the fieldset already
7004     // has a legend which occurs earlier in its child list than this node,
7005     // and if so, proceed. But we'd have to extend nsFieldSetFrame
7006     // to locate this legend in the inserted frames and extract it.
7007     LAYOUT_PHASE_TEMP_EXIT();
7008     RecreateFramesForContent(insertion.mParentFrame->GetContent(),
7009                              InsertionKind::Async);
7010     LAYOUT_PHASE_TEMP_REENTER();
7011     return;
7012   }
7013 
7014   // Don't construct kids of leaves
7015   if (insertion.mParentFrame->IsLeaf()) {
7016     // Clear lazy bits so we don't try to construct again.
7017     ClearLazyBits(aStartChild, aEndChild);
7018     return;
7019   }
7020 
7021   LAYOUT_PHASE_TEMP_EXIT();
7022   if (WipeInsertionParent(insertion.mParentFrame)) {
7023     LAYOUT_PHASE_TEMP_REENTER();
7024     return;
7025   }
7026   LAYOUT_PHASE_TEMP_REENTER();
7027 
7028   nsFrameConstructorState state(
7029       mPresShell, GetAbsoluteContainingBlock(insertion.mParentFrame, FIXED_POS),
7030       GetAbsoluteContainingBlock(insertion.mParentFrame, ABS_POS),
7031       GetFloatContainingBlock(insertion.mParentFrame),
7032       do_AddRef(mFrameTreeState));
7033 
7034   // Recover state for the containing block - we need to know if
7035   // it has :first-letter or :first-line style applied to it. The
7036   // reason we care is that the internal structure in these cases
7037   // is not the normal structure and requires custom updating
7038   // logic.
7039   nsContainerFrame* containingBlock = state.mFloatedList.containingBlock;
7040   bool haveFirstLetterStyle = false;
7041   bool haveFirstLineStyle = false;
7042 
7043   // In order to shave off some cycles, we only dig up the
7044   // containing block haveFirst* flags if the parent frame where
7045   // the insertion/append is occurring is an inline or block
7046   // container. For other types of containers this isn't relevant.
7047   StyleDisplayInside parentDisplayInside =
7048       insertion.mParentFrame->StyleDisplay()->DisplayInside();
7049 
7050   // Examine the insertion.mParentFrame where the insertion is taking
7051   // place. If it's a certain kind of container then some special
7052   // processing is done.
7053   if (StyleDisplayInside::Flow == parentDisplayInside) {
7054     // Recover the special style flags for the containing block
7055     if (containingBlock) {
7056       haveFirstLetterStyle = HasFirstLetterStyle(containingBlock);
7057       haveFirstLineStyle = ShouldHaveFirstLineStyle(
7058           containingBlock->GetContent(), containingBlock->Style());
7059     }
7060 
7061     if (haveFirstLetterStyle) {
7062       // If our current insertion.mParentFrame is a Letter frame, use its parent
7063       // as our new parent hint
7064       if (insertion.mParentFrame->IsLetterFrame()) {
7065         // If insertion.mParentFrame is out of flow, then we actually want the
7066         // parent of the placeholder frame.
7067         if (insertion.mParentFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
7068           nsPlaceholderFrame* placeholderFrame =
7069               insertion.mParentFrame->GetPlaceholderFrame();
7070           NS_ASSERTION(placeholderFrame, "No placeholder for out-of-flow?");
7071           insertion.mParentFrame = placeholderFrame->GetParent();
7072         } else {
7073           insertion.mParentFrame = insertion.mParentFrame->GetParent();
7074         }
7075       }
7076 
7077       // Remove the old letter frames before doing the insertion
7078       RemoveLetterFrames(mPresShell, state.mFloatedList.containingBlock);
7079 
7080       // Removing the letterframes messes around with the frame tree, removing
7081       // and creating frames.  We need to reget our prevsibling, parent frame,
7082       // etc.
7083       prevSibling = GetInsertionPrevSibling(&insertion, aStartChild, &isAppend,
7084                                             &isRangeInsertSafe);
7085 
7086       // Need check whether a range insert is still safe.
7087       if (!isSingleInsert && !isRangeInsertSafe) {
7088         // Need to recover the letter frames first.
7089         RecoverLetterFrames(state.mFloatedList.containingBlock);
7090 
7091         // must fall back to a single ContertInserted for each child in the
7092         // range
7093         LAYOUT_PHASE_TEMP_EXIT();
7094         IssueSingleInsertNofications(aStartChild, aEndChild,
7095                                      InsertionKind::Sync);
7096         LAYOUT_PHASE_TEMP_REENTER();
7097         return;
7098       }
7099 
7100       frameType = insertion.mParentFrame->Type();
7101     }
7102   }
7103 
7104   AutoFrameConstructionItemList items(this);
7105   ParentType parentType = GetParentType(frameType);
7106   FlattenedChildIterator iter(insertion.mContainer);
7107   const bool haveNoShadowDOM =
7108       !iter.ShadowDOMInvolved() || !iter.GetNextChild();
7109   if (aStartChild->GetPreviousSibling() && parentType == eTypeBlock &&
7110       haveNoShadowDOM) {
7111     // If there's a text node in the normal content list just before the
7112     // new nodes, and it has no frame, make a frame construction item for
7113     // it, because it might need a frame now.  No need to do this if our
7114     // parent type is not block, though, since WipeContainingBlock
7115     // already handles that situation.
7116     AddTextItemIfNeeded(state, insertion, aStartChild->GetPreviousSibling(),
7117                         items);
7118   }
7119 
7120   if (isSingleInsert) {
7121     AddFrameConstructionItems(state, aStartChild,
7122                               aStartChild->IsRootOfNativeAnonymousSubtree(),
7123                               insertion, items);
7124   } else {
7125     for (nsIContent* child = aStartChild; child != aEndChild;
7126          child = child->GetNextSibling()) {
7127       AddFrameConstructionItems(state, child, false, insertion, items);
7128     }
7129   }
7130 
7131   if (aEndChild && parentType == eTypeBlock && haveNoShadowDOM) {
7132     // If there's a text node in the normal content list just after the
7133     // new nodes, and it has no frame, make a frame construction item for
7134     // it, because it might need a frame now.  No need to do this if our
7135     // parent type is not block, though, since WipeContainingBlock
7136     // already handles that situation.
7137     AddTextItemIfNeeded(state, insertion, aEndChild, items);
7138   }
7139 
7140   // Perform special check for diddling around with the frames in
7141   // a special inline frame.
7142   // If we're appending before :after content, then we're not really
7143   // appending, so let WipeContainingBlock know that.
7144   LAYOUT_PHASE_TEMP_EXIT();
7145   if (WipeContainingBlock(state, containingBlock, insertion.mParentFrame, items,
7146                           isAppend, prevSibling)) {
7147     LAYOUT_PHASE_TEMP_REENTER();
7148     return;
7149   }
7150   LAYOUT_PHASE_TEMP_REENTER();
7151 
7152   // If the container is a table and a caption will be appended, it needs to be
7153   // put in the table wrapper frame's additional child list.
7154   // We make no attempt here to set flags to indicate whether the list
7155   // will be at the start or end of a block. It doesn't seem worthwhile.
7156   nsFrameList frameList, captionList;
7157   ConstructFramesFromItemList(state, items, insertion.mParentFrame,
7158                               ParentIsWrapperAnonBox(insertion.mParentFrame),
7159                               frameList);
7160 
7161   if (frameList.NotEmpty()) {
7162     for (nsIContent* child = aStartChild; child != aEndChild;
7163          child = child->GetNextSibling()) {
7164       InvalidateCanvasIfNeeded(mPresShell, child);
7165     }
7166 
7167     if (LayoutFrameType::Table == frameType ||
7168         LayoutFrameType::TableWrapper == frameType) {
7169       PullOutCaptionFrames(frameList, captionList);
7170     }
7171   }
7172 
7173   if (haveFirstLineStyle && insertion.mParentFrame == containingBlock &&
7174       isAppend) {
7175     // It's possible that the new frame goes into a first-line
7176     // frame. Look at it and see...
7177     AppendFirstLineFrames(state, containingBlock->GetContent(), containingBlock,
7178                           frameList);
7179   } else if (insertion.mParentFrame->Style()->HasPseudoElementData()) {
7180     CheckForFirstLineInsertion(insertion.mParentFrame, frameList);
7181     CheckForFirstLineInsertion(insertion.mParentFrame, captionList);
7182   }
7183 
7184   // We might have captions; put them into the caption list of the
7185   // table wrapper frame.
7186   if (captionList.NotEmpty()) {
7187     NS_ASSERTION(LayoutFrameType::Table == frameType ||
7188                      LayoutFrameType::TableWrapper == frameType,
7189                  "parent for caption is not table?");
7190     // We need to determine where to put the caption items; start with the
7191     // the parent frame that has already been determined and get the insertion
7192     // prevsibling of the first caption item.
7193     bool captionIsAppend;
7194     nsIFrame* captionPrevSibling = nullptr;
7195 
7196     // aIsRangeInsertSafe is ignored on purpose because it is irrelevant here.
7197     bool ignored;
7198     InsertionPoint captionInsertion(insertion.mParentFrame,
7199                                     insertion.mContainer);
7200     if (isSingleInsert) {
7201       captionPrevSibling = GetInsertionPrevSibling(
7202           &captionInsertion, aStartChild, &captionIsAppend, &ignored);
7203     } else {
7204       nsIContent* firstCaption = captionList.FirstChild()->GetContent();
7205       // It is very important here that we skip the children in
7206       // [aStartChild,aEndChild) when looking for a
7207       // prevsibling.
7208       captionPrevSibling = GetInsertionPrevSibling(
7209           &captionInsertion, firstCaption, &captionIsAppend, &ignored,
7210           aStartChild, aEndChild);
7211     }
7212 
7213     nsContainerFrame* outerTable =
7214         captionInsertion.mParentFrame->IsTableFrame()
7215             ? captionInsertion.mParentFrame->GetParent()
7216             : captionInsertion.mParentFrame;
7217 
7218     // If the parent is not a table wrapper frame we will try to add frames
7219     // to a named child list that the parent does not honor and the frames
7220     // will get lost.
7221     MOZ_ASSERT(outerTable->IsTableWrapperFrame(),
7222                "Pseudo frame construction failure; "
7223                "a caption can be only a child of a table wrapper frame");
7224 
7225     // If the parent of our current prevSibling is different from the frame
7226     // we'll actually use as the parent, then the calculated insertion
7227     // point is now invalid (bug 341382).
7228     if (captionPrevSibling && captionPrevSibling->GetParent() != outerTable) {
7229       captionPrevSibling = nullptr;
7230     }
7231 
7232     captionList.ApplySetParent(outerTable);
7233     if (captionIsAppend) {
7234       AppendFrames(outerTable, nsIFrame::kCaptionList, captionList);
7235     } else {
7236       InsertFrames(outerTable, nsIFrame::kCaptionList, captionPrevSibling,
7237                    captionList);
7238     }
7239   }
7240 
7241   LAYOUT_PHASE_TEMP_EXIT();
7242   if (MaybeRecreateForColumnSpan(state, insertion.mParentFrame, frameList,
7243                                  prevSibling)) {
7244     LAYOUT_PHASE_TEMP_REENTER();
7245     return;
7246   }
7247   LAYOUT_PHASE_TEMP_REENTER();
7248 
7249   if (frameList.NotEmpty()) {
7250     // Notify the parent frame
7251     if (isAppend) {
7252       AppendFramesToParent(state, insertion.mParentFrame, frameList,
7253                            prevSibling);
7254     } else {
7255       InsertFrames(insertion.mParentFrame, kPrincipalList, prevSibling,
7256                    frameList);
7257     }
7258   }
7259 
7260   if (haveFirstLetterStyle) {
7261     // Recover the letter frames for the containing block when
7262     // it has first-letter style.
7263     RecoverLetterFrames(state.mFloatedList.containingBlock);
7264   }
7265 
7266 #ifdef DEBUG
7267   if (gReallyNoisyContentUpdates && insertion.mParentFrame) {
7268     printf(
7269         "nsCSSFrameConstructor::ContentRangeInserted: resulting frame "
7270         "model:\n");
7271     insertion.mParentFrame->List(stdout);
7272   }
7273 #endif
7274 
7275 #ifdef ACCESSIBILITY
7276   if (nsAccessibilityService* accService =
7277           PresShell::GetAccessibilityService()) {
7278     accService->ContentRangeInserted(mPresShell, aStartChild, aEndChild);
7279   }
7280 #endif
7281 }
7282 
ContentRemoved(nsIContent * aChild,nsIContent * aOldNextSibling,RemoveFlags aFlags)7283 bool nsCSSFrameConstructor::ContentRemoved(nsIContent* aChild,
7284                                            nsIContent* aOldNextSibling,
7285                                            RemoveFlags aFlags) {
7286   MOZ_ASSERT(aChild);
7287   MOZ_ASSERT(!aChild->IsRootOfNativeAnonymousSubtree() || !aOldNextSibling,
7288              "Anonymous roots don't have siblings");
7289   AUTO_PROFILER_LABEL("nsCSSFrameConstructor::ContentRemoved",
7290                       LAYOUT_FrameConstruction);
7291   AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
7292   nsPresContext* presContext = mPresShell->GetPresContext();
7293   MOZ_ASSERT(presContext, "Our presShell should have a valid presContext");
7294 
7295   // We want to detect when the viewport override element stored in the
7296   // prescontext is in the subtree being removed.  Except in fullscreen cases
7297   // (which are handled in Element::UnbindFromTree and do not get stored on the
7298   // prescontext), the override element is always either the root element or a
7299   // <body> child of the root element.  So we can only be removing the stored
7300   // override element if the thing being removed is either the override element
7301   // itself or the root element (which can be a parent of the override element).
7302   if (aChild == presContext->GetViewportScrollStylesOverrideElement() ||
7303       (aChild->IsElement() && !aChild->GetParent())) {
7304     // We might be removing the element that we propagated viewport scrollbar
7305     // styles from.  Recompute those. (This clause covers two of the three
7306     // possible scrollbar-propagation sources: the <body> [as aChild or a
7307     // descendant] and the root node. The other possible scrollbar-propagation
7308     // source is a fullscreen element, and we have code elsewhere to update
7309     // scrollbars after fullscreen elements are removed -- specifically, it's
7310     // part of the fullscreen cleanup code called by Element::UnbindFromTree.
7311     // We don't handle the fullscreen case here, because it doesn't change the
7312     // scrollbar styles override element stored on the prescontext.)
7313     Element* newOverrideElement =
7314         presContext->UpdateViewportScrollStylesOverride();
7315 
7316     // If aChild is the root, then we don't need to do any reframing of
7317     // newOverrideElement, because we're about to tear down the whole frame tree
7318     // anyway.  And we need to make sure we don't do any such reframing, because
7319     // reframing the <body> can trigger a reframe of the <html> and then reenter
7320     // here.
7321     //
7322     // But if aChild is not the root, and if newOverrideElement is not
7323     // the root and isn't aChild (which it could be if all we're doing
7324     // here is reframing the current override element), it needs
7325     // reframing.  In particular, it used to have a scrollframe
7326     // (because its overflow was not "visible"), but now it will
7327     // propagate its overflow to the viewport, so it should not need a
7328     // scrollframe anymore.
7329     if (aChild->GetParent() && newOverrideElement &&
7330         newOverrideElement->GetParent() && newOverrideElement != aChild) {
7331       LAYOUT_PHASE_TEMP_EXIT();
7332       RecreateFramesForContent(newOverrideElement, InsertionKind::Async);
7333       LAYOUT_PHASE_TEMP_REENTER();
7334     }
7335   }
7336 
7337 #ifdef DEBUG
7338   if (gNoisyContentUpdates) {
7339     printf(
7340         "nsCSSFrameConstructor::ContentRemoved container=%p child=%p "
7341         "old-next-sibling=%p\n",
7342         aChild->GetParent(), aChild, aOldNextSibling);
7343     if (gReallyNoisyContentUpdates) {
7344       aChild->GetParent()->List(stdout, 0);
7345     }
7346   }
7347 #endif
7348 
7349   nsIFrame* childFrame = aChild->GetPrimaryFrame();
7350   if (!childFrame || childFrame->GetContent() != aChild) {
7351     // XXXbz the GetContent() != aChild check is needed due to bug 135040.
7352     // Remove it once that's fixed.
7353     childFrame = nullptr;
7354   }
7355 
7356   // If we're removing the root, then make sure to remove things starting at
7357   // the viewport's child instead of the primary frame (which might even be
7358   // null if the root was display:none, even though the frames above it got
7359   // created).  Detecting removal of a root is a little exciting; in particular,
7360   // having no parent is necessary but NOT sufficient.
7361   //
7362   // Due to how we process reframes, the content node might not even be in our
7363   // document by now.  So explicitly check whether the viewport's first kid's
7364   // content node is aChild.
7365   //
7366   // FIXME(emilio): I think the "might not be in our document" bit is impossible
7367   // now.
7368   bool isRoot = false;
7369   if (!aChild->GetParent()) {
7370     if (nsIFrame* viewport = GetRootFrame()) {
7371       nsIFrame* firstChild = viewport->PrincipalChildList().FirstChild();
7372       if (firstChild && firstChild->GetContent() == aChild) {
7373         isRoot = true;
7374         childFrame = firstChild;
7375         NS_ASSERTION(!childFrame->GetNextSibling(), "How did that happen?");
7376       }
7377     }
7378   }
7379 
7380   // We need to be conservative about when to determine whether something has
7381   // display: contents or not because at this point our actual display may be
7382   // different.
7383   //
7384   // Consider the case of:
7385   //
7386   //   <div id="A" style="display: contents"><div id="B"></div></div>
7387   //
7388   // If we reconstruct A because its display changed to "none", we still need to
7389   // cleanup the frame on B, but A's display is now "none", so we can't poke at
7390   // the style of it.
7391   //
7392   // FIXME(emilio, bug 1450366): We can make this faster without adding much
7393   // complexity for the display: none -> other case, which right now
7394   // unnecessarily walks the content tree down.
7395   auto CouldHaveBeenDisplayContents = [aFlags](nsIContent* aContent) -> bool {
7396     return aFlags == REMOVE_FOR_RECONSTRUCTION || IsDisplayContents(aContent);
7397   };
7398 
7399   if (!childFrame && CouldHaveBeenDisplayContents(aChild)) {
7400     // NOTE(emilio): We may iterate through ::before and ::after here and they
7401     // may be gone after the respective ContentRemoved call. Right now
7402     // StyleChildrenIterator handles that properly, so it's not an issue.
7403     StyleChildrenIterator iter(aChild);
7404     for (nsIContent* c = iter.GetNextChild(); c; c = iter.GetNextChild()) {
7405       if (c->GetPrimaryFrame() || CouldHaveBeenDisplayContents(c)) {
7406         LAYOUT_PHASE_TEMP_EXIT();
7407         bool didReconstruct = ContentRemoved(c, nullptr, aFlags);
7408         LAYOUT_PHASE_TEMP_REENTER();
7409         if (didReconstruct) {
7410           return true;
7411         }
7412       }
7413     }
7414     return false;
7415   }
7416 
7417   if (childFrame) {
7418     if (aFlags == REMOVE_FOR_RECONSTRUCTION) {
7419       // Before removing the frames associated with the content object,
7420       // ask them to save their state onto our state object.
7421       CaptureStateForFramesOf(aChild, mFrameTreeState);
7422     }
7423 
7424     InvalidateCanvasIfNeeded(mPresShell, aChild);
7425 
7426     // See whether we need to remove more than just childFrame
7427     LAYOUT_PHASE_TEMP_EXIT();
7428     if (MaybeRecreateContainerForFrameRemoval(childFrame)) {
7429       LAYOUT_PHASE_TEMP_REENTER();
7430       return true;
7431     }
7432     LAYOUT_PHASE_TEMP_REENTER();
7433 
7434     // Get the childFrame's parent frame
7435     nsIFrame* parentFrame = childFrame->GetParent();
7436     LayoutFrameType parentType = parentFrame->Type();
7437 
7438     if (parentType == LayoutFrameType::FrameSet &&
7439         IsSpecialFramesetChild(aChild)) {
7440       // Just reframe the parent, since framesets are weird like that.
7441       LAYOUT_PHASE_TEMP_EXIT();
7442       RecreateFramesForContent(parentFrame->GetContent(), InsertionKind::Async);
7443       LAYOUT_PHASE_TEMP_REENTER();
7444       return true;
7445     }
7446 
7447     // If we're a child of MathML, then we should reframe the MathML content.
7448     // If we're non-MathML, then we would be wrapped in a block so we need to
7449     // check our grandparent in that case.
7450     nsIFrame* possibleMathMLAncestor = parentType == LayoutFrameType::Block
7451                                            ? parentFrame->GetParent()
7452                                            : parentFrame;
7453     if (possibleMathMLAncestor->IsFrameOfType(nsIFrame::eMathML)) {
7454       LAYOUT_PHASE_TEMP_EXIT();
7455       RecreateFramesForContent(parentFrame->GetContent(), InsertionKind::Async);
7456       LAYOUT_PHASE_TEMP_REENTER();
7457       return true;
7458     }
7459 
7460     // Undo XUL wrapping if it's no longer needed.
7461     // (If we're in the XUL block-wrapping situation, parentFrame is the
7462     // wrapper frame.)
7463     nsIFrame* grandparentFrame = parentFrame->GetParent();
7464     if (grandparentFrame && grandparentFrame->IsXULBoxFrame() &&
7465         (grandparentFrame->GetStateBits() & NS_STATE_BOX_WRAPS_KIDS_IN_BLOCK) &&
7466         // check if this frame is the only one needing wrapping
7467         aChild == AnyKidsNeedBlockParent(
7468                       parentFrame->PrincipalChildList().FirstChild()) &&
7469         !AnyKidsNeedBlockParent(childFrame->GetNextSibling())) {
7470       LAYOUT_PHASE_TEMP_EXIT();
7471       RecreateFramesForContent(grandparentFrame->GetContent(),
7472                                InsertionKind::Async);
7473       LAYOUT_PHASE_TEMP_REENTER();
7474       return true;
7475     }
7476 
7477 #ifdef ACCESSIBILITY
7478     if (aFlags != REMOVE_FOR_RECONSTRUCTION) {
7479       if (nsAccessibilityService* accService =
7480               PresShell::GetAccessibilityService()) {
7481         accService->ContentRemoved(mPresShell, aChild);
7482       }
7483     }
7484 #endif
7485 
7486     // Examine the containing-block for the removed content and see if
7487     // :first-letter style applies.
7488     nsIFrame* inflowChild = childFrame;
7489     if (childFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
7490       inflowChild = childFrame->GetPlaceholderFrame();
7491       NS_ASSERTION(inflowChild, "No placeholder for out-of-flow?");
7492     }
7493     nsContainerFrame* containingBlock =
7494         GetFloatContainingBlock(inflowChild->GetParent());
7495     bool haveFLS = containingBlock && HasFirstLetterStyle(containingBlock);
7496     if (haveFLS) {
7497       // Trap out to special routine that handles adjusting a blocks
7498       // frame tree when first-letter style is present.
7499 #ifdef NOISY_FIRST_LETTER
7500       printf("ContentRemoved: containingBlock=");
7501       containingBlock->ListTag(stdout);
7502       printf(" parentFrame=");
7503       parentFrame->ListTag(stdout);
7504       printf(" childFrame=");
7505       childFrame->ListTag(stdout);
7506       printf("\n");
7507 #endif
7508 
7509       // First update the containing blocks structure by removing the
7510       // existing letter frames. This makes the subsequent logic
7511       // simpler.
7512       RemoveLetterFrames(mPresShell, containingBlock);
7513 
7514       // Recover childFrame and parentFrame
7515       childFrame = aChild->GetPrimaryFrame();
7516       if (!childFrame || childFrame->GetContent() != aChild) {
7517         // XXXbz the GetContent() != aChild check is needed due to bug 135040.
7518         // Remove it once that's fixed.
7519         return false;
7520       }
7521       parentFrame = childFrame->GetParent();
7522       parentType = parentFrame->Type();
7523 
7524 #ifdef NOISY_FIRST_LETTER
7525       printf("  ==> revised parentFrame=");
7526       parentFrame->ListTag(stdout);
7527       printf(" childFrame=");
7528       childFrame->ListTag(stdout);
7529       printf("\n");
7530 #endif
7531     }
7532 
7533 #ifdef DEBUG
7534     if (gReallyNoisyContentUpdates) {
7535       printf("nsCSSFrameConstructor::ContentRemoved: childFrame=");
7536       childFrame->ListTag(stdout);
7537       putchar('\n');
7538       parentFrame->List(stdout);
7539     }
7540 #endif
7541 
7542     // Notify the parent frame that it should delete the frame
7543     if (childFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
7544       childFrame = childFrame->GetPlaceholderFrame();
7545       NS_ASSERTION(childFrame, "Missing placeholder frame for out of flow.");
7546       parentFrame = childFrame->GetParent();
7547     }
7548 
7549     RemoveFrame(nsLayoutUtils::GetChildListNameFor(childFrame), childFrame);
7550 
7551     // NOTE(emilio): aChild could be dead here already if it is a ::before or
7552     // ::after pseudo-element (since in that case it was owned by childFrame,
7553     // which we just destroyed).
7554 
7555     if (isRoot) {
7556       mRootElementFrame = nullptr;
7557       mRootElementStyleFrame = nullptr;
7558       mDocElementContainingBlock = nullptr;
7559       mPageSequenceFrame = nullptr;
7560       mHasRootAbsPosContainingBlock = false;
7561     }
7562 
7563     if (haveFLS && mRootElementFrame) {
7564       RecoverLetterFrames(containingBlock);
7565     }
7566 
7567     // If we're just reconstructing frames for the element, then the
7568     // following ContentInserted notification on the element will
7569     // take care of fixing up any adjacent text nodes.  We don't need
7570     // to do this if the table parent type of our parent type is not
7571     // eTypeBlock, though, because in that case the whitespace isn't
7572     // being suppressed due to us anyway.
7573     if (aOldNextSibling && aFlags == REMOVE_CONTENT &&
7574         GetParentType(parentType) == eTypeBlock) {
7575       MOZ_ASSERT(aChild->GetParentNode(),
7576                  "How did we have a sibling without a parent?");
7577       // Adjacent whitespace-only text nodes might have been suppressed if
7578       // this node does not have inline ends. Create frames for them now
7579       // if necessary.
7580       // Reframe any text node just before the node being removed, if there is
7581       // one, and if it's not the last child or the first child. If a whitespace
7582       // textframe was being suppressed and it's now the last child or first
7583       // child then it can stay suppressed since the parent must be a block
7584       // and hence it's adjacent to a block end.
7585       // If aOldNextSibling is null, then the text node before the node being
7586       // removed is the last node, and we don't need to worry about it.
7587       //
7588       // FIXME(emilio): This should probably use the lazy frame construction
7589       // bits if possible instead of reframing it in place.
7590       nsIContent* prevSibling = aOldNextSibling->GetPreviousSibling();
7591       if (prevSibling && prevSibling->GetPreviousSibling()) {
7592         LAYOUT_PHASE_TEMP_EXIT();
7593         ReframeTextIfNeeded(prevSibling);
7594         LAYOUT_PHASE_TEMP_REENTER();
7595       }
7596       // Reframe any text node just after the node being removed, if there is
7597       // one, and if it's not the last child or the first child.
7598       if (aOldNextSibling->GetNextSibling() &&
7599           aOldNextSibling->GetPreviousSibling()) {
7600         LAYOUT_PHASE_TEMP_EXIT();
7601         ReframeTextIfNeeded(aOldNextSibling);
7602         LAYOUT_PHASE_TEMP_REENTER();
7603       }
7604     }
7605 
7606 #ifdef DEBUG
7607     if (gReallyNoisyContentUpdates && parentFrame) {
7608       printf("nsCSSFrameConstructor::ContentRemoved: resulting frame model:\n");
7609       parentFrame->List(stdout);
7610     }
7611 #endif
7612   }
7613 
7614   return false;
7615 }
7616 
7617 /**
7618  * This method invalidates the canvas when frames are removed or added for a
7619  * node that might have its background propagated to the canvas, i.e., a
7620  * document root node or an HTML BODY which is a child of the root node.
7621  *
7622  * @param aFrame a frame for a content node about to be removed or a frame that
7623  *               was just created for a content node that was inserted.
7624  */
InvalidateCanvasIfNeeded(PresShell * aPresShell,nsIContent * aNode)7625 static void InvalidateCanvasIfNeeded(PresShell* aPresShell, nsIContent* aNode) {
7626   MOZ_ASSERT(aPresShell->GetRootFrame(), "What happened here?");
7627   MOZ_ASSERT(aPresShell->GetPresContext(), "Say what?");
7628 
7629   //  Note that both in ContentRemoved and ContentInserted the content node
7630   //  will still have the right parent pointer, so looking at that is ok.
7631 
7632   nsIContent* parent = aNode->GetParent();
7633   if (parent) {
7634     // Has a parent; might not be what we want
7635     nsIContent* grandParent = parent->GetParent();
7636     if (grandParent) {
7637       // Has a grandparent, so not what we want
7638       return;
7639     }
7640 
7641     // Check whether it's an HTML body
7642     if (!aNode->IsHTMLElement(nsGkAtoms::body)) {
7643       return;
7644     }
7645   }
7646 
7647   // At this point the node has no parent or it's an HTML <body> child of the
7648   // root.  We might not need to invalidate in this case (eg we might be in
7649   // XHTML or something), but chances are we want to.  Play it safe.
7650   // Invalidate the viewport.
7651 
7652   nsIFrame* rootFrame = aPresShell->GetRootFrame();
7653   rootFrame->InvalidateFrameSubtree();
7654 }
7655 
EnsureFrameForTextNodeIsCreatedAfterFlush(CharacterData * aContent)7656 bool nsCSSFrameConstructor::EnsureFrameForTextNodeIsCreatedAfterFlush(
7657     CharacterData* aContent) {
7658   if (!aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE)) {
7659     return false;
7660   }
7661 
7662   if (mAlwaysCreateFramesForIgnorableWhitespace) {
7663     return false;
7664   }
7665 
7666   // Text frame may have been suppressed. Disable suppression and signal that a
7667   // flush should be performed. We do this on a document-wide basis so that
7668   // pages that repeatedly query metrics for collapsed-whitespace text nodes
7669   // don't trigger pathological behavior.
7670   mAlwaysCreateFramesForIgnorableWhitespace = true;
7671   Element* root = mDocument->GetRootElement();
7672   if (!root) {
7673     return false;
7674   }
7675 
7676   RestyleManager()->PostRestyleEvent(root, RestyleHint{0},
7677                                      nsChangeHint_ReconstructFrame);
7678   return true;
7679 }
7680 
CharacterDataChanged(nsIContent * aContent,const CharacterDataChangeInfo & aInfo)7681 void nsCSSFrameConstructor::CharacterDataChanged(
7682     nsIContent* aContent, const CharacterDataChangeInfo& aInfo) {
7683   AUTO_PROFILER_LABEL("nsCSSFrameConstructor::CharacterDataChanged",
7684                       LAYOUT_FrameConstruction);
7685   AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
7686 
7687   if ((aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE) &&
7688        !aContent->TextIsOnlyWhitespace()) ||
7689       (aContent->HasFlag(NS_REFRAME_IF_WHITESPACE) &&
7690        aContent->TextIsOnlyWhitespace())) {
7691 #ifdef DEBUG
7692     nsIFrame* frame = aContent->GetPrimaryFrame();
7693     NS_ASSERTION(!frame || !frame->IsGeneratedContentFrame(),
7694                  "Bit should never be set on generated content");
7695 #endif
7696     LAYOUT_PHASE_TEMP_EXIT();
7697     RecreateFramesForContent(aContent, InsertionKind::Async);
7698     LAYOUT_PHASE_TEMP_REENTER();
7699     return;
7700   }
7701 
7702   // It's possible the frame whose content changed isn't inserted into the
7703   // frame hierarchy yet, or that there is no frame that maps the content
7704   if (nsIFrame* frame = aContent->GetPrimaryFrame()) {
7705 #if 0
7706     NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
7707        ("nsCSSFrameConstructor::CharacterDataChanged: content=%p[%s] subcontent=%p frame=%p",
7708         aContent, ContentTag(aContent, 0),
7709         aSubContent, frame));
7710 #endif
7711 
7712     // Special check for text content that is a child of a letter frame.  If
7713     // this happens, we should remove the letter frame, do whatever we're
7714     // planning to do with this notification, then put the letter frame back.
7715     // Note that this is basically what RecreateFramesForContent ends up doing;
7716     // the reason we dont' want to call that here is that our text content
7717     // could be native anonymous, in which case RecreateFramesForContent would
7718     // completely barf on it.  And recreating the non-anonymous ancestor would
7719     // just lead us to come back into this notification (e.g. if quotes or
7720     // counters are involved), leading to a loop.
7721     nsContainerFrame* block = GetFloatContainingBlock(frame);
7722     bool haveFirstLetterStyle = false;
7723     if (block) {
7724       // See if the block has first-letter style applied to it.
7725       haveFirstLetterStyle = HasFirstLetterStyle(block);
7726       if (haveFirstLetterStyle) {
7727         RemoveLetterFrames(mPresShell, block);
7728         // Reget |frame|, since we might have killed it.
7729         // Do we really need to call CharacterDataChanged in this case, though?
7730         frame = aContent->GetPrimaryFrame();
7731         NS_ASSERTION(frame, "Should have frame here!");
7732       }
7733     }
7734 
7735     // Notify the first frame that maps the content. It will generate a reflow
7736     // command
7737     frame->CharacterDataChanged(aInfo);
7738 
7739     if (haveFirstLetterStyle) {
7740       RecoverLetterFrames(block);
7741     }
7742   }
7743 }
7744 
RecalcQuotesAndCounters()7745 void nsCSSFrameConstructor::RecalcQuotesAndCounters() {
7746   nsAutoScriptBlocker scriptBlocker;
7747 
7748   if (mQuotesDirty) {
7749     mQuotesDirty = false;
7750     mQuoteList.RecalcAll();
7751   }
7752 
7753   if (mCountersDirty) {
7754     mCountersDirty = false;
7755     mCounterManager.RecalcAll();
7756   }
7757 
7758   NS_ASSERTION(!mQuotesDirty, "Quotes updates will be lost");
7759   NS_ASSERTION(!mCountersDirty, "Counter updates will be lost");
7760 }
7761 
NotifyCounterStylesAreDirty()7762 void nsCSSFrameConstructor::NotifyCounterStylesAreDirty() {
7763   mCounterManager.SetAllDirty();
7764   CountersDirty();
7765 }
7766 
WillDestroyFrameTree()7767 void nsCSSFrameConstructor::WillDestroyFrameTree() {
7768 #if defined(DEBUG_dbaron_off)
7769   mCounterManager.Dump();
7770 #endif
7771 
7772   mIsDestroyingFrameTree = true;
7773 
7774   // Prevent frame tree destruction from being O(N^2)
7775   mQuoteList.Clear();
7776   mCounterManager.Clear();
7777   nsFrameManager::Destroy();
7778 }
7779 
7780 // STATIC
7781 
7782 // XXXbz I'd really like this method to go away. Once we have inline-block and
7783 // I can just use that for sized broken images, that can happen, maybe.
GetAlternateTextFor(Element * aElement,nsAtom * aTag,nsAString & aAltText)7784 void nsCSSFrameConstructor::GetAlternateTextFor(Element* aElement, nsAtom* aTag,
7785                                                 nsAString& aAltText) {
7786   // The "alt" attribute specifies alternate text that is rendered
7787   // when the image can not be displayed.
7788   if (aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::alt, aAltText)) {
7789     return;
7790   }
7791 
7792   if (nsGkAtoms::input == aTag) {
7793     // If there's no "alt" attribute, and aContent is an input element, then use
7794     // the value of the "value" attribute
7795     if (aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::value, aAltText)) {
7796       return;
7797     }
7798 
7799     // If there's no "value" attribute either, then use the localized string for
7800     // "Submit" as the alternate text.
7801     nsContentUtils::GetMaybeLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
7802                                             "Submit", aElement->OwnerDoc(),
7803                                             aAltText);
7804   }
7805 }
7806 
CreateContinuingOuterTableFrame(nsIFrame * aFrame,nsContainerFrame * aParentFrame,nsIContent * aContent,ComputedStyle * aComputedStyle)7807 nsIFrame* nsCSSFrameConstructor::CreateContinuingOuterTableFrame(
7808     nsIFrame* aFrame, nsContainerFrame* aParentFrame, nsIContent* aContent,
7809     ComputedStyle* aComputedStyle) {
7810   nsTableWrapperFrame* newFrame =
7811       NS_NewTableWrapperFrame(mPresShell, aComputedStyle);
7812 
7813   newFrame->Init(aContent, aParentFrame, aFrame);
7814 
7815   // Create a continuing inner table frame, and if there's a caption then
7816   // replicate the caption
7817   nsFrameList newChildFrames;
7818 
7819   nsIFrame* childFrame = aFrame->PrincipalChildList().FirstChild();
7820   if (childFrame) {
7821     nsIFrame* continuingTableFrame =
7822         CreateContinuingFrame(childFrame, newFrame);
7823     newChildFrames.AppendFrame(nullptr, continuingTableFrame);
7824 
7825     NS_ASSERTION(!childFrame->GetNextSibling(),
7826                  "there can be only one inner table frame");
7827   }
7828 
7829   // Set the table wrapper's initial child list
7830   newFrame->SetInitialChildList(kPrincipalList, newChildFrames);
7831 
7832   return newFrame;
7833 }
7834 
CreateContinuingTableFrame(nsIFrame * aFrame,nsContainerFrame * aParentFrame,nsIContent * aContent,ComputedStyle * aComputedStyle)7835 nsIFrame* nsCSSFrameConstructor::CreateContinuingTableFrame(
7836     nsIFrame* aFrame, nsContainerFrame* aParentFrame, nsIContent* aContent,
7837     ComputedStyle* aComputedStyle) {
7838   nsTableFrame* newFrame = NS_NewTableFrame(mPresShell, aComputedStyle);
7839 
7840   newFrame->Init(aContent, aParentFrame, aFrame);
7841 
7842   // Replicate any header/footer frames
7843   nsFrameList childFrames;
7844   for (nsIFrame* childFrame : aFrame->PrincipalChildList()) {
7845     // See if it's a header/footer, possibly wrapped in a scroll frame.
7846     nsTableRowGroupFrame* rowGroupFrame =
7847         static_cast<nsTableRowGroupFrame*>(childFrame);
7848     // If the row group was continued, then don't replicate it.
7849     nsIFrame* rgNextInFlow = rowGroupFrame->GetNextInFlow();
7850     if (rgNextInFlow) {
7851       rowGroupFrame->SetRepeatable(false);
7852     } else if (rowGroupFrame->IsRepeatable()) {
7853       // Replicate the header/footer frame.
7854       nsTableRowGroupFrame* headerFooterFrame;
7855       nsFrameList childList;
7856 
7857       nsFrameConstructorState state(
7858           mPresShell, GetAbsoluteContainingBlock(newFrame, FIXED_POS),
7859           GetAbsoluteContainingBlock(newFrame, ABS_POS), nullptr);
7860       state.mCreatingExtraFrames = true;
7861 
7862       ComputedStyle* const headerFooterComputedStyle = rowGroupFrame->Style();
7863       headerFooterFrame = static_cast<nsTableRowGroupFrame*>(
7864           NS_NewTableRowGroupFrame(mPresShell, headerFooterComputedStyle));
7865 
7866       nsIContent* headerFooter = rowGroupFrame->GetContent();
7867       headerFooterFrame->Init(headerFooter, newFrame, nullptr);
7868 
7869       nsFrameConstructorSaveState absoluteSaveState;
7870       MakeTablePartAbsoluteContainingBlockIfNeeded(
7871           state, headerFooterComputedStyle->StyleDisplay(), absoluteSaveState,
7872           headerFooterFrame);
7873 
7874       ProcessChildren(state, headerFooter, rowGroupFrame->Style(),
7875                       headerFooterFrame, true, childList, false, nullptr);
7876       NS_ASSERTION(state.mFloatedList.IsEmpty(), "unexpected floated element");
7877       headerFooterFrame->SetInitialChildList(kPrincipalList, childList);
7878       headerFooterFrame->SetRepeatable(true);
7879 
7880       // Table specific initialization
7881       headerFooterFrame->InitRepeatedFrame(rowGroupFrame);
7882 
7883       // XXX Deal with absolute and fixed frames...
7884       childFrames.AppendFrame(nullptr, headerFooterFrame);
7885     }
7886   }
7887 
7888   // Set the table frame's initial child list
7889   newFrame->SetInitialChildList(kPrincipalList, childFrames);
7890 
7891   return newFrame;
7892 }
7893 
CreateContinuingFrame(nsIFrame * aFrame,nsContainerFrame * aParentFrame,bool aIsFluid)7894 nsIFrame* nsCSSFrameConstructor::CreateContinuingFrame(
7895     nsIFrame* aFrame, nsContainerFrame* aParentFrame, bool aIsFluid) {
7896   ComputedStyle* computedStyle = aFrame->Style();
7897   nsIFrame* newFrame = nullptr;
7898   nsIFrame* nextContinuation = aFrame->GetNextContinuation();
7899   nsIFrame* nextInFlow = aFrame->GetNextInFlow();
7900 
7901   // Use the frame type to determine what type of frame to create
7902   LayoutFrameType frameType = aFrame->Type();
7903   nsIContent* content = aFrame->GetContent();
7904 
7905   if (LayoutFrameType::Text == frameType) {
7906     newFrame = NS_NewContinuingTextFrame(mPresShell, computedStyle);
7907     newFrame->Init(content, aParentFrame, aFrame);
7908   } else if (LayoutFrameType::Inline == frameType) {
7909     newFrame = NS_NewInlineFrame(mPresShell, computedStyle);
7910     newFrame->Init(content, aParentFrame, aFrame);
7911   } else if (LayoutFrameType::Block == frameType) {
7912     MOZ_ASSERT(!aFrame->IsTableCaption(),
7913                "no support for fragmenting table captions yet");
7914     newFrame = NS_NewBlockFrame(mPresShell, computedStyle);
7915     newFrame->Init(content, aParentFrame, aFrame);
7916 #ifdef MOZ_XUL
7917   } else if (LayoutFrameType::XULLabel == frameType) {
7918     newFrame = NS_NewXULLabelFrame(mPresShell, computedStyle);
7919     newFrame->Init(content, aParentFrame, aFrame);
7920 #endif
7921   } else if (LayoutFrameType::ColumnSetWrapper == frameType) {
7922     newFrame =
7923         NS_NewColumnSetWrapperFrame(mPresShell, computedStyle, nsFrameState(0));
7924     newFrame->Init(content, aParentFrame, aFrame);
7925   } else if (LayoutFrameType::ColumnSet == frameType) {
7926     MOZ_ASSERT(!aFrame->IsTableCaption(),
7927                "no support for fragmenting table captions yet");
7928     newFrame = NS_NewColumnSetFrame(mPresShell, computedStyle, nsFrameState(0));
7929     newFrame->Init(content, aParentFrame, aFrame);
7930   } else if (LayoutFrameType::Page == frameType) {
7931     nsContainerFrame* canvasFrame;
7932     newFrame =
7933         ConstructPageFrame(mPresShell, aParentFrame, aFrame, canvasFrame);
7934   } else if (LayoutFrameType::TableWrapper == frameType) {
7935     newFrame = CreateContinuingOuterTableFrame(aFrame, aParentFrame, content,
7936                                                computedStyle);
7937   } else if (LayoutFrameType::Table == frameType) {
7938     newFrame = CreateContinuingTableFrame(aFrame, aParentFrame, content,
7939                                           computedStyle);
7940   } else if (LayoutFrameType::TableRowGroup == frameType) {
7941     newFrame = NS_NewTableRowGroupFrame(mPresShell, computedStyle);
7942     newFrame->Init(content, aParentFrame, aFrame);
7943     if (newFrame->GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN) {
7944       nsTableFrame::RegisterPositionedTablePart(newFrame);
7945     }
7946   } else if (LayoutFrameType::TableRow == frameType) {
7947     nsTableRowFrame* rowFrame = NS_NewTableRowFrame(mPresShell, computedStyle);
7948 
7949     rowFrame->Init(content, aParentFrame, aFrame);
7950     if (rowFrame->GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN) {
7951       nsTableFrame::RegisterPositionedTablePart(rowFrame);
7952     }
7953 
7954     // Create a continuing frame for each table cell frame
7955     nsFrameList newChildList;
7956     nsIFrame* cellFrame = aFrame->PrincipalChildList().FirstChild();
7957     while (cellFrame) {
7958       // See if it's a table cell frame
7959       if (cellFrame->IsTableCellFrame()) {
7960         nsIFrame* continuingCellFrame =
7961             CreateContinuingFrame(cellFrame, rowFrame);
7962         newChildList.AppendFrame(nullptr, continuingCellFrame);
7963       }
7964       cellFrame = cellFrame->GetNextSibling();
7965     }
7966 
7967     rowFrame->SetInitialChildList(kPrincipalList, newChildList);
7968     newFrame = rowFrame;
7969 
7970   } else if (LayoutFrameType::TableCell == frameType) {
7971     // Warning: If you change this and add a wrapper frame around table cell
7972     // frames, make sure Bug 368554 doesn't regress!
7973     // See IsInAutoWidthTableCellForQuirk() in nsImageFrame.cpp.
7974     nsTableFrame* tableFrame =
7975         static_cast<nsTableRowFrame*>(aParentFrame)->GetTableFrame();
7976     nsTableCellFrame* cellFrame =
7977         NS_NewTableCellFrame(mPresShell, computedStyle, tableFrame);
7978 
7979     cellFrame->Init(content, aParentFrame, aFrame);
7980     if (cellFrame->GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN) {
7981       nsTableFrame::RegisterPositionedTablePart(cellFrame);
7982     }
7983 
7984     // Create a continuing area frame
7985     nsIFrame* blockFrame = aFrame->PrincipalChildList().FirstChild();
7986     nsIFrame* continuingBlockFrame =
7987         CreateContinuingFrame(blockFrame, cellFrame);
7988 
7989     SetInitialSingleChild(cellFrame, continuingBlockFrame);
7990     newFrame = cellFrame;
7991   } else if (LayoutFrameType::Line == frameType) {
7992     newFrame = NS_NewFirstLineFrame(mPresShell, computedStyle);
7993     newFrame->Init(content, aParentFrame, aFrame);
7994   } else if (LayoutFrameType::Letter == frameType) {
7995     newFrame = NS_NewFirstLetterFrame(mPresShell, computedStyle);
7996     newFrame->Init(content, aParentFrame, aFrame);
7997   } else if (LayoutFrameType::Image == frameType) {
7998     auto* imageFrame = static_cast<nsImageFrame*>(aFrame);
7999     newFrame = imageFrame->CreateContinuingFrame(mPresShell, computedStyle);
8000     newFrame->Init(content, aParentFrame, aFrame);
8001   } else if (LayoutFrameType::ImageControl == frameType) {
8002     newFrame = NS_NewImageControlFrame(mPresShell, computedStyle);
8003     newFrame->Init(content, aParentFrame, aFrame);
8004   } else if (LayoutFrameType::FieldSet == frameType) {
8005     newFrame = NS_NewFieldSetFrame(mPresShell, computedStyle);
8006     newFrame->Init(content, aParentFrame, aFrame);
8007   } else if (LayoutFrameType::Legend == frameType) {
8008     newFrame = NS_NewLegendFrame(mPresShell, computedStyle);
8009     newFrame->Init(content, aParentFrame, aFrame);
8010   } else if (LayoutFrameType::FlexContainer == frameType) {
8011     newFrame = NS_NewFlexContainerFrame(mPresShell, computedStyle);
8012     newFrame->Init(content, aParentFrame, aFrame);
8013   } else if (LayoutFrameType::GridContainer == frameType) {
8014     newFrame = NS_NewGridContainerFrame(mPresShell, computedStyle);
8015     newFrame->Init(content, aParentFrame, aFrame);
8016   } else if (LayoutFrameType::Ruby == frameType) {
8017     newFrame = NS_NewRubyFrame(mPresShell, computedStyle);
8018     newFrame->Init(content, aParentFrame, aFrame);
8019   } else if (LayoutFrameType::RubyBaseContainer == frameType) {
8020     newFrame = NS_NewRubyBaseContainerFrame(mPresShell, computedStyle);
8021     newFrame->Init(content, aParentFrame, aFrame);
8022   } else if (LayoutFrameType::RubyTextContainer == frameType) {
8023     newFrame = NS_NewRubyTextContainerFrame(mPresShell, computedStyle);
8024     newFrame->Init(content, aParentFrame, aFrame);
8025   } else if (LayoutFrameType::Details == frameType) {
8026     newFrame = NS_NewDetailsFrame(mPresShell, computedStyle);
8027     newFrame->Init(content, aParentFrame, aFrame);
8028   } else {
8029     MOZ_CRASH("unexpected frame type");
8030   }
8031 
8032   // Init() set newFrame to be a fluid continuation of aFrame.
8033   // If we want a non-fluid continuation, we need to call SetPrevContinuation()
8034   // to reset NS_FRAME_IS_FLUID_CONTINUATION.
8035   if (!aIsFluid) {
8036     newFrame->SetPrevContinuation(aFrame);
8037   }
8038 
8039   // If a continuing frame needs to carry frame state bits from its previous
8040   // continuation or parent, set them in nsFrame::Init(), or in any derived
8041   // frame class's Init() if the bits are belong to specific group.
8042 
8043   if (nextInFlow) {
8044     nextInFlow->SetPrevInFlow(newFrame);
8045     newFrame->SetNextInFlow(nextInFlow);
8046   } else if (nextContinuation) {
8047     nextContinuation->SetPrevContinuation(newFrame);
8048     newFrame->SetNextContinuation(nextContinuation);
8049   }
8050 
8051   MOZ_ASSERT(!newFrame->GetNextSibling(), "unexpected sibling");
8052   return newFrame;
8053 }
8054 
ReplicateFixedFrames(nsPageContentFrame * aParentFrame)8055 nsresult nsCSSFrameConstructor::ReplicateFixedFrames(
8056     nsPageContentFrame* aParentFrame) {
8057   // Now deal with fixed-pos things....  They should appear on all pages,
8058   // so we want to move over the placeholders when processing the child
8059   // of the pageContentFrame.
8060 
8061   nsIFrame* prevPageContentFrame = aParentFrame->GetPrevInFlow();
8062   if (!prevPageContentFrame) {
8063     return NS_OK;
8064   }
8065   nsContainerFrame* canvasFrame =
8066       do_QueryFrame(aParentFrame->PrincipalChildList().FirstChild());
8067   nsIFrame* prevCanvasFrame =
8068       prevPageContentFrame->PrincipalChildList().FirstChild();
8069   if (!canvasFrame || !prevCanvasFrame) {
8070     // document's root element frame missing
8071     return NS_ERROR_UNEXPECTED;
8072   }
8073 
8074   nsFrameList fixedPlaceholders;
8075   nsIFrame* firstFixed =
8076       prevPageContentFrame->GetChildList(nsIFrame::kFixedList).FirstChild();
8077   if (!firstFixed) {
8078     return NS_OK;
8079   }
8080 
8081   // Don't allow abs-pos descendants of the fixed content to escape the content.
8082   // This should not normally be possible (because fixed-pos elements should
8083   // be absolute containers) but fixed-pos tables currently aren't abs-pos
8084   // containers.
8085   nsFrameConstructorState state(mPresShell, aParentFrame, nullptr,
8086                                 mRootElementFrame);
8087   state.mCreatingExtraFrames = true;
8088 
8089   // We can't use an ancestor filter here, because we're not going to
8090   // be usefully recurring down the tree.  This means that other
8091   // places in frame construction can't assume a filter is
8092   // initialized!
8093 
8094   // Iterate across fixed frames and replicate each whose placeholder is a
8095   // descendant of aFrame. (We don't want to explicitly copy placeholders that
8096   // are within fixed frames, because that would cause duplicates on the new
8097   // page - bug 389619)
8098   for (nsIFrame* fixed = firstFixed; fixed; fixed = fixed->GetNextSibling()) {
8099     nsIFrame* prevPlaceholder = fixed->GetPlaceholderFrame();
8100     if (prevPlaceholder && nsLayoutUtils::IsProperAncestorFrame(
8101                                prevCanvasFrame, prevPlaceholder)) {
8102       // We want to use the same style as the primary style frame for
8103       // our content
8104       nsIContent* content = fixed->GetContent();
8105       ComputedStyle* computedStyle =
8106           nsLayoutUtils::GetStyleFrame(content)->Style();
8107       AutoFrameConstructionItemList items(this);
8108       AddFrameConstructionItemsInternal(state, content, canvasFrame, true,
8109                                         computedStyle,
8110                                         {ItemFlag::AllowPageBreak}, items);
8111       ConstructFramesFromItemList(state, items, canvasFrame,
8112                                   /* aParentIsWrapperAnonBox = */ false,
8113                                   fixedPlaceholders);
8114     }
8115   }
8116 
8117   // Add the placeholders to our primary child list.
8118   // XXXbz this is a little screwed up, since the fixed frames will have
8119   // broken auto-positioning. Oh, well.
8120   NS_ASSERTION(!canvasFrame->PrincipalChildList().FirstChild(),
8121                "leaking frames; doc root continuation must be empty");
8122   canvasFrame->SetInitialChildList(kPrincipalList, fixedPlaceholders);
8123   return NS_OK;
8124 }
8125 
GetInsertionPoint(nsIContent * aChild)8126 nsCSSFrameConstructor::InsertionPoint nsCSSFrameConstructor::GetInsertionPoint(
8127     nsIContent* aChild) {
8128   MOZ_ASSERT(aChild);
8129   nsIContent* insertionElement = aChild->GetFlattenedTreeParent();
8130   if (!insertionElement) {
8131     // The element doesn't belong in the flattened tree, and thus we don't want
8132     // to render it.
8133     return {};
8134   }
8135 
8136   return {GetContentInsertionFrameFor(insertionElement), insertionElement};
8137 }
8138 
8139 // Capture state for the frame tree rooted at the frame associated with the
8140 // content object, aContent
CaptureStateForFramesOf(nsIContent * aContent,nsILayoutHistoryState * aHistoryState)8141 void nsCSSFrameConstructor::CaptureStateForFramesOf(
8142     nsIContent* aContent, nsILayoutHistoryState* aHistoryState) {
8143   if (!aHistoryState) {
8144     return;
8145   }
8146   nsIFrame* frame = aContent->GetPrimaryFrame();
8147   if (frame == mRootElementFrame) {
8148     frame = mRootElementFrame
8149                 ? GetAbsoluteContainingBlock(mRootElementFrame, FIXED_POS)
8150                 : GetRootFrame();
8151   }
8152   for (; frame;
8153        frame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(frame)) {
8154     CaptureFrameState(frame, aHistoryState);
8155   }
8156 }
8157 
IsWhitespaceFrame(nsIFrame * aFrame)8158 static bool IsWhitespaceFrame(nsIFrame* aFrame) {
8159   MOZ_ASSERT(aFrame, "invalid argument");
8160   return aFrame->IsTextFrame() && aFrame->GetContent()->TextIsOnlyWhitespace();
8161 }
8162 
FindFirstNonWhitespaceChild(nsIFrame * aParentFrame)8163 static nsIFrame* FindFirstNonWhitespaceChild(nsIFrame* aParentFrame) {
8164   nsIFrame* f = aParentFrame->PrincipalChildList().FirstChild();
8165   while (f && IsWhitespaceFrame(f)) {
8166     f = f->GetNextSibling();
8167   }
8168   return f;
8169 }
8170 
FindNextNonWhitespaceSibling(nsIFrame * aFrame)8171 static nsIFrame* FindNextNonWhitespaceSibling(nsIFrame* aFrame) {
8172   nsIFrame* f = aFrame;
8173   do {
8174     f = f->GetNextSibling();
8175   } while (f && IsWhitespaceFrame(f));
8176   return f;
8177 }
8178 
FindPreviousNonWhitespaceSibling(nsIFrame * aFrame)8179 static nsIFrame* FindPreviousNonWhitespaceSibling(nsIFrame* aFrame) {
8180   nsIFrame* f = aFrame;
8181   do {
8182     f = f->GetPrevSibling();
8183   } while (f && IsWhitespaceFrame(f));
8184   return f;
8185 }
8186 
MaybeRecreateContainerForFrameRemoval(nsIFrame * aFrame)8187 bool nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(
8188     nsIFrame* aFrame) {
8189 #define TRACE(reason)                                                       \
8190   PROFILER_TRACING_MARKER("Layout",                                         \
8191                           "MaybeRecreateContainerForFrameRemoval: " reason, \
8192                           LAYOUT, TRACING_EVENT)
8193   MOZ_ASSERT(aFrame, "Must have a frame");
8194   MOZ_ASSERT(aFrame->GetParent(), "Frame shouldn't be root");
8195   MOZ_ASSERT(aFrame == aFrame->FirstContinuation(),
8196              "aFrame not the result of GetPrimaryFrame()?");
8197 
8198   nsIFrame* inFlowFrame = (aFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW)
8199                               ? aFrame->GetPlaceholderFrame()
8200                               : aFrame;
8201   MOZ_ASSERT(inFlowFrame, "How did that happen?");
8202   MOZ_ASSERT(inFlowFrame == inFlowFrame->FirstContinuation(),
8203              "placeholder for primary frame has previous continuations?");
8204   nsIFrame* parent = inFlowFrame->GetParent();
8205 
8206   if (inFlowFrame->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR)) {
8207     nsIFrame* grandparent = parent->GetParent();
8208     MOZ_ASSERT(grandparent);
8209 
8210     bool needsReframe =
8211         // 1. Removing a column-span may lead to an empty
8212         // ::-moz-column-span-wrapper.
8213         inFlowFrame->IsColumnSpan() ||
8214         // 2. Removing a frame which has any column-span siblings may also
8215         // lead to an empty ::-moz-column-span-wrapper subtree. The
8216         // column-span siblings were the frame's children, but later become
8217         // the frame's siblings after CreateColumnSpanSiblings().
8218         inFlowFrame->HasColumnSpanSiblings() ||
8219         // 3. Removing the only child of a ::-moz-column-content, whose
8220         // ColumnSet grandparent has a previous column-span sibling, requires
8221         // reframing since we might connect the ColumnSet's next column-span
8222         // sibling (if there's one). Note that this isn't actually needed if
8223         // the ColumnSet is at the end of ColumnSetWrapper since we create
8224         // empty ones at the end anyway, but we're not worried about
8225         // optimizing that case.
8226         (parent->Style()->GetPseudoType() == PseudoStyleType::columnContent &&
8227          // The only child in ::-moz-column-content (might be tall enough to
8228          // split across columns)
8229          !inFlowFrame->GetPrevSibling() && !inFlowFrame->GetNextSibling() &&
8230          // That ::-moz-column-content is the first column.
8231          !parent->GetPrevInFlow() &&
8232          // The ColumnSet grandparent has a previous sibling that is a
8233          // column-span.
8234          grandparent->GetPrevSibling());
8235 
8236     if (needsReframe) {
8237       nsContainerFrame* containingBlock =
8238           GetMultiColumnContainingBlockFor(inFlowFrame);
8239 
8240 #ifdef DEBUG
8241       if (IsFramePartOfIBSplit(inFlowFrame)) {
8242         nsIFrame* ibContainingBlock = GetIBContainingBlockFor(inFlowFrame);
8243         MOZ_ASSERT(containingBlock == ibContainingBlock ||
8244                        nsLayoutUtils::IsProperAncestorFrame(containingBlock,
8245                                                             ibContainingBlock),
8246                    "Multi-column containing block should be equal to or be the "
8247                    "ancestor of the IB containing block!");
8248       }
8249 #endif
8250 
8251       TRACE("Multi-column");
8252       RecreateFramesForContent(containingBlock->GetContent(),
8253                                InsertionKind::Async);
8254       return true;
8255     }
8256   }
8257 
8258   if (IsFramePartOfIBSplit(aFrame)) {
8259     // The removal functions can't handle removal of an {ib} split directly; we
8260     // need to rebuild the containing block.
8261     TRACE("IB split removal");
8262     ReframeContainingBlock(aFrame);
8263     return true;
8264   }
8265 
8266   nsContainerFrame* insertionFrame = aFrame->GetContentInsertionFrame();
8267   if (insertionFrame && insertionFrame->IsLegendFrame() &&
8268       aFrame->GetParent()->IsFieldSetFrame()) {
8269     TRACE("Fieldset / Legend");
8270     RecreateFramesForContent(aFrame->GetParent()->GetContent(),
8271                              InsertionKind::Async);
8272     return true;
8273   }
8274 
8275   if (parent && parent->IsDetailsFrame()) {
8276     HTMLSummaryElement* summary =
8277         HTMLSummaryElement::FromNode(aFrame->GetContent());
8278     DetailsFrame* detailsFrame = static_cast<DetailsFrame*>(parent);
8279 
8280     // Unlike adding summary element cases, we need to check children of the
8281     // parent details frame since at this moment the summary element has been
8282     // already removed from the parent details element's child list.
8283     if (summary && detailsFrame->HasMainSummaryFrame(aFrame)) {
8284       // When removing a summary, we should reframe the parent details frame to
8285       // ensure that another summary is used or the default summary is
8286       // generated.
8287       TRACE("Details / Summary");
8288       RecreateFramesForContent(parent->GetContent(), InsertionKind::Async);
8289       return true;
8290     }
8291   }
8292 
8293   // Now check for possibly needing to reconstruct due to a pseudo parent
8294   // For the case of ruby pseudo parent, effectively, only pseudo rb/rt frame
8295   // need to be checked here, since all other types of parent will be catched
8296   // by "Check ruby containers" section below.
8297   if (IsTableOrRubyPseudo(parent)) {
8298     if (FindFirstNonWhitespaceChild(parent) == inFlowFrame ||
8299         !FindNextNonWhitespaceSibling(inFlowFrame->LastContinuation()) ||
8300         // If it is a whitespace, and is the only child of the parent, the
8301         // pseudo parent was created for the space, and should now be removed.
8302         (IsWhitespaceFrame(aFrame) &&
8303          parent->PrincipalChildList().OnlyChild()) ||
8304         // If we're a table-column-group, then the OnlyChild check above is
8305         // not going to catch cases when we're the first child.
8306         (inFlowFrame->IsTableColGroupFrame() &&
8307          parent->GetChildList(nsIFrame::kColGroupList).FirstChild() ==
8308              inFlowFrame) ||
8309         // Similar if we're a table-caption.
8310         (inFlowFrame->IsTableCaption() &&
8311          parent->GetChildList(nsIFrame::kCaptionList).FirstChild() ==
8312              inFlowFrame)) {
8313       TRACE("Table or ruby pseudo parent");
8314       RecreateFramesForContent(parent->GetContent(), InsertionKind::Async);
8315       return true;
8316     }
8317   }
8318 
8319   // Might need to reconstruct things if this frame's nextSibling is a table
8320   // or ruby pseudo, since removal of this frame might mean that this pseudo
8321   // needs to get merged with the frame's prevSibling if that's also a table
8322   // or ruby pseudo.
8323   nsIFrame* nextSibling =
8324       FindNextNonWhitespaceSibling(inFlowFrame->LastContinuation());
8325   NS_ASSERTION(!IsTableOrRubyPseudo(inFlowFrame), "Shouldn't happen here");
8326   // Effectively, for the ruby pseudo sibling case, only pseudo <ruby> frame
8327   // need to be checked here, since all other types of such frames will have
8328   // a ruby container parent, and be catched by "Check ruby containers" below.
8329   if (nextSibling && IsTableOrRubyPseudo(nextSibling)) {
8330     nsIFrame* prevSibling = FindPreviousNonWhitespaceSibling(inFlowFrame);
8331     if (prevSibling && IsTableOrRubyPseudo(prevSibling)) {
8332       TRACE("Table or ruby pseudo sibling");
8333       // Good enough to recreate frames for aFrame's parent's content; even if
8334       // aFrame's parent is a pseudo, that'll be the right content node.
8335       RecreateFramesForContent(parent->GetContent(), InsertionKind::Async);
8336       return true;
8337     }
8338   }
8339 
8340   // Check ruby containers
8341   LayoutFrameType parentType = parent->Type();
8342   if (parentType == LayoutFrameType::Ruby ||
8343       RubyUtils::IsRubyContainerBox(parentType)) {
8344     // In ruby containers, pseudo frames may be created from
8345     // whitespaces or even nothing. There are two cases we actually
8346     // need to handle here, but hard to check exactly:
8347     // 1. Status of spaces beside the frame may vary, and related
8348     //    frames may be constructed or destroyed accordingly.
8349     // 2. The type of the first child of a ruby frame determines
8350     //    whether a pseudo ruby base container should exist.
8351     TRACE("Ruby container");
8352     RecreateFramesForContent(parent->GetContent(), InsertionKind::Async);
8353     return true;
8354   }
8355 
8356   // Might need to reconstruct things if the removed frame's nextSibling is an
8357   // anonymous flex item.  The removed frame might've been what divided two
8358   // runs of inline content into two anonymous flex items, which would now
8359   // need to be merged.
8360   // NOTE: It's fine that we've advanced nextSibling past whitespace (up above);
8361   // we're only interested in anonymous flex items here, and those can never
8362   // be adjacent to whitespace, since they absorb contiguous runs of inline
8363   // non-replaced content (including whitespace).
8364   if (nextSibling && IsAnonymousFlexOrGridItem(nextSibling)) {
8365     AssertAnonymousFlexOrGridItemParent(nextSibling, parent);
8366     TRACE("Anon flex or grid item next sibling");
8367     // Recreate frames for the flex container (the removed frame's parent)
8368     RecreateFramesForContent(parent->GetContent(), InsertionKind::Async);
8369     return true;
8370   }
8371 
8372   // Might need to reconstruct things if the removed frame's nextSibling is
8373   // null and its parent is an anonymous flex item. (This might be the last
8374   // remaining child of that anonymous flex item, which can then go away.)
8375   if (!nextSibling && IsAnonymousFlexOrGridItem(parent)) {
8376     AssertAnonymousFlexOrGridItemParent(parent, parent->GetParent());
8377     TRACE("Anon flex or grid item parent");
8378     // Recreate frames for the flex container (the removed frame's grandparent)
8379     RecreateFramesForContent(parent->GetParent()->GetContent(),
8380                              InsertionKind::Async);
8381     return true;
8382   }
8383 
8384 #ifdef MOZ_XUL
8385   if (aFrame->IsPopupSetFrame()) {
8386     nsIPopupContainer* popupContainer =
8387         nsIPopupContainer::GetPopupContainer(mPresShell);
8388     if (popupContainer && popupContainer->GetPopupSetFrame() == aFrame) {
8389       TRACE("PopupSet");
8390       ReconstructDocElementHierarchy(InsertionKind::Async);
8391       return true;
8392     }
8393   }
8394 #endif
8395 
8396   // Reconstruct if inflowFrame is parent's only child, and parent is, or has,
8397   // a non-fluid continuation, i.e. it was split by bidi resolution
8398   if (!inFlowFrame->GetPrevSibling() && !inFlowFrame->GetNextSibling() &&
8399       ((parent->GetPrevContinuation() && !parent->GetPrevInFlow()) ||
8400        (parent->GetNextContinuation() && !parent->GetNextInFlow()))) {
8401     TRACE("Removing last child of non-fluid split parent");
8402     RecreateFramesForContent(parent->GetContent(), InsertionKind::Async);
8403     return true;
8404   }
8405 
8406   // We might still need to reconstruct things if the parent of inFlowFrame is
8407   // ib-split, since in that case the removal of aFrame might affect the
8408   // splitting of its parent.
8409   if (!IsFramePartOfIBSplit(parent)) {
8410     return false;
8411   }
8412 
8413   // If inFlowFrame is not the only in-flow child of |parent|, then removing
8414   // it will change nothing about the {ib} split.
8415   if (inFlowFrame != parent->PrincipalChildList().FirstChild() ||
8416       inFlowFrame->LastContinuation()->GetNextSibling()) {
8417     return false;
8418   }
8419 
8420   // If the parent is the first or last part of the {ib} split, then
8421   // removing one of its kids will have no effect on the splitting.
8422   // Get the first continuation up front so we don't have to do it twice.
8423   nsIFrame* parentFirstContinuation = parent->FirstContinuation();
8424   if (!GetIBSplitSibling(parentFirstContinuation) ||
8425       !GetIBSplitPrevSibling(parentFirstContinuation)) {
8426     return false;
8427   }
8428 
8429   TRACE("IB split parent");
8430   ReframeContainingBlock(parent);
8431   return true;
8432 #undef TRACE
8433 }
8434 
UpdateTableCellSpans(nsIContent * aContent)8435 void nsCSSFrameConstructor::UpdateTableCellSpans(nsIContent* aContent) {
8436   nsTableCellFrame* cellFrame = do_QueryFrame(aContent->GetPrimaryFrame());
8437 
8438   // It's possible that this warning could fire if some other style change
8439   // simultaneously changes the 'display' of the element and makes it no
8440   // longer be a table cell.
8441   NS_WARNING_ASSERTION(cellFrame, "Hint should only be posted on table cells!");
8442 
8443   if (cellFrame) {
8444     cellFrame->GetTableFrame()->RowOrColSpanChanged(cellFrame);
8445   }
8446 }
8447 
GetTopmostMathMLElement(nsIContent * aMathMLContent)8448 static nsIContent* GetTopmostMathMLElement(nsIContent* aMathMLContent) {
8449   MOZ_ASSERT(aMathMLContent->IsMathMLElement());
8450   MOZ_ASSERT(aMathMLContent->GetPrimaryFrame());
8451   MOZ_ASSERT(
8452       aMathMLContent->GetPrimaryFrame()->IsFrameOfType(nsIFrame::eMathML));
8453   nsIContent* root = aMathMLContent;
8454 
8455   for (nsIContent* parent = aMathMLContent->GetFlattenedTreeParent(); parent;
8456        parent = parent->GetFlattenedTreeParent()) {
8457     nsIFrame* frame = parent->GetPrimaryFrame();
8458     if (!frame || !frame->IsFrameOfType(nsIFrame::eMathML)) {
8459       break;
8460     }
8461     root = parent;
8462   }
8463 
8464   return root;
8465 }
8466 
RecreateFramesForContent(nsIContent * aContent,InsertionKind aInsertionKind)8467 void nsCSSFrameConstructor::RecreateFramesForContent(
8468     nsIContent* aContent, InsertionKind aInsertionKind) {
8469   MOZ_ASSERT(aContent);
8470 
8471   // If there is no document, we don't want to recreate frames for it.  (You
8472   // shouldn't generally be giving this method content without a document
8473   // anyway).
8474   // Rebuilding the frame tree can have bad effects, especially if it's the
8475   // frame tree for chrome (see bug 157322).
8476   if (NS_WARN_IF(!aContent->GetComposedDoc())) {
8477     return;
8478   }
8479 
8480   // We don't know how to re-insert an anonymous subtree root, so recreate the
8481   // closest non-generated ancestor instead... Except for editor NAC, which
8482   // would enter an infinite loop, and we sorta get away with it because it's
8483   // all abspos.
8484   //
8485   // TODO(emilio): We technically can find the right insertion point nowadays
8486   // using StyleChildrenIterator rather than FlattenedTreeIterator. But we'd
8487   // need to tweak the setup to insert into replaced elements to filter which
8488   // anonymous roots can be allowed, and which can't.
8489   if (aContent->IsRootOfNativeAnonymousSubtree() &&
8490       !ManualNACPtr::IsManualNAC(aContent)) {
8491     do {
8492       aContent = aContent->GetParent();
8493     } while (aContent->IsRootOfNativeAnonymousSubtree());
8494     return RecreateFramesForContent(aContent, InsertionKind::Async);
8495   }
8496 
8497   nsIFrame* frame = aContent->GetPrimaryFrame();
8498   if (frame && frame->IsFrameOfType(nsIFrame::eMathML)) {
8499     // Reframe the topmost MathML element to prevent exponential blowup
8500     // (see bug 397518).
8501     aContent = GetTopmostMathMLElement(aContent);
8502     frame = aContent->GetPrimaryFrame();
8503   }
8504 
8505   if (frame) {
8506     nsIFrame* parent = frame->GetParent();
8507     nsIContent* parentContent = parent ? parent->GetContent() : nullptr;
8508     // If the parent frame is a leaf then the subsequent insert will fail to
8509     // create a frame, so we need to recreate the parent content. This happens
8510     // with native anonymous content from the editor.
8511     if (parent && parent->IsLeaf() && parentContent &&
8512         parentContent != aContent) {
8513       return RecreateFramesForContent(parentContent, InsertionKind::Async);
8514     }
8515   }
8516 
8517   if (frame && MaybeRecreateContainerForFrameRemoval(frame)) {
8518     return;
8519   }
8520 
8521   MOZ_ASSERT(aContent->GetParentNode());
8522 
8523   // Remove the frames associated with the content object.
8524   nsIContent* nextSibling = aContent->IsRootOfNativeAnonymousSubtree()
8525                                 ? nullptr
8526                                 : aContent->GetNextSibling();
8527   bool didReconstruct =
8528       ContentRemoved(aContent, nextSibling, REMOVE_FOR_RECONSTRUCTION);
8529 
8530   if (!didReconstruct) {
8531     if (aInsertionKind == InsertionKind::Async && aContent->IsElement()) {
8532       // FIXME(emilio, bug 1397239): There's nothing removing the frame state
8533       // for elements that go away before we come back to the frame
8534       // constructor.
8535       //
8536       // Also, it'd be nice to just use the `ContentRangeInserted` path for
8537       // both elements and non-elements, but we need to make lazy frame
8538       // construction to apply to all elements first.
8539       RestyleManager()->PostRestyleEvent(aContent->AsElement(), RestyleHint{0},
8540                                          nsChangeHint_ReconstructFrame);
8541     } else {
8542       // Now, recreate the frames associated with this content object. If
8543       // ContentRemoved triggered reconstruction, then we don't need to do this
8544       // because the frames will already have been built.
8545       ContentRangeInserted(aContent, aContent->GetNextSibling(),
8546                            aInsertionKind);
8547     }
8548   }
8549 }
8550 
DestroyFramesFor(Element * aElement)8551 bool nsCSSFrameConstructor::DestroyFramesFor(Element* aElement) {
8552   MOZ_ASSERT(aElement && aElement->GetParentNode());
8553 
8554   nsIContent* nextSibling = aElement->IsRootOfNativeAnonymousSubtree()
8555                                 ? nullptr
8556                                 : aElement->GetNextSibling();
8557 
8558   return ContentRemoved(aElement, nextSibling, REMOVE_FOR_RECONSTRUCTION);
8559 }
8560 
8561 //////////////////////////////////////////////////////////////////////
8562 
8563 // Block frame construction code
8564 
GetFirstLetterStyle(nsIContent * aContent,ComputedStyle * aComputedStyle)8565 already_AddRefed<ComputedStyle> nsCSSFrameConstructor::GetFirstLetterStyle(
8566     nsIContent* aContent, ComputedStyle* aComputedStyle) {
8567   if (aContent) {
8568     return mPresShell->StyleSet()->ResolvePseudoElementStyle(
8569         *aContent->AsElement(), PseudoStyleType::firstLetter, aComputedStyle);
8570   }
8571   return nullptr;
8572 }
8573 
GetFirstLineStyle(nsIContent * aContent,ComputedStyle * aComputedStyle)8574 already_AddRefed<ComputedStyle> nsCSSFrameConstructor::GetFirstLineStyle(
8575     nsIContent* aContent, ComputedStyle* aComputedStyle) {
8576   if (aContent) {
8577     return mPresShell->StyleSet()->ResolvePseudoElementStyle(
8578         *aContent->AsElement(), PseudoStyleType::firstLine, aComputedStyle);
8579   }
8580   return nullptr;
8581 }
8582 
8583 // Predicate to see if a given content (block element) has
8584 // first-letter style applied to it.
ShouldHaveFirstLetterStyle(nsIContent * aContent,ComputedStyle * aComputedStyle)8585 bool nsCSSFrameConstructor::ShouldHaveFirstLetterStyle(
8586     nsIContent* aContent, ComputedStyle* aComputedStyle) {
8587   return nsLayoutUtils::HasPseudoStyle(aContent, aComputedStyle,
8588                                        PseudoStyleType::firstLetter,
8589                                        mPresShell->GetPresContext());
8590 }
8591 
HasFirstLetterStyle(nsIFrame * aBlockFrame)8592 bool nsCSSFrameConstructor::HasFirstLetterStyle(nsIFrame* aBlockFrame) {
8593   MOZ_ASSERT(aBlockFrame, "Need a frame");
8594   NS_ASSERTION(aBlockFrame->IsBlockFrameOrSubclass(), "Not a block frame?");
8595 
8596   return (aBlockFrame->GetStateBits() & NS_BLOCK_HAS_FIRST_LETTER_STYLE) != 0;
8597 }
8598 
ShouldHaveFirstLineStyle(nsIContent * aContent,ComputedStyle * aComputedStyle)8599 bool nsCSSFrameConstructor::ShouldHaveFirstLineStyle(
8600     nsIContent* aContent, ComputedStyle* aComputedStyle) {
8601   bool hasFirstLine = nsLayoutUtils::HasPseudoStyle(
8602       aContent, aComputedStyle, PseudoStyleType::firstLine,
8603       mPresShell->GetPresContext());
8604   return hasFirstLine && !aContent->IsHTMLElement(nsGkAtoms::fieldset);
8605 }
8606 
ShouldHaveSpecialBlockStyle(nsIContent * aContent,ComputedStyle * aComputedStyle,bool * aHaveFirstLetterStyle,bool * aHaveFirstLineStyle)8607 void nsCSSFrameConstructor::ShouldHaveSpecialBlockStyle(
8608     nsIContent* aContent, ComputedStyle* aComputedStyle,
8609     bool* aHaveFirstLetterStyle, bool* aHaveFirstLineStyle) {
8610   *aHaveFirstLetterStyle = ShouldHaveFirstLetterStyle(aContent, aComputedStyle);
8611   *aHaveFirstLineStyle = ShouldHaveFirstLineStyle(aContent, aComputedStyle);
8612 }
8613 
8614 /* static */
8615 const nsCSSFrameConstructor::PseudoParentData
8616     nsCSSFrameConstructor::sPseudoParentData[eParentTypeCount] = {
8617         // Cell
8618         {FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET |
8619                               FCDATA_USE_CHILD_ITEMS |
8620                               FCDATA_IS_WRAPPER_ANON_BOX |
8621                               FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRow),
8622                           &nsCSSFrameConstructor::ConstructTableCell),
8623          PseudoStyleType::tableCell},
8624         // Row
8625         {FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET |
8626                               FCDATA_USE_CHILD_ITEMS |
8627                               FCDATA_IS_WRAPPER_ANON_BOX |
8628                               FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRowGroup),
8629                           &nsCSSFrameConstructor::ConstructTableRowOrRowGroup),
8630          PseudoStyleType::tableRow},
8631         // Row group
8632         {FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET |
8633                               FCDATA_USE_CHILD_ITEMS |
8634                               FCDATA_IS_WRAPPER_ANON_BOX |
8635                               FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
8636                           &nsCSSFrameConstructor::ConstructTableRowOrRowGroup),
8637          PseudoStyleType::tableRowGroup},
8638         // Column group
8639         {FCDATA_DECL(
8640              FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET |
8641                  FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_USE_CHILD_ITEMS |
8642                  FCDATA_SKIP_ABSPOS_PUSH |
8643                  // Not FCDATA_IS_WRAPPER_ANON_BOX, because we don't need to
8644                  // restyle these: they have non-inheriting styles.
8645                  FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
8646              NS_NewTableColGroupFrame),
8647          PseudoStyleType::tableColGroup},
8648         // Table
8649         {FULL_CTOR_FCDATA(FCDATA_SKIP_FRAMESET | FCDATA_USE_CHILD_ITEMS |
8650                               FCDATA_IS_WRAPPER_ANON_BOX,
8651                           &nsCSSFrameConstructor::ConstructTable),
8652          PseudoStyleType::table},
8653         // Ruby
8654         {FCDATA_DECL(FCDATA_IS_LINE_PARTICIPANT | FCDATA_USE_CHILD_ITEMS |
8655                          FCDATA_IS_WRAPPER_ANON_BOX | FCDATA_SKIP_FRAMESET,
8656                      NS_NewRubyFrame),
8657          PseudoStyleType::ruby},
8658         // Ruby Base
8659         {FCDATA_DECL(
8660              FCDATA_USE_CHILD_ITEMS | FCDATA_IS_LINE_PARTICIPANT |
8661                  FCDATA_IS_WRAPPER_ANON_BOX |
8662                  FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRubyBaseContainer) |
8663                  FCDATA_SKIP_FRAMESET,
8664              NS_NewRubyBaseFrame),
8665          PseudoStyleType::rubyBase},
8666         // Ruby Base Container
8667         {FCDATA_DECL(FCDATA_USE_CHILD_ITEMS | FCDATA_IS_LINE_PARTICIPANT |
8668                          FCDATA_IS_WRAPPER_ANON_BOX |
8669                          FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRuby) |
8670                          FCDATA_SKIP_FRAMESET,
8671                      NS_NewRubyBaseContainerFrame),
8672          PseudoStyleType::rubyBaseContainer},
8673         // Ruby Text
8674         {FCDATA_DECL(
8675              FCDATA_USE_CHILD_ITEMS | FCDATA_IS_LINE_PARTICIPANT |
8676                  FCDATA_IS_WRAPPER_ANON_BOX |
8677                  FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRubyTextContainer) |
8678                  FCDATA_SKIP_FRAMESET,
8679              NS_NewRubyTextFrame),
8680          PseudoStyleType::rubyText},
8681         // Ruby Text Container
8682         {FCDATA_DECL(FCDATA_USE_CHILD_ITEMS | FCDATA_IS_WRAPPER_ANON_BOX |
8683                          FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRuby) |
8684                          FCDATA_SKIP_FRAMESET,
8685                      NS_NewRubyTextContainerFrame),
8686          PseudoStyleType::rubyTextContainer}};
8687 
CreateNeededAnonFlexOrGridItems(nsFrameConstructorState & aState,FrameConstructionItemList & aItems,nsIFrame * aParentFrame)8688 void nsCSSFrameConstructor::CreateNeededAnonFlexOrGridItems(
8689     nsFrameConstructorState& aState, FrameConstructionItemList& aItems,
8690     nsIFrame* aParentFrame) {
8691   if (aItems.IsEmpty() || !aParentFrame->IsFlexOrGridContainer()) {
8692     return;
8693   }
8694 
8695   const bool isLegacyBox = IsFlexContainerForLegacyBox(aParentFrame);
8696   FCItemIterator iter(aItems);
8697   do {
8698     // Advance iter past children that don't want to be wrapped
8699     if (iter.SkipItemsThatDontNeedAnonFlexOrGridItem(aState, isLegacyBox)) {
8700       // Hit the end of the items without finding any remaining children that
8701       // need to be wrapped. We're finished!
8702       return;
8703     }
8704 
8705     // If our next potentially-wrappable child is whitespace, then see if
8706     // there's anything wrappable immediately after it. If not, we just drop
8707     // the whitespace and move on. (We're not supposed to create any anonymous
8708     // flex/grid items that _only_ contain whitespace).
8709     // (BUT if this is generated content, then we don't give whitespace nodes
8710     // any special treatment, because they're probably not really whitespace --
8711     // they're just temporarily empty, waiting for their generated text.)
8712     // XXXdholbert If this node's generated text will *actually end up being
8713     // entirely whitespace*, then we technically should still skip over it, per
8714     // the CSS grid & flexbox specs. I'm not bothering with that at this point,
8715     // since it's a pretty extreme edge case.
8716     if (!aParentFrame->IsGeneratedContentFrame() &&
8717         iter.item().IsWhitespace(aState)) {
8718       FCItemIterator afterWhitespaceIter(iter);
8719       bool hitEnd = afterWhitespaceIter.SkipWhitespace(aState);
8720       bool nextChildNeedsAnonItem =
8721           !hitEnd && afterWhitespaceIter.item().NeedsAnonFlexOrGridItem(
8722                          aState, isLegacyBox);
8723 
8724       if (!nextChildNeedsAnonItem) {
8725         // There's nothing after the whitespace that we need to wrap, so we
8726         // just drop this run of whitespace.
8727         iter.DeleteItemsTo(this, afterWhitespaceIter);
8728         if (hitEnd) {
8729           // Nothing left to do -- we're finished!
8730           return;
8731         }
8732         // else, we have a next child and it does not want to be wrapped.  So,
8733         // we jump back to the beginning of the loop to skip over that child
8734         // (and anything else non-wrappable after it)
8735         MOZ_ASSERT(!iter.IsDone() && !iter.item().NeedsAnonFlexOrGridItem(
8736                                          aState, isLegacyBox),
8737                    "hitEnd and/or nextChildNeedsAnonItem lied");
8738         continue;
8739       }
8740     }
8741 
8742     // Now |iter| points to the first child that needs to be wrapped in an
8743     // anonymous flex/grid item. Now we see how many children after it also want
8744     // to be wrapped in an anonymous flex/grid item.
8745     FCItemIterator endIter(iter);  // iterator to find the end of the group
8746     endIter.SkipItemsThatNeedAnonFlexOrGridItem(aState, isLegacyBox);
8747 
8748     NS_ASSERTION(iter != endIter,
8749                  "Should've had at least one wrappable child to seek past");
8750 
8751     // Now, we create the anonymous flex or grid item to contain the children
8752     // between |iter| and |endIter|.
8753     auto pseudoType = aParentFrame->IsFlexContainerFrame()
8754                           ? PseudoStyleType::anonymousFlexItem
8755                           : PseudoStyleType::anonymousGridItem;
8756     ComputedStyle* parentStyle = aParentFrame->Style();
8757     nsIContent* parentContent = aParentFrame->GetContent();
8758     RefPtr<ComputedStyle> wrapperStyle =
8759         mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(pseudoType,
8760                                                                    parentStyle);
8761 
8762     static const FrameConstructionData sBlockFormattingContextFCData =
8763         FCDATA_DECL(FCDATA_SKIP_FRAMESET | FCDATA_USE_CHILD_ITEMS |
8764                         FCDATA_IS_WRAPPER_ANON_BOX,
8765                     NS_NewBlockFormattingContext);
8766 
8767     FrameConstructionItem* newItem = new (this)
8768         FrameConstructionItem(&sBlockFormattingContextFCData,
8769                               // Use the content of our parent frame
8770                               parentContent, wrapperStyle.forget(), true);
8771 
8772     newItem->mIsAllInline =
8773         newItem->mComputedStyle->StyleDisplay()->IsInlineOutsideStyle();
8774     newItem->mIsBlock = !newItem->mIsAllInline;
8775 
8776     MOZ_ASSERT(!newItem->mIsAllInline && newItem->mIsBlock,
8777                "expecting anonymous flex/grid items to be block-level "
8778                "(this will make a difference when we encounter "
8779                "'align-items: baseline')");
8780 
8781     // Anonymous flex and grid items induce line boundaries around their
8782     // contents.
8783     newItem->mChildItems.SetLineBoundaryAtStart(true);
8784     newItem->mChildItems.SetLineBoundaryAtEnd(true);
8785     // The parent of the items in aItems is also the parent of the items
8786     // in mChildItems
8787     newItem->mChildItems.SetParentHasNoShadowDOM(aItems.ParentHasNoShadowDOM());
8788 
8789     // Eat up all items between |iter| and |endIter| and put them in our
8790     // wrapper. This advances |iter| to point to |endIter|.
8791     iter.AppendItemsToList(this, endIter, newItem->mChildItems);
8792 
8793     iter.InsertItem(newItem);
8794   } while (!iter.IsDone());
8795 }
8796 
8797 /* static */ nsCSSFrameConstructor::RubyWhitespaceType
ComputeRubyWhitespaceType(StyleDisplay aPrevDisplay,StyleDisplay aNextDisplay)8798 nsCSSFrameConstructor::ComputeRubyWhitespaceType(StyleDisplay aPrevDisplay,
8799                                                  StyleDisplay aNextDisplay) {
8800   MOZ_ASSERT(nsStyleDisplay::IsRubyDisplayType(aPrevDisplay) &&
8801              nsStyleDisplay::IsRubyDisplayType(aNextDisplay));
8802   if (aPrevDisplay == aNextDisplay &&
8803       (aPrevDisplay == StyleDisplay::RubyBase ||
8804        aPrevDisplay == StyleDisplay::RubyText)) {
8805     return eRubyInterLeafWhitespace;
8806   }
8807   if (aNextDisplay == StyleDisplay::RubyText ||
8808       aNextDisplay == StyleDisplay::RubyTextContainer) {
8809     return eRubyInterLevelWhitespace;
8810   }
8811   return eRubyInterSegmentWhitespace;
8812 }
8813 
8814 /**
8815  * This function checks the content from |aStartIter| to |aEndIter|,
8816  * determines whether it contains only whitespace, and if yes,
8817  * interprets the type of whitespace. This method does not change
8818  * any of the iters.
8819  */
8820 /* static */ nsCSSFrameConstructor::RubyWhitespaceType
InterpretRubyWhitespace(nsFrameConstructorState & aState,const FCItemIterator & aStartIter,const FCItemIterator & aEndIter)8821 nsCSSFrameConstructor::InterpretRubyWhitespace(nsFrameConstructorState& aState,
8822                                                const FCItemIterator& aStartIter,
8823                                                const FCItemIterator& aEndIter) {
8824   if (!aStartIter.item().IsWhitespace(aState)) {
8825     return eRubyNotWhitespace;
8826   }
8827 
8828   FCItemIterator spaceEndIter(aStartIter);
8829   spaceEndIter.SkipWhitespace(aState);
8830   if (spaceEndIter != aEndIter) {
8831     return eRubyNotWhitespace;
8832   }
8833 
8834   // Any leading or trailing whitespace in non-pseudo ruby box
8835   // should have been trimmed, hence there should not be any
8836   // whitespace at the start or the end.
8837   MOZ_ASSERT(!aStartIter.AtStart() && !aEndIter.IsDone());
8838   FCItemIterator prevIter(aStartIter);
8839   prevIter.Prev();
8840   return ComputeRubyWhitespaceType(
8841       prevIter.item().mComputedStyle->StyleDisplay()->mDisplay,
8842       aEndIter.item().mComputedStyle->StyleDisplay()->mDisplay);
8843 }
8844 
8845 /**
8846  * This function eats up consecutive items which do not want the current
8847  * parent into either a ruby base box or a ruby text box.  When it
8848  * returns, |aIter| points to the first item it doesn't wrap.
8849  */
WrapItemsInPseudoRubyLeafBox(FCItemIterator & aIter,ComputedStyle * aParentStyle,nsIContent * aParentContent)8850 void nsCSSFrameConstructor::WrapItemsInPseudoRubyLeafBox(
8851     FCItemIterator& aIter, ComputedStyle* aParentStyle,
8852     nsIContent* aParentContent) {
8853   StyleDisplay parentDisplay = aParentStyle->StyleDisplay()->mDisplay;
8854   ParentType parentType, wrapperType;
8855   if (parentDisplay == StyleDisplay::RubyTextContainer) {
8856     parentType = eTypeRubyTextContainer;
8857     wrapperType = eTypeRubyText;
8858   } else {
8859     MOZ_ASSERT(parentDisplay == StyleDisplay::RubyBaseContainer);
8860     parentType = eTypeRubyBaseContainer;
8861     wrapperType = eTypeRubyBase;
8862   }
8863 
8864   MOZ_ASSERT(aIter.item().DesiredParentType() != parentType,
8865              "Should point to something needs to be wrapped.");
8866 
8867   FCItemIterator endIter(aIter);
8868   endIter.SkipItemsNotWantingParentType(parentType);
8869 
8870   WrapItemsInPseudoParent(aParentContent, aParentStyle, wrapperType, aIter,
8871                           endIter);
8872 }
8873 
8874 /**
8875  * This function eats up consecutive items into a ruby level container.
8876  * It may create zero or one level container. When it returns, |aIter|
8877  * points to the first item it doesn't wrap.
8878  */
WrapItemsInPseudoRubyLevelContainer(nsFrameConstructorState & aState,FCItemIterator & aIter,ComputedStyle * aParentStyle,nsIContent * aParentContent)8879 void nsCSSFrameConstructor::WrapItemsInPseudoRubyLevelContainer(
8880     nsFrameConstructorState& aState, FCItemIterator& aIter,
8881     ComputedStyle* aParentStyle, nsIContent* aParentContent) {
8882   MOZ_ASSERT(aIter.item().DesiredParentType() != eTypeRuby,
8883              "Pointing to a level container?");
8884 
8885   FrameConstructionItem& firstItem = aIter.item();
8886   ParentType wrapperType = firstItem.DesiredParentType();
8887   if (wrapperType != eTypeRubyTextContainer) {
8888     // If the first item is not ruby text,
8889     // it should be in a base container.
8890     wrapperType = eTypeRubyBaseContainer;
8891   }
8892 
8893   FCItemIterator endIter(aIter);
8894   do {
8895     if (endIter.SkipItemsWantingParentType(wrapperType) ||
8896         // If the skipping above stops at some item which wants a
8897         // different ruby parent, then we have finished.
8898         IsRubyParentType(endIter.item().DesiredParentType())) {
8899       // No more items need to be wrapped in this level container.
8900       break;
8901     }
8902 
8903     FCItemIterator contentEndIter(endIter);
8904     contentEndIter.SkipItemsNotWantingRubyParent();
8905     // endIter must be on something doesn't want a ruby parent.
8906     MOZ_ASSERT(contentEndIter != endIter);
8907 
8908     // InterpretRubyWhitespace depends on the fact that any leading or
8909     // trailing whitespace described in the spec have been trimmed at
8910     // this point. With this precondition, it is safe not to check
8911     // whether contentEndIter has been done.
8912     RubyWhitespaceType whitespaceType =
8913         InterpretRubyWhitespace(aState, endIter, contentEndIter);
8914     if (whitespaceType == eRubyInterLevelWhitespace) {
8915       // Remove inter-level whitespace.
8916       bool atStart = (aIter == endIter);
8917       endIter.DeleteItemsTo(this, contentEndIter);
8918       if (atStart) {
8919         aIter = endIter;
8920       }
8921     } else if (whitespaceType == eRubyInterSegmentWhitespace) {
8922       // If this level container starts with inter-segment whitespaces,
8923       // wrap them. Break at contentEndIter. Otherwise, leave it here.
8924       // Break at endIter. They will be wrapped when we are here again.
8925       if (aIter == endIter) {
8926         MOZ_ASSERT(wrapperType == eTypeRubyBaseContainer,
8927                    "Inter-segment whitespace should be wrapped in rbc");
8928         endIter = contentEndIter;
8929       }
8930       break;
8931     } else if (wrapperType == eTypeRubyTextContainer &&
8932                whitespaceType != eRubyInterLeafWhitespace) {
8933       // Misparented inline content that's not inter-annotation
8934       // whitespace doesn't belong in a pseudo ruby text container.
8935       // Break at endIter.
8936       break;
8937     } else {
8938       endIter = contentEndIter;
8939     }
8940   } while (!endIter.IsDone());
8941 
8942   // It is possible that everything our parent wants us to wrap is
8943   // simply an inter-level whitespace, which has been trimmed, or
8944   // an inter-segment whitespace, which will be wrapped later.
8945   // In those cases, don't create anything.
8946   if (aIter != endIter) {
8947     WrapItemsInPseudoParent(aParentContent, aParentStyle, wrapperType, aIter,
8948                             endIter);
8949   }
8950 }
8951 
8952 /**
8953  * This function trims leading and trailing whitespaces
8954  * in the given item list.
8955  */
TrimLeadingAndTrailingWhitespaces(nsFrameConstructorState & aState,FrameConstructionItemList & aItems)8956 void nsCSSFrameConstructor::TrimLeadingAndTrailingWhitespaces(
8957     nsFrameConstructorState& aState, FrameConstructionItemList& aItems) {
8958   FCItemIterator iter(aItems);
8959   if (!iter.IsDone() && iter.item().IsWhitespace(aState)) {
8960     FCItemIterator spaceEndIter(iter);
8961     spaceEndIter.SkipWhitespace(aState);
8962     iter.DeleteItemsTo(this, spaceEndIter);
8963   }
8964 
8965   iter.SetToEnd();
8966   if (!iter.AtStart()) {
8967     FCItemIterator spaceEndIter(iter);
8968     do {
8969       iter.Prev();
8970       if (iter.AtStart()) {
8971         // It's fine to not check the first item, because we
8972         // should have trimmed leading whitespaces above.
8973         break;
8974       }
8975     } while (iter.item().IsWhitespace(aState));
8976     iter.Next();
8977     if (iter != spaceEndIter) {
8978       iter.DeleteItemsTo(this, spaceEndIter);
8979     }
8980   }
8981 }
8982 
8983 /**
8984  * This function walks through the child list (aItems) and creates
8985  * needed pseudo ruby boxes to wrap misparented children.
8986  */
CreateNeededPseudoInternalRubyBoxes(nsFrameConstructorState & aState,FrameConstructionItemList & aItems,nsIFrame * aParentFrame)8987 void nsCSSFrameConstructor::CreateNeededPseudoInternalRubyBoxes(
8988     nsFrameConstructorState& aState, FrameConstructionItemList& aItems,
8989     nsIFrame* aParentFrame) {
8990   const ParentType ourParentType = GetParentType(aParentFrame);
8991   if (!IsRubyParentType(ourParentType) ||
8992       aItems.AllWantParentType(ourParentType)) {
8993     return;
8994   }
8995 
8996   if (!IsRubyPseudo(aParentFrame) ||
8997       ourParentType == eTypeRuby /* for 'display:block ruby' */) {
8998     // Normally, ruby pseudo frames start from and end at some elements,
8999     // which means they don't have leading and trailing whitespaces at
9000     // all.  But there are two cases where they do actually have leading
9001     // or trailing whitespaces:
9002     // 1. It is an inter-segment whitespace which in an individual ruby
9003     //    base container.
9004     // 2. The pseudo frame starts from or ends at consecutive inline
9005     //    content, which is not pure whitespace, but includes some.
9006     // In either case, the whitespaces are not the leading or trailing
9007     // whitespaces defined in the spec, and thus should not be trimmed.
9008     TrimLeadingAndTrailingWhitespaces(aState, aItems);
9009   }
9010 
9011   FCItemIterator iter(aItems);
9012   nsIContent* parentContent = aParentFrame->GetContent();
9013   ComputedStyle* parentStyle = aParentFrame->Style();
9014   while (!iter.IsDone()) {
9015     if (!iter.SkipItemsWantingParentType(ourParentType)) {
9016       if (ourParentType == eTypeRuby) {
9017         WrapItemsInPseudoRubyLevelContainer(aState, iter, parentStyle,
9018                                             parentContent);
9019       } else {
9020         WrapItemsInPseudoRubyLeafBox(iter, parentStyle, parentContent);
9021       }
9022     }
9023   }
9024 }
9025 
9026 /*
9027  * This function works as follows: we walk through the child list (aItems) and
9028  * find items that cannot have aParentFrame as their parent.  We wrap
9029  * continuous runs of such items into a FrameConstructionItem for a frame that
9030  * gets them closer to their desired parents.  For example, a run of non-row
9031  * children of a row-group will get wrapped in a row.  When we later construct
9032  * the frame for this wrapper (in this case for the row), it'll be the correct
9033  * parent for the cells in the set of items we wrapped or we'll wrap cells
9034  * around everything else.  At the end of this method, aItems is guaranteed to
9035  * contain only items for frames that can be direct kids of aParentFrame.
9036  */
CreateNeededPseudoContainers(nsFrameConstructorState & aState,FrameConstructionItemList & aItems,nsIFrame * aParentFrame)9037 void nsCSSFrameConstructor::CreateNeededPseudoContainers(
9038     nsFrameConstructorState& aState, FrameConstructionItemList& aItems,
9039     nsIFrame* aParentFrame) {
9040   ParentType ourParentType = GetParentType(aParentFrame);
9041   if (IsRubyParentType(ourParentType) ||
9042       aItems.AllWantParentType(ourParentType)) {
9043     // Nothing to do here
9044     return;
9045   }
9046 
9047   FCItemIterator iter(aItems);
9048   do {
9049     if (iter.SkipItemsWantingParentType(ourParentType)) {
9050       // Nothing else to do here; we're finished
9051       return;
9052     }
9053 
9054     // Now we're pointing to the first child that wants a different parent
9055     // type.
9056 
9057     // Now try to figure out what kids we can group together.  We can generally
9058     // group everything that has a different desired parent type from us.  Two
9059     // exceptions to this:
9060     // 1) If our parent type is table, we can't group columns with anything
9061     //    else other than whitespace.
9062     // 2) Whitespace that lies between two things we can group which both want
9063     //    a non-block parent should be dropped, even if we can't group them
9064     //    with each other and even if the whitespace wants a parent of
9065     //    ourParentType.  Ends of the list count as things that don't want a
9066     //    block parent (so that for example we'll drop a whitespace-only list).
9067 
9068     FCItemIterator endIter(iter); /* iterator to find the end of the group */
9069     ParentType groupingParentType = endIter.item().DesiredParentType();
9070     if (aItems.AllWantParentType(groupingParentType) &&
9071         groupingParentType != eTypeBlock) {
9072       // Just group them all and be done with it.  We need the check for
9073       // eTypeBlock here to catch the "all the items are whitespace" case
9074       // described above.
9075       endIter.SetToEnd();
9076     } else {
9077       // Locate the end of the group.
9078 
9079       // Keep track of the type the previous item wanted, in case we have to
9080       // deal with whitespace.  Start it off with ourParentType, since that's
9081       // the last thing |iter| would have skipped over.
9082       ParentType prevParentType = ourParentType;
9083       do {
9084         // Walk an iterator past any whitespace that we might be able to drop
9085         // from the list
9086         FCItemIterator spaceEndIter(endIter);
9087         if (prevParentType != eTypeBlock &&
9088             !aParentFrame->IsGeneratedContentFrame() &&
9089             spaceEndIter.item().IsWhitespace(aState)) {
9090           bool trailingSpaces = spaceEndIter.SkipWhitespace(aState);
9091 
9092           // We drop the whitespace in the following cases:
9093           // 1) If these are not trailing spaces and the next item wants a table
9094           //    or table-part parent
9095           // 2) If these are trailing spaces and aParentFrame is a
9096           //    tabular container according to rule 1.3 of CSS 2.1 Sec 17.2.1.
9097           //    (Being a tabular container pretty much means ourParentType is
9098           //    not eTypeBlock besides the eTypeColGroup case, which won't
9099           //    reach here.)
9100           if ((!trailingSpaces &&
9101                IsTableParentType(spaceEndIter.item().DesiredParentType())) ||
9102               (trailingSpaces && ourParentType != eTypeBlock)) {
9103             bool updateStart = (iter == endIter);
9104             endIter.DeleteItemsTo(this, spaceEndIter);
9105             NS_ASSERTION(trailingSpaces == endIter.IsDone(),
9106                          "These should match");
9107 
9108             if (updateStart) {
9109               iter = endIter;
9110             }
9111 
9112             if (trailingSpaces) {
9113               break; /* Found group end */
9114             }
9115 
9116             if (updateStart) {
9117               // Update groupingParentType, since it might have been eTypeBlock
9118               // just because of the whitespace.
9119               groupingParentType = iter.item().DesiredParentType();
9120             }
9121           }
9122         }
9123 
9124         // Now endIter points to a non-whitespace item or a non-droppable
9125         // whitespace item. In the latter case, if this is the end of the group
9126         // we'll traverse this whitespace again.  But it'll all just be quick
9127         // DesiredParentType() checks which will match ourParentType (that's
9128         // what it means that this is the group end), so it's OK.
9129         // However, when we are grouping a ruby parent, and endIter points to
9130         // a non-droppable whitespace, if the next non-whitespace item also
9131         // wants a ruby parent, the whitespace should also be included into
9132         // the current ruby container.
9133         prevParentType = endIter.item().DesiredParentType();
9134         if (prevParentType == ourParentType &&
9135             (endIter == spaceEndIter || spaceEndIter.IsDone() ||
9136              !IsRubyParentType(groupingParentType) ||
9137              !IsRubyParentType(spaceEndIter.item().DesiredParentType()))) {
9138           // End the group at endIter.
9139           break;
9140         }
9141 
9142         if (ourParentType == eTypeTable &&
9143             (prevParentType == eTypeColGroup) !=
9144                 (groupingParentType == eTypeColGroup)) {
9145           // Either we started with columns and now found something else, or
9146           // vice versa.  In any case, end the grouping.
9147           break;
9148         }
9149 
9150         // If we have some whitespace that we were not able to drop and there is
9151         // an item after the whitespace that is already properly parented, then
9152         // make sure to include the spaces in our group but stop the group after
9153         // that.
9154         if (spaceEndIter != endIter && !spaceEndIter.IsDone() &&
9155             ourParentType == spaceEndIter.item().DesiredParentType()) {
9156           endIter = spaceEndIter;
9157           break;
9158         }
9159 
9160         // Include the whitespace we didn't drop (if any) in the group.
9161         endIter = spaceEndIter;
9162         prevParentType = endIter.item().DesiredParentType();
9163 
9164         endIter.Next();
9165       } while (!endIter.IsDone());
9166     }
9167 
9168     if (iter == endIter) {
9169       // Nothing to wrap here; just skipped some whitespace
9170       continue;
9171     }
9172 
9173     // Now group together all the items between iter and endIter.  The right
9174     // parent type to use depends on ourParentType.
9175     ParentType wrapperType;
9176     switch (ourParentType) {
9177       case eTypeRow:
9178         // The parent type for a cell is eTypeBlock, since that's what a cell
9179         // looks like to its kids.
9180         wrapperType = eTypeBlock;
9181         break;
9182       case eTypeRowGroup:
9183         wrapperType = eTypeRow;
9184         break;
9185       case eTypeTable:
9186         // Either colgroup or rowgroup, depending on what we're grouping.
9187         wrapperType =
9188             groupingParentType == eTypeColGroup ? eTypeColGroup : eTypeRowGroup;
9189         break;
9190       case eTypeColGroup:
9191         MOZ_CRASH("Colgroups should be suppresing non-col child items");
9192       default:
9193         NS_ASSERTION(ourParentType == eTypeBlock, "Unrecognized parent type");
9194         if (IsRubyParentType(groupingParentType)) {
9195           wrapperType = eTypeRuby;
9196         } else {
9197           NS_ASSERTION(IsTableParentType(groupingParentType),
9198                        "groupingParentType should be either Ruby or table");
9199           wrapperType = eTypeTable;
9200         }
9201     }
9202 
9203     ComputedStyle* parentStyle = aParentFrame->Style();
9204     WrapItemsInPseudoParent(aParentFrame->GetContent(), parentStyle,
9205                             wrapperType, iter, endIter);
9206 
9207     // Now |iter| points to the item that was the first one we didn't wrap;
9208     // loop and see whether we need to skip it or wrap it in something
9209     // different.
9210   } while (!iter.IsDone());
9211 }
9212 
9213 /**
9214  * This method wraps frame construction item from |aIter| to
9215  * |aEndIter|. After it returns, aIter points to the first item
9216  * after the wrapper.
9217  */
WrapItemsInPseudoParent(nsIContent * aParentContent,ComputedStyle * aParentStyle,ParentType aWrapperType,FCItemIterator & aIter,const FCItemIterator & aEndIter)9218 void nsCSSFrameConstructor::WrapItemsInPseudoParent(
9219     nsIContent* aParentContent, ComputedStyle* aParentStyle,
9220     ParentType aWrapperType, FCItemIterator& aIter,
9221     const FCItemIterator& aEndIter) {
9222   const PseudoParentData& pseudoData = sPseudoParentData[aWrapperType];
9223   PseudoStyleType pseudoType = pseudoData.mPseudoType;
9224   auto& parentDisplay = *aParentStyle->StyleDisplay();
9225   auto parentDisplayInside = parentDisplay.DisplayInside();
9226 
9227   // XXXmats should we use IsInlineInsideStyle() here instead? seems odd to
9228   // exclude RubyBaseContainer/RubyTextContainer...
9229   if (pseudoType == PseudoStyleType::table &&
9230       (parentDisplay.IsInlineFlow() ||
9231        parentDisplayInside == StyleDisplayInside::RubyBase ||
9232        parentDisplayInside == StyleDisplayInside::RubyText)) {
9233     pseudoType = PseudoStyleType::inlineTable;
9234   }
9235 
9236   RefPtr<ComputedStyle> wrapperStyle;
9237   if (pseudoData.mFCData.mBits & FCDATA_IS_WRAPPER_ANON_BOX) {
9238     wrapperStyle = mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
9239         pseudoType, aParentStyle);
9240   } else {
9241     wrapperStyle =
9242         mPresShell->StyleSet()->ResolveNonInheritingAnonymousBoxStyle(
9243             pseudoType);
9244   }
9245 
9246   FrameConstructionItem* newItem = new (this)
9247       FrameConstructionItem(&pseudoData.mFCData,
9248                             // Use the content of our parent frame
9249                             aParentContent, wrapperStyle.forget(), true);
9250 
9251   const nsStyleDisplay* disp = newItem->mComputedStyle->StyleDisplay();
9252   // Here we're cheating a tad... technically, table-internal items should be
9253   // inline if aParentFrame is inline, but they'll get wrapped in an
9254   // inline-table in the end, so it'll all work out.  In any case, arguably
9255   // we don't need to maintain this state at this point... but it's better
9256   // to, I guess.
9257   newItem->mIsAllInline = disp->IsInlineOutsideStyle();
9258 
9259   bool isRuby = disp->IsRubyDisplayType();
9260   // All types of ruby frames need a block frame to provide line layout,
9261   // hence they are always line participant.
9262   newItem->mIsLineParticipant = isRuby;
9263 
9264   if (!isRuby) {
9265     // Table pseudo frames always induce line boundaries around their
9266     // contents.
9267     newItem->mChildItems.SetLineBoundaryAtStart(true);
9268     newItem->mChildItems.SetLineBoundaryAtEnd(true);
9269   }
9270   // The parent of the items in aItems is also the parent of the items
9271   // in mChildItems
9272   newItem->mChildItems.SetParentHasNoShadowDOM(
9273       aIter.List()->ParentHasNoShadowDOM());
9274 
9275   // Eat up all items between |aIter| and |aEndIter| and put them in our
9276   // wrapper Advances |aIter| to point to |aEndIter|.
9277   aIter.AppendItemsToList(this, aEndIter, newItem->mChildItems);
9278 
9279   aIter.InsertItem(newItem);
9280 }
9281 
CreateNeededPseudoSiblings(nsFrameConstructorState & aState,FrameConstructionItemList & aItems,nsIFrame * aParentFrame)9282 void nsCSSFrameConstructor::CreateNeededPseudoSiblings(
9283     nsFrameConstructorState& aState, FrameConstructionItemList& aItems,
9284     nsIFrame* aParentFrame) {
9285   if (aItems.IsEmpty() || GetParentType(aParentFrame) != eTypeRuby) {
9286     return;
9287   }
9288 
9289   FCItemIterator iter(aItems);
9290   StyleDisplay firstDisplay =
9291       iter.item().mComputedStyle->StyleDisplay()->mDisplay;
9292   if (firstDisplay == StyleDisplay::RubyBaseContainer) {
9293     return;
9294   }
9295   NS_ASSERTION(firstDisplay == StyleDisplay::RubyTextContainer,
9296                "Child of ruby frame should either a rbc or a rtc");
9297 
9298   const PseudoParentData& pseudoData =
9299       sPseudoParentData[eTypeRubyBaseContainer];
9300   RefPtr<ComputedStyle> pseudoStyle =
9301       mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
9302           pseudoData.mPseudoType, aParentFrame->Style());
9303   FrameConstructionItem* newItem = new (this) FrameConstructionItem(
9304       &pseudoData.mFCData,
9305       // Use the content of the parent frame
9306       aParentFrame->GetContent(), pseudoStyle.forget(), true);
9307   newItem->mIsAllInline = true;
9308   newItem->mChildItems.SetParentHasNoShadowDOM(true);
9309   iter.InsertItem(newItem);
9310 }
9311 
9312 #ifdef DEBUG
9313 /**
9314  * Returns true iff aFrame should be wrapped in an anonymous flex/grid item,
9315  * rather than being a direct child of aContainerFrame.
9316  *
9317  * NOTE: aContainerFrame must be a flex or grid container - this function is
9318  * purely for sanity-checking the children of these container types.
9319  * NOTE: See also NeedsAnonFlexOrGridItem(), for the non-debug version of this
9320  * logic (which operates a bit earlier, on FCData instead of frames).
9321  */
FrameWantsToBeInAnonymousItem(const nsIFrame * aContainerFrame,const nsIFrame * aFrame)9322 static bool FrameWantsToBeInAnonymousItem(const nsIFrame* aContainerFrame,
9323                                           const nsIFrame* aFrame) {
9324   MOZ_ASSERT(aContainerFrame->IsFlexOrGridContainer());
9325 
9326   // Any line-participant frames (e.g. text) definitely want to be wrapped in
9327   // an anonymous flex/grid item.
9328   if (aFrame->IsFrameOfType(nsIFrame::eLineParticipant)) {
9329     return true;
9330   }
9331 
9332   // If the container is a -webkit-{inline-}box or -moz-{inline-}box container,
9333   // then placeholders also need to be wrapped, for compatibility.
9334   if (IsFlexContainerForLegacyBox(aContainerFrame) &&
9335       aFrame->IsPlaceholderFrame()) {
9336     return true;
9337   }
9338 
9339   return false;
9340 }
9341 #endif
9342 
VerifyGridFlexContainerChildren(nsIFrame * aParentFrame,const nsFrameList & aChildren)9343 static void VerifyGridFlexContainerChildren(nsIFrame* aParentFrame,
9344                                             const nsFrameList& aChildren) {
9345 #ifdef DEBUG
9346   if (!aParentFrame->IsFlexOrGridContainer()) {
9347     return;
9348   }
9349 
9350   bool prevChildWasAnonItem = false;
9351   for (const nsIFrame* child : aChildren) {
9352     MOZ_ASSERT(!FrameWantsToBeInAnonymousItem(aParentFrame, child),
9353                "frame wants to be inside an anonymous item, but it isn't");
9354     if (IsAnonymousFlexOrGridItem(child)) {
9355       AssertAnonymousFlexOrGridItemParent(child, aParentFrame);
9356       MOZ_ASSERT(!prevChildWasAnonItem, "two anon items in a row");
9357       nsIFrame* firstWrappedChild = child->PrincipalChildList().FirstChild();
9358       MOZ_ASSERT(firstWrappedChild, "anonymous item shouldn't be empty");
9359       prevChildWasAnonItem = true;
9360     } else {
9361       prevChildWasAnonItem = false;
9362     }
9363   }
9364 #endif
9365 }
9366 
ConstructFramesFromItemList(nsFrameConstructorState & aState,FrameConstructionItemList & aItems,nsContainerFrame * aParentFrame,bool aParentIsWrapperAnonBox,nsFrameList & aFrameList)9367 inline void nsCSSFrameConstructor::ConstructFramesFromItemList(
9368     nsFrameConstructorState& aState, FrameConstructionItemList& aItems,
9369     nsContainerFrame* aParentFrame, bool aParentIsWrapperAnonBox,
9370     nsFrameList& aFrameList) {
9371   // Ensure aParentIsWrapperAnonBox is correct.  We _could_ compute it directly,
9372   // but it would be a bit slow, which is why we pass it from callers, who have
9373   // that information offhand in many cases.
9374   MOZ_ASSERT(ParentIsWrapperAnonBox(aParentFrame) == aParentIsWrapperAnonBox);
9375 
9376   CreateNeededPseudoContainers(aState, aItems, aParentFrame);
9377   CreateNeededAnonFlexOrGridItems(aState, aItems, aParentFrame);
9378   CreateNeededPseudoInternalRubyBoxes(aState, aItems, aParentFrame);
9379   CreateNeededPseudoSiblings(aState, aItems, aParentFrame);
9380 
9381   bool listItemListIsDirty = false;
9382   for (FCItemIterator iter(aItems); !iter.IsDone(); iter.Next()) {
9383     NS_ASSERTION(iter.item().DesiredParentType() == GetParentType(aParentFrame),
9384                  "Needed pseudos didn't get created; expect bad things");
9385     // display:list-item boxes affects the start value of the "list-item"
9386     // counter when an <ol reversed> element doesn't have an explicit start
9387     // value.
9388     if (!listItemListIsDirty &&
9389         iter.item().mComputedStyle->StyleList()->mMozListReversed ==
9390             StyleMozListReversed::True &&
9391         iter.item().mComputedStyle->StyleDisplay()->IsListItem()) {
9392       auto* list = mCounterManager.CounterListFor(nsGkAtoms::list_item);
9393       list->SetDirty();
9394       CountersDirty();
9395       listItemListIsDirty = true;
9396     }
9397     ConstructFramesFromItem(aState, iter, aParentFrame, aFrameList);
9398   }
9399 
9400   VerifyGridFlexContainerChildren(aParentFrame, aFrameList);
9401 
9402   if (aParentIsWrapperAnonBox) {
9403     for (nsIFrame* f : aFrameList) {
9404       f->SetParentIsWrapperAnonBox();
9405     }
9406   }
9407 }
9408 
AddFCItemsForAnonymousContent(nsFrameConstructorState & aState,nsContainerFrame * aFrame,const nsTArray<nsIAnonymousContentCreator::ContentInfo> & aAnonymousItems,FrameConstructionItemList & aItemsToConstruct,ItemFlags aExtraFlags)9409 void nsCSSFrameConstructor::AddFCItemsForAnonymousContent(
9410     nsFrameConstructorState& aState, nsContainerFrame* aFrame,
9411     const nsTArray<nsIAnonymousContentCreator::ContentInfo>& aAnonymousItems,
9412     FrameConstructionItemList& aItemsToConstruct, ItemFlags aExtraFlags) {
9413   for (const auto& info : aAnonymousItems) {
9414     nsIContent* content = info.mContent;
9415     // Gecko-styled nodes should have no pending restyle flags.
9416     // Assert some things about this content
9417     MOZ_ASSERT(!(content->GetFlags() &
9418                  (NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME)),
9419                "Should not be marked as needing frames");
9420     MOZ_ASSERT(!content->GetPrimaryFrame(), "Should have no existing frame");
9421     MOZ_ASSERT(!content->IsComment() && !content->IsProcessingInstruction(),
9422                "Why is someone creating garbage anonymous content");
9423 
9424     // Make sure we eagerly performed the servo cascade when the anonymous
9425     // nodes were created.
9426     MOZ_ASSERT(!content->IsElement() || content->AsElement()->HasServoData());
9427 
9428     RefPtr<ComputedStyle> computedStyle = ResolveComputedStyle(content);
9429 
9430     ItemFlags flags = aExtraFlags;
9431     flags += ItemFlag::AllowPageBreak;
9432 
9433     AddFrameConstructionItemsInternal(aState, content, aFrame, true,
9434                                       computedStyle, flags, aItemsToConstruct);
9435   }
9436 }
9437 
ProcessChildren(nsFrameConstructorState & aState,nsIContent * aContent,ComputedStyle * aComputedStyle,nsContainerFrame * aFrame,const bool aCanHaveGeneratedContent,nsFrameList & aFrameList,const bool aAllowBlockStyles,nsIFrame * aPossiblyLeafFrame)9438 void nsCSSFrameConstructor::ProcessChildren(
9439     nsFrameConstructorState& aState, nsIContent* aContent,
9440     ComputedStyle* aComputedStyle, nsContainerFrame* aFrame,
9441     const bool aCanHaveGeneratedContent, nsFrameList& aFrameList,
9442     const bool aAllowBlockStyles, nsIFrame* aPossiblyLeafFrame) {
9443   MOZ_ASSERT(aFrame, "Must have parent frame here");
9444   MOZ_ASSERT(aFrame->GetContentInsertionFrame() == aFrame,
9445              "Parent frame in ProcessChildren should be its own "
9446              "content insertion frame");
9447 
9448   const uint32_t kMaxDepth = 2 * MAX_REFLOW_DEPTH;
9449   static_assert(kMaxDepth <= UINT16_MAX, "mCurrentDepth type is too narrow");
9450   AutoRestore<uint16_t> savedDepth(mCurrentDepth);
9451   if (mCurrentDepth != UINT16_MAX) {
9452     ++mCurrentDepth;
9453   }
9454 
9455   if (!aPossiblyLeafFrame) {
9456     aPossiblyLeafFrame = aFrame;
9457   }
9458 
9459   // XXXbz ideally, this would do all the pushing of various
9460   // containing blocks as needed, so callers don't have to do it...
9461 
9462   // Check that our parent frame is a block before allowing ::first-letter/line.
9463   // E.g. <button style="display:grid"> should not allow it.
9464   const bool allowFirstPseudos =
9465       aAllowBlockStyles && aFrame->IsBlockFrameOrSubclass();
9466   bool haveFirstLetterStyle = false, haveFirstLineStyle = false;
9467   if (allowFirstPseudos) {
9468     ShouldHaveSpecialBlockStyle(aContent, aComputedStyle, &haveFirstLetterStyle,
9469                                 &haveFirstLineStyle);
9470   }
9471 
9472   // The logic here needs to match the logic in GetFloatContainingBlock()
9473   nsFrameConstructorSaveState floatSaveState;
9474   if (ShouldSuppressFloatingOfDescendants(aFrame)) {
9475     aState.PushFloatContainingBlock(nullptr, floatSaveState);
9476   } else if (aFrame->IsFloatContainingBlock()) {
9477     aState.PushFloatContainingBlock(aFrame, floatSaveState);
9478   }
9479 
9480   AutoFrameConstructionItemList itemsToConstruct(this);
9481 
9482   // If we have first-letter or first-line style then frames can get
9483   // moved around so don't set these flags.
9484   if (allowFirstPseudos && !haveFirstLetterStyle && !haveFirstLineStyle) {
9485     itemsToConstruct.SetLineBoundaryAtStart(true);
9486     itemsToConstruct.SetLineBoundaryAtEnd(true);
9487   }
9488 
9489   // Create any anonymous frames we need here.  This must happen before the
9490   // non-anonymous children are processed to ensure that popups are never
9491   // constructed before the popupset.
9492   AutoTArray<nsIAnonymousContentCreator::ContentInfo, 4> anonymousItems;
9493   GetAnonymousContent(aContent, aPossiblyLeafFrame, anonymousItems);
9494 #ifdef DEBUG
9495   for (uint32_t i = 0; i < anonymousItems.Length(); ++i) {
9496     MOZ_ASSERT(anonymousItems[i].mContent->IsRootOfNativeAnonymousSubtree(),
9497                "Content should know it's an anonymous subtree");
9498   }
9499 #endif
9500   AddFCItemsForAnonymousContent(aState, aFrame, anonymousItems,
9501                                 itemsToConstruct);
9502 
9503   nsBlockFrame* listItem = nullptr;
9504   bool isOutsideMarker = false;
9505   if (!aPossiblyLeafFrame->IsLeaf()) {
9506     // :before/:after content should have the same style parent as normal kids.
9507     //
9508     // Note that we don't use this style for looking up things like special
9509     // block styles because in some cases involving table pseudo-frames it has
9510     // nothing to do with the parent frame's desired behavior.
9511     ComputedStyle* computedStyle;
9512 
9513     if (aCanHaveGeneratedContent) {
9514       auto* styleParentFrame =
9515           nsFrame::CorrectStyleParentFrame(aFrame, PseudoStyleType::NotPseudo);
9516       computedStyle = styleParentFrame->Style();
9517       if (computedStyle->StyleDisplay()->IsListItem() &&
9518           (listItem = do_QueryFrame(aFrame)) &&
9519           !styleParentFrame->IsFieldSetFrame()) {
9520         isOutsideMarker = computedStyle->StyleList()->mListStylePosition ==
9521                           NS_STYLE_LIST_STYLE_POSITION_OUTSIDE;
9522         CreateGeneratedContentItem(aState, aFrame, *aContent->AsElement(),
9523                                    *computedStyle, PseudoStyleType::marker,
9524                                    itemsToConstruct);
9525       }
9526       // Probe for generated content before
9527       CreateGeneratedContentItem(aState, aFrame, *aContent->AsElement(),
9528                                  *computedStyle, PseudoStyleType::before,
9529                                  itemsToConstruct);
9530     }
9531 
9532     const bool addChildItems = MOZ_LIKELY(mCurrentDepth < kMaxDepth);
9533     if (!addChildItems) {
9534       NS_WARNING("ProcessChildren max depth exceeded");
9535     }
9536 
9537     FlattenedChildIterator iter(aContent);
9538     const InsertionPoint insertion(aFrame, aContent);
9539     for (nsIContent* child = iter.GetNextChild(); child;
9540          child = iter.GetNextChild()) {
9541       MOZ_ASSERT(insertion.mContainer == GetInsertionPoint(child).mContainer,
9542                  "GetInsertionPoint should agree with us");
9543       if (addChildItems) {
9544         AddFrameConstructionItems(aState, child, iter.ShadowDOMInvolved(),
9545                                   insertion, itemsToConstruct);
9546       } else {
9547         ClearLazyBits(child, child->GetNextSibling());
9548       }
9549     }
9550     itemsToConstruct.SetParentHasNoShadowDOM(!iter.ShadowDOMInvolved());
9551 
9552     if (aCanHaveGeneratedContent) {
9553       // Probe for generated content after
9554       CreateGeneratedContentItem(aState, aFrame, *aContent->AsElement(),
9555                                  *computedStyle, PseudoStyleType::after,
9556                                  itemsToConstruct);
9557     }
9558   } else {
9559     ClearLazyBits(aContent->GetFirstChild(), nullptr);
9560   }
9561 
9562   ConstructFramesFromItemList(aState, itemsToConstruct, aFrame,
9563                               /* aParentIsWrapperAnonBox = */ false,
9564                               aFrameList);
9565 
9566   NS_ASSERTION(!allowFirstPseudos || !aFrame->IsXULBoxFrame(),
9567                "can't be both block and box");
9568 
9569   if (listItem) {
9570     if (auto* markerFrame = nsLayoutUtils::GetMarkerFrame(aContent)) {
9571       for (auto* childFrame : aFrameList) {
9572         if (markerFrame == childFrame) {
9573           if (isOutsideMarker) {
9574             // SetMarkerFrameForListItem will add childFrame to the kBulletList
9575             aFrameList.RemoveFrame(childFrame);
9576             auto* grandParent = listItem->GetParent()->GetParent();
9577             if (listItem->Style()->GetPseudoType() ==
9578                     PseudoStyleType::columnContent &&
9579                 grandParent && grandParent->IsColumnSetWrapperFrame()) {
9580               listItem = do_QueryFrame(grandParent);
9581               MOZ_ASSERT(listItem,
9582                          "ColumnSetWrapperFrame is expected to be "
9583                          "a nsBlockFrame subclass");
9584               childFrame->SetParent(listItem);
9585             }
9586           }
9587           listItem->SetMarkerFrameForListItem(childFrame);
9588           MOZ_ASSERT(listItem->HasAnyStateBits(
9589                          NS_BLOCK_FRAME_HAS_OUTSIDE_MARKER) == isOutsideMarker);
9590           break;
9591         }
9592       }
9593     }
9594   }
9595 
9596   if (haveFirstLetterStyle) {
9597     WrapFramesInFirstLetterFrame(aFrame, aFrameList);
9598   }
9599   if (haveFirstLineStyle) {
9600     WrapFramesInFirstLineFrame(aState, aContent, aFrame, nullptr, aFrameList);
9601   }
9602 
9603   // We might end up with first-line frames that change
9604   // AnyKidsNeedBlockParent() without changing itemsToConstruct, but that
9605   // should never happen for cases whan aFrame->IsXULBoxFrame().
9606   NS_ASSERTION(!haveFirstLineStyle || !aFrame->IsXULBoxFrame(),
9607                "Shouldn't have first-line style if we're a box");
9608   NS_ASSERTION(
9609       !aFrame->IsXULBoxFrame() ||
9610           itemsToConstruct.AnyItemsNeedBlockParent() ==
9611               (AnyKidsNeedBlockParent(aFrameList.FirstChild()) != nullptr),
9612       "Something went awry in our block parent calculations");
9613 
9614   if (aFrame->IsXULBoxFrame() && itemsToConstruct.AnyItemsNeedBlockParent()) {
9615     // XXXbz we could do this on the FrameConstructionItemList level,
9616     // no?  And if we cared we could look through the item list
9617     // instead of groveling through the framelist here..
9618     RefPtr<ComputedStyle> blockSC =
9619         mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
9620             PseudoStyleType::mozXULAnonymousBlock, aFrame->Style());
9621     nsBlockFrame* blockFrame = NS_NewBlockFrame(mPresShell, blockSC);
9622     // We might, in theory, want to set NS_BLOCK_FLOAT_MGR and
9623     // NS_BLOCK_MARGIN_ROOT, but I think it's a bad idea given that
9624     // a real block placed here wouldn't get those set on it.
9625 
9626     InitAndRestoreFrame(aState, aContent, aFrame, blockFrame, false);
9627 
9628     NS_ASSERTION(!blockFrame->HasView(), "need to do view reparenting");
9629     ReparentFrames(this, blockFrame, aFrameList, false);
9630 
9631     blockFrame->SetInitialChildList(kPrincipalList, aFrameList);
9632     NS_ASSERTION(aFrameList.IsEmpty(), "How did that happen?");
9633     aFrameList.Clear();
9634     aFrameList.AppendFrame(nullptr, blockFrame);
9635 
9636     aFrame->AddStateBits(NS_STATE_BOX_WRAPS_KIDS_IN_BLOCK);
9637     MOZ_ASSERT(!aFrame->IsLeaf(), "Why do we have an nsLeafBoxFrame here?");
9638     aFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
9639   }
9640 }
9641 
9642 //----------------------------------------------------------------------
9643 
9644 // Support for :first-line style
9645 
9646 // Special routine to handle placing a list of frames into a block
9647 // frame that has first-line style. The routine ensures that the first
9648 // collection of inline frames end up in a first-line frame.
9649 // NOTE: aState may have containing block information related to a
9650 // different part of the frame tree than where the first line occurs.
9651 // In particular aState may be set up for where ContentInserted or
9652 // ContentAppended is inserting content, which may be some
9653 // non-first-in-flow continuation of the block to which the first-line
9654 // belongs. So this function needs to be careful about how it uses
9655 // aState.
WrapFramesInFirstLineFrame(nsFrameConstructorState & aState,nsIContent * aBlockContent,nsContainerFrame * aBlockFrame,nsFirstLineFrame * aLineFrame,nsFrameList & aFrameList)9656 void nsCSSFrameConstructor::WrapFramesInFirstLineFrame(
9657     nsFrameConstructorState& aState, nsIContent* aBlockContent,
9658     nsContainerFrame* aBlockFrame, nsFirstLineFrame* aLineFrame,
9659     nsFrameList& aFrameList) {
9660   // Extract any initial inline frames from aFrameList so we can put them
9661   // in the first-line.
9662   nsFrameList firstLineChildren =
9663       aFrameList.Split([](nsIFrame* f) { return !f->IsInlineOutside(); });
9664 
9665   if (firstLineChildren.IsEmpty()) {
9666     // Nothing is supposed to go into the first-line; nothing to do
9667     return;
9668   }
9669 
9670   if (!aLineFrame) {
9671     // Create line frame
9672     ComputedStyle* parentStyle = nsFrame::CorrectStyleParentFrame(
9673                                      aBlockFrame, PseudoStyleType::firstLine)
9674                                      ->Style();
9675     RefPtr<ComputedStyle> firstLineStyle =
9676         GetFirstLineStyle(aBlockContent, parentStyle);
9677 
9678     aLineFrame = NS_NewFirstLineFrame(mPresShell, firstLineStyle);
9679 
9680     // Initialize the line frame
9681     InitAndRestoreFrame(aState, aBlockContent, aBlockFrame, aLineFrame);
9682 
9683     // The lineFrame will be the block's first child; the rest of the
9684     // frame list (after lastInlineFrame) will be the second and
9685     // subsequent children; insert lineFrame into aFrameList.
9686     aFrameList.InsertFrame(nullptr, nullptr, aLineFrame);
9687 
9688     NS_ASSERTION(aLineFrame->Style() == firstLineStyle,
9689                  "Bogus style on line frame");
9690   }
9691 
9692   // Give the inline frames to the lineFrame <b>after</b> reparenting them
9693   ReparentFrames(this, aLineFrame, firstLineChildren, true);
9694   if (aLineFrame->PrincipalChildList().IsEmpty() &&
9695       (aLineFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
9696     aLineFrame->SetInitialChildList(kPrincipalList, firstLineChildren);
9697   } else {
9698     AppendFrames(aLineFrame, kPrincipalList, firstLineChildren);
9699   }
9700 }
9701 
9702 // Special routine to handle appending a new frame to a block frame's
9703 // child list. Takes care of placing the new frame into the right
9704 // place when first-line style is present.
AppendFirstLineFrames(nsFrameConstructorState & aState,nsIContent * aBlockContent,nsContainerFrame * aBlockFrame,nsFrameList & aFrameList)9705 void nsCSSFrameConstructor::AppendFirstLineFrames(
9706     nsFrameConstructorState& aState, nsIContent* aBlockContent,
9707     nsContainerFrame* aBlockFrame, nsFrameList& aFrameList) {
9708   // It's possible that aBlockFrame needs to have a first-line frame
9709   // created because it doesn't currently have any children.
9710   const nsFrameList& blockKids = aBlockFrame->PrincipalChildList();
9711   if (blockKids.IsEmpty()) {
9712     WrapFramesInFirstLineFrame(aState, aBlockContent, aBlockFrame, nullptr,
9713                                aFrameList);
9714     return;
9715   }
9716 
9717   // Examine the last block child - if it's a first-line frame then
9718   // appended frames need special treatment.
9719   nsIFrame* lastBlockKid = blockKids.LastChild();
9720   if (!lastBlockKid->IsLineFrame()) {
9721     // No first-line frame at the end of the list, therefore there is
9722     // an intervening block between any first-line frame the frames
9723     // we are appending. Therefore, we don't need any special
9724     // treatment of the appended frames.
9725     return;
9726   }
9727 
9728   nsFirstLineFrame* lineFrame = static_cast<nsFirstLineFrame*>(lastBlockKid);
9729   WrapFramesInFirstLineFrame(aState, aBlockContent, aBlockFrame, lineFrame,
9730                              aFrameList);
9731 }
9732 
CheckForFirstLineInsertion(nsIFrame * aParentFrame,nsFrameList & aFrameList)9733 void nsCSSFrameConstructor::CheckForFirstLineInsertion(
9734     nsIFrame* aParentFrame, nsFrameList& aFrameList) {
9735   MOZ_ASSERT(aParentFrame->Style()->HasPseudoElementData(),
9736              "Why were we called?");
9737 
9738   if (aFrameList.IsEmpty()) {
9739     // Happens often enough, with the caption stuff.  No need to do the ancestor
9740     // walk here.
9741     return;
9742   }
9743 
9744   class RestyleManager* restyleManager = RestyleManager();
9745 
9746   // Check whether there's a ::first-line on the path up from aParentFrame.
9747   // Note that we can't stop until we've run out of ancestors with
9748   // pseudo-element data, because the first-letter might be somewhere way up the
9749   // tree; in particular it might be past our containing block.
9750   nsIFrame* ancestor = aParentFrame;
9751   while (ancestor) {
9752     if (!ancestor->Style()->HasPseudoElementData()) {
9753       // We know we won't find a ::first-line now.
9754       return;
9755     }
9756 
9757     if (!ancestor->IsLineFrame()) {
9758       ancestor = ancestor->GetParent();
9759       continue;
9760     }
9761 
9762     if (!ancestor->Style()->IsPseudoElement()) {
9763       // This is a continuation lineframe, not the first line; no need to do
9764       // anything to the styles.
9765       return;
9766     }
9767 
9768     // Fix up the styles of aFrameList for ::first-line.
9769     for (nsIFrame* f : aFrameList) {
9770       restyleManager->ReparentComputedStyleForFirstLine(f);
9771     }
9772     return;
9773   }
9774 }
9775 
9776 //----------------------------------------------------------------------
9777 
9778 // First-letter support
9779 
9780 // Determine how many characters in the text fragment apply to the
9781 // first letter
FirstLetterCount(const nsTextFragment * aFragment)9782 static int32_t FirstLetterCount(const nsTextFragment* aFragment) {
9783   int32_t count = 0;
9784   int32_t firstLetterLength = 0;
9785 
9786   int32_t i, n = aFragment->GetLength();
9787   for (i = 0; i < n; i++) {
9788     char16_t ch = aFragment->CharAt(i);
9789     // FIXME: take content language into account when deciding whitespace.
9790     if (dom::IsSpaceCharacter(ch)) {
9791       if (firstLetterLength) {
9792         break;
9793       }
9794       count++;
9795       continue;
9796     }
9797     // XXX I18n
9798     if ((ch == '\'') || (ch == '\"')) {
9799       if (firstLetterLength) {
9800         break;
9801       }
9802       // keep looping
9803       firstLetterLength = 1;
9804     } else {
9805       count++;
9806       break;
9807     }
9808   }
9809 
9810   return count;
9811 }
9812 
NeedFirstLetterContinuation(Text * aText)9813 static bool NeedFirstLetterContinuation(Text* aText) {
9814   MOZ_ASSERT(aText, "null ptr");
9815   int32_t flc = FirstLetterCount(&aText->TextFragment());
9816   int32_t tl = aText->TextDataLength();
9817   return flc < tl;
9818 }
9819 
IsFirstLetterContent(Text * aText)9820 static bool IsFirstLetterContent(Text* aText) {
9821   return aText->TextDataLength() && !aText->TextIsOnlyWhitespace();
9822 }
9823 
9824 /**
9825  * Create a letter frame, only make it a floating frame.
9826  */
CreateFloatingLetterFrame(nsFrameConstructorState & aState,Text * aTextContent,nsIFrame * aTextFrame,nsContainerFrame * aParentFrame,ComputedStyle * aParentComputedStyle,ComputedStyle * aComputedStyle,nsFrameList & aResult)9827 nsFirstLetterFrame* nsCSSFrameConstructor::CreateFloatingLetterFrame(
9828     nsFrameConstructorState& aState, Text* aTextContent, nsIFrame* aTextFrame,
9829     nsContainerFrame* aParentFrame, ComputedStyle* aParentComputedStyle,
9830     ComputedStyle* aComputedStyle, nsFrameList& aResult) {
9831   MOZ_ASSERT(aParentComputedStyle);
9832 
9833   nsFirstLetterFrame* letterFrame =
9834       NS_NewFirstLetterFrame(mPresShell, aComputedStyle);
9835   // We don't want to use a text content for a non-text frame (because we want
9836   // its primary frame to be a text frame).
9837   nsIContent* letterContent = aParentFrame->GetContent();
9838   nsContainerFrame* containingBlock =
9839       aState.GetGeometricParent(*aComputedStyle->StyleDisplay(), aParentFrame);
9840   InitAndRestoreFrame(aState, letterContent, containingBlock, letterFrame);
9841 
9842   // Init the text frame to refer to the letter frame.
9843   //
9844   // Make sure we get a proper style for it (the one passed in is for the letter
9845   // frame and will have the float property set on it; the text frame shouldn't
9846   // have that set).
9847   ServoStyleSet* styleSet = mPresShell->StyleSet();
9848   RefPtr<ComputedStyle> textSC =
9849       styleSet->ResolveStyleForText(aTextContent, aComputedStyle);
9850   aTextFrame->SetComputedStyleWithoutNotification(textSC);
9851   InitAndRestoreFrame(aState, aTextContent, letterFrame, aTextFrame);
9852 
9853   // And then give the text frame to the letter frame
9854   SetInitialSingleChild(letterFrame, aTextFrame);
9855 
9856   // See if we will need to continue the text frame (does it contain
9857   // more than just the first-letter text or not?) If it does, then we
9858   // create (in advance) a continuation frame for it.
9859   nsIFrame* nextTextFrame = nullptr;
9860   if (NeedFirstLetterContinuation(aTextContent)) {
9861     // Create continuation
9862     nextTextFrame = CreateContinuingFrame(aTextFrame, aParentFrame);
9863     RefPtr<ComputedStyle> newSC =
9864         styleSet->ResolveStyleForText(aTextContent, aParentComputedStyle);
9865     nextTextFrame->SetComputedStyle(newSC);
9866   }
9867 
9868   NS_ASSERTION(aResult.IsEmpty(), "aResult should be an empty nsFrameList!");
9869   // Put the new float before any of the floats in the block we're doing
9870   // first-letter for, that is, before any floats whose parent is
9871   // containingBlock.
9872   nsFrameList::FrameLinkEnumerator link(aState.mFloatedList);
9873   while (!link.AtEnd() && link.NextFrame()->GetParent() != containingBlock) {
9874     link.Next();
9875   }
9876 
9877   aState.AddChild(letterFrame, aResult, letterContent, aParentFrame, false,
9878                   true, false, true, link.PrevFrame());
9879 
9880   if (nextTextFrame) {
9881     aResult.AppendFrame(nullptr, nextTextFrame);
9882   }
9883 
9884   return letterFrame;
9885 }
9886 
9887 /**
9888  * Create a new letter frame for aTextFrame. The letter frame will be
9889  * a child of aParentFrame.
9890  */
CreateLetterFrame(nsContainerFrame * aBlockFrame,nsContainerFrame * aBlockContinuation,Text * aTextContent,nsContainerFrame * aParentFrame,nsFrameList & aResult)9891 void nsCSSFrameConstructor::CreateLetterFrame(
9892     nsContainerFrame* aBlockFrame, nsContainerFrame* aBlockContinuation,
9893     Text* aTextContent, nsContainerFrame* aParentFrame, nsFrameList& aResult) {
9894   NS_ASSERTION(aBlockFrame->IsBlockFrameOrSubclass(), "Not a block frame?");
9895 
9896   // Get a ComputedStyle for the first-letter-frame.
9897   //
9898   // Keep this in sync with nsBlockFrame::UpdatePseudoElementStyles.
9899   nsIFrame* parentFrame = nsFrame::CorrectStyleParentFrame(
9900       aParentFrame, PseudoStyleType::firstLetter);
9901 
9902   ComputedStyle* parentComputedStyle = parentFrame->Style();
9903 
9904   // Use content from containing block so that we can actually
9905   // find a matching style rule.
9906   nsIContent* blockContent = aBlockFrame->GetContent();
9907 
9908   // Create first-letter style rule
9909   RefPtr<ComputedStyle> sc =
9910       GetFirstLetterStyle(blockContent, parentComputedStyle);
9911 
9912   if (sc) {
9913     if (parentFrame->IsLineFrame()) {
9914       nsIFrame* parentIgnoringFirstLine = nsFrame::CorrectStyleParentFrame(
9915           aBlockFrame, PseudoStyleType::firstLetter);
9916 
9917       sc = mPresShell->StyleSet()->ReparentComputedStyle(
9918           sc, parentComputedStyle, parentIgnoringFirstLine->Style(),
9919           parentComputedStyle, blockContent->AsElement());
9920     }
9921 
9922     RefPtr<ComputedStyle> textSC =
9923         mPresShell->StyleSet()->ResolveStyleForText(aTextContent, sc);
9924 
9925     // Create a new text frame (the original one will be discarded)
9926     // pass a temporary stylecontext, the correct one will be set
9927     // later.  Start off by unsetting the primary frame for
9928     // aTextContent, so it's no longer pointing to the to-be-destroyed
9929     // frame.
9930     // XXXbz it would be really nice to destroy the old frame _first_,
9931     // then create the new one, so we could avoid this hack.
9932     aTextContent->SetPrimaryFrame(nullptr);
9933     nsIFrame* textFrame = NS_NewTextFrame(mPresShell, textSC);
9934 
9935     NS_ASSERTION(aBlockContinuation == GetFloatContainingBlock(aParentFrame),
9936                  "Containing block is confused");
9937     nsFrameConstructorState state(
9938         mPresShell, GetAbsoluteContainingBlock(aParentFrame, FIXED_POS),
9939         GetAbsoluteContainingBlock(aParentFrame, ABS_POS), aBlockContinuation);
9940 
9941     // Create the right type of first-letter frame
9942     const nsStyleDisplay* display = sc->StyleDisplay();
9943     nsFirstLetterFrame* letterFrame;
9944     if (display->IsFloatingStyle() &&
9945         !nsSVGUtils::IsInSVGTextSubtree(aParentFrame)) {
9946       // Make a floating first-letter frame
9947       letterFrame = CreateFloatingLetterFrame(state, aTextContent, textFrame,
9948                                               aParentFrame, parentComputedStyle,
9949                                               sc, aResult);
9950     } else {
9951       // Make an inflow first-letter frame
9952       letterFrame = NS_NewFirstLetterFrame(mPresShell, sc);
9953 
9954       // Initialize the first-letter-frame.  We don't want to use a text
9955       // content for a non-text frame (because we want its primary frame to
9956       // be a text frame).
9957       nsIContent* letterContent = aParentFrame->GetContent();
9958       letterFrame->Init(letterContent, aParentFrame, nullptr);
9959 
9960       InitAndRestoreFrame(state, aTextContent, letterFrame, textFrame);
9961 
9962       SetInitialSingleChild(letterFrame, textFrame);
9963       aResult.Clear();
9964       aResult.AppendFrame(nullptr, letterFrame);
9965       NS_ASSERTION(!aBlockFrame->GetPrevContinuation(),
9966                    "should have the first continuation here");
9967       aBlockFrame->AddStateBits(NS_BLOCK_HAS_FIRST_LETTER_CHILD);
9968     }
9969     MOZ_ASSERT(
9970         !aBlockFrame->GetPrevContinuation(),
9971         "Setting up a first-letter frame on a non-first block continuation?");
9972     auto parent =
9973         static_cast<nsContainerFrame*>(aParentFrame->FirstContinuation());
9974     if (MOZ_UNLIKELY(parent->IsLineFrame())) {
9975       parent = static_cast<nsContainerFrame*>(
9976           parent->GetParent()->FirstContinuation());
9977     }
9978     parent->SetHasFirstLetterChild();
9979     aBlockFrame->SetProperty(nsContainerFrame::FirstLetterProperty(),
9980                              letterFrame);
9981     aTextContent->SetPrimaryFrame(textFrame);
9982   }
9983 }
9984 
WrapFramesInFirstLetterFrame(nsContainerFrame * aBlockFrame,nsFrameList & aBlockFrames)9985 void nsCSSFrameConstructor::WrapFramesInFirstLetterFrame(
9986     nsContainerFrame* aBlockFrame, nsFrameList& aBlockFrames) {
9987   aBlockFrame->AddStateBits(NS_BLOCK_HAS_FIRST_LETTER_STYLE);
9988 
9989   nsContainerFrame* parentFrame = nullptr;
9990   nsIFrame* textFrame = nullptr;
9991   nsIFrame* prevFrame = nullptr;
9992   nsFrameList letterFrames;
9993   bool stopLooking = false;
9994   WrapFramesInFirstLetterFrame(
9995       aBlockFrame, aBlockFrame, aBlockFrame, aBlockFrames.FirstChild(),
9996       &parentFrame, &textFrame, &prevFrame, letterFrames, &stopLooking);
9997   if (parentFrame) {
9998     if (parentFrame == aBlockFrame) {
9999       // Take textFrame out of the block's frame list and substitute the
10000       // letter frame(s) instead.
10001       aBlockFrames.DestroyFrame(textFrame);
10002       aBlockFrames.InsertFrames(nullptr, prevFrame, letterFrames);
10003     } else {
10004       // Take the old textFrame out of the inline parent's child list
10005       RemoveFrame(kPrincipalList, textFrame);
10006 
10007       // Insert in the letter frame(s)
10008       parentFrame->InsertFrames(kPrincipalList, prevFrame, nullptr,
10009                                 letterFrames);
10010     }
10011   }
10012 }
10013 
WrapFramesInFirstLetterFrame(nsContainerFrame * aBlockFrame,nsContainerFrame * aBlockContinuation,nsContainerFrame * aParentFrame,nsIFrame * aParentFrameList,nsContainerFrame ** aModifiedParent,nsIFrame ** aTextFrame,nsIFrame ** aPrevFrame,nsFrameList & aLetterFrames,bool * aStopLooking)10014 void nsCSSFrameConstructor::WrapFramesInFirstLetterFrame(
10015     nsContainerFrame* aBlockFrame, nsContainerFrame* aBlockContinuation,
10016     nsContainerFrame* aParentFrame, nsIFrame* aParentFrameList,
10017     nsContainerFrame** aModifiedParent, nsIFrame** aTextFrame,
10018     nsIFrame** aPrevFrame, nsFrameList& aLetterFrames, bool* aStopLooking) {
10019   nsIFrame* prevFrame = nullptr;
10020   nsIFrame* frame = aParentFrameList;
10021 
10022   // This loop attempts to implement "Finding the First Letter":
10023   // https://drafts.csswg.org/css-pseudo-4/#application-in-css
10024   // FIXME: we don't handle nested blocks correctly yet though (bug 214004)
10025   while (frame) {
10026     nsIFrame* nextFrame = frame->GetNextSibling();
10027 
10028     // Skip all ::markers.
10029     if (frame->Style()->GetPseudoType() == PseudoStyleType::marker) {
10030       prevFrame = frame;
10031       frame = nextFrame;
10032       continue;
10033     }
10034     LayoutFrameType frameType = frame->Type();
10035     if (LayoutFrameType::Text == frameType) {
10036       // Wrap up first-letter content in a letter frame
10037       Text* textContent = frame->GetContent()->AsText();
10038       if (IsFirstLetterContent(textContent)) {
10039         // Create letter frame to wrap up the text
10040         CreateLetterFrame(aBlockFrame, aBlockContinuation, textContent,
10041                           aParentFrame, aLetterFrames);
10042 
10043         // Provide adjustment information for parent
10044         *aModifiedParent = aParentFrame;
10045         *aTextFrame = frame;
10046         *aPrevFrame = prevFrame;
10047         *aStopLooking = true;
10048         return;
10049       }
10050     } else if (IsInlineFrame(frame) && frameType != LayoutFrameType::Br) {
10051       nsIFrame* kids = frame->PrincipalChildList().FirstChild();
10052       WrapFramesInFirstLetterFrame(aBlockFrame, aBlockContinuation,
10053                                    static_cast<nsContainerFrame*>(frame), kids,
10054                                    aModifiedParent, aTextFrame, aPrevFrame,
10055                                    aLetterFrames, aStopLooking);
10056       if (*aStopLooking) {
10057         return;
10058       }
10059     } else {
10060       // This will stop us looking to create more letter frames. For
10061       // example, maybe the frame-type is "letterFrame" or
10062       // "placeholderFrame". This keeps us from creating extra letter
10063       // frames, and also prevents us from creating letter frames when
10064       // the first real content child of a block is not text (e.g. an
10065       // image, hr, etc.)
10066       *aStopLooking = true;
10067       break;
10068     }
10069 
10070     prevFrame = frame;
10071     frame = nextFrame;
10072   }
10073 }
10074 
FindFirstLetterFrame(nsIFrame * aFrame,nsIFrame::ChildListID aListID)10075 static nsIFrame* FindFirstLetterFrame(nsIFrame* aFrame,
10076                                       nsIFrame::ChildListID aListID) {
10077   nsFrameList list = aFrame->GetChildList(aListID);
10078   for (nsFrameList::Enumerator e(list); !e.AtEnd(); e.Next()) {
10079     if (e.get()->IsLetterFrame()) {
10080       return e.get();
10081     }
10082   }
10083   return nullptr;
10084 }
10085 
ClearHasFirstLetterChildFrom(nsContainerFrame * aParentFrame)10086 static void ClearHasFirstLetterChildFrom(nsContainerFrame* aParentFrame) {
10087   MOZ_ASSERT(aParentFrame);
10088   auto* parent =
10089       static_cast<nsContainerFrame*>(aParentFrame->FirstContinuation());
10090   if (MOZ_UNLIKELY(parent->IsLineFrame())) {
10091     MOZ_ASSERT(!parent->HasFirstLetterChild());
10092     parent = static_cast<nsContainerFrame*>(
10093         parent->GetParent()->FirstContinuation());
10094   }
10095   MOZ_ASSERT(parent->HasFirstLetterChild());
10096   parent->ClearHasFirstLetterChild();
10097 }
10098 
RemoveFloatingFirstLetterFrames(PresShell * aPresShell,nsIFrame * aBlockFrame)10099 void nsCSSFrameConstructor::RemoveFloatingFirstLetterFrames(
10100     PresShell* aPresShell, nsIFrame* aBlockFrame) {
10101   // Look for the first letter frame on the kFloatList, then kPushedFloatsList.
10102   nsIFrame* floatFrame =
10103       ::FindFirstLetterFrame(aBlockFrame, nsIFrame::kFloatList);
10104   if (!floatFrame) {
10105     floatFrame =
10106         ::FindFirstLetterFrame(aBlockFrame, nsIFrame::kPushedFloatsList);
10107     if (!floatFrame) {
10108       return;
10109     }
10110   }
10111 
10112   // Take the text frame away from the letter frame (so it isn't
10113   // destroyed when we destroy the letter frame).
10114   nsIFrame* textFrame = floatFrame->PrincipalChildList().FirstChild();
10115   if (!textFrame) {
10116     return;
10117   }
10118 
10119   // Discover the placeholder frame for the letter frame
10120   nsPlaceholderFrame* placeholderFrame = floatFrame->GetPlaceholderFrame();
10121   if (!placeholderFrame) {
10122     // Somethings really wrong
10123     return;
10124   }
10125   nsContainerFrame* parentFrame = placeholderFrame->GetParent();
10126   if (!parentFrame) {
10127     // Somethings really wrong
10128     return;
10129   }
10130 
10131   ClearHasFirstLetterChildFrom(parentFrame);
10132 
10133   // Create a new text frame with the right style that maps all of the content
10134   // that was previously part of the letter frame (and probably continued
10135   // elsewhere).
10136   ComputedStyle* parentSC = parentFrame->Style();
10137   nsIContent* textContent = textFrame->GetContent();
10138   if (!textContent) {
10139     return;
10140   }
10141   RefPtr<ComputedStyle> newSC =
10142       aPresShell->StyleSet()->ResolveStyleForText(textContent, parentSC);
10143   nsIFrame* newTextFrame = NS_NewTextFrame(aPresShell, newSC);
10144   newTextFrame->Init(textContent, parentFrame, nullptr);
10145 
10146   // Destroy the old text frame's continuations (the old text frame
10147   // will be destroyed when its letter frame is destroyed).
10148   nsIFrame* frameToDelete = textFrame->LastContinuation();
10149   while (frameToDelete != textFrame) {
10150     nsIFrame* nextFrameToDelete = frameToDelete->GetPrevContinuation();
10151     RemoveFrame(kPrincipalList, frameToDelete);
10152     frameToDelete = nextFrameToDelete;
10153   }
10154 
10155   nsIFrame* prevSibling = placeholderFrame->GetPrevSibling();
10156 
10157   // Now that everything is set...
10158 #ifdef NOISY_FIRST_LETTER
10159   printf(
10160       "RemoveFloatingFirstLetterFrames: textContent=%p oldTextFrame=%p "
10161       "newTextFrame=%p\n",
10162       textContent.get(), textFrame, newTextFrame);
10163 #endif
10164 
10165   // Remove placeholder frame and the float
10166   RemoveFrame(kPrincipalList, placeholderFrame);
10167 
10168   // Now that the old frames are gone, we can start pointing to our
10169   // new primary frame.
10170   textContent->SetPrimaryFrame(newTextFrame);
10171 
10172   // Wallpaper bug 822910.
10173   bool offsetsNeedFixing = prevSibling && prevSibling->IsTextFrame();
10174   if (offsetsNeedFixing) {
10175     prevSibling->AddStateBits(TEXT_OFFSETS_NEED_FIXING);
10176   }
10177 
10178   // Insert text frame in its place
10179   nsFrameList textList(newTextFrame, newTextFrame);
10180   InsertFrames(parentFrame, kPrincipalList, prevSibling, textList);
10181 
10182   if (offsetsNeedFixing) {
10183     prevSibling->RemoveStateBits(TEXT_OFFSETS_NEED_FIXING);
10184   }
10185 }
10186 
RemoveFirstLetterFrames(PresShell * aPresShell,nsContainerFrame * aFrame,nsContainerFrame * aBlockFrame,bool * aStopLooking)10187 void nsCSSFrameConstructor::RemoveFirstLetterFrames(
10188     PresShell* aPresShell, nsContainerFrame* aFrame,
10189     nsContainerFrame* aBlockFrame, bool* aStopLooking) {
10190   nsIFrame* prevSibling = nullptr;
10191   nsIFrame* kid = aFrame->PrincipalChildList().FirstChild();
10192 
10193   while (kid) {
10194     if (kid->IsLetterFrame()) {
10195       ClearHasFirstLetterChildFrom(aFrame);
10196       nsIFrame* textFrame = kid->PrincipalChildList().FirstChild();
10197       if (!textFrame) {
10198         break;
10199       }
10200 
10201       // Create a new textframe
10202       ComputedStyle* parentSC = aFrame->Style();
10203       if (!parentSC) {
10204         break;
10205       }
10206       nsIContent* textContent = textFrame->GetContent();
10207       if (!textContent) {
10208         break;
10209       }
10210       RefPtr<ComputedStyle> newSC =
10211           aPresShell->StyleSet()->ResolveStyleForText(textContent, parentSC);
10212       textFrame = NS_NewTextFrame(aPresShell, newSC);
10213       textFrame->Init(textContent, aFrame, nullptr);
10214 
10215       // Next rip out the kid and replace it with the text frame
10216       RemoveFrame(kPrincipalList, kid);
10217 
10218       // Now that the old frames are gone, we can start pointing to our
10219       // new primary frame.
10220       textContent->SetPrimaryFrame(textFrame);
10221 
10222       // Wallpaper bug 822910.
10223       bool offsetsNeedFixing = prevSibling && prevSibling->IsTextFrame();
10224       if (offsetsNeedFixing) {
10225         prevSibling->AddStateBits(TEXT_OFFSETS_NEED_FIXING);
10226       }
10227 
10228       // Insert text frame in its place
10229       nsFrameList textList(textFrame, textFrame);
10230       InsertFrames(aFrame, kPrincipalList, prevSibling, textList);
10231 
10232       if (offsetsNeedFixing) {
10233         prevSibling->RemoveStateBits(TEXT_OFFSETS_NEED_FIXING);
10234       }
10235 
10236       *aStopLooking = true;
10237       NS_ASSERTION(!aBlockFrame->GetPrevContinuation(),
10238                    "should have the first continuation here");
10239       aBlockFrame->RemoveStateBits(NS_BLOCK_HAS_FIRST_LETTER_CHILD);
10240       break;
10241     } else if (IsInlineFrame(kid)) {
10242       nsContainerFrame* kidAsContainerFrame = do_QueryFrame(kid);
10243       if (kidAsContainerFrame) {
10244         // Look inside child inline frame for the letter frame.
10245         RemoveFirstLetterFrames(aPresShell, kidAsContainerFrame, aBlockFrame,
10246                                 aStopLooking);
10247         if (*aStopLooking) {
10248           break;
10249         }
10250       }
10251     }
10252     prevSibling = kid;
10253     kid = kid->GetNextSibling();
10254   }
10255 }
10256 
RemoveLetterFrames(PresShell * aPresShell,nsContainerFrame * aBlockFrame)10257 void nsCSSFrameConstructor::RemoveLetterFrames(PresShell* aPresShell,
10258                                                nsContainerFrame* aBlockFrame) {
10259   aBlockFrame =
10260       static_cast<nsContainerFrame*>(aBlockFrame->FirstContinuation());
10261   aBlockFrame->RemoveProperty(nsContainerFrame::FirstLetterProperty());
10262   nsContainerFrame* continuation = aBlockFrame;
10263 
10264   bool stopLooking = false;
10265   do {
10266     RemoveFloatingFirstLetterFrames(aPresShell, continuation);
10267     RemoveFirstLetterFrames(aPresShell, continuation, aBlockFrame,
10268                             &stopLooking);
10269     if (stopLooking) {
10270       break;
10271     }
10272     continuation =
10273         static_cast<nsContainerFrame*>(continuation->GetNextContinuation());
10274   } while (continuation);
10275 }
10276 
10277 // Fixup the letter frame situation for the given block
RecoverLetterFrames(nsContainerFrame * aBlockFrame)10278 void nsCSSFrameConstructor::RecoverLetterFrames(nsContainerFrame* aBlockFrame) {
10279   aBlockFrame =
10280       static_cast<nsContainerFrame*>(aBlockFrame->FirstContinuation());
10281   nsContainerFrame* continuation = aBlockFrame;
10282 
10283   nsContainerFrame* parentFrame = nullptr;
10284   nsIFrame* textFrame = nullptr;
10285   nsIFrame* prevFrame = nullptr;
10286   nsFrameList letterFrames;
10287   bool stopLooking = false;
10288   do {
10289     // XXX shouldn't this bit be set already (bug 408493), assert instead?
10290     continuation->AddStateBits(NS_BLOCK_HAS_FIRST_LETTER_STYLE);
10291     WrapFramesInFirstLetterFrame(
10292         aBlockFrame, continuation, continuation,
10293         continuation->PrincipalChildList().FirstChild(), &parentFrame,
10294         &textFrame, &prevFrame, letterFrames, &stopLooking);
10295     if (stopLooking) {
10296       break;
10297     }
10298     continuation =
10299         static_cast<nsContainerFrame*>(continuation->GetNextContinuation());
10300   } while (continuation);
10301 
10302   if (parentFrame) {
10303     // Take the old textFrame out of the parent's child list
10304     RemoveFrame(kPrincipalList, textFrame);
10305 
10306     // Insert in the letter frame(s)
10307     parentFrame->InsertFrames(kPrincipalList, prevFrame, nullptr, letterFrames);
10308   }
10309 }
10310 
10311 //----------------------------------------------------------------------
10312 
ConstructBlock(nsFrameConstructorState & aState,nsIContent * aContent,nsContainerFrame * aParentFrame,nsContainerFrame * aContentParentFrame,ComputedStyle * aComputedStyle,nsContainerFrame ** aNewFrame,nsFrameList & aFrameList,nsIFrame * aPositionedFrameForAbsPosContainer)10313 void nsCSSFrameConstructor::ConstructBlock(
10314     nsFrameConstructorState& aState, nsIContent* aContent,
10315     nsContainerFrame* aParentFrame, nsContainerFrame* aContentParentFrame,
10316     ComputedStyle* aComputedStyle, nsContainerFrame** aNewFrame,
10317     nsFrameList& aFrameList, nsIFrame* aPositionedFrameForAbsPosContainer) {
10318   // clang-format off
10319   //
10320   // If a block frame is in a multi-column subtree, its children may need to
10321   // be chopped into runs of blocks containing column-spans and runs of
10322   // blocks containing no column-spans. Each run containing column-spans
10323   // will be wrapped by an anonymous block. See CreateColumnSpanSiblings() for
10324   // the implementation.
10325   //
10326   // If a block frame is a multi-column container, its children will need to
10327   // be processed as above. Moreover, it creates a ColumnSetWrapperFrame as
10328   // its outermost frame, and its children which have no
10329   // -moz-column-span-wrapper pseudo will be wrapped in ColumnSetFrames. See
10330   // FinishBuildingColumns() for the implementation.
10331   //
10332   // The multi-column subtree maintains the following invariants:
10333   //
10334   // 1) All the frames have the frame state bit
10335   //    NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR set, except for top-level
10336   //    ColumnSetWrapperFrame and those children in the column-span subtrees.
10337   //
10338   // 2) The first and last frame under ColumnSetWrapperFrame are always
10339   //    ColumnSetFrame.
10340   //
10341   // 3) ColumnSetFrames are linked together as continuations.
10342   //
10343   // 4) Those column-span wrappers are *not* linked together with themselves nor
10344   //    with the original block frame. The continuation chain consists of the
10345   //    original block frame and the original block's continuations wrapping
10346   //    non-column-spans.
10347   //
10348   // For example, this HTML
10349   //  <div id="x" style="column-count: 2;">
10350   //    <div style="column-span: all">a</div>
10351   //    <div id="y">
10352   //      b
10353   //      <div style="column-span: all">c</div>
10354   //      <div style="column-span: all">d</div>
10355   //      e
10356   //    </div>
10357   //  </div>
10358   //  <div style="column-span: all">f</div>
10359   //
10360   //  yields the following frame tree.
10361   //
10362   // A) ColumnSetWrapper (original style)
10363   // B)   ColumnSet (-moz-column-set)   <-- always created by BeginBuildingColumns
10364   // C)     Block (-moz-column-content)
10365   // D)   Block (-moz-column-span-wrapper, created by x)
10366   // E)     Block (div)
10367   // F)       Text ("a")
10368   // G)   ColumnSet (-moz-column-set)
10369   // H)     Block (-moz-column-content, created by x)
10370   // I)       Block (div, y)
10371   // J)         Text ("b")
10372   // K)   Block (-moz-column-span-wrapper, created by x)
10373   // L)     Block (-moz-column-span-wrapper, created by y)
10374   // M)       Block (div, new BFC)
10375   // N)         Text ("c")
10376   // O)       Block (div, new BFC)
10377   // P)         Text ("d")
10378   // Q)   ColumnSet (-moz-column-set)
10379   // R)     Block (-moz-column-content, created by x)
10380   // S)       Block (div, y)
10381   // T)         Text ("e")
10382   // U) Block (div, new BFC)   <-- not in multi-column hierarchy
10383   // V)   Text ("f")
10384   //
10385   // ColumnSet linkage described in 3): B -> G -> Q
10386   //
10387   // Block linkage described in 4): C -> H -> R  and  I -> S
10388   //
10389   // clang-format on
10390 
10391   nsBlockFrame* blockFrame = do_QueryFrame(*aNewFrame);
10392   MOZ_ASSERT(blockFrame &&
10393                  (blockFrame->IsBlockFrame() || blockFrame->IsDetailsFrame()),
10394              "not a block frame nor a details frame?");
10395 
10396   // Create column hierarchy if necessary.
10397   const bool needsColumn =
10398       aComputedStyle->StyleColumn()->IsColumnContainerStyle();
10399   if (needsColumn) {
10400     *aNewFrame = BeginBuildingColumns(aState, aContent, aParentFrame,
10401                                       blockFrame, aComputedStyle);
10402 
10403     if (aPositionedFrameForAbsPosContainer == blockFrame) {
10404       aPositionedFrameForAbsPosContainer = *aNewFrame;
10405     }
10406   } else {
10407     // No need to create column hierarchy. Initialize block frame.
10408     blockFrame->SetComputedStyleWithoutNotification(aComputedStyle);
10409     InitAndRestoreFrame(aState, aContent, aParentFrame, blockFrame);
10410   }
10411 
10412   aState.AddChild(*aNewFrame, aFrameList, aContent,
10413                   aContentParentFrame ? aContentParentFrame : aParentFrame);
10414   if (!mRootElementFrame) {
10415     // The frame we're constructing will be the root element frame.
10416     SetRootElementFrameAndConstructCanvasAnonContent(*aNewFrame, aState,
10417                                                      aFrameList);
10418   }
10419 
10420   // We should make the outer frame be the absolute containing block,
10421   // if one is required. We have to do this because absolute
10422   // positioning must be computed with respect to the CSS dimensions
10423   // of the element, which are the dimensions of the outer block. But
10424   // we can't really do that because only blocks can have absolute
10425   // children. So use the block and try to compensate with hacks
10426   // in nsBlockFrame::CalculateContainingBlockSizeForAbsolutes.
10427   nsFrameConstructorSaveState absoluteSaveState;
10428   (*aNewFrame)->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
10429   if (aPositionedFrameForAbsPosContainer) {
10430     //    NS_ASSERTION(aRelPos, "should have made area frame for this");
10431     aState.PushAbsoluteContainingBlock(
10432         *aNewFrame, aPositionedFrameForAbsPosContainer, absoluteSaveState);
10433   }
10434 
10435   if (aParentFrame->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR) &&
10436       !ShouldSuppressColumnSpanDescendants(aParentFrame)) {
10437     blockFrame->AddStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR);
10438   }
10439 
10440   // Process the child content
10441   nsFrameList childList;
10442   ProcessChildren(aState, aContent, aComputedStyle, blockFrame, true, childList,
10443                   true);
10444 
10445   if (!MayNeedToCreateColumnSpanSiblings(blockFrame, childList)) {
10446     // No need to create column-span siblings.
10447     blockFrame->SetInitialChildList(kPrincipalList, childList);
10448     return;
10449   }
10450 
10451   // Extract any initial non-column-span kids, and put them in block frame's
10452   // child list.
10453   nsFrameList initialNonColumnSpanKids =
10454       childList.Split([](nsIFrame* f) { return f->IsColumnSpan(); });
10455   blockFrame->SetInitialChildList(kPrincipalList, initialNonColumnSpanKids);
10456 
10457   if (childList.IsEmpty()) {
10458     // No more kids to process (there weren't any column-span kids).
10459     return;
10460   }
10461 
10462   nsFrameList columnSpanSiblings = CreateColumnSpanSiblings(
10463       aState, blockFrame, childList,
10464       // If we're constructing a column container, pass nullptr as
10465       // aPositionedFrame to forbid reparenting absolute/fixed positioned frames
10466       // to column contents or column-span wrappers.
10467       needsColumn ? nullptr : aPositionedFrameForAbsPosContainer);
10468 
10469   if (needsColumn) {
10470     // We're constructing a column container; need to finish building it.
10471     FinishBuildingColumns(aState, *aNewFrame, blockFrame, columnSpanSiblings);
10472   } else {
10473     // We're constructing a normal block which has column-span children in a
10474     // column hierarchy such as "x" in the following example.
10475     //
10476     // <div style="column-count: 2">
10477     //   <div id="x">
10478     //     <div>normal child</div>
10479     //     <div style="column-span">spanner</div>
10480     //   </div>
10481     // </div>
10482     aFrameList.AppendFrames(nullptr, columnSpanSiblings);
10483   }
10484 
10485   MOZ_ASSERT(columnSpanSiblings.IsEmpty(),
10486              "The column-span siblings should be moved to the proper place!");
10487 }
10488 
BeginBuildingColumns(nsFrameConstructorState & aState,nsIContent * aContent,nsContainerFrame * aParentFrame,nsContainerFrame * aColumnContent,ComputedStyle * aComputedStyle)10489 nsBlockFrame* nsCSSFrameConstructor::BeginBuildingColumns(
10490     nsFrameConstructorState& aState, nsIContent* aContent,
10491     nsContainerFrame* aParentFrame, nsContainerFrame* aColumnContent,
10492     ComputedStyle* aComputedStyle) {
10493   MOZ_ASSERT(
10494       aColumnContent->IsBlockFrame() || aColumnContent->IsDetailsFrame(),
10495       "aColumnContent should either be a block frame or a details frame.");
10496   MOZ_ASSERT(aComputedStyle->StyleColumn()->IsColumnContainerStyle(),
10497              "No need to build a column hierarchy!");
10498 
10499   // The initial column hierarchy looks like this:
10500   //
10501   // ColumnSetWrapper (original style)
10502   //   ColumnSet (-moz-column-set)
10503   //     Block (-moz-column-content)
10504   //
10505   nsBlockFrame* columnSetWrapper = NS_NewColumnSetWrapperFrame(
10506       mPresShell, aComputedStyle, nsFrameState(NS_FRAME_OWNS_ANON_BOXES));
10507   InitAndRestoreFrame(aState, aContent, aParentFrame, columnSetWrapper);
10508   if (aParentFrame->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR) &&
10509       !ShouldSuppressColumnSpanDescendants(aParentFrame)) {
10510     columnSetWrapper->AddStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR);
10511   }
10512 
10513   RefPtr<ComputedStyle> columnSetStyle =
10514       mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
10515           PseudoStyleType::columnSet, aComputedStyle);
10516   nsContainerFrame* columnSet = NS_NewColumnSetFrame(
10517       mPresShell, columnSetStyle, nsFrameState(NS_FRAME_OWNS_ANON_BOXES));
10518   InitAndRestoreFrame(aState, aContent, columnSetWrapper, columnSet);
10519   columnSet->AddStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR);
10520 
10521   RefPtr<ComputedStyle> blockStyle =
10522       mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
10523           PseudoStyleType::columnContent, columnSetStyle);
10524   aColumnContent->SetComputedStyleWithoutNotification(blockStyle);
10525   InitAndRestoreFrame(aState, aContent, columnSet, aColumnContent);
10526   aColumnContent->AddStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR |
10527                                NS_BLOCK_FORMATTING_CONTEXT_STATE_BITS);
10528 
10529   // Set up the parent-child chain.
10530   SetInitialSingleChild(columnSetWrapper, columnSet);
10531   SetInitialSingleChild(columnSet, aColumnContent);
10532 
10533   return columnSetWrapper;
10534 }
10535 
FinishBuildingColumns(nsFrameConstructorState & aState,nsContainerFrame * aColumnSetWrapper,nsContainerFrame * aColumnContent,nsFrameList & aColumnContentSiblings)10536 void nsCSSFrameConstructor::FinishBuildingColumns(
10537     nsFrameConstructorState& aState, nsContainerFrame* aColumnSetWrapper,
10538     nsContainerFrame* aColumnContent, nsFrameList& aColumnContentSiblings) {
10539   nsContainerFrame* prevColumnSet = aColumnContent->GetParent();
10540 
10541   MOZ_ASSERT(prevColumnSet->IsColumnSetFrame() &&
10542                  prevColumnSet->GetParent() == aColumnSetWrapper,
10543              "Should have established column hierarchy!");
10544 
10545   // Tag the first ColumnSet to have column-span siblings so that the bit can
10546   // propagate to all the continuations. We don't want the last ColumnSet to
10547   // have this bit, so we will unset the bit for it at the end of this function.
10548   prevColumnSet->SetHasColumnSpanSiblings(true);
10549 
10550   nsFrameList finalList;
10551   while (aColumnContentSiblings.NotEmpty()) {
10552     nsIFrame* f = aColumnContentSiblings.RemoveFirstChild();
10553     if (f->IsColumnSpan()) {
10554       // Do nothing for column-span wrappers. Just move it to the final
10555       // items.
10556       finalList.AppendFrame(aColumnSetWrapper, f);
10557     } else {
10558       auto* continuingColumnSet = static_cast<nsContainerFrame*>(
10559           CreateContinuingFrame(prevColumnSet, aColumnSetWrapper, false));
10560       MOZ_ASSERT(continuingColumnSet->HasColumnSpanSiblings(),
10561                  "The bit should propagate to the next continuation!");
10562 
10563       f->SetParent(continuingColumnSet);
10564       SetInitialSingleChild(continuingColumnSet, f);
10565       finalList.AppendFrame(aColumnSetWrapper, continuingColumnSet);
10566       prevColumnSet = continuingColumnSet;
10567     }
10568   }
10569 
10570   // Unset the bit because the last ColumnSet has no column-span siblings.
10571   prevColumnSet->SetHasColumnSpanSiblings(false);
10572 
10573   aColumnSetWrapper->AppendFrames(kPrincipalList, finalList);
10574 }
10575 
MayNeedToCreateColumnSpanSiblings(nsContainerFrame * aBlockFrame,const nsFrameList & aChildList)10576 bool nsCSSFrameConstructor::MayNeedToCreateColumnSpanSiblings(
10577     nsContainerFrame* aBlockFrame, const nsFrameList& aChildList) {
10578   if (!aBlockFrame->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR)) {
10579     // The block frame isn't in a multi-column block formatting context.
10580     return false;
10581   }
10582 
10583   if (ShouldSuppressColumnSpanDescendants(aBlockFrame)) {
10584     // No need to create column-span siblings for a frame that suppresses them.
10585     return false;
10586   }
10587 
10588   if (aChildList.IsEmpty()) {
10589     // No child needs to be processed.
10590     return false;
10591   }
10592 
10593   // Need to actually look into the child list.
10594   return true;
10595 }
10596 
CreateColumnSpanSiblings(nsFrameConstructorState & aState,nsContainerFrame * aInitialBlock,nsFrameList & aChildList,nsIFrame * aPositionedFrame)10597 nsFrameList nsCSSFrameConstructor::CreateColumnSpanSiblings(
10598     nsFrameConstructorState& aState, nsContainerFrame* aInitialBlock,
10599     nsFrameList& aChildList, nsIFrame* aPositionedFrame) {
10600   MOZ_ASSERT(!aPositionedFrame || aPositionedFrame->IsAbsPosContainingBlock());
10601 
10602   nsIContent* const content = aInitialBlock->GetContent();
10603   nsContainerFrame* const parentFrame = aInitialBlock->GetParent();
10604 
10605   nsFrameList siblings;
10606   nsContainerFrame* lastNonColumnSpanWrapper = aInitialBlock;
10607 
10608   // Tag the first non-column-span wrapper to have column-span siblings so that
10609   // the bit can propagate to all the continuations. We don't want the last
10610   // wrapper to have this bit, so we will unset the bit for it at the end of
10611   // this function.
10612   lastNonColumnSpanWrapper->SetHasColumnSpanSiblings(true);
10613   do {
10614     MOZ_ASSERT(aChildList.NotEmpty(), "Why call this if child list is empty?");
10615     MOZ_ASSERT(aChildList.FirstChild()->IsColumnSpan(),
10616                "Must have the child starting with column-span!");
10617 
10618     // Grab the consecutive column-span kids, and reparent them into a
10619     // block frame.
10620     RefPtr<ComputedStyle> columnSpanWrapperStyle =
10621         mPresShell->StyleSet()->ResolveNonInheritingAnonymousBoxStyle(
10622             PseudoStyleType::columnSpanWrapper);
10623     nsBlockFrame* columnSpanWrapper =
10624         NS_NewBlockFrame(mPresShell, columnSpanWrapperStyle);
10625     InitAndRestoreFrame(aState, content, parentFrame, columnSpanWrapper, false);
10626     columnSpanWrapper->AddStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR |
10627                                     NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
10628 
10629     nsFrameList columnSpanKids =
10630         aChildList.Split([](nsIFrame* f) { return !f->IsColumnSpan(); });
10631     columnSpanKids.ApplySetParent(columnSpanWrapper);
10632     columnSpanWrapper->SetInitialChildList(kPrincipalList, columnSpanKids);
10633     if (aPositionedFrame) {
10634       aState.ReparentAbsoluteItems(columnSpanWrapper);
10635     }
10636 
10637     siblings.AppendFrame(nullptr, columnSpanWrapper);
10638 
10639     // Grab the consecutive non-column-span kids, and reparent them into a new
10640     // continuation of the last non-column-span wrapper frame.
10641     auto* nonColumnSpanWrapper = static_cast<nsContainerFrame*>(
10642         CreateContinuingFrame(lastNonColumnSpanWrapper, parentFrame, false));
10643     nonColumnSpanWrapper->AddStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR |
10644                                        NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
10645     MOZ_ASSERT(nonColumnSpanWrapper->HasColumnSpanSiblings(),
10646                "The bit should propagate to the next continuation!");
10647 
10648     if (aChildList.NotEmpty()) {
10649       nsFrameList nonColumnSpanKids =
10650           aChildList.Split([](nsIFrame* f) { return f->IsColumnSpan(); });
10651 
10652       nonColumnSpanKids.ApplySetParent(nonColumnSpanWrapper);
10653       nonColumnSpanWrapper->SetInitialChildList(kPrincipalList,
10654                                                 nonColumnSpanKids);
10655       if (aPositionedFrame) {
10656         aState.ReparentAbsoluteItems(nonColumnSpanWrapper);
10657       }
10658     }
10659 
10660     siblings.AppendFrame(nullptr, nonColumnSpanWrapper);
10661 
10662     lastNonColumnSpanWrapper = nonColumnSpanWrapper;
10663   } while (aChildList.NotEmpty());
10664 
10665   // Unset the bit because the last non-column-span wrapper has no column-span
10666   // siblings.
10667   lastNonColumnSpanWrapper->SetHasColumnSpanSiblings(false);
10668 
10669   return siblings;
10670 }
10671 
MaybeRecreateForColumnSpan(nsFrameConstructorState & aState,nsContainerFrame * aParentFrame,nsFrameList & aFrameList,nsIFrame * aPrevSibling)10672 bool nsCSSFrameConstructor::MaybeRecreateForColumnSpan(
10673     nsFrameConstructorState& aState, nsContainerFrame* aParentFrame,
10674     nsFrameList& aFrameList, nsIFrame* aPrevSibling) {
10675   if (!aParentFrame->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR)) {
10676     return false;
10677   }
10678 
10679   if (aFrameList.IsEmpty()) {
10680     return false;
10681   }
10682 
10683   MOZ_ASSERT(!IsFramePartOfIBSplit(aParentFrame),
10684              "We should have wiped aParentFrame in WipeContainingBlock if it's "
10685              "part of IB split!");
10686 
10687   nsIFrame* nextSibling = ::GetInsertNextSibling(aParentFrame, aPrevSibling);
10688   if (!nextSibling && IsLastContinuationForColumnContent(aParentFrame)) {
10689     // We are appending a list of frames to the last continuation of a
10690     // ::-moz-column-content. This is the case where we can fix the frame tree
10691     // instead of reframing the containing block. Return false and let
10692     // AppendFramesToParent() deal with this.
10693     return false;
10694   }
10695 
10696   auto HasColumnSpan = [](const nsFrameList& aList) {
10697     for (nsIFrame* f : aList) {
10698       if (f->IsColumnSpan()) {
10699         return true;
10700       }
10701     }
10702     return false;
10703   };
10704 
10705   if (HasColumnSpan(aFrameList)) {
10706     // If any frame in the frame list has "column-span:all" style, i.e. a
10707     // -moz-column-span-wrapper frame, we need to reframe the multi-column
10708     // containing block.
10709     //
10710     // We can only be here if none of the new inserted nsIContent* nodes (via
10711     // ContentAppended or ContentRangeInserted) have column-span:all style, yet
10712     // some of them have column-span:all descendants. Sadly, there's no way to
10713     // detect this by checking FrameConstructionItems in WipeContainingBlock().
10714     // Otherwise, we would have already wiped the multi-column containing block.
10715     PROFILER_TRACING_MARKER(
10716         "Layout", "Reframe multi-column after constructing frame list", LAYOUT,
10717         TRACING_EVENT);
10718 
10719     // aFrameList can contain placeholder frames. In order to destroy their
10720     // associated out-of-flow frames properly, we need to manually flush all the
10721     // out-of-flow frames in aState to their container frames.
10722     aState.ProcessFrameInsertionsForAllLists();
10723     aFrameList.DestroyFrames();
10724     RecreateFramesForContent(
10725         GetMultiColumnContainingBlockFor(aParentFrame)->GetContent(),
10726         InsertionKind::Async);
10727     return true;
10728   }
10729 
10730   return false;
10731 }
10732 
ConstructInline(nsFrameConstructorState & aState,FrameConstructionItem & aItem,nsContainerFrame * aParentFrame,const nsStyleDisplay * aDisplay,nsFrameList & aFrameList)10733 nsIFrame* nsCSSFrameConstructor::ConstructInline(
10734     nsFrameConstructorState& aState, FrameConstructionItem& aItem,
10735     nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay,
10736     nsFrameList& aFrameList) {
10737   // If an inline frame has non-inline kids, then we chop up the child list
10738   // into runs of blocks and runs of inlines, create anonymous block frames to
10739   // contain the runs of blocks, inline frames with our style for the runs of
10740   // inlines, and put all these frames, in order, into aFrameList.
10741   //
10742   // When there are column-span blocks in a run of blocks, instead of creating
10743   // an anonymous block to wrap them, we create multiple anonymous blocks,
10744   // wrapping runs of non-column-spans and runs of column-spans.
10745   //
10746   // We return the the first one.  The whole setup is called an {ib}
10747   // split; in what follows "frames in the split" refers to the anonymous blocks
10748   // and inlines that contain our children.
10749   //
10750   // {ib} splits maintain the following invariants:
10751   // 1) All frames in the split have the NS_FRAME_PART_OF_IBSPLIT bit
10752   //    set.
10753   //
10754   // 2) Each frame in the split has the nsIFrame::IBSplitSibling
10755   //    property pointing to the next frame in the split, except for the last
10756   //    one, which does not have it set.
10757   //
10758   // 3) Each frame in the split has the nsIFrame::IBSplitPrevSibling
10759   //    property pointing to the previous frame in the split, except for the
10760   //    first one, which does not have it set.
10761   //
10762   // 4) The first and last frame in the split are always inlines.
10763   //
10764   // 5) The frames wrapping runs of non-column-spans are linked together as
10765   //    continuations. The frames wrapping runs of column-spans are *not*
10766   //    linked with each other nor with other non-column-span wrappers.
10767   //
10768   // 6) The first and last frame in the chains of blocks are always wrapping
10769   //    non-column-spans. Both of them are created even if they're empty.
10770   //
10771   // An invariant that is NOT maintained is that the wrappers are actually
10772   // linked via GetNextSibling linkage.  A simple example is an inline
10773   // containing an inline that contains a block.  The three parts of the inner
10774   // inline end up with three different parents.
10775   //
10776   // For example, this HTML:
10777   // <span>
10778   //   <div>a</div>
10779   //   <span>
10780   //     b
10781   //     <div>c</div>
10782   //   </span>
10783   //   d
10784   //   <div>e</div>
10785   //   f
10786   //  </span>
10787   // Gives the following frame tree:
10788   //
10789   // Inline (outer span)
10790   // Block (anonymous, outer span)
10791   //   Block (div)
10792   //     Text("a")
10793   // Inline (outer span)
10794   //   Inline (inner span)
10795   //     Text("b")
10796   // Block (anonymous, outer span)
10797   //   Block (anonymous, inner span)
10798   //     Block (div)
10799   //       Text("c")
10800   // Inline (outer span)
10801   //   Inline (inner span)
10802   //   Text("d")
10803   // Block (anonymous, outer span)
10804   //   Block (div)
10805   //     Text("e")
10806   // Inline (outer span)
10807   //   Text("f")
10808 
10809   nsIContent* const content = aItem.mContent;
10810   ComputedStyle* const computedStyle = aItem.mComputedStyle;
10811 
10812   nsInlineFrame* newFrame = NS_NewInlineFrame(mPresShell, computedStyle);
10813 
10814   // Initialize the frame
10815   InitAndRestoreFrame(aState, content, aParentFrame, newFrame);
10816 
10817   // definition cannot be inside next block because the object's destructor is
10818   // significant. this is part of the fix for bug 42372
10819   nsFrameConstructorSaveState absoluteSaveState;
10820 
10821   bool isAbsPosCB = newFrame->IsAbsPosContainingBlock();
10822   newFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
10823   if (isAbsPosCB) {
10824     // Relatively positioned frames becomes a container for child
10825     // frames that are positioned
10826     aState.PushAbsoluteContainingBlock(newFrame, newFrame, absoluteSaveState);
10827   }
10828 
10829   if (aParentFrame->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR) &&
10830       !ShouldSuppressColumnSpanDescendants(aParentFrame)) {
10831     newFrame->AddStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR);
10832   }
10833 
10834   // Process the child content
10835   nsFrameList childList;
10836   ConstructFramesFromItemList(aState, aItem.mChildItems, newFrame,
10837                               /* aParentIsWrapperAnonBox = */ false, childList);
10838 
10839   nsFrameList::FrameLinkEnumerator firstBlockEnumerator(childList);
10840   if (!aItem.mIsAllInline) {
10841     firstBlockEnumerator.Find(
10842         [](nsIFrame* aFrame) { return aFrame->IsBlockOutside(); });
10843   }
10844 
10845   if (aItem.mIsAllInline || firstBlockEnumerator.AtEnd()) {
10846     // This part is easy.  We either already know we have no non-inline kids,
10847     // or haven't found any when constructing actual frames (the latter can
10848     // happen only if out-of-flows that we thought had no containing block
10849     // acquired one when ancestor inline frames and {ib} splits got
10850     // constructed).  Just put all the kids into the single inline frame and
10851     // bail.
10852     newFrame->SetInitialChildList(kPrincipalList, childList);
10853     aState.AddChild(newFrame, aFrameList, content, aParentFrame);
10854     return newFrame;
10855   }
10856 
10857   // This inline frame contains several types of children. Therefore this frame
10858   // has to be chopped into several pieces, as described above.
10859 
10860   // Grab the first inline's kids
10861   nsFrameList firstInlineKids = childList.ExtractHead(firstBlockEnumerator);
10862   newFrame->SetInitialChildList(kPrincipalList, firstInlineKids);
10863 
10864   aFrameList.AppendFrame(nullptr, newFrame);
10865 
10866   newFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
10867   CreateIBSiblings(aState, newFrame, isAbsPosCB, childList, aFrameList);
10868 
10869   return newFrame;
10870 }
10871 
CreateIBSiblings(nsFrameConstructorState & aState,nsContainerFrame * aInitialInline,bool aIsAbsPosCB,nsFrameList & aChildList,nsFrameList & aSiblings)10872 void nsCSSFrameConstructor::CreateIBSiblings(nsFrameConstructorState& aState,
10873                                              nsContainerFrame* aInitialInline,
10874                                              bool aIsAbsPosCB,
10875                                              nsFrameList& aChildList,
10876                                              nsFrameList& aSiblings) {
10877   MOZ_ASSERT(aIsAbsPosCB == aInitialInline->IsAbsPosContainingBlock());
10878 
10879   nsIContent* content = aInitialInline->GetContent();
10880   ComputedStyle* computedStyle = aInitialInline->Style();
10881   nsContainerFrame* parentFrame = aInitialInline->GetParent();
10882 
10883   // Resolve the right style for our anonymous blocks.
10884   //
10885   // The distinction in styles is needed because of CSS 2.1, section
10886   // 9.2.1.1, which says:
10887   //
10888   //   When such an inline box is affected by relative positioning, any
10889   //   resulting translation also affects the block-level box contained
10890   //   in the inline box.
10891   RefPtr<ComputedStyle> blockSC =
10892       mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
10893           PseudoStyleType::mozBlockInsideInlineWrapper, computedStyle);
10894 
10895   nsContainerFrame* lastNewInline =
10896       static_cast<nsContainerFrame*>(aInitialInline->FirstContinuation());
10897   do {
10898     // On entry to this loop aChildList is not empty and the first frame in it
10899     // is block-level.
10900     MOZ_ASSERT(aChildList.NotEmpty(), "Should have child items");
10901     MOZ_ASSERT(aChildList.FirstChild()->IsBlockOutside(),
10902                "Must have list starting with block");
10903 
10904     // The initial run of blocks belongs to an anonymous block that we create
10905     // right now. The anonymous block will be the parent of these block
10906     // children of the inline.
10907     nsBlockFrame* blockFrame = NS_NewBlockFrame(mPresShell, blockSC);
10908     InitAndRestoreFrame(aState, content, parentFrame, blockFrame, false);
10909     if (aInitialInline->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR)) {
10910       blockFrame->AddStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR);
10911     }
10912 
10913     // Find the first non-block child which defines the end of our block kids
10914     // and the start of our next inline's kids
10915     nsFrameList blockKids =
10916         aChildList.Split([](nsIFrame* f) { return !f->IsBlockOutside(); });
10917 
10918     if (!aInitialInline->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR)) {
10919       MoveChildrenTo(aInitialInline, blockFrame, blockKids);
10920 
10921       SetFrameIsIBSplit(lastNewInline, blockFrame);
10922       aSiblings.AppendFrame(nullptr, blockFrame);
10923     } else {
10924       // Extract any initial non-column-span frames, and put them in
10925       // blockFrame's child list.
10926       nsFrameList initialNonColumnSpanKids =
10927           blockKids.Split([](nsIFrame* f) { return f->IsColumnSpan(); });
10928       MoveChildrenTo(aInitialInline, blockFrame, initialNonColumnSpanKids);
10929 
10930       SetFrameIsIBSplit(lastNewInline, blockFrame);
10931       aSiblings.AppendFrame(nullptr, blockFrame);
10932 
10933       if (blockKids.NotEmpty()) {
10934         // Although SetFrameIsIBSplit() will add NS_FRAME_PART_OF_IBSPLIT for
10935         // blockFrame later, we manually add the bit earlier here to make all
10936         // the continuations of blockFrame created in
10937         // CreateColumnSpanSiblings(), i.e. non-column-span wrappers, have the
10938         // bit via nsFrame::Init().
10939         blockFrame->AddStateBits(NS_FRAME_PART_OF_IBSPLIT);
10940 
10941         nsFrameList columnSpanSiblings =
10942             CreateColumnSpanSiblings(aState, blockFrame, blockKids,
10943                                      aIsAbsPosCB ? aInitialInline : nullptr);
10944         aSiblings.AppendFrames(nullptr, columnSpanSiblings);
10945       }
10946     }
10947 
10948     // Now grab the initial inlines in aChildList and put them into an inline
10949     // frame.
10950     nsInlineFrame* inlineFrame = NS_NewInlineFrame(mPresShell, computedStyle);
10951     InitAndRestoreFrame(aState, content, parentFrame, inlineFrame, false);
10952     inlineFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
10953     if (aInitialInline->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR)) {
10954       inlineFrame->AddStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR);
10955     }
10956 
10957     if (aIsAbsPosCB) {
10958       inlineFrame->MarkAsAbsoluteContainingBlock();
10959     }
10960 
10961     if (aChildList.NotEmpty()) {
10962       nsFrameList inlineKids =
10963           aChildList.Split([](nsIFrame* f) { return f->IsBlockOutside(); });
10964       MoveChildrenTo(aInitialInline, inlineFrame, inlineKids);
10965     }
10966 
10967     SetFrameIsIBSplit(blockFrame, inlineFrame);
10968     aSiblings.AppendFrame(nullptr, inlineFrame);
10969     lastNewInline = inlineFrame;
10970   } while (aChildList.NotEmpty());
10971 
10972   SetFrameIsIBSplit(lastNewInline, nullptr);
10973 }
10974 
BuildInlineChildItems(nsFrameConstructorState & aState,FrameConstructionItem & aParentItem,bool aItemIsWithinSVGText,bool aItemAllowsTextPathChild)10975 void nsCSSFrameConstructor::BuildInlineChildItems(
10976     nsFrameConstructorState& aState, FrameConstructionItem& aParentItem,
10977     bool aItemIsWithinSVGText, bool aItemAllowsTextPathChild) {
10978   ComputedStyle* const parentComputedStyle = aParentItem.mComputedStyle;
10979   nsIContent* const parentContent = aParentItem.mContent;
10980 
10981   if (!aItemIsWithinSVGText) {
10982     if (parentComputedStyle->StyleDisplay()->IsListItem()) {
10983       CreateGeneratedContentItem(aState, nullptr, *parentContent->AsElement(),
10984                                  *parentComputedStyle, PseudoStyleType::marker,
10985                                  aParentItem.mChildItems);
10986     }
10987     // Probe for generated content before
10988     CreateGeneratedContentItem(aState, nullptr, *parentContent->AsElement(),
10989                                *parentComputedStyle, PseudoStyleType::before,
10990                                aParentItem.mChildItems);
10991   }
10992 
10993   ItemFlags flags;
10994   if (aItemIsWithinSVGText) {
10995     flags += ItemFlag::IsWithinSVGText;
10996   }
10997   if (aItemAllowsTextPathChild &&
10998       aParentItem.mContent->IsSVGElement(nsGkAtoms::a)) {
10999     flags += ItemFlag::AllowTextPathChild;
11000   }
11001 
11002   FlattenedChildIterator iter(parentContent);
11003   for (nsIContent* content = iter.GetNextChild(); content;
11004        content = iter.GetNextChild()) {
11005     AddFrameConstructionItems(aState, content, iter.ShadowDOMInvolved(),
11006                               InsertionPoint(), aParentItem.mChildItems, flags);
11007   }
11008 
11009   if (!aItemIsWithinSVGText) {
11010     // Probe for generated content after
11011     CreateGeneratedContentItem(aState, nullptr, *parentContent->AsElement(),
11012                                *parentComputedStyle, PseudoStyleType::after,
11013                                aParentItem.mChildItems);
11014   }
11015 
11016   aParentItem.mIsAllInline = aParentItem.mChildItems.AreAllItemsInline();
11017 }
11018 
11019 // return whether it's ok to append (in the AppendFrames sense) to
11020 // aParentFrame if our nextSibling is aNextSibling.  aParentFrame must
11021 // be an ib-split inline.
IsSafeToAppendToIBSplitInline(nsIFrame * aParentFrame,nsIFrame * aNextSibling)11022 static bool IsSafeToAppendToIBSplitInline(nsIFrame* aParentFrame,
11023                                           nsIFrame* aNextSibling) {
11024   MOZ_ASSERT(IsInlineFrame(aParentFrame), "Must have an inline parent here");
11025 
11026   do {
11027     NS_ASSERTION(IsFramePartOfIBSplit(aParentFrame),
11028                  "How is this not part of an ib-split?");
11029     if (aNextSibling || aParentFrame->GetNextContinuation() ||
11030         GetIBSplitSibling(aParentFrame)) {
11031       return false;
11032     }
11033 
11034     aNextSibling = aParentFrame->GetNextSibling();
11035     aParentFrame = aParentFrame->GetParent();
11036   } while (IsInlineFrame(aParentFrame));
11037 
11038   return true;
11039 }
11040 
WipeInsertionParent(nsContainerFrame * aFrame)11041 bool nsCSSFrameConstructor::WipeInsertionParent(nsContainerFrame* aFrame) {
11042 #define TRACE(reason)                                                       \
11043   PROFILER_TRACING_MARKER("Layout", "WipeInsertionParent: " reason, LAYOUT, \
11044                           TRACING_EVENT)
11045 
11046   const LayoutFrameType frameType = aFrame->Type();
11047 
11048   // FIXME(emilio): This looks terribly inefficient if you insert elements deep
11049   // in a MathML subtree.
11050   if (aFrame->IsFrameOfType(nsIFrame::eMathML)) {
11051     TRACE("MathML");
11052     RecreateFramesForContent(aFrame->GetContent(), InsertionKind::Async);
11053     return true;
11054   }
11055 
11056   // A ruby-related frame that's getting new children.
11057   // The situation for ruby is complex, especially when interacting with
11058   // spaces. It contains these two special cases apart from tables:
11059   // 1) There are effectively three types of white spaces in ruby frames
11060   //    we handle differently: leading/tailing/inter-level space,
11061   //    inter-base/inter-annotation space, and inter-segment space.
11062   //    These three types of spaces can be converted to each other when
11063   //    their sibling changes.
11064   // 2) The first effective child of a ruby frame must always be a ruby
11065   //    base container. It should be created or destroyed accordingly.
11066   if (IsRubyPseudo(aFrame) || frameType == LayoutFrameType::Ruby ||
11067       RubyUtils::IsRubyContainerBox(frameType)) {
11068     // We want to optimize it better, and avoid reframing as much as
11069     // possible. But given the cases above, and the fact that a ruby
11070     // usually won't be very large, it should be fine to reframe it.
11071     TRACE("Ruby");
11072     RecreateFramesForContent(aFrame->GetContent(), InsertionKind::Async);
11073     return true;
11074   }
11075 
11076   // A <details> that's getting new children. When inserting
11077   // elements into <details>, we reframe the <details> and let frame constructor
11078   // move the main <summary> to the front when constructing the frame
11079   // construction items.
11080   if (auto* details =
11081           HTMLDetailsElement::FromNodeOrNull(aFrame->GetContent())) {
11082     TRACE("Details / Summary");
11083     RecreateFramesForContent(details, InsertionKind::Async);
11084     return true;
11085   }
11086 
11087   // Reframe the multi-column container whenever elements insert/append
11088   // into it because we need to reconstruct column-span split.
11089   if (aFrame->IsColumnSetWrapperFrame()) {
11090     TRACE("Multi-column");
11091     RecreateFramesForContent(aFrame->GetContent(), InsertionKind::Async);
11092     return true;
11093   }
11094 
11095   return false;
11096 
11097 #undef TRACE
11098 }
11099 
WipeContainingBlock(nsFrameConstructorState & aState,nsIFrame * aContainingBlock,nsIFrame * aFrame,FrameConstructionItemList & aItems,bool aIsAppend,nsIFrame * aPrevSibling)11100 bool nsCSSFrameConstructor::WipeContainingBlock(
11101     nsFrameConstructorState& aState, nsIFrame* aContainingBlock,
11102     nsIFrame* aFrame, FrameConstructionItemList& aItems, bool aIsAppend,
11103     nsIFrame* aPrevSibling) {
11104 #define TRACE(reason)                                                       \
11105   PROFILER_TRACING_MARKER("Layout", "WipeContainingBlock: " reason, LAYOUT, \
11106                           TRACING_EVENT)
11107 
11108   if (aItems.IsEmpty()) {
11109     return false;
11110   }
11111 
11112   // Before we go and append the frames, we must check for several
11113   // special situations.
11114 
11115   if (aFrame->GetContent() == mDocument->GetRootElement()) {
11116     // If we insert a content that becomes the canonical body element, and its
11117     // used WritingMode is different from the root element's used WritingMode,
11118     // we need to reframe the root element so that the root element's frames has
11119     // the correct writing-mode propagated from body element. (See
11120     // nsCSSFrameConstructor::ConstructDocElementFrame.)
11121     //
11122     // Bug 1594297: When inserting a new <body>, we may need to reframe the old
11123     // <body> which has a "overflow" value other than simple "visible". But it's
11124     // tricky, see bug 1593752.
11125     nsIContent* bodyElement = mDocument->GetBodyElement();
11126     for (FCItemIterator iter(aItems); !iter.IsDone(); iter.Next()) {
11127       const WritingMode bodyWM(iter.item().mComputedStyle);
11128       if (iter.item().mContent == bodyElement &&
11129           bodyWM != aFrame->GetWritingMode()) {
11130         TRACE("Root");
11131         RecreateFramesForContent(mDocument->GetRootElement(),
11132                                  InsertionKind::Async);
11133         return true;
11134       }
11135     }
11136   }
11137 
11138   // Situation #1 is a XUL frame that contains frames that are required
11139   // to be wrapped in blocks.
11140   if (aFrame->IsXULBoxFrame() &&
11141       !(aFrame->GetStateBits() & NS_STATE_BOX_WRAPS_KIDS_IN_BLOCK) &&
11142       aItems.AnyItemsNeedBlockParent()) {
11143     TRACE("XUL with block-wrapped kids");
11144     RecreateFramesForContent(aFrame->GetContent(), InsertionKind::Async);
11145     return true;
11146   }
11147 
11148   nsIFrame* nextSibling = ::GetInsertNextSibling(aFrame, aPrevSibling);
11149 
11150   // Situation #2 is a flex or grid container frame into which we're inserting
11151   // new inline non-replaced children, adjacent to an existing anonymous
11152   // flex or grid item.
11153   LayoutFrameType frameType = aFrame->Type();
11154   if (frameType == LayoutFrameType::FlexContainer ||
11155       frameType == LayoutFrameType::GridContainer) {
11156     FCItemIterator iter(aItems);
11157 
11158     // Check if we're adding to-be-wrapped content right *after* an existing
11159     // anonymous flex or grid item (which would need to absorb this content).
11160     const bool isLegacyBox = IsFlexContainerForLegacyBox(aFrame);
11161     if (aPrevSibling && IsAnonymousFlexOrGridItem(aPrevSibling) &&
11162         iter.item().NeedsAnonFlexOrGridItem(aState, isLegacyBox)) {
11163       TRACE("Inserting inline after anon flex or grid item");
11164       RecreateFramesForContent(aFrame->GetContent(), InsertionKind::Async);
11165       return true;
11166     }
11167 
11168     // Check if we're adding to-be-wrapped content right *before* an existing
11169     // anonymous flex or grid item (which would need to absorb this content).
11170     if (nextSibling && IsAnonymousFlexOrGridItem(nextSibling)) {
11171       // Jump to the last entry in the list
11172       iter.SetToEnd();
11173       iter.Prev();
11174       if (iter.item().NeedsAnonFlexOrGridItem(aState, isLegacyBox)) {
11175         TRACE("Inserting inline before anon flex or grid item");
11176         RecreateFramesForContent(aFrame->GetContent(), InsertionKind::Async);
11177         return true;
11178       }
11179     }
11180   }
11181 
11182   // Situation #3 is an anonymous flex or grid item that's getting new children
11183   // who don't want to be wrapped.
11184   if (IsAnonymousFlexOrGridItem(aFrame)) {
11185     AssertAnonymousFlexOrGridItemParent(aFrame, aFrame->GetParent());
11186 
11187     // We need to push a null float containing block to be sure that
11188     // "NeedsAnonFlexOrGridItem" will know we're not honoring floats for this
11189     // inserted content. (In particular, this is necessary in order for
11190     // its "GetGeometricParent" call to return the correct result.)
11191     // We're not honoring floats on this content because it has the
11192     // _flex/grid container_ as its parent in the content tree.
11193     nsFrameConstructorSaveState floatSaveState;
11194     aState.PushFloatContainingBlock(nullptr, floatSaveState);
11195 
11196     FCItemIterator iter(aItems);
11197     // Skip over things that _do_ need an anonymous flex item, because
11198     // they're perfectly happy to go here -- they won't cause a reframe.
11199     nsIFrame* containerFrame = aFrame->GetParent();
11200     const bool isLegacyBox = IsFlexContainerForLegacyBox(containerFrame);
11201     if (!iter.SkipItemsThatNeedAnonFlexOrGridItem(aState, isLegacyBox)) {
11202       // We hit something that _doesn't_ need an anonymous flex item!
11203       // Rebuild the flex container to bust it out.
11204       TRACE("Inserting non-inlines inside anon flex or grid item");
11205       RecreateFramesForContent(containerFrame->GetContent(),
11206                                InsertionKind::Async);
11207       return true;
11208     }
11209 
11210     // If we get here, then everything in |aItems| needs to be wrapped in
11211     // an anonymous flex or grid item.  That's where it's already going - good!
11212   }
11213 
11214   // Situation #4 is a case when table pseudo-frames don't work out right
11215   ParentType parentType = GetParentType(aFrame);
11216   // If all the kids want a parent of the type that aFrame is, then we're all
11217   // set to go.  Indeed, there won't be any table pseudo-frames created between
11218   // aFrame and the kids, so those won't need to be merged with any table
11219   // pseudo-frames that might already be kids of aFrame.  If aFrame itself is a
11220   // table pseudo-frame, then all the kids in this list would have wanted a
11221   // frame of that type wrapping them anyway, so putting them inside it is ok.
11222   if (!aItems.AllWantParentType(parentType)) {
11223     // Don't give up yet.  If parentType is not eTypeBlock and the parent is
11224     // not a generated content frame, then try filtering whitespace out of the
11225     // list.
11226     if (parentType != eTypeBlock && !aFrame->IsGeneratedContentFrame()) {
11227       // For leading whitespace followed by a kid that wants our parent type,
11228       // there are four cases:
11229       // 1) We have a previous sibling which is not a table pseudo.  That means
11230       //    that previous sibling wanted a (non-block) parent of the type we're
11231       //    looking at.  Then the whitespace comes between two table-internal
11232       //    elements, so should be collapsed out.
11233       // 2) We have a previous sibling which is a table pseudo.  It might have
11234       //    kids who want this whitespace, so we need to reframe.
11235       // 3) We have no previous sibling and our parent frame is not a table
11236       //    pseudo.  That means that we'll be at the beginning of our actual
11237       //    non-block-type parent, and the whitespace is OK to collapse out.
11238       //    If something is ever inserted before us, it'll find our own parent
11239       //    as its parent and if it's something that would care about the
11240       //    whitespace it'll want a block parent, so it'll trigger a reframe at
11241       //    that point.
11242       // 4) We have no previous sibling and our parent frame is a table pseudo.
11243       //    Need to reframe.
11244       // All that is predicated on finding the correct previous sibling.  We
11245       // might have to walk backwards along continuations from aFrame to do so.
11246       //
11247       // It's always OK to drop whitespace between any two items that want a
11248       // parent of type parentType.
11249       //
11250       // For trailing whitespace preceded by a kid that wants our parent type,
11251       // there are four cases:
11252       // 1) We have a next sibling which is not a table pseudo.  That means
11253       //    that next sibling wanted a (non-block) parent of the type we're
11254       //    looking at.  Then the whitespace comes between two table-internal
11255       //    elements, so should be collapsed out.
11256       // 2) We have a next sibling which is a table pseudo.  It might have
11257       //    kids who want this whitespace, so we need to reframe.
11258       // 3) We have no next sibling and our parent frame is not a table
11259       //    pseudo.  That means that we'll be at the end of our actual
11260       //    non-block-type parent, and the whitespace is OK to collapse out.
11261       //    If something is ever inserted after us, it'll find our own parent
11262       //    as its parent and if it's something that would care about the
11263       //    whitespace it'll want a block parent, so it'll trigger a reframe at
11264       //    that point.
11265       // 4) We have no next sibling and our parent frame is a table pseudo.
11266       //    Need to reframe.
11267       // All that is predicated on finding the correct next sibling.  We might
11268       // have to walk forward along continuations from aFrame to do so.  That
11269       // said, in the case when nextSibling is null at this point and aIsAppend
11270       // is true, we know we're in case 3.  Furthermore, in that case we don't
11271       // even have to worry about the table pseudo situation; we know our
11272       // parent is not a table pseudo there.
11273       FCItemIterator iter(aItems);
11274       FCItemIterator start(iter);
11275       do {
11276         if (iter.SkipItemsWantingParentType(parentType)) {
11277           break;
11278         }
11279 
11280         // iter points to an item that wants a different parent.  If it's not
11281         // whitespace, we're done; no more point scanning the list.
11282         if (!iter.item().IsWhitespace(aState)) {
11283           break;
11284         }
11285 
11286         if (iter == start) {
11287           // Leading whitespace.  How to handle this depends on our
11288           // previous sibling and aFrame.  See the long comment above.
11289           nsIFrame* prevSibling = aPrevSibling;
11290           if (!prevSibling) {
11291             // Try to find one after all
11292             nsIFrame* parentPrevCont = aFrame->GetPrevContinuation();
11293             while (parentPrevCont) {
11294               prevSibling =
11295                   parentPrevCont->GetChildList(kPrincipalList).LastChild();
11296               if (prevSibling) {
11297                 break;
11298               }
11299               parentPrevCont = parentPrevCont->GetPrevContinuation();
11300             }
11301           };
11302           if (prevSibling) {
11303             if (IsTablePseudo(prevSibling)) {
11304               // need to reframe
11305               break;
11306             }
11307           } else if (IsTablePseudo(aFrame)) {
11308             // need to reframe
11309             break;
11310           }
11311         }
11312 
11313         FCItemIterator spaceEndIter(iter);
11314         // Advance spaceEndIter past any whitespace
11315         bool trailingSpaces = spaceEndIter.SkipWhitespace(aState);
11316 
11317         bool okToDrop;
11318         if (trailingSpaces) {
11319           // Trailing whitespace.  How to handle this depeds on aIsAppend, our
11320           // next sibling and aFrame.  See the long comment above.
11321           okToDrop = aIsAppend && !nextSibling;
11322           if (!okToDrop) {
11323             if (!nextSibling) {
11324               // Try to find one after all
11325               nsIFrame* parentNextCont = aFrame->GetNextContinuation();
11326               while (parentNextCont) {
11327                 nextSibling = parentNextCont->PrincipalChildList().FirstChild();
11328                 if (nextSibling) {
11329                   break;
11330                 }
11331                 parentNextCont = parentNextCont->GetNextContinuation();
11332               }
11333             }
11334 
11335             okToDrop = (nextSibling && !IsTablePseudo(nextSibling)) ||
11336                        (!nextSibling && !IsTablePseudo(aFrame));
11337           }
11338 #ifdef DEBUG
11339           else {
11340             NS_ASSERTION(!IsTablePseudo(aFrame), "How did that happen?");
11341           }
11342 #endif
11343         } else {
11344           okToDrop = (spaceEndIter.item().DesiredParentType() == parentType);
11345         }
11346 
11347         if (okToDrop) {
11348           iter.DeleteItemsTo(this, spaceEndIter);
11349         } else {
11350           // We're done: we don't want to drop the whitespace, and it has the
11351           // wrong parent type.
11352           break;
11353         }
11354 
11355         // Now loop, since |iter| points to item right after the whitespace we
11356         // removed.
11357       } while (!iter.IsDone());
11358     }
11359 
11360     // We might be able to figure out some sort of optimizations here, but they
11361     // would have to depend on having a correct aPrevSibling and a correct next
11362     // sibling.  For example, we can probably avoid reframing if none of
11363     // aFrame, aPrevSibling, and next sibling are table pseudo-frames.  But it
11364     // doesn't seem worth it to worry about that for now, especially since we
11365     // in fact do not have a reliable aPrevSibling, nor any next sibling, in
11366     // this method.
11367 
11368     // aItems might have changed, so recheck the parent type thing.  In fact,
11369     // it might be empty, so recheck that too.
11370     if (aItems.IsEmpty()) {
11371       return false;
11372     }
11373 
11374     if (!aItems.AllWantParentType(parentType)) {
11375       // Reframing aFrame->GetContent() is good enough, since the content of
11376       // table pseudo-frames is the ancestor content.
11377       TRACE("Pseudo-frames going wrong");
11378       RecreateFramesForContent(aFrame->GetContent(), InsertionKind::Async);
11379       return true;
11380     }
11381   }
11382 
11383   // Situation #5 is a frame in multicol subtree that's getting new children.
11384   if (aFrame->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR)) {
11385     MOZ_ASSERT(!aFrame->IsDetailsFrame(),
11386                "Inserting elements into <details> should have been reframed!");
11387 
11388     bool anyColumnSpanItems = false;
11389     for (FCItemIterator iter(aItems); !iter.IsDone(); iter.Next()) {
11390       if (iter.item().mComputedStyle->StyleColumn()->IsColumnSpanStyle()) {
11391         anyColumnSpanItems = true;
11392         break;
11393       }
11394     }
11395 
11396     bool needsReframe =
11397         // 1. Insert / append any column-span children.
11398         anyColumnSpanItems ||
11399         // 2. GetInsertionPrevSibling() modifies insertion parent. If the prev
11400         // sibling is a column-span, aFrame ends up being the
11401         // column-span-wrapper.
11402         aFrame->Style()->GetPseudoType() ==
11403             PseudoStyleType::columnSpanWrapper ||
11404         // 3. Append into {ib} split container. There might be room for
11405         // optimization, but let's reframe for correctness...
11406         IsFramePartOfIBSplit(aFrame);
11407 
11408     if (needsReframe) {
11409       TRACE("Multi-column");
11410       RecreateFramesForContent(
11411           GetMultiColumnContainingBlockFor(aFrame)->GetContent(),
11412           InsertionKind::Async);
11413       return true;
11414     }
11415 
11416     // If we get here, then we need further check for {ib} split to decide
11417     // whether to reframe. For example, appending a block into an empty inline
11418     // that is not part of an {ib} split, but should become an {ib} split.
11419   }
11420 
11421   // Now we have several cases involving {ib} splits.  Put them all in a
11422   // do/while with breaks to take us to the "go and reconstruct" code.
11423   do {
11424     if (IsInlineFrame(aFrame)) {
11425       if (aItems.AreAllItemsInline()) {
11426         // We can just put the kids in.
11427         return false;
11428       }
11429 
11430       if (!IsFramePartOfIBSplit(aFrame)) {
11431         // Need to go ahead and reconstruct.
11432         break;
11433       }
11434 
11435       // Now we're adding kids including some blocks to an inline part of an
11436       // {ib} split.  If we plan to call AppendFrames, and don't have a next
11437       // sibling for the new frames, and our parent is the last continuation of
11438       // the last part of the {ib} split, and the same is true of all our
11439       // ancestor inlines (they have no following continuations and they're the
11440       // last part of their {ib} splits and we'd be adding to the end for all
11441       // of them), then AppendFrames will handle things for us.  Bail out in
11442       // that case.
11443       if (aIsAppend && IsSafeToAppendToIBSplitInline(aFrame, nextSibling)) {
11444         return false;
11445       }
11446 
11447       // Need to reconstruct.
11448       break;
11449     }
11450 
11451     // Now we know we have a block parent.  If it's not part of an
11452     // ib-split, we're all set.
11453     if (!IsFramePartOfIBSplit(aFrame)) {
11454       return false;
11455     }
11456 
11457     // We're adding some kids to a block part of an {ib} split.  If all the
11458     // kids are blocks, we don't need to reconstruct.
11459     if (aItems.AreAllItemsBlock()) {
11460       return false;
11461     }
11462 
11463     // We might have some inline kids for this block.  Just fall out of the
11464     // loop and reconstruct.
11465   } while (0);
11466 
11467   // If we don't have a containing block, start with aFrame and look for one.
11468   if (!aContainingBlock) {
11469     aContainingBlock = aFrame;
11470   }
11471 
11472   // To find the right block to reframe, just walk up the tree until we find a
11473   // frame that is:
11474   // 1)  Not part of an IB split
11475   // 2)  Not a pseudo-frame
11476   // 3)  Not an inline frame
11477   // We're guaranteed to find one, since ComputedStyle::ApplyStyleFixups
11478   // enforces that the root is display:none, display:table, or display:block.
11479   // Note that walking up "too far" is OK in terms of correctness, even if it
11480   // might be a little inefficient.  This is why we walk out of all
11481   // pseudo-frames -- telling which ones are or are not OK to walk out of is
11482   // too hard (and I suspect that we do in fact need to walk out of all of
11483   // them).
11484   while (IsFramePartOfIBSplit(aContainingBlock) ||
11485          aContainingBlock->IsInlineOutside() ||
11486          aContainingBlock->Style()->IsPseudoOrAnonBox()) {
11487     aContainingBlock = aContainingBlock->GetParent();
11488     NS_ASSERTION(aContainingBlock,
11489                  "Must have non-inline, non-ib-split, non-pseudo frame as "
11490                  "root (or child of root, for a table root)!");
11491   }
11492 
11493   // Tell parent of the containing block to reformulate the
11494   // entire block. This is painful and definitely not optimal
11495   // but it will *always* get the right answer.
11496 
11497   nsIContent* blockContent = aContainingBlock->GetContent();
11498   TRACE("IB splits");
11499   RecreateFramesForContent(blockContent, InsertionKind::Async);
11500   return true;
11501 #undef TRACE
11502 }
11503 
ReframeContainingBlock(nsIFrame * aFrame)11504 void nsCSSFrameConstructor::ReframeContainingBlock(nsIFrame* aFrame) {
11505   // XXXbz how exactly would we get here while isReflowing anyway?  Should this
11506   // whole test be ifdef DEBUG?
11507   if (mPresShell->IsReflowLocked()) {
11508     // don't ReframeContainingBlock, this will result in a crash
11509     // if we remove a tree that's in reflow - see bug 121368 for testcase
11510     NS_ERROR(
11511         "Atemptted to nsCSSFrameConstructor::ReframeContainingBlock during a "
11512         "Reflow!!!");
11513     return;
11514   }
11515 
11516   // Get the first "normal" ancestor of the target frame.
11517   nsIFrame* containingBlock = GetIBContainingBlockFor(aFrame);
11518   if (containingBlock) {
11519     // From here we look for the containing block in case the target
11520     // frame is already a block (which can happen when an inline frame
11521     // wraps some of its content in an anonymous block; see
11522     // ConstructInline)
11523 
11524     // NOTE: We used to get the FloatContainingBlock here, but it was often
11525     // wrong. GetIBContainingBlock works much better and provides the correct
11526     // container in all cases so GetFloatContainingBlock(aFrame) has been
11527     // removed
11528 
11529     // And get the containingBlock's content
11530     if (nsIContent* blockContent = containingBlock->GetContent()) {
11531 #ifdef DEBUG
11532       if (gNoisyContentUpdates) {
11533         printf("  ==> blockContent=%p\n", blockContent);
11534       }
11535 #endif
11536       RecreateFramesForContent(blockContent, InsertionKind::Async);
11537       return;
11538     }
11539   }
11540 
11541   // If we get here, we're screwed!
11542   RecreateFramesForContent(mPresShell->GetDocument()->GetRootElement(),
11543                            InsertionKind::Async);
11544 }
11545 
GenerateChildFrames(nsContainerFrame * aFrame)11546 void nsCSSFrameConstructor::GenerateChildFrames(nsContainerFrame* aFrame) {
11547   {
11548     nsAutoScriptBlocker scriptBlocker;
11549     nsFrameList childList;
11550     nsFrameConstructorState state(mPresShell, nullptr, nullptr, nullptr);
11551     ProcessChildren(state, aFrame->GetContent(), aFrame->Style(), aFrame, false,
11552                     childList, false);
11553 
11554     aFrame->SetInitialChildList(kPrincipalList, childList);
11555   }
11556 
11557 #ifdef ACCESSIBILITY
11558   if (nsAccessibilityService* accService =
11559           PresShell::GetAccessibilityService()) {
11560     if (nsIContent* child = aFrame->GetContent()->GetFirstChild()) {
11561       accService->ContentRangeInserted(mPresShell, child, nullptr);
11562     }
11563   }
11564 #endif
11565 }
11566 
11567 //////////////////////////////////////////////////////////
11568 // nsCSSFrameConstructor::FrameConstructionItem methods //
11569 //////////////////////////////////////////////////////////
IsWhitespace(nsFrameConstructorState & aState) const11570 bool nsCSSFrameConstructor::FrameConstructionItem::IsWhitespace(
11571     nsFrameConstructorState& aState) const {
11572   MOZ_ASSERT(aState.mCreatingExtraFrames || !mContent->GetPrimaryFrame(),
11573              "How did that happen?");
11574   if (!mIsText) {
11575     return false;
11576   }
11577   mContent->SetFlags(NS_CREATE_FRAME_IF_NON_WHITESPACE |
11578                      NS_REFRAME_IF_WHITESPACE);
11579   return mContent->TextIsOnlyWhitespace();
11580 }
11581 
11582 //////////////////////////////////////////////////////////////
11583 // nsCSSFrameConstructor::FrameConstructionItemList methods //
11584 //////////////////////////////////////////////////////////////
AdjustCountsForItem(FrameConstructionItem * aItem,int32_t aDelta)11585 void nsCSSFrameConstructor::FrameConstructionItemList::AdjustCountsForItem(
11586     FrameConstructionItem* aItem, int32_t aDelta) {
11587   MOZ_ASSERT(aDelta == 1 || aDelta == -1, "Unexpected delta");
11588   mItemCount += aDelta;
11589   if (aItem->mIsAllInline) {
11590     mInlineCount += aDelta;
11591   }
11592   if (aItem->mIsBlock) {
11593     mBlockCount += aDelta;
11594   }
11595   if (aItem->mIsLineParticipant) {
11596     mLineParticipantCount += aDelta;
11597   }
11598   mDesiredParentCounts[aItem->DesiredParentType()] += aDelta;
11599 }
11600 
11601 ////////////////////////////////////////////////////////////////////////
11602 // nsCSSFrameConstructor::FrameConstructionItemList::Iterator methods //
11603 ////////////////////////////////////////////////////////////////////////
11604 inline bool nsCSSFrameConstructor::FrameConstructionItemList::Iterator::
SkipItemsWantingParentType(ParentType aParentType)11605     SkipItemsWantingParentType(ParentType aParentType) {
11606   MOZ_ASSERT(!IsDone(), "Shouldn't be done yet");
11607   while (item().DesiredParentType() == aParentType) {
11608     Next();
11609     if (IsDone()) {
11610       return true;
11611     }
11612   }
11613   return false;
11614 }
11615 
11616 inline bool nsCSSFrameConstructor::FrameConstructionItemList::Iterator::
SkipItemsNotWantingParentType(ParentType aParentType)11617     SkipItemsNotWantingParentType(ParentType aParentType) {
11618   MOZ_ASSERT(!IsDone(), "Shouldn't be done yet");
11619   while (item().DesiredParentType() != aParentType) {
11620     Next();
11621     if (IsDone()) {
11622       return true;
11623     }
11624   }
11625   return false;
11626 }
11627 
11628 // Note: we implement -webkit-{inline-}box (and optionally -moz-{inline-}box)
11629 // using nsFlexContainerFrame, but we use different rules for what gets wrapped
11630 // in an anonymous flex item.
NeedsAnonFlexOrGridItem(const nsFrameConstructorState & aState,bool aIsLegacyBox)11631 bool nsCSSFrameConstructor::FrameConstructionItem::NeedsAnonFlexOrGridItem(
11632     const nsFrameConstructorState& aState, bool aIsLegacyBox) {
11633   if (mFCData->mBits & FCDATA_IS_LINE_PARTICIPANT) {
11634     // This will be an inline non-replaced box.
11635     return true;
11636   }
11637 
11638   if (aIsLegacyBox) {
11639     if (mComputedStyle->StyleDisplay()->IsInlineOutsideStyle()) {
11640       // In an emulated legacy box, all inline-level content gets wrapped in an
11641       // anonymous flex item.
11642       return true;
11643     }
11644     if (mIsPopup ||
11645         (!(mFCData->mBits & FCDATA_DISALLOW_OUT_OF_FLOW) &&
11646          aState.GetGeometricParent(*mComputedStyle->StyleDisplay(), nullptr))) {
11647       // We're abspos or fixedpos (or a XUL popup), which means we'll spawn a
11648       // placeholder which (because our container is an emulated legacy box)
11649       // we'll need to wrap in an anonymous flex item.  So, we just treat
11650       // _this_ frame as if _it_ needs to be wrapped in an anonymous flex item,
11651       // and then when we spawn the placeholder, it'll end up in the right
11652       // spot.
11653       return true;
11654     }
11655   }
11656 
11657   return false;
11658 }
11659 
11660 inline bool nsCSSFrameConstructor::FrameConstructionItemList::Iterator::
SkipItemsThatNeedAnonFlexOrGridItem(const nsFrameConstructorState & aState,bool aIsLegacyBox)11661     SkipItemsThatNeedAnonFlexOrGridItem(const nsFrameConstructorState& aState,
11662                                         bool aIsLegacyBox) {
11663   MOZ_ASSERT(!IsDone(), "Shouldn't be done yet");
11664   while (item().NeedsAnonFlexOrGridItem(aState, aIsLegacyBox)) {
11665     Next();
11666     if (IsDone()) {
11667       return true;
11668     }
11669   }
11670   return false;
11671 }
11672 
11673 inline bool nsCSSFrameConstructor::FrameConstructionItemList::Iterator::
SkipItemsThatDontNeedAnonFlexOrGridItem(const nsFrameConstructorState & aState,bool aIsLegacyBox)11674     SkipItemsThatDontNeedAnonFlexOrGridItem(
11675         const nsFrameConstructorState& aState, bool aIsLegacyBox) {
11676   MOZ_ASSERT(!IsDone(), "Shouldn't be done yet");
11677   while (!(item().NeedsAnonFlexOrGridItem(aState, aIsLegacyBox))) {
11678     Next();
11679     if (IsDone()) {
11680       return true;
11681     }
11682   }
11683   return false;
11684 }
11685 
11686 inline bool nsCSSFrameConstructor::FrameConstructionItemList::Iterator::
SkipItemsNotWantingRubyParent()11687     SkipItemsNotWantingRubyParent() {
11688   MOZ_ASSERT(!IsDone(), "Shouldn't be done yet");
11689   while (!IsRubyParentType(item().DesiredParentType())) {
11690     Next();
11691     if (IsDone()) {
11692       return true;
11693     }
11694   }
11695   return false;
11696 }
11697 
11698 inline bool
SkipWhitespace(nsFrameConstructorState & aState)11699 nsCSSFrameConstructor::FrameConstructionItemList::Iterator::SkipWhitespace(
11700     nsFrameConstructorState& aState) {
11701   MOZ_ASSERT(!IsDone(), "Shouldn't be done yet");
11702   MOZ_ASSERT(item().IsWhitespace(aState), "Not pointing to whitespace?");
11703   do {
11704     Next();
11705     if (IsDone()) {
11706       return true;
11707     }
11708   } while (item().IsWhitespace(aState));
11709 
11710   return false;
11711 }
11712 
11713 void nsCSSFrameConstructor::FrameConstructionItemList::Iterator::
AppendItemToList(FrameConstructionItemList & aTargetList)11714     AppendItemToList(FrameConstructionItemList& aTargetList) {
11715   NS_ASSERTION(&aTargetList != &mList, "Unexpected call");
11716   MOZ_ASSERT(!IsDone(), "should not be done");
11717 
11718   FrameConstructionItem* item = mCurrent;
11719   Next();
11720   item->remove();
11721   aTargetList.mItems.insertBack(item);
11722 
11723   mList.AdjustCountsForItem(item, -1);
11724   aTargetList.AdjustCountsForItem(item, 1);
11725 }
11726 
11727 void nsCSSFrameConstructor::FrameConstructionItemList::Iterator::
AppendItemsToList(nsCSSFrameConstructor * aFCtor,const Iterator & aEnd,FrameConstructionItemList & aTargetList)11728     AppendItemsToList(nsCSSFrameConstructor* aFCtor, const Iterator& aEnd,
11729                       FrameConstructionItemList& aTargetList) {
11730   NS_ASSERTION(&aTargetList != &mList, "Unexpected call");
11731   MOZ_ASSERT(&mList == &aEnd.mList, "End iterator for some other list?");
11732 
11733   // We can't just move our guts to the other list if it already has
11734   // some information or if we're not moving our entire list.
11735   if (!AtStart() || !aEnd.IsDone() || !aTargetList.IsEmpty()) {
11736     do {
11737       AppendItemToList(aTargetList);
11738     } while (*this != aEnd);
11739     return;
11740   }
11741 
11742   // Move our entire list of items into the empty target list.
11743   aTargetList.mItems = std::move(mList.mItems);
11744 
11745   // Copy over the various counters
11746   aTargetList.mInlineCount = mList.mInlineCount;
11747   aTargetList.mBlockCount = mList.mBlockCount;
11748   aTargetList.mLineParticipantCount = mList.mLineParticipantCount;
11749   aTargetList.mItemCount = mList.mItemCount;
11750   memcpy(aTargetList.mDesiredParentCounts, mList.mDesiredParentCounts,
11751          sizeof(aTargetList.mDesiredParentCounts));
11752 
11753   // reset mList
11754   mList.Reset(aFCtor);
11755 
11756   // Point ourselves to aEnd, as advertised
11757   SetToEnd();
11758   MOZ_ASSERT(*this == aEnd, "How did that happen?");
11759 }
11760 
InsertItem(FrameConstructionItem * aItem)11761 void nsCSSFrameConstructor::FrameConstructionItemList::Iterator::InsertItem(
11762     FrameConstructionItem* aItem) {
11763   if (IsDone()) {
11764     mList.mItems.insertBack(aItem);
11765   } else {
11766     // Just insert the item before us.  There's no magic here.
11767     mCurrent->setPrevious(aItem);
11768   }
11769   mList.AdjustCountsForItem(aItem, 1);
11770 
11771   MOZ_ASSERT(aItem->getNext() == mCurrent, "How did that happen?");
11772 }
11773 
DeleteItemsTo(nsCSSFrameConstructor * aFCtor,const Iterator & aEnd)11774 void nsCSSFrameConstructor::FrameConstructionItemList::Iterator::DeleteItemsTo(
11775     nsCSSFrameConstructor* aFCtor, const Iterator& aEnd) {
11776   MOZ_ASSERT(&mList == &aEnd.mList, "End iterator for some other list?");
11777   MOZ_ASSERT(*this != aEnd, "Shouldn't be at aEnd yet");
11778 
11779   do {
11780     NS_ASSERTION(!IsDone(), "Ran off end of list?");
11781     FrameConstructionItem* item = mCurrent;
11782     Next();
11783     item->remove();
11784     mList.AdjustCountsForItem(item, -1);
11785     item->Delete(aFCtor);
11786   } while (*this != aEnd);
11787 }
11788 
QuotesDirty()11789 void nsCSSFrameConstructor::QuotesDirty() {
11790   mQuotesDirty = true;
11791   mPresShell->SetNeedLayoutFlush();
11792 }
11793 
CountersDirty()11794 void nsCSSFrameConstructor::CountersDirty() {
11795   mCountersDirty = true;
11796   mPresShell->SetNeedLayoutFlush();
11797 }
11798 
AllocateFCItem()11799 void* nsCSSFrameConstructor::AllocateFCItem() {
11800   void* item;
11801   if (mFirstFreeFCItem) {
11802     item = mFirstFreeFCItem;
11803     mFirstFreeFCItem = mFirstFreeFCItem->mNext;
11804   } else {
11805     item = mFCItemPool.Allocate(sizeof(FrameConstructionItem));
11806   }
11807   ++mFCItemsInUse;
11808   return item;
11809 }
11810 
FreeFCItem(FrameConstructionItem * aItem)11811 void nsCSSFrameConstructor::FreeFCItem(FrameConstructionItem* aItem) {
11812   MOZ_ASSERT(mFCItemsInUse != 0);
11813   if (--mFCItemsInUse == 0) {
11814     // The arena is now unused - clear it but retain one chunk.
11815     mFirstFreeFCItem = nullptr;
11816     mFCItemPool.Clear();
11817   } else {
11818     // Prepend it to the list of free items.
11819     FreeFCItemLink* item = reinterpret_cast<FreeFCItemLink*>(aItem);
11820     item->mNext = mFirstFreeFCItem;
11821     mFirstFreeFCItem = item;
11822   }
11823 }
11824 
AddSizeOfIncludingThis(nsWindowSizes & aSizes) const11825 void nsCSSFrameConstructor::AddSizeOfIncludingThis(
11826     nsWindowSizes& aSizes) const {
11827   if (nsIFrame* rootFrame = GetRootFrame()) {
11828     rootFrame->AddSizeOfExcludingThisForTree(aSizes);
11829     if (RetainedDisplayListBuilder* builder =
11830             rootFrame->GetProperty(RetainedDisplayListBuilder::Cached())) {
11831       builder->AddSizeOfIncludingThis(aSizes);
11832     }
11833   }
11834 
11835   // This must be done after measuring from the frame tree, since frame
11836   // manager will measure sizes of staled computed values and style
11837   // structs, which only make sense after we know what are being used.
11838   nsFrameManager::AddSizeOfIncludingThis(aSizes);
11839 
11840   // Measurement of the following members may be added later if DMD finds it
11841   // is worthwhile:
11842   // - mFCItemPool
11843   // - mQuoteList
11844   // - mCounterManager
11845 }
11846