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