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/DebugOnly.h"
16 #include "mozilla/ErrorResult.h"
17 #include "mozilla/dom/HTMLDetailsElement.h"
18 #include "mozilla/dom/HTMLSelectElement.h"
19 #include "mozilla/dom/HTMLSummaryElement.h"
20 #include "mozilla/EventStates.h"
21 #include "mozilla/Likely.h"
22 #include "mozilla/LinkedList.h"
23 #include "mozilla/MemoryReporting.h"
24 #include "mozilla/PresShell.h"
25 #include "mozilla/ServoBindings.h"
26 #include "nsAbsoluteContainingBlock.h"
27 #include "nsCSSPseudoElements.h"
28 #include "nsAtom.h"
29 #include "nsIFrameInlines.h"
30 #include "nsGkAtoms.h"
31 #include "nsPresContext.h"
32 #include "nsIDocument.h"
33 #include "nsIDocumentInlines.h"
34 #include "nsTableFrame.h"
35 #include "nsTableColFrame.h"
36 #include "nsTableRowFrame.h"
37 #include "nsTableCellFrame.h"
38 #include "nsHTMLParts.h"
39 #include "nsIPresShell.h"
40 #include "nsUnicharUtils.h"
41 #include "mozilla/StyleSetHandle.h"
42 #include "mozilla/StyleSetHandleInlines.h"
43 #include "nsViewManager.h"
44 #include "nsStyleConsts.h"
45 #ifdef MOZ_XUL
46 #include "nsXULElement.h"
47 #include "mozilla/dom/BoxObject.h"
48 #endif // MOZ_XUL
49 #include "nsContainerFrame.h"
50 #include "nsNameSpaceManager.h"
51 #include "nsIComboboxControlFrame.h"
52 #include "nsComboboxControlFrame.h"
53 #include "nsIListControlFrame.h"
54 #include "nsIDOMCharacterData.h"
55 #include "nsPlaceholderFrame.h"
56 #include "nsTableRowGroupFrame.h"
57 #include "nsIFormControl.h"
58 #include "nsCSSAnonBoxes.h"
59 #include "nsTextFragment.h"
60 #include "nsIAnonymousContentCreator.h"
61 #include "nsBindingManager.h"
62 #include "nsXBLBinding.h"
63 #include "nsContentUtils.h"
64 #include "nsIScriptError.h"
65 #ifdef XP_MACOSX
66 #include "nsIDocShell.h"
67 #endif
68 #include "ChildIterator.h"
69 #include "nsError.h"
70 #include "nsLayoutUtils.h"
71 #include "nsAutoPtr.h"
72 #include "nsBoxFrame.h"
73 #include "nsBoxLayout.h"
74 #include "nsFlexContainerFrame.h"
75 #include "nsGridContainerFrame.h"
76 #include "RubyUtils.h"
77 #include "nsRubyFrame.h"
78 #include "nsRubyBaseFrame.h"
79 #include "nsRubyBaseContainerFrame.h"
80 #include "nsRubyTextFrame.h"
81 #include "nsRubyTextContainerFrame.h"
82 #include "nsImageFrame.h"
83 #include "nsIObjectLoadingContent.h"
84 #include "nsTArray.h"
85 #include "nsGenericDOMDataNode.h"
86 #include "mozilla/dom/Element.h"
87 #include "mozilla/dom/ElementInlines.h"
88 #include "nsAutoLayoutPhase.h"
89 #include "nsStyleContextInlines.h"
90 #include "nsStyleStructInlines.h"
91 #include "nsPageContentFrame.h"
92 #include "mozilla/RestyleManager.h"
93 #include "mozilla/RestyleManagerInlines.h"
94 #include "mozilla/StylePrefs.h"
95 #include "StickyScrollContainer.h"
96 #include "nsFieldSetFrame.h"
97 #include "nsInlineFrame.h"
98 #include "nsBlockFrame.h"
99 #include "nsCanvasFrame.h"
100 #include "nsFirstLetterFrame.h"
101 #include "nsGfxScrollFrame.h"
102 #include "nsPageFrame.h"
103 #include "nsSimplePageSequenceFrame.h"
104 #include "nsTableWrapperFrame.h"
105 #include "nsIScrollableFrame.h"
106 #include "nsBackdropFrame.h"
107 #include "nsTransitionManager.h"
108 #include "DetailsFrame.h"
109 #include "nsThemeConstants.h"
110
111 #ifdef MOZ_XUL
112 #include "nsIRootBox.h"
113 #endif
114 #ifdef ACCESSIBILITY
115 #include "nsAccessibilityService.h"
116 #endif
117
118 #include "nsXBLService.h"
119
120 #undef NOISY_FIRST_LETTER
121
122 #include "nsMathMLParts.h"
123 #include "mozilla/dom/SVGTests.h"
124 #include "nsSVGUtils.h"
125
126 #include "nsRefreshDriver.h"
127 #include "nsRuleProcessorData.h"
128 #include "nsTextNode.h"
129 #include "ActiveLayerTracker.h"
130 #include "nsIPresShellInlines.h"
131
132 using namespace mozilla;
133 using namespace mozilla::dom;
134
135 // An alias for convenience.
136 static const nsIFrame::ChildListID kPrincipalList = nsIFrame::kPrincipalList;
137
138 nsIFrame* NS_NewHTMLCanvasFrame(nsIPresShell* aPresShell,
139 nsStyleContext* aContext);
140
141 nsIFrame* NS_NewHTMLVideoFrame(nsIPresShell* aPresShell,
142 nsStyleContext* aContext);
143
144 nsContainerFrame* NS_NewSVGOuterSVGFrame(nsIPresShell* aPresShell,
145 nsStyleContext* aContext);
146 nsContainerFrame* NS_NewSVGOuterSVGAnonChildFrame(nsIPresShell* aPresShell,
147 nsStyleContext* aContext);
148 nsIFrame* NS_NewSVGInnerSVGFrame(nsIPresShell* aPresShell,
149 nsStyleContext* aContext);
150 nsIFrame* NS_NewSVGGeometryFrame(nsIPresShell* aPresShell,
151 nsStyleContext* aContext);
152 nsIFrame* NS_NewSVGGFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
153 nsIFrame* NS_NewSVGGenericContainerFrame(nsIPresShell* aPresShell,
154 nsStyleContext* aContext);
155 nsContainerFrame* NS_NewSVGForeignObjectFrame(nsIPresShell* aPresShell,
156 nsStyleContext* aContext);
157 nsIFrame* NS_NewSVGAFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
158 nsIFrame* NS_NewSVGSwitchFrame(nsIPresShell* aPresShell,
159 nsStyleContext* aContext);
160 nsIFrame* NS_NewSVGSymbolFrame(nsIPresShell* aPresShell,
161 nsStyleContext* aContext);
162 nsIFrame* NS_NewSVGTextFrame(nsIPresShell* aPresShell,
163 nsStyleContext* aContext);
164 nsIFrame* NS_NewSVGContainerFrame(nsIPresShell* aPresShell,
165 nsStyleContext* aContext);
166 nsIFrame* NS_NewSVGUseFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
167 nsIFrame* NS_NewSVGViewFrame(nsIPresShell* aPresShell,
168 nsStyleContext* aContext);
169 extern nsIFrame* NS_NewSVGLinearGradientFrame(nsIPresShell* aPresShell,
170 nsStyleContext* aContext);
171 extern nsIFrame* NS_NewSVGRadialGradientFrame(nsIPresShell* aPresShell,
172 nsStyleContext* aContext);
173 extern nsIFrame* NS_NewSVGStopFrame(nsIPresShell* aPresShell,
174 nsStyleContext* aContext);
175 nsContainerFrame* NS_NewSVGMarkerFrame(nsIPresShell* aPresShell,
176 nsStyleContext* aContext);
177 nsContainerFrame* NS_NewSVGMarkerAnonChildFrame(nsIPresShell* aPresShell,
178 nsStyleContext* aContext);
179 extern nsIFrame* NS_NewSVGImageFrame(nsIPresShell* aPresShell,
180 nsStyleContext* aContext);
181 nsIFrame* NS_NewSVGClipPathFrame(nsIPresShell* aPresShell,
182 nsStyleContext* aContext);
183 nsIFrame* NS_NewSVGFilterFrame(nsIPresShell* aPresShell,
184 nsStyleContext* aContext);
185 nsIFrame* NS_NewSVGPatternFrame(nsIPresShell* aPresShell,
186 nsStyleContext* aContext);
187 nsIFrame* NS_NewSVGMaskFrame(nsIPresShell* aPresShell,
188 nsStyleContext* aContext);
189 nsIFrame* NS_NewSVGFEContainerFrame(nsIPresShell* aPresShell,
190 nsStyleContext* aContext);
191 nsIFrame* NS_NewSVGFELeafFrame(nsIPresShell* aPresShell,
192 nsStyleContext* aContext);
193 nsIFrame* NS_NewSVGFEImageFrame(nsIPresShell* aPresShell,
194 nsStyleContext* aContext);
195 nsIFrame* NS_NewSVGFEUnstyledLeafFrame(nsIPresShell* aPresShell,
196 nsStyleContext* aContext);
197
198 #include "mozilla/dom/NodeInfo.h"
199 #include "prenv.h"
200 #include "nsNodeInfoManager.h"
201 #include "nsContentCreatorFunctions.h"
202
203 #ifdef DEBUG
204 // Set the environment variable GECKO_FRAMECTOR_DEBUG_FLAGS to one or
205 // more of the following flags (comma separated) for handy debug
206 // output.
207 static bool gNoisyContentUpdates = false;
208 static bool gReallyNoisyContentUpdates = false;
209 static bool gNoisyInlineConstruction = false;
210
211 struct FrameCtorDebugFlags {
212 const char* name;
213 bool* on;
214 };
215
216 static FrameCtorDebugFlags gFlags[] = {
217 {"content-updates", &gNoisyContentUpdates},
218 {"really-noisy-content-updates", &gReallyNoisyContentUpdates},
219 {"noisy-inline", &gNoisyInlineConstruction}};
220
221 #define NUM_DEBUG_FLAGS (sizeof(gFlags) / sizeof(gFlags[0]))
222 #endif
223
224 #ifdef MOZ_XUL
225 #include "nsMenuFrame.h"
226 #include "nsPopupSetFrame.h"
227 #include "nsTreeColFrame.h"
228 #include "nsIBoxObject.h"
229 #include "nsPIListBoxObject.h"
230 #include "nsListBoxBodyFrame.h"
231 #include "nsListItemFrame.h"
232 #include "nsXULLabelFrame.h"
233
234 //------------------------------------------------------------------
235
236 nsIFrame* NS_NewAutoRepeatBoxFrame(nsIPresShell* aPresShell,
237 nsStyleContext* aContext);
238
239 nsContainerFrame* NS_NewRootBoxFrame(nsIPresShell* aPresShell,
240 nsStyleContext* aContext);
241
242 nsContainerFrame* NS_NewDocElementBoxFrame(nsIPresShell* aPresShell,
243 nsStyleContext* aContext);
244
245 nsIFrame* NS_NewDeckFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
246
247 nsIFrame* NS_NewLeafBoxFrame(nsIPresShell* aPresShell,
248 nsStyleContext* aContext);
249
250 nsIFrame* NS_NewStackFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
251
252 nsIFrame* NS_NewProgressMeterFrame(nsIPresShell* aPresShell,
253 nsStyleContext* aContext);
254
255 nsIFrame* NS_NewRangeFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
256
257 nsIFrame* NS_NewImageBoxFrame(nsIPresShell* aPresShell,
258 nsStyleContext* aContext);
259
260 nsIFrame* NS_NewTextBoxFrame(nsIPresShell* aPresShell,
261 nsStyleContext* aContext);
262
263 nsIFrame* NS_NewGroupBoxFrame(nsIPresShell* aPresShell,
264 nsStyleContext* aContext);
265
266 nsIFrame* NS_NewButtonBoxFrame(nsIPresShell* aPresShell,
267 nsStyleContext* aContext);
268
269 nsIFrame* NS_NewSplitterFrame(nsIPresShell* aPresShell,
270 nsStyleContext* aContext);
271
272 nsIFrame* NS_NewMenuPopupFrame(nsIPresShell* aPresShell,
273 nsStyleContext* aContext);
274
275 nsIFrame* NS_NewPopupSetFrame(nsIPresShell* aPresShell,
276 nsStyleContext* aContext);
277
278 nsIFrame* NS_NewMenuFrame(nsIPresShell* aPresShell, nsStyleContext* aContext,
279 uint32_t aFlags);
280
281 nsIFrame* NS_NewMenuBarFrame(nsIPresShell* aPresShell,
282 nsStyleContext* aContext);
283
284 nsIFrame* NS_NewTreeBodyFrame(nsIPresShell* aPresShell,
285 nsStyleContext* aContext);
286
287 // grid
288 nsresult NS_NewGridLayout2(nsIPresShell* aPresShell, nsBoxLayout** aNewLayout);
289 nsIFrame* NS_NewGridRowLeafFrame(nsIPresShell* aPresShell,
290 nsStyleContext* aContext);
291 nsIFrame* NS_NewGridRowGroupFrame(nsIPresShell* aPresShell,
292 nsStyleContext* aContext);
293
294 // end grid
295
296 nsIFrame* NS_NewTitleBarFrame(nsIPresShell* aPresShell,
297 nsStyleContext* aContext);
298
299 nsIFrame* NS_NewResizerFrame(nsIPresShell* aPresShell,
300 nsStyleContext* aContext);
301
302 #endif
303
304 nsHTMLScrollFrame* NS_NewHTMLScrollFrame(nsIPresShell* aPresShell,
305 nsStyleContext* aContext,
306 bool aIsRoot);
307
308 nsXULScrollFrame* NS_NewXULScrollFrame(nsIPresShell* aPresShell,
309 nsStyleContext* aContext, bool aIsRoot,
310 bool aClipAllDescendants);
311
312 nsIFrame* NS_NewSliderFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
313
314 nsIFrame* NS_NewScrollbarFrame(nsIPresShell* aPresShell,
315 nsStyleContext* aContext);
316
317 nsIFrame* NS_NewScrollbarButtonFrame(nsIPresShell* aPresShell,
318 nsStyleContext* aContext);
319
320 #ifdef NOISY_FINDFRAME
321 static int32_t FFWC_totalCount = 0;
322 static int32_t FFWC_doLoop = 0;
323 static int32_t FFWC_doSibling = 0;
324 static int32_t FFWC_recursions = 0;
325 static int32_t FFWC_nextInFlows = 0;
326 #endif
327
328 #ifdef MOZ_OLD_STYLE
329
330 // Wrapper class to handle stack-construction a TreeMatchContext only if we're
331 // using the Gecko style system.
332 class MOZ_STACK_CLASS TreeMatchContextHolder {
333 public:
TreeMatchContextHolder(nsIDocument * aDocument)334 explicit TreeMatchContextHolder(nsIDocument* aDocument) {
335 if (!aDocument->IsStyledByServo()) {
336 mMaybeTreeMatchContext.emplace(aDocument,
337 TreeMatchContext::ForFrameConstruction);
338 }
339 }
340
Exists() const341 bool Exists() const { return mMaybeTreeMatchContext.isSome(); }
operator TreeMatchContext*()342 operator TreeMatchContext*() { return mMaybeTreeMatchContext.ptrOr(nullptr); }
343
operator ->()344 TreeMatchContext* operator->() {
345 MOZ_ASSERT(mMaybeTreeMatchContext.isSome());
346 return mMaybeTreeMatchContext.ptr();
347 }
348
349 private:
350 Maybe<TreeMatchContext> mMaybeTreeMatchContext;
351 };
352
353 #else
354
355 // Define this dummy class so there are fewer call sites to change when the old
356 // style system code is compiled out.
357 class TreeMatchContextHolder {
358 public:
TreeMatchContextHolder(nsIDocument * aDocument)359 explicit TreeMatchContextHolder(nsIDocument* aDocument) {}
Exists() const360 bool Exists() const { return false; }
operator TreeMatchContext*()361 operator TreeMatchContext*() { return nullptr; }
operator ->()362 TreeMatchContext* operator->() { MOZ_CRASH("old style system disabled"); }
363 };
364
365 #endif
366
367 // Returns true if aFrame is an anonymous flex/grid item.
IsAnonymousFlexOrGridItem(const nsIFrame * aFrame)368 static inline bool IsAnonymousFlexOrGridItem(const nsIFrame* aFrame) {
369 const nsAtom* pseudoType = aFrame->StyleContext()->GetPseudo();
370 return pseudoType == nsCSSAnonBoxes::anonymousFlexItem ||
371 pseudoType == nsCSSAnonBoxes::anonymousGridItem;
372 }
373
374 // Returns true if aFrame is a flex/grid container.
IsFlexOrGridContainer(const nsIFrame * aFrame)375 static inline bool IsFlexOrGridContainer(const nsIFrame* aFrame) {
376 const LayoutFrameType t = aFrame->Type();
377 return t == LayoutFrameType::FlexContainer ||
378 t == LayoutFrameType::GridContainer;
379 }
380
381 // Returns true IFF the given nsIFrame is a nsFlexContainerFrame and
382 // represents a -webkit-{inline-}box or -moz-{inline-}box container.
IsFlexContainerForLegacyBox(const nsIFrame * aFrame)383 static inline bool IsFlexContainerForLegacyBox(const nsIFrame* aFrame) {
384 return aFrame->IsFlexContainerFrame() &&
385 aFrame->HasAnyStateBits(NS_STATE_FLEX_IS_EMULATING_LEGACY_BOX);
386 }
387
388 #if DEBUG
AssertAnonymousFlexOrGridItemParent(const nsIFrame * aChild,const nsIFrame * aParent)389 static void AssertAnonymousFlexOrGridItemParent(const nsIFrame* aChild,
390 const nsIFrame* aParent) {
391 MOZ_ASSERT(IsAnonymousFlexOrGridItem(aChild),
392 "expected an anonymous flex or grid item child frame");
393 MOZ_ASSERT(aParent, "expected a parent frame");
394 const nsAtom* pseudoType = aChild->StyleContext()->GetPseudo();
395 if (pseudoType == nsCSSAnonBoxes::anonymousFlexItem) {
396 MOZ_ASSERT(aParent->IsFlexContainerFrame(),
397 "anonymous flex items should only exist as children "
398 "of flex container frames");
399 } else {
400 MOZ_ASSERT(aParent->IsGridContainerFrame(),
401 "anonymous grid items should only exist as children "
402 "of grid container frames");
403 }
404 }
405 #else
406 #define AssertAnonymousFlexOrGridItemParent(x, y) \
407 do { /* nothing */ \
408 } while (0)
409 #endif
410
GetFieldSetBlockFrame(nsIFrame * aFieldsetFrame)411 static inline nsContainerFrame* GetFieldSetBlockFrame(
412 nsIFrame* aFieldsetFrame) {
413 // Depends on the fieldset child frame order - see ConstructFieldSetFrame()
414 // below.
415 nsIFrame* firstChild = aFieldsetFrame->PrincipalChildList().FirstChild();
416 nsIFrame* inner = firstChild && firstChild->GetNextSibling()
417 ? firstChild->GetNextSibling()
418 : firstChild;
419 return inner ? inner->GetContentInsertionFrame() : nullptr;
420 }
421
422 #define FCDATA_DECL(_flags, _func) \
423 { _flags, {(FrameCreationFunc)_func}, nullptr, nullptr }
424 #define FCDATA_WITH_WRAPPING_BLOCK(_flags, _func, _anon_box) \
425 { \
426 _flags | FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS, \
427 {(FrameCreationFunc)_func}, nullptr, &_anon_box \
428 }
429
430 #define UNREACHABLE_FCDATA() \
431 { 0, {(FrameCreationFunc) nullptr}, nullptr, nullptr }
432 //----------------------------------------------------------------------
433
434 /**
435 * True if aFrame is an actual inline frame in the sense of non-replaced
436 * display:inline CSS boxes. In other words, it can be affected by {ib}
437 * splitting and can contain first-letter frames. Basically, this is either an
438 * inline frame (positioned or otherwise) or an line frame (this last because
439 * it can contain first-letter and because inserting blocks in the middle of it
440 * needs to terminate it).
441 */
IsInlineFrame(const nsIFrame * aFrame)442 static bool IsInlineFrame(const nsIFrame* aFrame) {
443 return aFrame->IsFrameOfType(nsIFrame::eLineParticipant);
444 }
445
446 /**
447 * True if aFrame is an instance of an SVG frame class or is an inline/block
448 * frame being used for SVG text.
449 */
IsFrameForSVG(const nsIFrame * aFrame)450 static bool IsFrameForSVG(const nsIFrame* aFrame) {
451 return aFrame->IsFrameOfType(nsIFrame::eSVG) ||
452 nsSVGUtils::IsInSVGTextSubtree(aFrame);
453 }
454
455 /**
456 * Returns true iff aFrame explicitly prevents its descendants from floating
457 * (at least, down to the level of descendants which themselves are
458 * float-containing blocks -- those will manage the floating status of any
459 * lower-level descendents inside them, of course).
460 */
ShouldSuppressFloatingOfDescendants(nsIFrame * aFrame)461 static bool ShouldSuppressFloatingOfDescendants(nsIFrame* aFrame) {
462 return aFrame->IsFrameOfType(nsIFrame::eMathML) || aFrame->IsXULBoxFrame() ||
463 ::IsFlexOrGridContainer(aFrame);
464 }
465
466 /**
467 * If any children require a block parent, return the first such child.
468 * Otherwise return null.
469 */
AnyKidsNeedBlockParent(nsIFrame * aFrameList)470 static nsIContent* AnyKidsNeedBlockParent(nsIFrame* aFrameList) {
471 for (nsIFrame* k = aFrameList; k; k = k->GetNextSibling()) {
472 // Line participants, such as text and inline frames, can't be
473 // directly inside a XUL box; they must be wrapped in an
474 // intermediate block.
475 if (k->IsFrameOfType(nsIFrame::eLineParticipant)) {
476 return k->GetContent();
477 }
478 }
479 return nullptr;
480 }
481
482 // Reparent a frame into a wrapper frame that is a child of its old parent.
ReparentFrame(RestyleManager * aRestyleManager,nsContainerFrame * aNewParentFrame,nsIFrame * aFrame,bool aForceStyleReparent)483 static void ReparentFrame(RestyleManager* aRestyleManager,
484 nsContainerFrame* aNewParentFrame, nsIFrame* aFrame,
485 bool aForceStyleReparent) {
486 aFrame->SetParent(aNewParentFrame);
487 // We reparent frames for two reasons: to put them inside ::first-line, and to
488 // put them inside some wrapper anonymous boxes.
489 //
490 // The latter shouldn't affect any styles in practice, so only needs style
491 // context reparenting in the Gecko backend, to make our style context tree
492 // assertions happy. The former passes aForceStyleReparent == true.
493 if (aForceStyleReparent || aRestyleManager->IsGecko()) {
494 aRestyleManager->ReparentStyleContext(aFrame);
495 }
496 }
497
ReparentFrames(nsCSSFrameConstructor * aFrameConstructor,nsContainerFrame * aNewParentFrame,const nsFrameList & aFrameList,bool aForceStyleReparent)498 static void ReparentFrames(nsCSSFrameConstructor* aFrameConstructor,
499 nsContainerFrame* aNewParentFrame,
500 const nsFrameList& aFrameList,
501 bool aForceStyleReparent) {
502 RestyleManager* restyleManager = aFrameConstructor->RestyleManager();
503 for (nsIFrame* f : aFrameList) {
504 ReparentFrame(restyleManager, aNewParentFrame, f, aForceStyleReparent);
505 }
506 }
507
508 //----------------------------------------------------------------------
509 //
510 // When inline frames get weird and have block frames in them, we
511 // annotate them to help us respond to incremental content changes
512 // more easily.
513
IsFramePartOfIBSplit(nsIFrame * aFrame)514 static inline bool IsFramePartOfIBSplit(nsIFrame* aFrame) {
515 bool result = (aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) != 0;
516 MOZ_ASSERT(!result || static_cast<nsBlockFrame*>(do_QueryFrame(aFrame)) ||
517 static_cast<nsInlineFrame*>(do_QueryFrame(aFrame)),
518 "only block/inline frames can have NS_FRAME_PART_OF_IBSPLIT");
519 return result;
520 }
521
GetIBSplitSibling(nsIFrame * aFrame)522 static nsContainerFrame* GetIBSplitSibling(nsIFrame* aFrame) {
523 NS_PRECONDITION(IsFramePartOfIBSplit(aFrame), "Shouldn't call this");
524
525 // We only store the "ib-split sibling" annotation with the first
526 // frame in the continuation chain. Walk back to find that frame now.
527 return aFrame->FirstContinuation()->GetProperty(nsIFrame::IBSplitSibling());
528 }
529
GetIBSplitPrevSibling(nsIFrame * aFrame)530 static nsContainerFrame* GetIBSplitPrevSibling(nsIFrame* aFrame) {
531 NS_PRECONDITION(IsFramePartOfIBSplit(aFrame), "Shouldn't call this");
532
533 // We only store the ib-split sibling annotation with the first
534 // frame in the continuation chain. Walk back to find that frame now.
535 return aFrame->FirstContinuation()->GetProperty(
536 nsIFrame::IBSplitPrevSibling());
537 }
538
GetLastIBSplitSibling(nsIFrame * aFrame)539 static nsContainerFrame* GetLastIBSplitSibling(nsIFrame* aFrame) {
540 for (nsIFrame *frame = aFrame, *next;; frame = next) {
541 next = GetIBSplitSibling(frame);
542 if (!next) {
543 return static_cast<nsContainerFrame*>(frame);
544 }
545 }
546 NS_NOTREACHED("unreachable code");
547 return nullptr;
548 }
549
SetFrameIsIBSplit(nsContainerFrame * aFrame,nsContainerFrame * aIBSplitSibling)550 static void SetFrameIsIBSplit(nsContainerFrame* aFrame,
551 nsContainerFrame* aIBSplitSibling) {
552 NS_PRECONDITION(aFrame, "bad args!");
553
554 // We should be the only continuation
555 NS_ASSERTION(!aFrame->GetPrevContinuation(),
556 "assigning ib-split sibling to other than first continuation!");
557 NS_ASSERTION(!aFrame->GetNextContinuation() ||
558 IsFramePartOfIBSplit(aFrame->GetNextContinuation()),
559 "should have no non-ib-split continuations here");
560
561 // Mark the frame as ib-split.
562 aFrame->AddStateBits(NS_FRAME_PART_OF_IBSPLIT);
563
564 if (aIBSplitSibling) {
565 NS_ASSERTION(!aIBSplitSibling->GetPrevContinuation(),
566 "assigning something other than the first continuation as the "
567 "ib-split sibling");
568
569 // Store the ib-split sibling (if we were given one) with the
570 // first frame in the flow.
571 aFrame->SetProperty(nsIFrame::IBSplitSibling(), aIBSplitSibling);
572 aIBSplitSibling->SetProperty(nsIFrame::IBSplitPrevSibling(), aFrame);
573 }
574 }
575
GetIBContainingBlockFor(nsIFrame * aFrame)576 static nsIFrame* GetIBContainingBlockFor(nsIFrame* aFrame) {
577 NS_PRECONDITION(
578 IsFramePartOfIBSplit(aFrame),
579 "GetIBContainingBlockFor() should only be called on known IB frames");
580
581 // Get the first "normal" ancestor of the target frame.
582 nsIFrame* parentFrame;
583 do {
584 parentFrame = aFrame->GetParent();
585
586 if (!parentFrame) {
587 NS_ERROR("no unsplit block frame in IB hierarchy");
588 return aFrame;
589 }
590
591 // Note that we ignore non-ib-split frames which have a pseudo on their
592 // style context -- they're not the frames we're looking for! In
593 // particular, they may be hiding a real parent that _is_ in an ib-split.
594 if (!IsFramePartOfIBSplit(parentFrame) &&
595 !parentFrame->StyleContext()->GetPseudo())
596 break;
597
598 aFrame = parentFrame;
599 } while (1);
600
601 // post-conditions
602 NS_ASSERTION(parentFrame,
603 "no normal ancestor found for ib-split frame "
604 "in GetIBContainingBlockFor");
605 NS_ASSERTION(parentFrame != aFrame,
606 "parentFrame is actually the child frame - bogus reslt");
607
608 return parentFrame;
609 }
610
611 // This is a bit slow, but sometimes we need it.
ParentIsWrapperAnonBox(nsIFrame * aParent)612 static bool ParentIsWrapperAnonBox(nsIFrame* aParent) {
613 nsIFrame* maybeAnonBox = aParent;
614 if (maybeAnonBox->StyleContext()->GetPseudo() ==
615 nsCSSAnonBoxes::cellContent) {
616 // The thing that would maybe be a wrapper anon box is the cell.
617 maybeAnonBox = maybeAnonBox->GetParent();
618 }
619 return maybeAnonBox->StyleContext()->IsWrapperAnonBox();
620 }
621
622 //----------------------------------------------------------------------
623
624 // Block/inline frame construction logic. We maintain a few invariants here:
625 //
626 // 1. Block frames contain block and inline frames.
627 //
628 // 2. Inline frames only contain inline frames. If an inline parent has a block
629 // child then the block child is migrated upward until it lands in a block
630 // parent (the inline frames containing block is where it will end up).
631
632 // After this function returns, aLink is pointing to the first link at or
633 // after its starting position for which the next frame is a block. If there
634 // is no such link, it points to the end of the list.
FindFirstBlock(nsFrameList::FrameLinkEnumerator & aLink)635 static void FindFirstBlock(nsFrameList::FrameLinkEnumerator& aLink) {
636 for (; !aLink.AtEnd(); aLink.Next()) {
637 if (!aLink.NextFrame()->IsInlineOutside()) {
638 return;
639 }
640 }
641 }
642
643 // This function returns a frame link enumerator pointing to the first link in
644 // the list for which the next frame is not block. If there is no such link,
645 // it points to the end of the list.
FindFirstNonBlock(const nsFrameList & aList)646 static nsFrameList::FrameLinkEnumerator FindFirstNonBlock(
647 const nsFrameList& aList) {
648 nsFrameList::FrameLinkEnumerator link(aList);
649 for (; !link.AtEnd(); link.Next()) {
650 if (link.NextFrame()->IsInlineOutside()) {
651 break;
652 }
653 }
654 return link;
655 }
656
SetInitialSingleChild(nsContainerFrame * aParent,nsIFrame * aFrame)657 inline void SetInitialSingleChild(nsContainerFrame* aParent, nsIFrame* aFrame) {
658 NS_PRECONDITION(!aFrame->GetNextSibling(), "Should be using a frame list");
659 nsFrameList temp(aFrame, aFrame);
660 aParent->SetInitialChildList(kPrincipalList, temp);
661 }
662
663 // -----------------------------------------------------------
664
665 // Structure used when constructing formatting object trees.
666 struct nsFrameItems : public nsFrameList {
667 // Appends the frame to the end of the list
668 void AddChild(nsIFrame* aChild);
669 };
670
AddChild(nsIFrame * aChild)671 void nsFrameItems::AddChild(nsIFrame* aChild) {
672 NS_PRECONDITION(aChild, "nsFrameItems::AddChild");
673
674 // It'd be really nice if we could just AppendFrames(kPrincipalList, aChild)
675 // here, but some of our callers put frames that have different parents
676 // (caption, I'm looking at you) on the same framelist, and nsFrameList
677 // asserts if you try to do that.
678 if (IsEmpty()) {
679 SetFrames(aChild);
680 } else {
681 NS_ASSERTION(aChild != mLastChild,
682 "Same frame being added to frame list twice?");
683 mLastChild->SetNextSibling(aChild);
684 mLastChild = nsLayoutUtils::GetLastSibling(aChild);
685 }
686 }
687
688 // -----------------------------------------------------------
689
690 // Structure used when constructing formatting object trees. Contains
691 // state information needed for absolutely positioned elements
692 struct nsAbsoluteItems : nsFrameItems {
693 // containing block for absolutely positioned elements
694 nsContainerFrame* containingBlock;
695
696 explicit nsAbsoluteItems(nsContainerFrame* aContainingBlock);
697 #ifdef DEBUG
698 // XXXbz Does this need a debug-only assignment operator that nulls out the
699 // childList in the nsAbsoluteItems we're copying? Introducing a difference
700 // between debug and non-debug behavior seems bad, so I guess not...
~nsAbsoluteItemsnsAbsoluteItems701 ~nsAbsoluteItems() {
702 NS_ASSERTION(!FirstChild(),
703 "Dangling child list. Someone forgot to insert it?");
704 }
705 #endif
706
707 // Appends the frame to the end of the list
708 void AddChild(nsIFrame* aChild);
709 };
710
nsAbsoluteItems(nsContainerFrame * aContainingBlock)711 nsAbsoluteItems::nsAbsoluteItems(nsContainerFrame* aContainingBlock)
712 : containingBlock(aContainingBlock) {}
713
714 // Additional behavior is that it sets the frame's NS_FRAME_OUT_OF_FLOW flag
AddChild(nsIFrame * aChild)715 void nsAbsoluteItems::AddChild(nsIFrame* aChild) {
716 aChild->AddStateBits(NS_FRAME_OUT_OF_FLOW);
717 NS_ASSERTION(aChild->GetPlaceholderFrame(),
718 "Child without placeholder being added to nsAbsoluteItems?");
719 nsFrameItems::AddChild(aChild);
720 }
721
722 // -----------------------------------------------------------
723
724 // Structure for saving the existing state when pushing/poping containing
725 // blocks. The destructor restores the state to its previous state
726 class MOZ_STACK_CLASS nsFrameConstructorSaveState {
727 public:
728 typedef nsIFrame::ChildListID ChildListID;
729 nsFrameConstructorSaveState();
730 ~nsFrameConstructorSaveState();
731
732 private:
733 nsAbsoluteItems* mItems; // pointer to struct whose data we save/restore
734 nsAbsoluteItems mSavedItems; // copy of original data
735
736 // The name of the child list in which our frames would belong
737 ChildListID mChildListID;
738 nsFrameConstructorState* mState;
739
740 // State used only when we're saving the abs-pos state for a transformed
741 // element.
742 nsAbsoluteItems mSavedFixedItems;
743
744 bool mSavedFixedPosIsAbsPos;
745
746 friend class nsFrameConstructorState;
747 };
748
749 // Structure used to keep track of a list of bindings we need to call
750 // AddToAttachedQueue on. These should be in post-order depth-first
751 // flattened tree traversal order.
752 struct PendingBinding : public LinkedListElement<PendingBinding> {
753 #ifdef NS_BUILD_REFCNT_LOGGING
PendingBindingPendingBinding754 PendingBinding() { MOZ_COUNT_CTOR(PendingBinding); }
~PendingBindingPendingBinding755 ~PendingBinding() { MOZ_COUNT_DTOR(PendingBinding); }
756 #endif
757
758 RefPtr<nsXBLBinding> mBinding;
759 };
760
761 // Structure used for maintaining state information during the
762 // frame construction process
763 class MOZ_STACK_CLASS nsFrameConstructorState {
764 public:
765 typedef nsIFrame::ChildListID ChildListID;
766
767 nsPresContext* mPresContext;
768 nsIPresShell* mPresShell;
769 nsFrameManager* mFrameManager;
770
771 #ifdef MOZ_XUL
772 // Frames destined for the kPopupList.
773 nsAbsoluteItems mPopupItems;
774 #endif
775
776 // Containing block information for out-of-flow frames.
777 nsAbsoluteItems mFixedItems;
778 nsAbsoluteItems mAbsoluteItems;
779 nsAbsoluteItems mFloatedItems;
780 // The containing block of a frame in the top layer is defined by the
781 // spec: fixed-positioned frames are children of the viewport frame,
782 // and absolutely-positioned frames are children of the initial
783 // containing block. They would not be caught by any other containing
784 // block, e.g. frames with transform or filter.
785 nsAbsoluteItems mTopLayerFixedItems;
786 nsAbsoluteItems mTopLayerAbsoluteItems;
787
788 nsCOMPtr<nsILayoutHistoryState> mFrameState;
789 // These bits will be added to the state bits of any frame we construct
790 // using this state.
791 nsFrameState mAdditionalStateBits;
792
793 // When working with the transform and filter properties, we want to hook
794 // the abs-pos and fixed-pos lists together, since such
795 // elements are fixed-pos containing blocks. This flag determines
796 // whether or not we want to wire the fixed-pos and abs-pos lists
797 // together.
798 bool mFixedPosIsAbsPos;
799
800 // A boolean to indicate whether we have a "pending" popupgroup. That is, we
801 // have already created the FrameConstructionItem for the root popupgroup but
802 // we have not yet created the relevant frame.
803 bool mHavePendingPopupgroup;
804
805 // If false (which is the default) then call SetPrimaryFrame() as needed
806 // during frame construction. If true, don't make any SetPrimaryFrame()
807 // calls, except for generated content which doesn't have a primary frame
808 // yet. The mCreatingExtraFrames == true mode is meant to be used for
809 // construction of random "extra" frames for elements via normal frame
810 // construction APIs (e.g. replication of things across pages in paginated
811 // mode).
812 bool mCreatingExtraFrames;
813
814 nsCOMArray<nsIContent> mGeneratedTextNodesWithInitializer;
815
816 // Selector matching context for. This is null when we're using the Servo
817 // style system.
818 TreeMatchContext* mTreeMatchContext;
819
820 // Constructor
821 // Use the passed-in history state.
822 //
823 // aTreeMatchContext is null when we're using the Servo style system.
824 nsFrameConstructorState(
825 nsIPresShell* aPresShell, TreeMatchContext* aTreeMatchContext,
826 nsContainerFrame* aFixedContainingBlock,
827 nsContainerFrame* aAbsoluteContainingBlock,
828 nsContainerFrame* aFloatContainingBlock,
829 already_AddRefed<nsILayoutHistoryState> aHistoryState);
830 // Get the history state from the pres context's pres shell.
831 nsFrameConstructorState(nsIPresShell* aPresShell,
832 TreeMatchContext* aTreeMatchContext,
833 nsContainerFrame* aFixedContainingBlock,
834 nsContainerFrame* aAbsoluteContainingBlock,
835 nsContainerFrame* aFloatContainingBlock);
836
837 ~nsFrameConstructorState();
838
HasAncestorFilter()839 bool HasAncestorFilter() {
840 #ifdef MOZ_OLD_STYLE
841 return mTreeMatchContext && mTreeMatchContext->mAncestorFilter.HasFilter();
842 #else
843 return false;
844 #endif
845 }
846
847 // Function to push the existing absolute containing block state and
848 // create a new scope. Code that uses this function should get matching
849 // logic in GetAbsoluteContainingBlock.
850 // Also makes aNewAbsoluteContainingBlock the containing block for
851 // fixed-pos elements if necessary.
852 // aPositionedFrame is the frame whose style actually makes
853 // aNewAbsoluteContainingBlock a containing block. E.g. for a scrollable
854 // element aPositionedFrame is the element's primary frame and
855 // aNewAbsoluteContainingBlock is the scrolled frame.
856 void PushAbsoluteContainingBlock(
857 nsContainerFrame* aNewAbsoluteContainingBlock, nsIFrame* aPositionedFrame,
858 nsFrameConstructorSaveState& aSaveState);
859
860 // Function to push the existing float containing block state and
861 // create a new scope. Code that uses this function should get matching
862 // logic in GetFloatContainingBlock.
863 // Pushing a null float containing block forbids any frames from being
864 // floated until a new float containing block is pushed.
865 // XXX we should get rid of null float containing blocks and teach the
866 // various frame classes to deal with floats instead.
867 void PushFloatContainingBlock(nsContainerFrame* aNewFloatContainingBlock,
868 nsFrameConstructorSaveState& aSaveState);
869
870 // Function to return the proper geometric parent for a frame with display
871 // struct given by aStyleDisplay and parent's frame given by
872 // aContentParentFrame.
873 nsContainerFrame* GetGeometricParent(
874 const nsStyleDisplay* aStyleDisplay,
875 nsContainerFrame* aContentParentFrame) const;
876
877 /**
878 * Function to add a new frame to the right frame list. This MUST be called
879 * on frames before their children have been processed if the frames might
880 * conceivably be out-of-flow; otherwise cleanup in error cases won't work
881 * right. Also, this MUST be called on frames after they have been
882 * initialized.
883 * @param aNewFrame the frame to add
884 * @param aFrameItems the list to add in-flow frames to
885 * @param aContent the content pointer for aNewFrame
886 * @param aParentFrame the parent frame for the content if it were in-flow
887 * @param aCanBePositioned pass false if the frame isn't allowed to be
888 * positioned
889 * @param aCanBeFloated pass false if the frame isn't allowed to be
890 * floated
891 * @param aIsOutOfFlowPopup pass true if the frame is an out-of-flow popup
892 * (XUL-only)
893 */
894 void AddChild(nsIFrame* aNewFrame, nsFrameItems& aFrameItems,
895 nsIContent* aContent, nsContainerFrame* aParentFrame,
896 bool aCanBePositioned = true, bool aCanBeFloated = true,
897 bool aIsOutOfFlowPopup = false, bool aInsertAfter = false,
898 nsIFrame* aInsertAfterFrame = nullptr);
899
900 /**
901 * Function to return the fixed-pos element list. Normally this will just
902 * hand back the fixed-pos element list, but in case we're dealing with a
903 * transformed element that's acting as an abs-pos and fixed-pos container,
904 * we'll hand back the abs-pos list. Callers should use this function if they
905 * want to get the list acting as the fixed-pos item parent.
906 */
GetFixedItems()907 nsAbsoluteItems& GetFixedItems() {
908 return mFixedPosIsAbsPos ? mAbsoluteItems : mFixedItems;
909 }
GetFixedItems() const910 const nsAbsoluteItems& GetFixedItems() const {
911 return mFixedPosIsAbsPos ? mAbsoluteItems : mFixedItems;
912 }
913
914 /**
915 * class to automatically push and pop a pending binding in the frame
916 * constructor state. See nsCSSFrameConstructor::FrameConstructionItem
917 * mPendingBinding documentation.
918 */
919 class PendingBindingAutoPusher;
920 friend class PendingBindingAutoPusher;
921 class MOZ_STACK_CLASS PendingBindingAutoPusher {
922 public:
PendingBindingAutoPusher(nsFrameConstructorState & aState,PendingBinding * aPendingBinding)923 PendingBindingAutoPusher(nsFrameConstructorState& aState,
924 PendingBinding* aPendingBinding)
925 : mState(aState),
926 mPendingBinding(aState.mCurrentPendingBindingInsertionPoint) {
927 if (aPendingBinding) {
928 aState.mCurrentPendingBindingInsertionPoint = aPendingBinding;
929 }
930 }
931
~PendingBindingAutoPusher()932 ~PendingBindingAutoPusher() {
933 mState.mCurrentPendingBindingInsertionPoint = mPendingBinding;
934 }
935
936 private:
937 nsFrameConstructorState& mState;
938 PendingBinding* mPendingBinding;
939 };
940
941 /**
942 * Add a new pending binding to the list
943 */
AddPendingBinding(PendingBinding * aPendingBinding)944 void AddPendingBinding(PendingBinding* aPendingBinding) {
945 if (mCurrentPendingBindingInsertionPoint) {
946 mCurrentPendingBindingInsertionPoint->setPrevious(aPendingBinding);
947 } else {
948 mPendingBindings.insertBack(aPendingBinding);
949 }
950 }
951
952 protected:
953 friend class nsFrameConstructorSaveState;
954
955 /**
956 * ProcessFrameInsertions takes the frames in aFrameItems and adds them as
957 * kids to the aChildListID child list of |aFrameItems.containingBlock|.
958 */
959 void ProcessFrameInsertions(nsAbsoluteItems& aFrameItems,
960 ChildListID aChildListID);
961
962 /**
963 * GetOutOfFlowFrameItems selects the out-of-flow frame list the new
964 * frame should be added to. If the frame shouldn't be added to any
965 * out-of-flow list, it returns nullptr. The corresponding type of
966 * placeholder is also returned via the aPlaceholderType parameter
967 * if this method doesn't return nullptr. The caller should check
968 * whether the returned list really has a containing block.
969 */
970 nsAbsoluteItems* GetOutOfFlowFrameItems(nsIFrame* aNewFrame,
971 bool aCanBePositioned,
972 bool aCanBeFloated,
973 bool aIsOutOfFlowPopup,
974 nsFrameState* aPlaceholderType);
975
976 void ConstructBackdropFrameFor(nsIContent* aContent, nsIFrame* aFrame);
977
978 // Our list of all pending bindings. When we're done, we need to call
979 // AddToAttachedQueue on all of them, in order.
980 LinkedList<PendingBinding> mPendingBindings;
981
982 PendingBinding* mCurrentPendingBindingInsertionPoint;
983 };
984
985 #ifndef MOZ_OLD_STYLE
986
987 namespace mozilla {
988
989 class AutoDisplayContentsAncestorPusher {
990 public:
AutoDisplayContentsAncestorPusher(TreeMatchContext & aTreeMatchContext,nsPresContext * aPresContext,nsIContent * aParent)991 AutoDisplayContentsAncestorPusher(TreeMatchContext& aTreeMatchContext,
992 nsPresContext* aPresContext,
993 nsIContent* aParent) {}
IsEmpty() const994 bool IsEmpty() const { return false; }
995 };
996
997 } // namespace mozilla
998
999 #endif
1000
nsFrameConstructorState(nsIPresShell * aPresShell,TreeMatchContext * aTreeMatchContext,nsContainerFrame * aFixedContainingBlock,nsContainerFrame * aAbsoluteContainingBlock,nsContainerFrame * aFloatContainingBlock,already_AddRefed<nsILayoutHistoryState> aHistoryState)1001 nsFrameConstructorState::nsFrameConstructorState(
1002 nsIPresShell* aPresShell, TreeMatchContext* aTreeMatchContext,
1003 nsContainerFrame* aFixedContainingBlock,
1004 nsContainerFrame* aAbsoluteContainingBlock,
1005 nsContainerFrame* aFloatContainingBlock,
1006 already_AddRefed<nsILayoutHistoryState> aHistoryState)
1007 : mPresContext(aPresShell->GetPresContext()),
1008 mPresShell(aPresShell),
1009 mFrameManager(aPresShell->FrameConstructor()),
1010 #ifdef MOZ_XUL
1011 mPopupItems(nullptr),
1012 #endif
1013 mFixedItems(aFixedContainingBlock),
1014 mAbsoluteItems(aAbsoluteContainingBlock),
1015 mFloatedItems(aFloatContainingBlock),
1016 mTopLayerFixedItems(
1017 static_cast<nsContainerFrame*>(mFrameManager->GetRootFrame())),
1018 mTopLayerAbsoluteItems(
1019 aPresShell->FrameConstructor()->GetDocElementContainingBlock()),
1020 // See PushAbsoluteContaningBlock below
1021 mFrameState(aHistoryState),
1022 mAdditionalStateBits(nsFrameState(0)),
1023 // If the fixed-pos containing block is equal to the abs-pos containing
1024 // block, use the abs-pos containing block's abs-pos list for fixed-pos
1025 // frames.
1026 mFixedPosIsAbsPos(aFixedContainingBlock == aAbsoluteContainingBlock),
1027 mHavePendingPopupgroup(false),
1028 mCreatingExtraFrames(false),
1029 mTreeMatchContext(aTreeMatchContext),
1030 mCurrentPendingBindingInsertionPoint(nullptr) {
1031 #ifdef MOZ_XUL
1032 nsIRootBox* rootBox = nsIRootBox::GetRootBox(aPresShell);
1033 if (rootBox) {
1034 mPopupItems.containingBlock = rootBox->GetPopupSetFrame();
1035 }
1036 #endif
1037 MOZ_COUNT_CTOR(nsFrameConstructorState);
1038 }
1039
nsFrameConstructorState(nsIPresShell * aPresShell,TreeMatchContext * aTreeMatchContext,nsContainerFrame * aFixedContainingBlock,nsContainerFrame * aAbsoluteContainingBlock,nsContainerFrame * aFloatContainingBlock)1040 nsFrameConstructorState::nsFrameConstructorState(
1041 nsIPresShell* aPresShell, TreeMatchContext* aTreeMatchContext,
1042 nsContainerFrame* aFixedContainingBlock,
1043 nsContainerFrame* aAbsoluteContainingBlock,
1044 nsContainerFrame* aFloatContainingBlock)
1045 : nsFrameConstructorState(
1046 aPresShell, aTreeMatchContext, aFixedContainingBlock,
1047 aAbsoluteContainingBlock, aFloatContainingBlock,
1048 aPresShell->GetDocument()->GetLayoutHistoryState()) {}
1049
~nsFrameConstructorState()1050 nsFrameConstructorState::~nsFrameConstructorState() {
1051 MOZ_COUNT_DTOR(nsFrameConstructorState);
1052 ProcessFrameInsertions(mTopLayerFixedItems, nsIFrame::kFixedList);
1053 ProcessFrameInsertions(mTopLayerAbsoluteItems, nsIFrame::kAbsoluteList);
1054 ProcessFrameInsertions(mFloatedItems, nsIFrame::kFloatList);
1055 ProcessFrameInsertions(mAbsoluteItems, nsIFrame::kAbsoluteList);
1056 ProcessFrameInsertions(mFixedItems, nsIFrame::kFixedList);
1057 #ifdef MOZ_XUL
1058 ProcessFrameInsertions(mPopupItems, nsIFrame::kPopupList);
1059 #endif
1060 for (int32_t i = mGeneratedTextNodesWithInitializer.Count() - 1; i >= 0;
1061 --i) {
1062 mGeneratedTextNodesWithInitializer[i]->DeleteProperty(
1063 nsGkAtoms::genConInitializerProperty);
1064 }
1065 if (!mPendingBindings.isEmpty()) {
1066 nsBindingManager* bindingManager =
1067 mPresShell->GetDocument()->BindingManager();
1068 do {
1069 nsAutoPtr<PendingBinding> pendingBinding;
1070 pendingBinding = mPendingBindings.popFirst();
1071 bindingManager->AddToAttachedQueue(pendingBinding->mBinding);
1072 } while (!mPendingBindings.isEmpty());
1073 mCurrentPendingBindingInsertionPoint = nullptr;
1074 }
1075 }
1076
AdjustAbsoluteContainingBlock(nsContainerFrame * aContainingBlockIn)1077 static nsContainerFrame* AdjustAbsoluteContainingBlock(
1078 nsContainerFrame* aContainingBlockIn) {
1079 if (!aContainingBlockIn) {
1080 return nullptr;
1081 }
1082
1083 // Always use the container's first continuation. (Inline frames can have
1084 // non-fluid bidi continuations...)
1085 return static_cast<nsContainerFrame*>(
1086 aContainingBlockIn->FirstContinuation());
1087 }
1088
PushAbsoluteContainingBlock(nsContainerFrame * aNewAbsoluteContainingBlock,nsIFrame * aPositionedFrame,nsFrameConstructorSaveState & aSaveState)1089 void nsFrameConstructorState::PushAbsoluteContainingBlock(
1090 nsContainerFrame* aNewAbsoluteContainingBlock, nsIFrame* aPositionedFrame,
1091 nsFrameConstructorSaveState& aSaveState) {
1092 aSaveState.mItems = &mAbsoluteItems;
1093 aSaveState.mSavedItems = mAbsoluteItems;
1094 aSaveState.mChildListID = nsIFrame::kAbsoluteList;
1095 aSaveState.mState = this;
1096 aSaveState.mSavedFixedPosIsAbsPos = mFixedPosIsAbsPos;
1097
1098 if (mFixedPosIsAbsPos) {
1099 // Since we're going to replace mAbsoluteItems, we need to save it into
1100 // mFixedItems now (and save the current value of mFixedItems).
1101 aSaveState.mSavedFixedItems = mFixedItems;
1102 mFixedItems = mAbsoluteItems;
1103 }
1104
1105 mAbsoluteItems = nsAbsoluteItems(
1106 AdjustAbsoluteContainingBlock(aNewAbsoluteContainingBlock));
1107
1108 /* See if we're wiring the fixed-pos and abs-pos lists together. This happens
1109 * iff we're a transformed element.
1110 */
1111 mFixedPosIsAbsPos =
1112 aPositionedFrame && aPositionedFrame->IsFixedPosContainingBlock();
1113
1114 if (aNewAbsoluteContainingBlock) {
1115 aNewAbsoluteContainingBlock->MarkAsAbsoluteContainingBlock();
1116 }
1117 }
1118
PushFloatContainingBlock(nsContainerFrame * aNewFloatContainingBlock,nsFrameConstructorSaveState & aSaveState)1119 void nsFrameConstructorState::PushFloatContainingBlock(
1120 nsContainerFrame* aNewFloatContainingBlock,
1121 nsFrameConstructorSaveState& aSaveState) {
1122 NS_PRECONDITION(!aNewFloatContainingBlock ||
1123 aNewFloatContainingBlock->IsFloatContainingBlock(),
1124 "Please push a real float containing block!");
1125 NS_ASSERTION(
1126 !aNewFloatContainingBlock ||
1127 !ShouldSuppressFloatingOfDescendants(aNewFloatContainingBlock),
1128 "We should not push a frame that is supposed to _suppress_ "
1129 "floats as a float containing block!");
1130 aSaveState.mItems = &mFloatedItems;
1131 aSaveState.mSavedItems = mFloatedItems;
1132 aSaveState.mChildListID = nsIFrame::kFloatList;
1133 aSaveState.mState = this;
1134 mFloatedItems = nsAbsoluteItems(aNewFloatContainingBlock);
1135 }
1136
GetGeometricParent(const nsStyleDisplay * aStyleDisplay,nsContainerFrame * aContentParentFrame) const1137 nsContainerFrame* nsFrameConstructorState::GetGeometricParent(
1138 const nsStyleDisplay* aStyleDisplay,
1139 nsContainerFrame* aContentParentFrame) const {
1140 NS_PRECONDITION(aStyleDisplay, "Must have display struct!");
1141
1142 // If there is no container for a fixed, absolute, or floating root
1143 // frame, we will ignore the positioning. This hack is originally
1144 // brought to you by the letter T: tables, since other roots don't
1145 // even call into this code. See bug 178855.
1146 //
1147 // XXX Disabling positioning in this case is a hack. If one was so inclined,
1148 // one could support this either by (1) inserting a dummy block between the
1149 // table and the canvas or (2) teaching the canvas how to reflow positioned
1150 // elements. (1) has the usual problems when multiple frames share the same
1151 // content (notice all the special cases in this file dealing with inner
1152 // tables and table wrappers which share the same content). (2) requires some
1153 // work and possible factoring.
1154 //
1155 // XXXbz couldn't we just force position to "static" on roots and
1156 // float to "none"? That's OK per CSS 2.1, as far as I can tell.
1157
1158 if (aContentParentFrame &&
1159 nsSVGUtils::IsInSVGTextSubtree(aContentParentFrame)) {
1160 return aContentParentFrame;
1161 }
1162
1163 if (aStyleDisplay->IsFloatingStyle() && mFloatedItems.containingBlock) {
1164 NS_ASSERTION(!aStyleDisplay->IsAbsolutelyPositionedStyle(),
1165 "Absolutely positioned _and_ floating?");
1166 return mFloatedItems.containingBlock;
1167 }
1168
1169 if (aStyleDisplay->mTopLayer != NS_STYLE_TOP_LAYER_NONE) {
1170 MOZ_ASSERT(aStyleDisplay->mTopLayer == NS_STYLE_TOP_LAYER_TOP,
1171 "-moz-top-layer should be either none or top");
1172 MOZ_ASSERT(aStyleDisplay->IsAbsolutelyPositionedStyle(),
1173 "Top layer items should always be absolutely positioned");
1174 if (aStyleDisplay->mPosition == NS_STYLE_POSITION_FIXED) {
1175 MOZ_ASSERT(mTopLayerFixedItems.containingBlock, "No root frame?");
1176 return mTopLayerFixedItems.containingBlock;
1177 }
1178 MOZ_ASSERT(aStyleDisplay->mPosition == NS_STYLE_POSITION_ABSOLUTE);
1179 MOZ_ASSERT(mTopLayerAbsoluteItems.containingBlock);
1180 return mTopLayerAbsoluteItems.containingBlock;
1181 }
1182
1183 if (aStyleDisplay->mPosition == NS_STYLE_POSITION_ABSOLUTE &&
1184 mAbsoluteItems.containingBlock) {
1185 return mAbsoluteItems.containingBlock;
1186 }
1187
1188 if (aStyleDisplay->mPosition == NS_STYLE_POSITION_FIXED &&
1189 GetFixedItems().containingBlock) {
1190 return GetFixedItems().containingBlock;
1191 }
1192
1193 return aContentParentFrame;
1194 }
1195
GetOutOfFlowFrameItems(nsIFrame * aNewFrame,bool aCanBePositioned,bool aCanBeFloated,bool aIsOutOfFlowPopup,nsFrameState * aPlaceholderType)1196 nsAbsoluteItems* nsFrameConstructorState::GetOutOfFlowFrameItems(
1197 nsIFrame* aNewFrame, bool aCanBePositioned, bool aCanBeFloated,
1198 bool aIsOutOfFlowPopup, nsFrameState* aPlaceholderType) {
1199 #ifdef MOZ_XUL
1200 if (MOZ_UNLIKELY(aIsOutOfFlowPopup)) {
1201 MOZ_ASSERT(mPopupItems.containingBlock, "Must have a popup set frame!");
1202 *aPlaceholderType = PLACEHOLDER_FOR_POPUP;
1203 return &mPopupItems;
1204 }
1205 #endif // MOZ_XUL
1206 if (aCanBeFloated && aNewFrame->IsFloating()) {
1207 *aPlaceholderType = PLACEHOLDER_FOR_FLOAT;
1208 return &mFloatedItems;
1209 }
1210
1211 if (aCanBePositioned) {
1212 const nsStyleDisplay* disp = aNewFrame->StyleDisplay();
1213 if (disp->mTopLayer != NS_STYLE_TOP_LAYER_NONE) {
1214 *aPlaceholderType = PLACEHOLDER_FOR_TOPLAYER;
1215 if (disp->mPosition == NS_STYLE_POSITION_FIXED) {
1216 *aPlaceholderType |= PLACEHOLDER_FOR_FIXEDPOS;
1217 return &mTopLayerFixedItems;
1218 }
1219 *aPlaceholderType |= PLACEHOLDER_FOR_ABSPOS;
1220 return &mTopLayerAbsoluteItems;
1221 }
1222 if (disp->mPosition == NS_STYLE_POSITION_ABSOLUTE) {
1223 *aPlaceholderType = PLACEHOLDER_FOR_ABSPOS;
1224 return &mAbsoluteItems;
1225 }
1226 if (disp->mPosition == NS_STYLE_POSITION_FIXED) {
1227 *aPlaceholderType = PLACEHOLDER_FOR_FIXEDPOS;
1228 return &GetFixedItems();
1229 }
1230 }
1231 return nullptr;
1232 }
1233
ConstructBackdropFrameFor(nsIContent * aContent,nsIFrame * aFrame)1234 void nsFrameConstructorState::ConstructBackdropFrameFor(nsIContent* aContent,
1235 nsIFrame* aFrame) {
1236 MOZ_ASSERT(aFrame->StyleDisplay()->mTopLayer == NS_STYLE_TOP_LAYER_TOP);
1237 nsContainerFrame* frame = do_QueryFrame(aFrame);
1238 if (!frame) {
1239 NS_WARNING("Cannot create backdrop frame for non-container frame");
1240 return;
1241 }
1242
1243 RefPtr<nsStyleContext> style =
1244 mPresShell->StyleSet()->ResolvePseudoElementStyle(
1245 aContent->AsElement(), CSSPseudoElementType::backdrop,
1246 /* aParentStyleContext */ nullptr,
1247 /* aPseudoElement */ nullptr);
1248 MOZ_ASSERT(style->StyleDisplay()->mTopLayer == NS_STYLE_TOP_LAYER_TOP);
1249 nsContainerFrame* parentFrame =
1250 GetGeometricParent(style->StyleDisplay(), nullptr);
1251
1252 nsBackdropFrame* backdropFrame = new (mPresShell) nsBackdropFrame(style);
1253 backdropFrame->Init(aContent, parentFrame, nullptr);
1254
1255 nsFrameState placeholderType;
1256 nsAbsoluteItems* frameItems = GetOutOfFlowFrameItems(
1257 backdropFrame, true, true, false, &placeholderType);
1258 MOZ_ASSERT(placeholderType & PLACEHOLDER_FOR_TOPLAYER);
1259
1260 nsIFrame* placeholder = nsCSSFrameConstructor::CreatePlaceholderFrameFor(
1261 mPresShell, aContent, backdropFrame, frame, nullptr, placeholderType);
1262 nsFrameList temp(placeholder, placeholder);
1263 frame->SetInitialChildList(nsIFrame::kBackdropList, temp);
1264
1265 frameItems->AddChild(backdropFrame);
1266 }
1267
AddChild(nsIFrame * aNewFrame,nsFrameItems & aFrameItems,nsIContent * aContent,nsContainerFrame * aParentFrame,bool aCanBePositioned,bool aCanBeFloated,bool aIsOutOfFlowPopup,bool aInsertAfter,nsIFrame * aInsertAfterFrame)1268 void nsFrameConstructorState::AddChild(
1269 nsIFrame* aNewFrame, nsFrameItems& aFrameItems, nsIContent* aContent,
1270 nsContainerFrame* aParentFrame, bool aCanBePositioned, bool aCanBeFloated,
1271 bool aIsOutOfFlowPopup, bool aInsertAfter, nsIFrame* aInsertAfterFrame) {
1272 NS_PRECONDITION(!aNewFrame->GetNextSibling(), "Shouldn't happen");
1273
1274 nsFrameState placeholderType;
1275 nsAbsoluteItems* outOfFlowFrameItems =
1276 GetOutOfFlowFrameItems(aNewFrame, aCanBePositioned, aCanBeFloated,
1277 aIsOutOfFlowPopup, &placeholderType);
1278
1279 // The comments in GetGeometricParent regarding root table frames
1280 // all apply here, unfortunately. Thus, we need to check whether
1281 // the returned frame items really has containing block.
1282 nsFrameItems* frameItems;
1283 if (outOfFlowFrameItems && outOfFlowFrameItems->containingBlock) {
1284 MOZ_ASSERT(aNewFrame->GetParent() == outOfFlowFrameItems->containingBlock,
1285 "Parent of the frame is not the containing block?");
1286 frameItems = outOfFlowFrameItems;
1287 } else {
1288 frameItems = &aFrameItems;
1289 placeholderType = nsFrameState(0);
1290 }
1291
1292 if (placeholderType) {
1293 NS_ASSERTION(frameItems != &aFrameItems,
1294 "Putting frame in-flow _and_ want a placeholder?");
1295 nsIFrame* placeholderFrame =
1296 nsCSSFrameConstructor::CreatePlaceholderFrameFor(
1297 mPresShell, aContent, aNewFrame, aParentFrame, nullptr,
1298 placeholderType);
1299
1300 placeholderFrame->AddStateBits(mAdditionalStateBits);
1301 // Add the placeholder frame to the flow
1302 aFrameItems.AddChild(placeholderFrame);
1303
1304 if (placeholderType & PLACEHOLDER_FOR_TOPLAYER) {
1305 ConstructBackdropFrameFor(aContent, aNewFrame);
1306 }
1307 }
1308 #ifdef DEBUG
1309 else {
1310 NS_ASSERTION(aNewFrame->GetParent() == aParentFrame,
1311 "In-flow frame has wrong parent");
1312 }
1313 #endif
1314
1315 if (aInsertAfter) {
1316 frameItems->InsertFrame(nullptr, aInsertAfterFrame, aNewFrame);
1317 } else {
1318 frameItems->AddChild(aNewFrame);
1319 }
1320 }
1321
ProcessFrameInsertions(nsAbsoluteItems & aFrameItems,ChildListID aChildListID)1322 void nsFrameConstructorState::ProcessFrameInsertions(
1323 nsAbsoluteItems& aFrameItems, ChildListID aChildListID) {
1324 #define NS_NONXUL_LIST_TEST \
1325 (&aFrameItems == &mFloatedItems && aChildListID == nsIFrame::kFloatList) || \
1326 ((&aFrameItems == &mAbsoluteItems || \
1327 &aFrameItems == &mTopLayerAbsoluteItems) && \
1328 aChildListID == nsIFrame::kAbsoluteList) || \
1329 ((&aFrameItems == &mFixedItems || \
1330 &aFrameItems == &mTopLayerFixedItems) && \
1331 aChildListID == nsIFrame::kFixedList)
1332 #ifdef MOZ_XUL
1333 NS_PRECONDITION(NS_NONXUL_LIST_TEST || (&aFrameItems == &mPopupItems &&
1334 aChildListID == nsIFrame::kPopupList),
1335 "Unexpected aFrameItems/aChildListID combination");
1336 #else
1337 NS_PRECONDITION(NS_NONXUL_LIST_TEST,
1338 "Unexpected aFrameItems/aChildListID combination");
1339 #endif
1340
1341 if (aFrameItems.IsEmpty()) {
1342 return;
1343 }
1344
1345 nsContainerFrame* containingBlock = aFrameItems.containingBlock;
1346
1347 NS_ASSERTION(containingBlock, "Child list without containing block?");
1348
1349 if (aChildListID == nsIFrame::kFixedList) {
1350 // Put this frame on the transformed-frame's abs-pos list instead, if
1351 // it has abs-pos children instead of fixed-pos children.
1352 aChildListID = containingBlock->GetAbsoluteListID();
1353 }
1354
1355 // Insert the frames hanging out in aItems. We can use SetInitialChildList()
1356 // if the containing block hasn't been reflowed yet (so NS_FRAME_FIRST_REFLOW
1357 // is set) and doesn't have any frames in the aChildListID child list yet.
1358 const nsFrameList& childList = containingBlock->GetChildList(aChildListID);
1359 if (childList.IsEmpty() &&
1360 (containingBlock->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
1361 // If we're injecting absolutely positioned frames, inject them on the
1362 // absolute containing block
1363 if (aChildListID == containingBlock->GetAbsoluteListID()) {
1364 containingBlock->GetAbsoluteContainingBlock()->SetInitialChildList(
1365 containingBlock, aChildListID, aFrameItems);
1366 } else {
1367 containingBlock->SetInitialChildList(aChildListID, aFrameItems);
1368 }
1369 } else if (aChildListID == nsIFrame::kFixedList ||
1370 aChildListID == nsIFrame::kAbsoluteList) {
1371 // The order is not important for abs-pos/fixed-pos frame list, just
1372 // append the frame items to the list directly.
1373 mFrameManager->AppendFrames(containingBlock, aChildListID, aFrameItems);
1374 } else {
1375 // Note that whether the frame construction context is doing an append or
1376 // not is not helpful here, since it could be appending to some frame in
1377 // the middle of the document, which means we're not necessarily
1378 // appending to the children of the containing block.
1379 //
1380 // We need to make sure the 'append to the end of document' case is fast.
1381 // So first test the last child of the containing block
1382 nsIFrame* lastChild = childList.LastChild();
1383
1384 // CompareTreePosition uses placeholder hierarchy for out of flow frames,
1385 // so this will make out-of-flows respect the ordering of placeholders,
1386 // which is great because it takes care of anonymous content.
1387 nsIFrame* firstNewFrame = aFrameItems.FirstChild();
1388
1389 // Cache the ancestor chain so that we can reuse it if needed.
1390 AutoTArray<nsIFrame*, 20> firstNewFrameAncestors;
1391 nsIFrame* notCommonAncestor = nullptr;
1392 if (lastChild) {
1393 notCommonAncestor = nsLayoutUtils::FillAncestors(
1394 firstNewFrame, containingBlock, &firstNewFrameAncestors);
1395 }
1396
1397 if (!lastChild || nsLayoutUtils::CompareTreePosition(
1398 lastChild, firstNewFrame, firstNewFrameAncestors,
1399 notCommonAncestor ? containingBlock : nullptr) < 0) {
1400 // no lastChild, or lastChild comes before the new children, so just
1401 // append
1402 mFrameManager->AppendFrames(containingBlock, aChildListID, aFrameItems);
1403 } else {
1404 // Try the other children. First collect them to an array so that a
1405 // reasonable fast binary search can be used to find the insertion point.
1406 AutoTArray<nsIFrame*, 128> children;
1407 for (nsIFrame* f = childList.FirstChild(); f != lastChild;
1408 f = f->GetNextSibling()) {
1409 children.AppendElement(f);
1410 }
1411
1412 nsIFrame* insertionPoint = nullptr;
1413 int32_t imin = 0;
1414 int32_t max = children.Length();
1415 while (max > imin) {
1416 int32_t imid = imin + ((max - imin) / 2);
1417 nsIFrame* f = children[imid];
1418 int32_t compare = nsLayoutUtils::CompareTreePosition(
1419 f, firstNewFrame, firstNewFrameAncestors,
1420 notCommonAncestor ? containingBlock : nullptr);
1421 if (compare > 0) {
1422 // f is after the new frame.
1423 max = imid;
1424 insertionPoint = imid > 0 ? children[imid - 1] : nullptr;
1425 } else if (compare < 0) {
1426 // f is before the new frame.
1427 imin = imid + 1;
1428 insertionPoint = f;
1429 } else {
1430 // This is for the old behavior. Should be removed once it is
1431 // guaranteed that CompareTreePosition can't return 0!
1432 // See bug 928645.
1433 NS_WARNING("Something odd happening???");
1434 insertionPoint = nullptr;
1435 for (uint32_t i = 0; i < children.Length(); ++i) {
1436 nsIFrame* f = children[i];
1437 if (nsLayoutUtils::CompareTreePosition(
1438 f, firstNewFrame, firstNewFrameAncestors,
1439 notCommonAncestor ? containingBlock : nullptr) > 0) {
1440 break;
1441 }
1442 insertionPoint = f;
1443 }
1444 break;
1445 }
1446 }
1447 mFrameManager->InsertFrames(containingBlock, aChildListID, insertionPoint,
1448 aFrameItems);
1449 }
1450 }
1451
1452 MOZ_ASSERT(aFrameItems.IsEmpty(), "How did that happen?");
1453 }
1454
nsFrameConstructorSaveState()1455 nsFrameConstructorSaveState::nsFrameConstructorSaveState()
1456 : mItems(nullptr),
1457 mSavedItems(nullptr),
1458 mChildListID(kPrincipalList),
1459 mState(nullptr),
1460 mSavedFixedItems(nullptr),
1461 mSavedFixedPosIsAbsPos(false) {}
1462
~nsFrameConstructorSaveState()1463 nsFrameConstructorSaveState::~nsFrameConstructorSaveState() {
1464 // Restore the state
1465 if (mItems) {
1466 NS_ASSERTION(mState, "Can't have mItems set without having a state!");
1467 mState->ProcessFrameInsertions(*mItems, mChildListID);
1468 *mItems = mSavedItems;
1469 #ifdef DEBUG
1470 // We've transferred the child list, so drop the pointer we held to it.
1471 // Note that this only matters for the assert in ~nsAbsoluteItems.
1472 mSavedItems.Clear();
1473 #endif
1474 if (mItems == &mState->mAbsoluteItems) {
1475 mState->mFixedPosIsAbsPos = mSavedFixedPosIsAbsPos;
1476 if (mSavedFixedPosIsAbsPos) {
1477 // mAbsoluteItems was moved to mFixedItems, so move mFixedItems back
1478 // and repair the old mFixedItems now.
1479 mState->mAbsoluteItems = mState->mFixedItems;
1480 mState->mFixedItems = mSavedFixedItems;
1481 #ifdef DEBUG
1482 mSavedFixedItems.Clear();
1483 #endif
1484 }
1485 }
1486 NS_ASSERTION(!mItems->LastChild() || !mItems->LastChild()->GetNextSibling(),
1487 "Something corrupted our list");
1488 }
1489 }
1490
1491 /**
1492 * Moves aFrameList from aOldParent to aNewParent. This updates the parent
1493 * pointer of the frames in the list, and reparents their views as needed.
1494 * nsFrame::SetParent sets the NS_FRAME_HAS_VIEW bit on aNewParent and its
1495 * ancestors as needed. Then it sets the list as the initial child list
1496 * on aNewParent, unless aNewParent either already has kids or has been
1497 * reflowed; in that case it appends the new frames. Note that this
1498 * method differs from ReparentFrames in that it doesn't change the kids'
1499 * style contexts.
1500 */
1501 // XXXbz Since this is only used for {ib} splits, could we just copy the view
1502 // bits from aOldParent to aNewParent and then use the
1503 // nsFrameList::ApplySetParent? That would still leave us doing two passes
1504 // over the list, of course; if we really wanted to we could factor out the
1505 // relevant part of ReparentFrameViewList, I suppose... Or just get rid of
1506 // views, which would make most of this function go away.
MoveChildrenTo(nsIFrame * aOldParent,nsContainerFrame * aNewParent,nsFrameList & aFrameList)1507 static void MoveChildrenTo(nsIFrame* aOldParent, nsContainerFrame* aNewParent,
1508 nsFrameList& aFrameList) {
1509 bool sameGrandParent = aOldParent->GetParent() == aNewParent->GetParent();
1510
1511 if (aNewParent->HasView() || aOldParent->HasView() || !sameGrandParent) {
1512 // Move the frames into the new view
1513 nsContainerFrame::ReparentFrameViewList(aFrameList, aOldParent, aNewParent);
1514 }
1515
1516 for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
1517 e.get()->SetParent(aNewParent);
1518 }
1519
1520 if (aNewParent->PrincipalChildList().IsEmpty() &&
1521 (aNewParent->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
1522 aNewParent->SetInitialChildList(kPrincipalList, aFrameList);
1523 } else {
1524 aNewParent->AppendFrames(kPrincipalList, aFrameList);
1525 }
1526 }
1527
1528 //----------------------------------------------------------------------
1529
nsCSSFrameConstructor(nsIDocument * aDocument,nsIPresShell * aPresShell)1530 nsCSSFrameConstructor::nsCSSFrameConstructor(nsIDocument* aDocument,
1531 nsIPresShell* aPresShell)
1532 : nsFrameManager(aPresShell),
1533 mDocument(aDocument),
1534 mRootElementFrame(nullptr),
1535 mRootElementStyleFrame(nullptr),
1536 mDocElementContainingBlock(nullptr),
1537 mPageSequenceFrame(nullptr),
1538 mFirstFreeFCItem(nullptr),
1539 mFCItemsInUse(0),
1540 mCurrentDepth(0),
1541 mQuotesDirty(false),
1542 mCountersDirty(false),
1543 mIsDestroyingFrameTree(false),
1544 mHasRootAbsPosContainingBlock(false),
1545 mAlwaysCreateFramesForIgnorableWhitespace(false) {
1546 #ifdef DEBUG
1547 static bool gFirstTime = true;
1548 if (gFirstTime) {
1549 gFirstTime = false;
1550 char* flags = PR_GetEnv("GECKO_FRAMECTOR_DEBUG_FLAGS");
1551 if (flags) {
1552 bool error = false;
1553 for (;;) {
1554 char* comma = PL_strchr(flags, ',');
1555 if (comma) *comma = '\0';
1556
1557 bool found = false;
1558 FrameCtorDebugFlags* flag = gFlags;
1559 FrameCtorDebugFlags* limit = gFlags + NUM_DEBUG_FLAGS;
1560 while (flag < limit) {
1561 if (PL_strcasecmp(flag->name, flags) == 0) {
1562 *(flag->on) = true;
1563 printf("nsCSSFrameConstructor: setting %s debug flag on\n",
1564 flag->name);
1565 found = true;
1566 break;
1567 }
1568 ++flag;
1569 }
1570
1571 if (!found) error = true;
1572
1573 if (!comma) break;
1574
1575 *comma = ',';
1576 flags = comma + 1;
1577 }
1578
1579 if (error) {
1580 printf("Here are the available GECKO_FRAMECTOR_DEBUG_FLAGS:\n");
1581 FrameCtorDebugFlags* flag = gFlags;
1582 FrameCtorDebugFlags* limit = gFlags + NUM_DEBUG_FLAGS;
1583 while (flag < limit) {
1584 printf(" %s\n", flag->name);
1585 ++flag;
1586 }
1587 printf(
1588 "Note: GECKO_FRAMECTOR_DEBUG_FLAGS is a comma separated list of "
1589 "flag\n");
1590 printf("names (no whitespace)\n");
1591 }
1592 }
1593 }
1594 #endif
1595 }
1596
NotifyDestroyingFrame(nsIFrame * aFrame)1597 void nsCSSFrameConstructor::NotifyDestroyingFrame(nsIFrame* aFrame) {
1598 if (aFrame->GetStateBits() & NS_FRAME_GENERATED_CONTENT) {
1599 if (mQuoteList.DestroyNodesFor(aFrame)) QuotesDirty();
1600 }
1601
1602 if (aFrame->HasAnyStateBits(NS_FRAME_HAS_CSS_COUNTER_STYLE) &&
1603 mCounterManager.DestroyNodesFor(aFrame)) {
1604 // Technically we don't need to update anything if we destroyed only
1605 // USE nodes. However, this is unlikely to happen in the real world
1606 // since USE nodes generally go along with INCREMENT nodes.
1607 CountersDirty();
1608 }
1609
1610 RestyleManager()->NotifyDestroyingFrame(aFrame);
1611
1612 nsFrameManager::NotifyDestroyingFrame(aFrame);
1613 }
1614
1615 struct nsGenConInitializer {
1616 nsAutoPtr<nsGenConNode> mNode;
1617 nsGenConList* mList;
1618 void (nsCSSFrameConstructor::*mDirtyAll)();
1619
nsGenConInitializernsGenConInitializer1620 nsGenConInitializer(nsGenConNode* aNode, nsGenConList* aList,
1621 void (nsCSSFrameConstructor::*aDirtyAll)())
1622 : mNode(aNode), mList(aList), mDirtyAll(aDirtyAll) {}
1623 };
1624
CreateGenConTextNode(nsFrameConstructorState & aState,const nsString & aString,RefPtr<nsTextNode> * aText,nsGenConInitializer * aInitializer)1625 already_AddRefed<nsIContent> nsCSSFrameConstructor::CreateGenConTextNode(
1626 nsFrameConstructorState& aState, const nsString& aString,
1627 RefPtr<nsTextNode>* aText, nsGenConInitializer* aInitializer) {
1628 RefPtr<nsTextNode> content = new nsTextNode(mDocument->NodeInfoManager());
1629 content->SetText(aString, false);
1630 if (aText) {
1631 *aText = content;
1632 }
1633 if (aInitializer) {
1634 content->SetProperty(nsGkAtoms::genConInitializerProperty, aInitializer,
1635 nsINode::DeleteProperty<nsGenConInitializer>);
1636 aState.mGeneratedTextNodesWithInitializer.AppendObject(content);
1637 }
1638 return content.forget();
1639 }
1640
CreateGeneratedContent(nsFrameConstructorState & aState,Element * aParentContent,nsStyleContext * aStyleContext,uint32_t aContentIndex)1641 already_AddRefed<nsIContent> nsCSSFrameConstructor::CreateGeneratedContent(
1642 nsFrameConstructorState& aState, Element* aParentContent,
1643 nsStyleContext* aStyleContext, uint32_t aContentIndex) {
1644 // Get the content value
1645 const nsStyleContentData& data =
1646 aStyleContext->StyleContent()->ContentAt(aContentIndex);
1647 nsStyleContentType type = data.GetType();
1648
1649 switch (type) {
1650 case eStyleContentType_Image: {
1651 imgRequestProxy* image = data.GetImage();
1652 if (!image) {
1653 // CSS had something specified that couldn't be converted to an
1654 // image object
1655 return nullptr;
1656 }
1657
1658 // Create an image content object and pass it the image request.
1659 // XXX Check if it's an image type we can handle...
1660
1661 return CreateGenConImageContent(mDocument, image);
1662 }
1663
1664 case eStyleContentType_String:
1665 return CreateGenConTextNode(aState, nsDependentString(data.GetString()),
1666 nullptr, nullptr);
1667
1668 case eStyleContentType_Attr: {
1669 RefPtr<nsAtom> attrName;
1670 int32_t attrNameSpace = kNameSpaceID_None;
1671 nsAutoString contentString(data.GetString());
1672
1673 int32_t barIndex =
1674 contentString.FindChar('|'); // CSS namespace delimiter
1675 if (-1 != barIndex) {
1676 nsAutoString nameSpaceVal;
1677 contentString.Left(nameSpaceVal, barIndex);
1678 nsresult error;
1679 attrNameSpace = nameSpaceVal.ToInteger(&error);
1680 contentString.Cut(0, barIndex + 1);
1681 if (contentString.Length()) {
1682 if (mDocument->IsHTMLDocument() && aParentContent->IsHTMLElement()) {
1683 ToLowerCase(contentString);
1684 }
1685 attrName = NS_Atomize(contentString);
1686 }
1687 } else {
1688 if (mDocument->IsHTMLDocument() && aParentContent->IsHTMLElement()) {
1689 ToLowerCase(contentString);
1690 }
1691 attrName = NS_Atomize(contentString);
1692 }
1693
1694 if (!attrName) {
1695 return nullptr;
1696 }
1697
1698 nsCOMPtr<nsIContent> content;
1699 NS_NewAttributeContent(mDocument->NodeInfoManager(), attrNameSpace,
1700 attrName, getter_AddRefs(content));
1701 return content.forget();
1702 }
1703
1704 case eStyleContentType_Counter:
1705 case eStyleContentType_Counters: {
1706 nsStyleContentData::CounterFunction* counters = data.GetCounters();
1707 nsCounterList* counterList =
1708 mCounterManager.CounterListFor(counters->mIdent);
1709
1710 nsCounterUseNode* node = new nsCounterUseNode(
1711 counters, aContentIndex, type == eStyleContentType_Counters);
1712
1713 nsGenConInitializer* initializer = new nsGenConInitializer(
1714 node, counterList, &nsCSSFrameConstructor::CountersDirty);
1715 return CreateGenConTextNode(aState, EmptyString(), &node->mText,
1716 initializer);
1717 }
1718
1719 case eStyleContentType_OpenQuote:
1720 case eStyleContentType_CloseQuote:
1721 case eStyleContentType_NoOpenQuote:
1722 case eStyleContentType_NoCloseQuote: {
1723 nsQuoteNode* node = new nsQuoteNode(type, aContentIndex);
1724
1725 nsGenConInitializer* initializer = new nsGenConInitializer(
1726 node, &mQuoteList, &nsCSSFrameConstructor::QuotesDirty);
1727 return CreateGenConTextNode(aState, EmptyString(), &node->mText,
1728 initializer);
1729 }
1730
1731 case eStyleContentType_AltContent: {
1732 // Use the "alt" attribute; if that fails and the node is an HTML
1733 // <input>, try the value attribute and then fall back to some default
1734 // localized text we have.
1735 // XXX what if the 'alt' attribute is added later, how will we
1736 // detect that and do the right thing here?
1737 if (aParentContent->HasAttr(kNameSpaceID_None, nsGkAtoms::alt)) {
1738 nsCOMPtr<nsIContent> content;
1739 NS_NewAttributeContent(mDocument->NodeInfoManager(), kNameSpaceID_None,
1740 nsGkAtoms::alt, getter_AddRefs(content));
1741 return content.forget();
1742 }
1743
1744 if (aParentContent->IsHTMLElement(nsGkAtoms::input)) {
1745 if (aParentContent->HasAttr(kNameSpaceID_None, nsGkAtoms::value)) {
1746 nsCOMPtr<nsIContent> content;
1747 NS_NewAttributeContent(mDocument->NodeInfoManager(),
1748 kNameSpaceID_None, nsGkAtoms::value,
1749 getter_AddRefs(content));
1750 return content.forget();
1751 }
1752
1753 nsAutoString temp;
1754 nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
1755 "Submit", temp);
1756 return CreateGenConTextNode(aState, temp, nullptr, nullptr);
1757 }
1758
1759 break;
1760 }
1761
1762 case eStyleContentType_Uninitialized:
1763 NS_NOTREACHED("uninitialized content type");
1764 return nullptr;
1765 }
1766
1767 return nullptr;
1768 }
1769
1770 /*
1771 * aParentFrame - the frame that should be the parent of the generated
1772 * content. This is the frame for the corresponding content node,
1773 * which must not be a leaf frame.
1774 *
1775 * Any items created are added to aItems.
1776 *
1777 * We create an XML element (tag _moz_generated_content_before or
1778 * _moz_generated_content_after) representing the pseudoelement. We
1779 * create a DOM node for each 'content' item and make those nodes the
1780 * children of the XML element. Then we create a frame subtree for
1781 * the XML element as if it were a regular child of
1782 * aParentFrame/aParentContent, giving the XML element the ::before or
1783 * ::after style.
1784 */
CreateGeneratedContentItem(nsFrameConstructorState & aState,nsContainerFrame * aParentFrame,Element * aParentContent,nsStyleContext * aStyleContext,CSSPseudoElementType aPseudoElement,FrameConstructionItemList & aItems)1785 void nsCSSFrameConstructor::CreateGeneratedContentItem(
1786 nsFrameConstructorState& aState, nsContainerFrame* aParentFrame,
1787 Element* aParentContent, nsStyleContext* aStyleContext,
1788 CSSPseudoElementType aPseudoElement, FrameConstructionItemList& aItems) {
1789 MOZ_ASSERT(aPseudoElement == CSSPseudoElementType::before ||
1790 aPseudoElement == CSSPseudoElementType::after,
1791 "unexpected aPseudoElement");
1792
1793 StyleSetHandle styleSet = mPresShell->StyleSet();
1794
1795 // Probe for the existence of the pseudo-element
1796 RefPtr<nsStyleContext> pseudoStyleContext;
1797 pseudoStyleContext = styleSet->ProbePseudoElementStyle(
1798 aParentContent, aPseudoElement, aStyleContext, aState.mTreeMatchContext);
1799 if (!pseudoStyleContext) return;
1800
1801 bool isBefore = aPseudoElement == CSSPseudoElementType::before;
1802
1803 // |ProbePseudoStyleFor| checked the 'display' property and the
1804 // |ContentCount()| of the 'content' property for us.
1805 RefPtr<NodeInfo> nodeInfo;
1806 nsAtom* elemName = isBefore ? nsGkAtoms::mozgeneratedcontentbefore
1807 : nsGkAtoms::mozgeneratedcontentafter;
1808 nodeInfo = mDocument->NodeInfoManager()->GetNodeInfo(
1809 elemName, nullptr, kNameSpaceID_None, nsINode::ELEMENT_NODE);
1810 nsCOMPtr<Element> container;
1811 nsresult rv = NS_NewXMLElement(getter_AddRefs(container), nodeInfo.forget());
1812 if (NS_FAILED(rv)) return;
1813
1814 // Cleared when the pseudo is unbound from the tree, so no need to store a
1815 // strong reference, nor a destructor.
1816 nsAtom* property = isBefore ? nsGkAtoms::beforePseudoProperty
1817 : nsGkAtoms::afterPseudoProperty;
1818 aParentContent->SetProperty(property, container.get());
1819
1820 container->SetIsNativeAnonymousRoot();
1821 container->SetPseudoElementType(aPseudoElement);
1822
1823 // If the parent is in a shadow tree, make sure we don't
1824 // bind with a document because shadow roots and its descendants
1825 // are not in document.
1826 nsIDocument* bindDocument =
1827 aParentContent->HasFlag(NODE_IS_IN_SHADOW_TREE) ? nullptr : mDocument;
1828 rv =
1829 container->BindToTree(bindDocument, aParentContent, aParentContent, true);
1830 if (NS_FAILED(rv)) {
1831 container->UnbindFromTree();
1832 return;
1833 }
1834
1835 // Servo has already eagerly computed the style for the container, so we can
1836 // just stick the style on the element and avoid an additional traversal.
1837 //
1838 // We don't do this for pseudos that may trigger animations or transitions,
1839 // since those need to be kicked off by the traversal machinery.
1840 auto* servoStyle = pseudoStyleContext->GetAsServo();
1841 if (servoStyle) {
1842 bool hasServoAnimations =
1843 Servo_ComputedValues_SpecifiesAnimationsOrTransitions(servoStyle);
1844 if (!hasServoAnimations) {
1845 Servo_SetExplicitStyle(container, servoStyle);
1846 } else {
1847 // If animations are involved, we avoid the SetExplicitStyle optimization
1848 // above. We need to grab style with animations from the pseudo element
1849 // and replace old one.
1850 mPresShell->StyleSet()->AsServo()->StyleNewSubtree(container);
1851 pseudoStyleContext = styleSet->AsServo()->ResolveServoStyle(container);
1852 }
1853 } else {
1854 #ifdef MOZ_OLD_STYLE
1855 mozilla::GeckoRestyleManager* geckoRM = RestyleManager()->AsGecko();
1856 GeckoRestyleManager::ReframingStyleContexts* rsc =
1857 geckoRM->GetReframingStyleContexts();
1858
1859 if (rsc) {
1860 RefPtr<GeckoStyleContext> newContext =
1861 GeckoStyleContext::TakeRef(pseudoStyleContext.forget());
1862 if (auto* oldStyleContext = rsc->Get(container, aPseudoElement)) {
1863 GeckoRestyleManager::TryInitiatingTransition(
1864 aState.mPresContext, container, oldStyleContext, &newContext);
1865 } else {
1866 aState.mPresContext->TransitionManager()->PruneCompletedTransitions(
1867 aParentContent->AsElement(), aPseudoElement, newContext);
1868 }
1869 pseudoStyleContext = newContext.forget();
1870 }
1871 #else
1872 MOZ_CRASH("old style system disabled");
1873 #endif
1874 }
1875
1876 uint32_t contentCount = pseudoStyleContext->StyleContent()->ContentCount();
1877 for (uint32_t contentIndex = 0; contentIndex < contentCount; contentIndex++) {
1878 nsCOMPtr<nsIContent> content = CreateGeneratedContent(
1879 aState, aParentContent, pseudoStyleContext, contentIndex);
1880 if (content) {
1881 // We don't strictly have to set NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE
1882 // here; it would get set under AppendChildTo. But AppendChildTo might
1883 // think that we're going from not being anonymous to being anonymous and
1884 // do some extra work; setting the flag here avoids that.
1885 content->SetFlags(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE);
1886 container->AppendChildTo(content, false);
1887 if (content->IsElement() && servoStyle) {
1888 // If we created any children elements, Servo needs to traverse them,
1889 // but the root is already set up.
1890 mPresShell->StyleSet()->AsServo()->StyleNewSubtree(
1891 content->AsElement());
1892 }
1893 }
1894 }
1895
1896 AddFrameConstructionItemsInternal(aState, container, aParentFrame, elemName,
1897 kNameSpaceID_None, true, pseudoStyleContext,
1898 ITEM_IS_GENERATED_CONTENT, nullptr, aItems);
1899 }
1900
1901 /****************************************************
1902 ** BEGIN TABLE SECTION
1903 ****************************************************/
1904
1905 // The term pseudo frame is being used instead of anonymous frame, since
1906 // anonymous frame has been used elsewhere to refer to frames that have
1907 // generated content
1908
1909 // Return whether the given frame is a table pseudo-frame. Note that
1910 // cell-content and table-outer frames have pseudo-types, but are always
1911 // created, even for non-anonymous cells and tables respectively. So for those
1912 // we have to examine the cell or table frame to see whether it's a pseudo
1913 // frame. In particular, a lone table caption will have a table wrapper as its
1914 // parent, but will also trigger construction of an empty inner table, which
1915 // will be the one we can examine to see whether the wrapper was a pseudo-frame.
IsTablePseudo(nsIFrame * aFrame)1916 static bool IsTablePseudo(nsIFrame* aFrame) {
1917 nsAtom* pseudoType = aFrame->StyleContext()->GetPseudo();
1918 return pseudoType &&
1919 (pseudoType == nsCSSAnonBoxes::table ||
1920 pseudoType == nsCSSAnonBoxes::inlineTable ||
1921 pseudoType == nsCSSAnonBoxes::tableColGroup ||
1922 pseudoType == nsCSSAnonBoxes::tableRowGroup ||
1923 pseudoType == nsCSSAnonBoxes::tableRow ||
1924 pseudoType == nsCSSAnonBoxes::tableCell ||
1925 (pseudoType == nsCSSAnonBoxes::cellContent &&
1926 aFrame->GetParent()->StyleContext()->GetPseudo() ==
1927 nsCSSAnonBoxes::tableCell) ||
1928 (pseudoType == nsCSSAnonBoxes::tableWrapper &&
1929 (aFrame->PrincipalChildList()
1930 .FirstChild()
1931 ->StyleContext()
1932 ->GetPseudo() == nsCSSAnonBoxes::table ||
1933 aFrame->PrincipalChildList()
1934 .FirstChild()
1935 ->StyleContext()
1936 ->GetPseudo() == nsCSSAnonBoxes::inlineTable)));
1937 }
1938
IsRubyPseudo(nsIFrame * aFrame)1939 static bool IsRubyPseudo(nsIFrame* aFrame) {
1940 return RubyUtils::IsRubyPseudo(aFrame->StyleContext()->GetPseudo());
1941 }
1942
IsTableOrRubyPseudo(nsIFrame * aFrame)1943 static bool IsTableOrRubyPseudo(nsIFrame* aFrame) {
1944 return IsTablePseudo(aFrame) || IsRubyPseudo(aFrame);
1945 }
1946
1947 /* static */
GetParentType(LayoutFrameType aFrameType)1948 nsCSSFrameConstructor::ParentType nsCSSFrameConstructor::GetParentType(
1949 LayoutFrameType aFrameType) {
1950 if (aFrameType == LayoutFrameType::Table) {
1951 return eTypeTable;
1952 }
1953 if (aFrameType == LayoutFrameType::TableRowGroup) {
1954 return eTypeRowGroup;
1955 }
1956 if (aFrameType == LayoutFrameType::TableRow) {
1957 return eTypeRow;
1958 }
1959 if (aFrameType == LayoutFrameType::TableColGroup) {
1960 return eTypeColGroup;
1961 }
1962 if (aFrameType == LayoutFrameType::RubyBaseContainer) {
1963 return eTypeRubyBaseContainer;
1964 }
1965 if (aFrameType == LayoutFrameType::RubyTextContainer) {
1966 return eTypeRubyTextContainer;
1967 }
1968 if (aFrameType == LayoutFrameType::Ruby) {
1969 return eTypeRuby;
1970 }
1971
1972 return eTypeBlock;
1973 }
1974
AdjustCaptionParentFrame(nsContainerFrame * aParentFrame)1975 static nsContainerFrame* AdjustCaptionParentFrame(
1976 nsContainerFrame* aParentFrame) {
1977 if (aParentFrame->IsTableFrame()) {
1978 return aParentFrame->GetParent();
1979 }
1980 return aParentFrame;
1981 }
1982
1983 /**
1984 * If the parent frame is a |tableFrame| and the child is a
1985 * |captionFrame|, then we want to insert the frames beneath the
1986 * |tableFrame|'s parent frame. Returns |true| if the parent frame
1987 * needed to be fixed up.
1988 */
GetCaptionAdjustedParent(nsContainerFrame * aParentFrame,const nsIFrame * aChildFrame,nsContainerFrame ** aAdjParentFrame)1989 static bool GetCaptionAdjustedParent(nsContainerFrame* aParentFrame,
1990 const nsIFrame* aChildFrame,
1991 nsContainerFrame** aAdjParentFrame) {
1992 *aAdjParentFrame = aParentFrame;
1993 bool haveCaption = false;
1994
1995 if (aChildFrame->IsTableCaption()) {
1996 haveCaption = true;
1997 *aAdjParentFrame = ::AdjustCaptionParentFrame(aParentFrame);
1998 }
1999 return haveCaption;
2000 }
2001
AdjustParentFrame(nsContainerFrame ** aParentFrame,const FrameConstructionData * aFCData,nsStyleContext * aStyleContext)2002 void nsCSSFrameConstructor::AdjustParentFrame(
2003 nsContainerFrame** aParentFrame, const FrameConstructionData* aFCData,
2004 nsStyleContext* aStyleContext) {
2005 NS_PRECONDITION(aStyleContext, "Must have child's style context");
2006 NS_PRECONDITION(aFCData, "Must have frame construction data");
2007
2008 bool tablePart = ((aFCData->mBits & FCDATA_IS_TABLE_PART) != 0);
2009
2010 if (tablePart &&
2011 aStyleContext->StyleDisplay()->mDisplay == StyleDisplay::TableCaption) {
2012 *aParentFrame = ::AdjustCaptionParentFrame(*aParentFrame);
2013 }
2014 }
2015
2016 // Pull all the captions present in aItems out into aCaptions
PullOutCaptionFrames(nsFrameItems & aItems,nsFrameItems & aCaptions)2017 static void PullOutCaptionFrames(nsFrameItems& aItems,
2018 nsFrameItems& aCaptions) {
2019 nsIFrame* child = aItems.FirstChild();
2020 while (child) {
2021 nsIFrame* nextSibling = child->GetNextSibling();
2022 if (child->IsTableCaption()) {
2023 aItems.RemoveFrame(child);
2024 aCaptions.AddChild(child);
2025 }
2026 child = nextSibling;
2027 }
2028 }
2029
2030 // Construct the outer, inner table frames and the children frames for the
2031 // table.
2032 // XXX Page break frames for pseudo table frames are not constructed to avoid
2033 // the risk associated with revising the pseudo frame mechanism. The long term
2034 // solution of having frames handle page-break-before/after will solve the
2035 // problem.
ConstructTable(nsFrameConstructorState & aState,FrameConstructionItem & aItem,nsContainerFrame * aParentFrame,const nsStyleDisplay * aDisplay,nsFrameItems & aFrameItems)2036 nsIFrame* nsCSSFrameConstructor::ConstructTable(nsFrameConstructorState& aState,
2037 FrameConstructionItem& aItem,
2038 nsContainerFrame* aParentFrame,
2039 const nsStyleDisplay* aDisplay,
2040 nsFrameItems& aFrameItems) {
2041 NS_PRECONDITION(aDisplay->mDisplay == StyleDisplay::Table ||
2042 aDisplay->mDisplay == StyleDisplay::InlineTable,
2043 "Unexpected call");
2044
2045 nsIContent* const content = aItem.mContent;
2046 nsStyleContext* const styleContext = aItem.mStyleContext;
2047 const uint32_t nameSpaceID = aItem.mNameSpaceID;
2048
2049 // create the pseudo SC for the table wrapper as a child of the inner SC
2050 RefPtr<nsStyleContext> outerStyleContext;
2051 outerStyleContext =
2052 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
2053 nsCSSAnonBoxes::tableWrapper, styleContext);
2054
2055 // Create the table wrapper frame which holds the caption and inner table
2056 // frame
2057 nsContainerFrame* newFrame;
2058 if (kNameSpaceID_MathML == nameSpaceID)
2059 newFrame = NS_NewMathMLmtableOuterFrame(mPresShell, outerStyleContext);
2060 else
2061 newFrame = NS_NewTableWrapperFrame(mPresShell, outerStyleContext);
2062
2063 nsContainerFrame* geometricParent = aState.GetGeometricParent(
2064 outerStyleContext->StyleDisplay(), aParentFrame);
2065
2066 // Init the table wrapper frame
2067 InitAndRestoreFrame(aState, content, geometricParent, newFrame);
2068
2069 // Create the inner table frame
2070 nsContainerFrame* innerFrame;
2071 if (kNameSpaceID_MathML == nameSpaceID)
2072 innerFrame = NS_NewMathMLmtableFrame(mPresShell, styleContext);
2073 else
2074 innerFrame = NS_NewTableFrame(mPresShell, styleContext);
2075
2076 InitAndRestoreFrame(aState, content, newFrame, innerFrame);
2077 innerFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
2078
2079 // Put the newly created frames into the right child list
2080 SetInitialSingleChild(newFrame, innerFrame);
2081
2082 aState.AddChild(newFrame, aFrameItems, content, aParentFrame);
2083
2084 if (!mRootElementFrame) {
2085 // The frame we're constructing will be the root element frame.
2086 // Set mRootElementFrame before processing children.
2087 mRootElementFrame = newFrame;
2088 }
2089
2090 nsFrameItems childItems;
2091
2092 // Process children
2093 nsFrameConstructorSaveState absoluteSaveState;
2094 const nsStyleDisplay* display = outerStyleContext->StyleDisplay();
2095
2096 // Mark the table frame as an absolute container if needed
2097 newFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
2098 if (display->IsAbsPosContainingBlock(newFrame)) {
2099 aState.PushAbsoluteContainingBlock(newFrame, newFrame, absoluteSaveState);
2100 }
2101 NS_ASSERTION(aItem.mAnonChildren.IsEmpty(),
2102 "nsIAnonymousContentCreator::CreateAnonymousContent "
2103 "implementations for table frames are not currently expected "
2104 "to output a list where the items have their own children");
2105 if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) {
2106 ConstructFramesFromItemList(
2107 aState, aItem.mChildItems, innerFrame,
2108 aItem.mFCData->mBits & FCDATA_IS_WRAPPER_ANON_BOX, childItems);
2109 } else {
2110 ProcessChildren(aState, content, styleContext, innerFrame, true, childItems,
2111 false, aItem.mPendingBinding);
2112 }
2113
2114 nsFrameItems captionItems;
2115 PullOutCaptionFrames(childItems, captionItems);
2116
2117 // Set the inner table frame's initial primary list
2118 innerFrame->SetInitialChildList(kPrincipalList, childItems);
2119
2120 // Set the table wrapper frame's secondary childlist lists
2121 if (captionItems.NotEmpty()) {
2122 newFrame->SetInitialChildList(nsIFrame::kCaptionList, captionItems);
2123 }
2124
2125 return newFrame;
2126 }
2127
MakeTablePartAbsoluteContainingBlockIfNeeded(nsFrameConstructorState & aState,const nsStyleDisplay * aDisplay,nsFrameConstructorSaveState & aAbsSaveState,nsContainerFrame * aFrame)2128 static void MakeTablePartAbsoluteContainingBlockIfNeeded(
2129 nsFrameConstructorState& aState, const nsStyleDisplay* aDisplay,
2130 nsFrameConstructorSaveState& aAbsSaveState, nsContainerFrame* aFrame) {
2131 // If we're positioned, then we need to become an absolute containing block
2132 // for any absolutely positioned children and register for post-reflow fixup.
2133 //
2134 // Note that usually if a frame type can be an absolute containing block, we
2135 // always set NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN, whether it actually is or
2136 // not. However, in this case flag serves the additional purpose of indicating
2137 // that the frame was registered with its table frame. This allows us to avoid
2138 // the overhead of unregistering the frame in most cases.
2139 if (aDisplay->IsAbsPosContainingBlock(aFrame)) {
2140 aFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
2141 aState.PushAbsoluteContainingBlock(aFrame, aFrame, aAbsSaveState);
2142 nsTableFrame::RegisterPositionedTablePart(aFrame);
2143 }
2144 }
2145
ConstructTableRowOrRowGroup(nsFrameConstructorState & aState,FrameConstructionItem & aItem,nsContainerFrame * aParentFrame,const nsStyleDisplay * aDisplay,nsFrameItems & aFrameItems)2146 nsIFrame* nsCSSFrameConstructor::ConstructTableRowOrRowGroup(
2147 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
2148 nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay,
2149 nsFrameItems& aFrameItems) {
2150 MOZ_ASSERT(aDisplay->mDisplay == StyleDisplay::TableRow ||
2151 aDisplay->mDisplay == StyleDisplay::TableRowGroup ||
2152 aDisplay->mDisplay == StyleDisplay::TableFooterGroup ||
2153 aDisplay->mDisplay == StyleDisplay::TableHeaderGroup,
2154 "Not a row or row group");
2155 MOZ_ASSERT(aItem.mStyleContext->StyleDisplay() == aDisplay,
2156 "Display style doesn't match style context");
2157 nsIContent* const content = aItem.mContent;
2158 nsStyleContext* const styleContext = aItem.mStyleContext;
2159 const uint32_t nameSpaceID = aItem.mNameSpaceID;
2160
2161 nsContainerFrame* newFrame;
2162 if (aDisplay->mDisplay == StyleDisplay::TableRow) {
2163 if (kNameSpaceID_MathML == nameSpaceID)
2164 newFrame = NS_NewMathMLmtrFrame(mPresShell, styleContext);
2165 else
2166 newFrame = NS_NewTableRowFrame(mPresShell, styleContext);
2167 } else {
2168 newFrame = NS_NewTableRowGroupFrame(mPresShell, styleContext);
2169 }
2170
2171 InitAndRestoreFrame(aState, content, aParentFrame, newFrame);
2172
2173 nsFrameConstructorSaveState absoluteSaveState;
2174 MakeTablePartAbsoluteContainingBlockIfNeeded(aState, aDisplay,
2175 absoluteSaveState, newFrame);
2176
2177 nsFrameItems childItems;
2178 NS_ASSERTION(aItem.mAnonChildren.IsEmpty(),
2179 "nsIAnonymousContentCreator::CreateAnonymousContent "
2180 "implementations for table frames are not currently expected "
2181 "to output a list where the items have their own children");
2182 if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) {
2183 ConstructFramesFromItemList(
2184 aState, aItem.mChildItems, newFrame,
2185 aItem.mFCData->mBits & FCDATA_IS_WRAPPER_ANON_BOX, childItems);
2186 } else {
2187 ProcessChildren(aState, content, styleContext, newFrame, true, childItems,
2188 false, aItem.mPendingBinding);
2189 }
2190
2191 newFrame->SetInitialChildList(kPrincipalList, childItems);
2192 aFrameItems.AddChild(newFrame);
2193 return newFrame;
2194 }
2195
ConstructTableCol(nsFrameConstructorState & aState,FrameConstructionItem & aItem,nsContainerFrame * aParentFrame,const nsStyleDisplay * aStyleDisplay,nsFrameItems & aFrameItems)2196 nsIFrame* nsCSSFrameConstructor::ConstructTableCol(
2197 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
2198 nsContainerFrame* aParentFrame, const nsStyleDisplay* aStyleDisplay,
2199 nsFrameItems& aFrameItems) {
2200 nsIContent* const content = aItem.mContent;
2201 nsStyleContext* const styleContext = aItem.mStyleContext;
2202
2203 nsTableColFrame* colFrame = NS_NewTableColFrame(mPresShell, styleContext);
2204 InitAndRestoreFrame(aState, content, aParentFrame, colFrame);
2205
2206 NS_ASSERTION(colFrame->StyleContext() == styleContext,
2207 "Unexpected style context");
2208
2209 aFrameItems.AddChild(colFrame);
2210
2211 // construct additional col frames if the col frame has a span > 1
2212 int32_t span = colFrame->GetSpan();
2213 for (int32_t spanX = 1; spanX < span; spanX++) {
2214 nsTableColFrame* newCol = NS_NewTableColFrame(mPresShell, styleContext);
2215 InitAndRestoreFrame(aState, content, aParentFrame, newCol, false);
2216 aFrameItems.LastChild()->SetNextContinuation(newCol);
2217 newCol->SetPrevContinuation(aFrameItems.LastChild());
2218 aFrameItems.AddChild(newCol);
2219 newCol->SetColType(eColAnonymousCol);
2220 }
2221
2222 return colFrame;
2223 }
2224
ConstructTableCell(nsFrameConstructorState & aState,FrameConstructionItem & aItem,nsContainerFrame * aParentFrame,const nsStyleDisplay * aDisplay,nsFrameItems & aFrameItems)2225 nsIFrame* nsCSSFrameConstructor::ConstructTableCell(
2226 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
2227 nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay,
2228 nsFrameItems& aFrameItems) {
2229 MOZ_ASSERT(aDisplay->mDisplay == StyleDisplay::TableCell, "Unexpected call");
2230
2231 nsIContent* const content = aItem.mContent;
2232 nsStyleContext* const styleContext = aItem.mStyleContext;
2233 const uint32_t nameSpaceID = aItem.mNameSpaceID;
2234
2235 nsTableFrame* tableFrame =
2236 static_cast<nsTableRowFrame*>(aParentFrame)->GetTableFrame();
2237 nsContainerFrame* newFrame;
2238 // <mtable> is border separate in mathml.css and the MathML code doesn't
2239 // implement border collapse. For those users who style <mtable> with border
2240 // collapse, give them the default non-MathML table frames that understand
2241 // border collapse. This won't break us because MathML table frames are all
2242 // subclasses of the default table code, and so we can freely mix <mtable>
2243 // with <mtr> or <tr>, <mtd> or <td>. What will happen is just that non-MathML
2244 // frames won't understand MathML attributes and will therefore miss the
2245 // special handling that the MathML code does.
2246 if (kNameSpaceID_MathML == nameSpaceID && !tableFrame->IsBorderCollapse()) {
2247 newFrame = NS_NewMathMLmtdFrame(mPresShell, styleContext, tableFrame);
2248 } else {
2249 // Warning: If you change this and add a wrapper frame around table cell
2250 // frames, make sure Bug 368554 doesn't regress!
2251 // See IsInAutoWidthTableCellForQuirk() in nsImageFrame.cpp.
2252 newFrame = NS_NewTableCellFrame(mPresShell, styleContext, tableFrame);
2253 }
2254
2255 // Initialize the table cell frame
2256 InitAndRestoreFrame(aState, content, aParentFrame, newFrame);
2257 newFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
2258
2259 // Resolve pseudo style and initialize the body cell frame
2260 RefPtr<nsStyleContext> innerPseudoStyle;
2261 innerPseudoStyle = mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
2262 nsCSSAnonBoxes::cellContent, styleContext);
2263
2264 // Create a block frame that will format the cell's content
2265 bool isBlock;
2266 nsContainerFrame* cellInnerFrame;
2267 if (kNameSpaceID_MathML == nameSpaceID) {
2268 cellInnerFrame = NS_NewMathMLmtdInnerFrame(mPresShell, innerPseudoStyle);
2269 isBlock = false;
2270 } else {
2271 cellInnerFrame = NS_NewBlockFormattingContext(mPresShell, innerPseudoStyle);
2272 isBlock = true;
2273 }
2274
2275 InitAndRestoreFrame(aState, content, newFrame, cellInnerFrame);
2276
2277 nsFrameConstructorSaveState absoluteSaveState;
2278 MakeTablePartAbsoluteContainingBlockIfNeeded(aState, aDisplay,
2279 absoluteSaveState, newFrame);
2280
2281 nsFrameItems childItems;
2282 NS_ASSERTION(aItem.mAnonChildren.IsEmpty(),
2283 "nsIAnonymousContentCreator::CreateAnonymousContent "
2284 "implementations for table frames are not currently expected "
2285 "to output a list where the items have their own children");
2286 if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) {
2287 // Need to push ourselves as a float containing block.
2288 // XXXbz it might be nice to work on getting the parent
2289 // FrameConstructionItem down into ProcessChildren and just making use of
2290 // the push there, but that's a bit of work.
2291 nsFrameConstructorSaveState floatSaveState;
2292 if (!isBlock) { /* MathML case */
2293 aState.PushFloatContainingBlock(nullptr, floatSaveState);
2294 } else {
2295 aState.PushFloatContainingBlock(cellInnerFrame, floatSaveState);
2296 }
2297
2298 ConstructFramesFromItemList(
2299 aState, aItem.mChildItems, cellInnerFrame,
2300 aItem.mFCData->mBits & FCDATA_IS_WRAPPER_ANON_BOX, childItems);
2301 } else {
2302 // Process the child content
2303 ProcessChildren(aState, content, styleContext, cellInnerFrame, true,
2304 childItems, isBlock, aItem.mPendingBinding);
2305 }
2306
2307 cellInnerFrame->SetInitialChildList(kPrincipalList, childItems);
2308 SetInitialSingleChild(newFrame, cellInnerFrame);
2309 aFrameItems.AddChild(newFrame);
2310 return newFrame;
2311 }
2312
NeedFrameFor(const nsFrameConstructorState & aState,nsIFrame * aParentFrame,nsIContent * aChildContent)2313 static inline bool NeedFrameFor(const nsFrameConstructorState& aState,
2314 nsIFrame* aParentFrame,
2315 nsIContent* aChildContent) {
2316 // XXX the GetContent() != aChildContent check is needed due to bug 135040.
2317 // Remove it once that's fixed.
2318 NS_PRECONDITION(
2319 !aChildContent->GetPrimaryFrame() || aState.mCreatingExtraFrames ||
2320 aChildContent->GetPrimaryFrame()->GetContent() != aChildContent,
2321 "Why did we get called?");
2322
2323 // don't create a whitespace frame if aParentFrame doesn't want it.
2324 // always create frames for children in generated content. counter(),
2325 // quotes, and attr() content can easily change dynamically and we don't
2326 // want to be reconstructing frames. It's not even clear that these
2327 // should be considered ignorable just because they evaluate to
2328 // whitespace.
2329
2330 // We could handle all this in CreateNeededPseudoContainers or some other
2331 // place after we build our frame construction items, but that would involve
2332 // creating frame construction items for whitespace kids of
2333 // eExcludesIgnorableWhitespace frames, where we know we'll be dropping them
2334 // all anyway, and involve an extra walk down the frame construction item
2335 // list.
2336 if ((aParentFrame &&
2337 (!aParentFrame->IsFrameOfType(nsIFrame::eExcludesIgnorableWhitespace) ||
2338 aParentFrame->IsGeneratedContentFrame())) ||
2339 !aChildContent->IsNodeOfType(nsINode::eTEXT)) {
2340 return true;
2341 }
2342
2343 aChildContent->SetFlags(NS_CREATE_FRAME_IF_NON_WHITESPACE |
2344 NS_REFRAME_IF_WHITESPACE);
2345 return !aChildContent->TextIsOnlyWhitespace();
2346 }
2347
2348 /***********************************************
2349 * END TABLE SECTION
2350 ***********************************************/
2351
ConstructDocElementFrame(Element * aDocElement,nsILayoutHistoryState * aFrameState)2352 nsIFrame* nsCSSFrameConstructor::ConstructDocElementFrame(
2353 Element* aDocElement, nsILayoutHistoryState* aFrameState) {
2354 MOZ_ASSERT(GetRootFrame(),
2355 "No viewport? Someone forgot to call ConstructRootFrame!");
2356 MOZ_ASSERT(!mDocElementContainingBlock,
2357 "Shouldn't have a doc element containing block here");
2358
2359 // Resolve a new style context for the viewport since it may be affected
2360 // by a new root element style (e.g. a propagated 'direction').
2361 // @see nsStyleContext::ApplyStyleFixups
2362 {
2363 RefPtr<nsStyleContext> sc =
2364 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
2365 nsCSSAnonBoxes::viewport, nullptr);
2366 GetRootFrame()->SetStyleContextWithoutNotification(sc);
2367 }
2368
2369 // Make sure to call UpdateViewportScrollbarStylesOverride before
2370 // SetUpDocElementContainingBlock, since it sets up our scrollbar state
2371 // properly.
2372 DebugOnly<nsIContent*> propagatedScrollFrom;
2373 if (nsPresContext* presContext = mPresShell->GetPresContext()) {
2374 propagatedScrollFrom = presContext->UpdateViewportScrollbarStylesOverride();
2375 }
2376
2377 SetUpDocElementContainingBlock(aDocElement);
2378
2379 NS_ASSERTION(mDocElementContainingBlock, "Should have parent by now");
2380
2381 TreeMatchContextHolder matchContext(mDocument);
2382 // Initialize the ancestor filter with null for now; we'll push
2383 // aDocElement once we finish resolving style for it.
2384 if (matchContext.Exists()) {
2385 matchContext->InitAncestors(nullptr);
2386 }
2387 nsFrameConstructorState state(
2388 mPresShell, matchContext,
2389 GetAbsoluteContainingBlock(mDocElementContainingBlock, FIXED_POS),
2390 nullptr, nullptr, do_AddRef(aFrameState));
2391
2392 // XXXbz why, exactly?
2393 if (!mTempFrameTreeState)
2394 state.mPresShell->CaptureHistoryState(getter_AddRefs(mTempFrameTreeState));
2395
2396 // Make sure that we'll handle restyles for this document element in
2397 // the future. We need this, because the document element might
2398 // have stale restyle bits from a previous frame constructor for
2399 // this document. Unlike in AddFrameConstructionItems, it's safe to
2400 // unset all element restyle flags, since we don't have any
2401 // siblings.
2402 aDocElement->UnsetRestyleFlagsIfGecko();
2403
2404 // --------- CREATE AREA OR BOX FRAME -------
2405 if (ServoStyleSet* set = mPresShell->StyleSet()->GetAsServo()) {
2406 // Ensure the document element is styled at this point.
2407 if (!aDocElement->HasServoData()) {
2408 // NOTE(emilio): If the root has a non-null binding, we'll stop at the
2409 // document element and won't process any children, loading the bindings
2410 // (or failing to do so) will take care of the rest.
2411 set->StyleNewSubtree(aDocElement);
2412 }
2413 }
2414
2415 // FIXME: Should this use ResolveStyleContext? (The calls in this
2416 // function are the only case in nsCSSFrameConstructor where we don't
2417 // do so for the construction of a style context for an element.)
2418 RefPtr<nsStyleContext> styleContext = mPresShell->StyleSet()->ResolveStyleFor(
2419 aDocElement, nullptr, LazyComputeBehavior::Assert);
2420
2421 const nsStyleDisplay* display = styleContext->StyleDisplay();
2422
2423 // Ensure that our XBL bindings are installed.
2424 if (display->mBinding) {
2425 // Get the XBL loader.
2426 nsresult rv;
2427 bool resolveStyle;
2428
2429 nsXBLService* xblService = nsXBLService::GetInstance();
2430 if (!xblService) {
2431 return nullptr;
2432 }
2433
2434 RefPtr<nsXBLBinding> binding;
2435 rv = xblService->LoadBindings(aDocElement, display->mBinding->GetURI(),
2436 display->mBinding->mExtraData->GetPrincipal(),
2437 getter_AddRefs(binding), &resolveStyle);
2438 if (NS_FAILED(rv) && rv != NS_ERROR_XBL_BLOCKED) {
2439 // Binding will load asynchronously.
2440 return nullptr;
2441 }
2442
2443 if (binding) {
2444 // For backwards compat, keep firing the root's constructor
2445 // after all of its kids' constructors. So tell the binding
2446 // manager about it right now.
2447 mDocument->BindingManager()->AddToAttachedQueue(binding);
2448 }
2449
2450 if (resolveStyle) {
2451 // FIXME: Should this use ResolveStyleContext? (The calls in this
2452 // function are the only case in nsCSSFrameConstructor where we don't do
2453 // so for the construction of a style context for an element.)
2454 styleContext = mPresShell->StyleSet()->ResolveStyleFor(
2455 aDocElement, nullptr, LazyComputeBehavior::Assert);
2456 display = styleContext->StyleDisplay();
2457 }
2458 }
2459
2460 // --------- IF SCROLLABLE WRAP IN SCROLLFRAME --------
2461
2462 NS_ASSERTION(!display->IsScrollableOverflow() ||
2463 state.mPresContext->IsPaginated() ||
2464 propagatedScrollFrom == aDocElement,
2465 "Scrollbars should have been propagated to the viewport");
2466
2467 if (MOZ_UNLIKELY(display->mDisplay == StyleDisplay::None)) {
2468 RegisterDisplayNoneStyleFor(aDocElement, styleContext);
2469 return nullptr;
2470 }
2471
2472 TreeMatchContext::AutoAncestorPusher ancestorPusher(state.mTreeMatchContext);
2473 ancestorPusher.PushAncestorAndStyleScope(aDocElement);
2474
2475 // Make sure to start any background image loads for the root element now.
2476 styleContext->StartBackgroundImageLoads();
2477
2478 nsFrameConstructorSaveState docElementContainingBlockAbsoluteSaveState;
2479 if (mHasRootAbsPosContainingBlock) {
2480 // Push the absolute containing block now so we can absolutely position
2481 // the root element
2482 mDocElementContainingBlock->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
2483 state.PushAbsoluteContainingBlock(
2484 mDocElementContainingBlock, mDocElementContainingBlock,
2485 docElementContainingBlockAbsoluteSaveState);
2486 }
2487
2488 // The rules from CSS 2.1, section 9.2.4, have already been applied
2489 // by the style system, so we can assume that display->mDisplay is
2490 // either NONE, BLOCK, or TABLE.
2491
2492 // contentFrame is the primary frame for the root element. newFrame
2493 // is the frame that will be the child of the initial containing block.
2494 // These are usually the same frame but they can be different, in
2495 // particular if the root frame is positioned, in which case
2496 // contentFrame is the out-of-flow frame and newFrame is the
2497 // placeholder.
2498 nsContainerFrame* contentFrame;
2499 nsIFrame* newFrame;
2500 bool processChildren = false;
2501
2502 nsFrameConstructorSaveState absoluteSaveState;
2503
2504 // Check whether we need to build a XUL box or SVG root frame
2505 #ifdef MOZ_XUL
2506 if (aDocElement->IsXULElement()) {
2507 contentFrame = NS_NewDocElementBoxFrame(mPresShell, styleContext);
2508 InitAndRestoreFrame(state, aDocElement, mDocElementContainingBlock,
2509 contentFrame);
2510 newFrame = contentFrame;
2511 processChildren = true;
2512 } else
2513 #endif
2514 if (aDocElement->IsSVGElement()) {
2515 if (!aDocElement->IsSVGElement(nsGkAtoms::svg)) {
2516 return nullptr;
2517 }
2518 // We're going to call the right function ourselves, so no need to give a
2519 // function to this FrameConstructionData.
2520
2521 // XXXbz on the other hand, if we converted this whole function to
2522 // FrameConstructionData/Item, then we'd need the right function
2523 // here... but would probably be able to get away with less code in this
2524 // function in general.
2525 // Use a null PendingBinding, since our binding is not in fact pending.
2526 static const FrameConstructionData rootSVGData = FCDATA_DECL(0, nullptr);
2527 already_AddRefed<nsStyleContext> extraRef =
2528 RefPtr<nsStyleContext>(styleContext).forget();
2529 AutoFrameConstructionItem item(
2530 this, &rootSVGData, aDocElement, aDocElement->NodeInfo()->NameAtom(),
2531 kNameSpaceID_SVG, nullptr, extraRef, true, nullptr);
2532
2533 nsFrameItems frameItems;
2534 contentFrame = static_cast<nsContainerFrame*>(
2535 ConstructOuterSVG(state, item, mDocElementContainingBlock,
2536 styleContext->StyleDisplay(), frameItems));
2537 newFrame = frameItems.FirstChild();
2538 NS_ASSERTION(frameItems.OnlyChild(), "multiple root element frames");
2539 } else if (display->mDisplay == StyleDisplay::Flex ||
2540 display->mDisplay == StyleDisplay::WebkitBox ||
2541 (StylePrefs::sEmulateMozBoxWithFlex &&
2542 display->mDisplay == StyleDisplay::MozBox)) {
2543 contentFrame = NS_NewFlexContainerFrame(mPresShell, styleContext);
2544 InitAndRestoreFrame(state, aDocElement, mDocElementContainingBlock,
2545 contentFrame);
2546 newFrame = contentFrame;
2547 processChildren = true;
2548
2549 newFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
2550 if (display->IsAbsPosContainingBlock(newFrame)) {
2551 state.PushAbsoluteContainingBlock(contentFrame, newFrame,
2552 absoluteSaveState);
2553 }
2554
2555 } else if (display->mDisplay == StyleDisplay::Grid) {
2556 contentFrame = NS_NewGridContainerFrame(mPresShell, styleContext);
2557 InitAndRestoreFrame(state, aDocElement, mDocElementContainingBlock,
2558 contentFrame);
2559 newFrame = contentFrame;
2560 processChildren = true;
2561
2562 newFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
2563 if (display->IsAbsPosContainingBlock(newFrame)) {
2564 state.PushAbsoluteContainingBlock(contentFrame, newFrame,
2565 absoluteSaveState);
2566 }
2567 } else if (display->mDisplay == StyleDisplay::Table) {
2568 // We're going to call the right function ourselves, so no need to give a
2569 // function to this FrameConstructionData.
2570
2571 // XXXbz on the other hand, if we converted this whole function to
2572 // FrameConstructionData/Item, then we'd need the right function
2573 // here... but would probably be able to get away with less code in this
2574 // function in general.
2575 // Use a null PendingBinding, since our binding is not in fact pending.
2576 static const FrameConstructionData rootTableData = FCDATA_DECL(0, nullptr);
2577 already_AddRefed<nsStyleContext> extraRef =
2578 RefPtr<nsStyleContext>(styleContext).forget();
2579 AutoFrameConstructionItem item(
2580 this, &rootTableData, aDocElement, aDocElement->NodeInfo()->NameAtom(),
2581 kNameSpaceID_None, nullptr, extraRef, true, nullptr);
2582
2583 nsFrameItems frameItems;
2584 // if the document is a table then just populate it.
2585 contentFrame = static_cast<nsContainerFrame*>(
2586 ConstructTable(state, item, mDocElementContainingBlock,
2587 styleContext->StyleDisplay(), frameItems));
2588 newFrame = frameItems.FirstChild();
2589 NS_ASSERTION(frameItems.OnlyChild(), "multiple root element frames");
2590 } else {
2591 MOZ_ASSERT(display->mDisplay == StyleDisplay::Block ||
2592 display->mDisplay == StyleDisplay::FlowRoot,
2593 "Unhandled display type for root element");
2594 contentFrame = NS_NewBlockFormattingContext(mPresShell, styleContext);
2595 nsFrameItems frameItems;
2596 // Use a null PendingBinding, since our binding is not in fact pending.
2597 ConstructBlock(
2598 state, aDocElement,
2599 state.GetGeometricParent(display, mDocElementContainingBlock),
2600 mDocElementContainingBlock, styleContext, &contentFrame, frameItems,
2601 display->IsAbsPosContainingBlock(contentFrame) ? contentFrame : nullptr,
2602 nullptr);
2603 newFrame = frameItems.FirstChild();
2604 NS_ASSERTION(frameItems.OnlyChild(), "multiple root element frames");
2605 }
2606
2607 MOZ_ASSERT(newFrame);
2608 MOZ_ASSERT(contentFrame);
2609
2610 NS_ASSERTION(
2611 processChildren ? !mRootElementFrame : mRootElementFrame == contentFrame,
2612 "unexpected mRootElementFrame");
2613 mRootElementFrame = contentFrame;
2614
2615 // Figure out which frame has the main style for the document element,
2616 // assigning it to mRootElementStyleFrame.
2617 // Backgrounds should be propagated from that frame to the viewport.
2618 contentFrame->GetParentStyleContext(&mRootElementStyleFrame);
2619 bool isChild = mRootElementStyleFrame &&
2620 mRootElementStyleFrame->GetParent() == contentFrame;
2621 if (!isChild) {
2622 mRootElementStyleFrame = mRootElementFrame;
2623 }
2624
2625 if (processChildren) {
2626 // Still need to process the child content
2627 nsFrameItems childItems;
2628
2629 NS_ASSERTION(!nsLayoutUtils::GetAsBlock(contentFrame) &&
2630 !contentFrame->IsFrameOfType(nsIFrame::eSVG),
2631 "Only XUL frames should reach here");
2632 // Use a null PendingBinding, since our binding is not in fact pending.
2633 ProcessChildren(state, aDocElement, styleContext, contentFrame, true,
2634 childItems, false, nullptr);
2635
2636 // Set the initial child lists
2637 contentFrame->SetInitialChildList(kPrincipalList, childItems);
2638 }
2639
2640 // set the primary frame
2641 aDocElement->SetPrimaryFrame(contentFrame);
2642
2643 SetInitialSingleChild(mDocElementContainingBlock, newFrame);
2644
2645 // Create frames for anonymous contents if there is a canvas frame.
2646 if (mDocElementContainingBlock->IsCanvasFrame()) {
2647 ConstructAnonymousContentForCanvas(state, mDocElementContainingBlock,
2648 aDocElement);
2649 }
2650
2651 return newFrame;
2652 }
2653
ConstructRootFrame()2654 nsIFrame* nsCSSFrameConstructor::ConstructRootFrame() {
2655 AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
2656
2657 StyleSetHandle styleSet = mPresShell->StyleSet();
2658
2659 // --------- BUILD VIEWPORT -----------
2660 RefPtr<nsStyleContext> viewportPseudoStyle =
2661 styleSet->ResolveInheritingAnonymousBoxStyle(nsCSSAnonBoxes::viewport,
2662 nullptr);
2663 ViewportFrame* viewportFrame =
2664 NS_NewViewportFrame(mPresShell, viewportPseudoStyle);
2665
2666 // XXXbz do we _have_ to pass a null content pointer to that frame?
2667 // Would it really kill us to pass in the root element or something?
2668 // What would that break?
2669 viewportFrame->Init(nullptr, nullptr, nullptr);
2670
2671 viewportFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
2672
2673 // Bind the viewport frame to the root view
2674 nsView* rootView = mPresShell->GetViewManager()->GetRootView();
2675 viewportFrame->SetView(rootView);
2676
2677 viewportFrame->SyncFrameViewProperties(rootView);
2678 nsContainerFrame::SyncWindowProperties(mPresShell->GetPresContext(),
2679 viewportFrame, rootView, nullptr,
2680 nsContainerFrame::SET_ASYNC);
2681
2682 // Make it an absolute container for fixed-pos elements
2683 viewportFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
2684 viewportFrame->MarkAsAbsoluteContainingBlock();
2685
2686 return viewportFrame;
2687 }
2688
SetUpDocElementContainingBlock(nsIContent * aDocElement)2689 void nsCSSFrameConstructor::SetUpDocElementContainingBlock(
2690 nsIContent* aDocElement) {
2691 NS_PRECONDITION(aDocElement, "No element?");
2692 NS_PRECONDITION(!aDocElement->GetParent(), "Not root content?");
2693 NS_PRECONDITION(aDocElement->GetUncomposedDoc(), "Not in a document?");
2694 NS_PRECONDITION(
2695 aDocElement->GetUncomposedDoc()->GetRootElement() == aDocElement,
2696 "Not the root of the document?");
2697
2698 /*
2699 how the root frame hierarchy should look
2700
2701 Galley presentation, non-XUL, with scrolling:
2702
2703 ViewportFrame [fixed-cb]
2704 nsHTMLScrollFrame
2705 nsCanvasFrame [abs-cb]
2706 root element frame (nsBlockFrame, nsSVGOuterSVGFrame,
2707 nsTableWrapperFrame, nsPlaceholderFrame)
2708
2709 Galley presentation, XUL
2710
2711 ViewportFrame [fixed-cb]
2712 nsRootBoxFrame
2713 root element frame (nsDocElementBoxFrame)
2714
2715 Print presentation, non-XUL
2716
2717 ViewportFrame
2718 nsSimplePageSequenceFrame
2719 nsPageFrame
2720 nsPageContentFrame [fixed-cb]
2721 nsCanvasFrame [abs-cb]
2722 root element frame (nsBlockFrame, nsSVGOuterSVGFrame,
2723 nsTableWrapperFrame, nsPlaceholderFrame)
2724
2725 Print-preview presentation, non-XUL
2726
2727 ViewportFrame
2728 nsHTMLScrollFrame
2729 nsSimplePageSequenceFrame
2730 nsPageFrame
2731 nsPageContentFrame [fixed-cb]
2732 nsCanvasFrame [abs-cb]
2733 root element frame (nsBlockFrame, nsSVGOuterSVGFrame,
2734 nsTableWrapperFrame, nsPlaceholderFrame)
2735
2736 Print/print preview of XUL is not supported.
2737 [fixed-cb]: the default containing block for fixed-pos content
2738 [abs-cb]: the default containing block for abs-pos content
2739
2740 Meaning of nsCSSFrameConstructor fields:
2741 mRootElementFrame is "root element frame". This is the primary frame for
2742 the root element.
2743 mDocElementContainingBlock is the parent of mRootElementFrame
2744 (i.e. nsCanvasFrame or nsRootBoxFrame)
2745
2746 mPageSequenceFrame is the nsSimplePageSequenceFrame, or null if there isn't
2747 one
2748 */
2749
2750 // --------- CREATE ROOT FRAME -------
2751
2752 // Create the root frame. The document element's frame is a child of the
2753 // root frame.
2754 //
2755 // The root frame serves two purposes:
2756 // - reserves space for any margins needed for the document element's frame
2757 // - renders the document element's background. This ensures the background
2758 // covers
2759 // the entire canvas as specified by the CSS2 spec
2760
2761 nsPresContext* presContext = mPresShell->GetPresContext();
2762 bool isPaginated = presContext->IsRootPaginatedDocument();
2763 nsContainerFrame* viewportFrame =
2764 static_cast<nsContainerFrame*>(GetRootFrame());
2765 nsStyleContext* viewportPseudoStyle = viewportFrame->StyleContext();
2766
2767 nsContainerFrame* rootFrame = nullptr;
2768 nsAtom* rootPseudo;
2769
2770 if (!isPaginated) {
2771 #ifdef MOZ_XUL
2772 if (aDocElement->IsXULElement()) {
2773 // pass a temporary stylecontext, the correct one will be set later
2774 rootFrame = NS_NewRootBoxFrame(mPresShell, viewportPseudoStyle);
2775 } else
2776 #endif
2777 {
2778 // pass a temporary stylecontext, the correct one will be set later
2779 rootFrame = NS_NewCanvasFrame(mPresShell, viewportPseudoStyle);
2780 mHasRootAbsPosContainingBlock = true;
2781 }
2782
2783 rootPseudo = nsCSSAnonBoxes::canvas;
2784 mDocElementContainingBlock = rootFrame;
2785 } else {
2786 // Create a page sequence frame
2787 rootFrame = NS_NewSimplePageSequenceFrame(mPresShell, viewportPseudoStyle);
2788 mPageSequenceFrame = rootFrame;
2789 rootPseudo = nsCSSAnonBoxes::pageSequence;
2790 rootFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
2791 }
2792
2793 // --------- IF SCROLLABLE WRAP IN SCROLLFRAME --------
2794
2795 // If the device supports scrolling (e.g., in galley mode on the screen and
2796 // for print-preview, but not when printing), then create a scroll frame that
2797 // will act as the scrolling mechanism for the viewport.
2798 // XXX Do we even need a viewport when printing to a printer?
2799
2800 bool isHTML = aDocElement->IsHTMLElement();
2801 bool isXUL = false;
2802
2803 if (!isHTML) {
2804 isXUL = aDocElement->IsXULElement();
2805 }
2806
2807 // Never create scrollbars for XUL documents
2808 bool isScrollable =
2809 isPaginated ? presContext->HasPaginatedScrolling() : !isXUL;
2810
2811 // We no longer need to do overflow propagation here. It's taken care of
2812 // when we construct frames for the element whose overflow might be
2813 // propagated
2814 NS_ASSERTION(!isScrollable || !isXUL,
2815 "XUL documents should never be scrollable - see above");
2816
2817 nsContainerFrame* newFrame = rootFrame;
2818 RefPtr<nsStyleContext> rootPseudoStyle;
2819 // we must create a state because if the scrollbars are GFX it needs the
2820 // state to build the scrollbar frames.
2821 TreeMatchContextHolder matchContext(mDocument);
2822 nsFrameConstructorState state(mPresShell, matchContext, nullptr, nullptr,
2823 nullptr);
2824
2825 // Start off with the viewport as parent; we'll adjust it as needed.
2826 nsContainerFrame* parentFrame = viewportFrame;
2827
2828 StyleSetHandle styleSet = mPresShell->StyleSet();
2829 // If paginated, make sure we don't put scrollbars in
2830 if (!isScrollable) {
2831 rootPseudoStyle = styleSet->ResolveInheritingAnonymousBoxStyle(
2832 rootPseudo, viewportPseudoStyle);
2833 } else {
2834 if (rootPseudo == nsCSSAnonBoxes::canvas) {
2835 rootPseudo = nsCSSAnonBoxes::scrolledCanvas;
2836 } else {
2837 NS_ASSERTION(rootPseudo == nsCSSAnonBoxes::pageSequence,
2838 "Unknown root pseudo");
2839 rootPseudo = nsCSSAnonBoxes::scrolledPageSequence;
2840 }
2841
2842 // Build the frame. We give it the content we are wrapping which is the
2843 // document element, the root frame, the parent view port frame, and we
2844 // should get back the new frame and the scrollable view if one was
2845 // created.
2846
2847 // resolve a context for the scrollframe
2848 RefPtr<nsStyleContext> styleContext;
2849 styleContext = styleSet->ResolveInheritingAnonymousBoxStyle(
2850 nsCSSAnonBoxes::viewportScroll, viewportPseudoStyle);
2851
2852 // Note that the viewport scrollframe is always built with
2853 // overflow:auto style. This forces the scroll frame to create
2854 // anonymous content for both scrollbars. This is necessary even
2855 // if the HTML or BODY elements are overriding the viewport
2856 // scroll style to 'hidden' --- dynamic style changes might put
2857 // scrollbars back on the viewport and we don't want to have to
2858 // reframe the viewport to create the scrollbar content.
2859 newFrame = nullptr;
2860 rootPseudoStyle =
2861 BeginBuildingScrollFrame(state, aDocElement, styleContext,
2862 viewportFrame, rootPseudo, true, newFrame);
2863 parentFrame = newFrame;
2864 }
2865
2866 rootFrame->SetStyleContextWithoutNotification(rootPseudoStyle);
2867 rootFrame->Init(aDocElement, parentFrame, nullptr);
2868
2869 if (isScrollable) {
2870 FinishBuildingScrollFrame(parentFrame, rootFrame);
2871 }
2872
2873 if (isPaginated) {
2874 // Create the first page
2875 // Set the initial child lists
2876 nsContainerFrame* canvasFrame;
2877 nsContainerFrame* pageFrame =
2878 ConstructPageFrame(mPresShell, rootFrame, nullptr, canvasFrame);
2879 pageFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
2880 SetInitialSingleChild(rootFrame, pageFrame);
2881
2882 // The eventual parent of the document element frame.
2883 // XXX should this be set for every new page (in ConstructPageFrame)?
2884 mDocElementContainingBlock = canvasFrame;
2885 mHasRootAbsPosContainingBlock = true;
2886 }
2887
2888 if (viewportFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) {
2889 SetInitialSingleChild(viewportFrame, newFrame);
2890 } else {
2891 nsFrameList newFrameList(newFrame, newFrame);
2892 viewportFrame->AppendFrames(kPrincipalList, newFrameList);
2893 }
2894 }
2895
ConstructAnonymousContentForCanvas(nsFrameConstructorState & aState,nsIFrame * aFrame,nsIContent * aDocElement)2896 void nsCSSFrameConstructor::ConstructAnonymousContentForCanvas(
2897 nsFrameConstructorState& aState, nsIFrame* aFrame,
2898 nsIContent* aDocElement) {
2899 NS_ASSERTION(aFrame->IsCanvasFrame(), "aFrame should be canvas frame!");
2900
2901 AutoTArray<nsIAnonymousContentCreator::ContentInfo, 4> anonymousItems;
2902 GetAnonymousContent(aDocElement, aFrame, anonymousItems);
2903 if (anonymousItems.IsEmpty()) {
2904 return;
2905 }
2906
2907 AutoFrameConstructionItemList itemsToConstruct(this);
2908 nsContainerFrame* frameAsContainer = do_QueryFrame(aFrame);
2909 AddFCItemsForAnonymousContent(aState, frameAsContainer, anonymousItems,
2910 itemsToConstruct);
2911
2912 nsFrameItems frameItems;
2913 ConstructFramesFromItemList(aState, itemsToConstruct, frameAsContainer,
2914 /* aParentIsWrapperAnonBox = */ false,
2915 frameItems);
2916 frameAsContainer->AppendFrames(kPrincipalList, frameItems);
2917 }
2918
ConstructPageFrame(nsIPresShell * aPresShell,nsContainerFrame * aParentFrame,nsIFrame * aPrevPageFrame,nsContainerFrame * & aCanvasFrame)2919 nsContainerFrame* nsCSSFrameConstructor::ConstructPageFrame(
2920 nsIPresShell* aPresShell, nsContainerFrame* aParentFrame,
2921 nsIFrame* aPrevPageFrame, nsContainerFrame*& aCanvasFrame) {
2922 nsStyleContext* parentStyleContext = aParentFrame->StyleContext();
2923 StyleSetHandle styleSet = aPresShell->StyleSet();
2924
2925 RefPtr<nsStyleContext> pagePseudoStyle;
2926 pagePseudoStyle = styleSet->ResolveInheritingAnonymousBoxStyle(
2927 nsCSSAnonBoxes::page, parentStyleContext);
2928
2929 nsContainerFrame* pageFrame = NS_NewPageFrame(aPresShell, pagePseudoStyle);
2930
2931 // Initialize the page frame and force it to have a view. This makes printing
2932 // of the pages easier and faster.
2933 pageFrame->Init(nullptr, aParentFrame, aPrevPageFrame);
2934
2935 RefPtr<nsStyleContext> pageContentPseudoStyle;
2936 pageContentPseudoStyle = styleSet->ResolveInheritingAnonymousBoxStyle(
2937 nsCSSAnonBoxes::pageContent, pagePseudoStyle);
2938
2939 nsContainerFrame* pageContentFrame =
2940 NS_NewPageContentFrame(aPresShell, pageContentPseudoStyle);
2941
2942 // Initialize the page content frame and force it to have a view. Also make it
2943 // the containing block for fixed elements which are repeated on every page.
2944 nsIFrame* prevPageContentFrame = nullptr;
2945 if (aPrevPageFrame) {
2946 prevPageContentFrame = aPrevPageFrame->PrincipalChildList().FirstChild();
2947 NS_ASSERTION(prevPageContentFrame, "missing page content frame");
2948 }
2949 pageContentFrame->Init(nullptr, pageFrame, prevPageContentFrame);
2950 if (!prevPageContentFrame) {
2951 pageContentFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
2952 }
2953 SetInitialSingleChild(pageFrame, pageContentFrame);
2954 // Make it an absolute container for fixed-pos elements
2955 pageContentFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
2956 pageContentFrame->MarkAsAbsoluteContainingBlock();
2957
2958 RefPtr<nsStyleContext> canvasPseudoStyle;
2959 canvasPseudoStyle = styleSet->ResolveInheritingAnonymousBoxStyle(
2960 nsCSSAnonBoxes::canvas, pageContentPseudoStyle);
2961
2962 aCanvasFrame = NS_NewCanvasFrame(aPresShell, canvasPseudoStyle);
2963
2964 nsIFrame* prevCanvasFrame = nullptr;
2965 if (prevPageContentFrame) {
2966 prevCanvasFrame = prevPageContentFrame->PrincipalChildList().FirstChild();
2967 NS_ASSERTION(prevCanvasFrame, "missing canvas frame");
2968 }
2969 aCanvasFrame->Init(nullptr, pageContentFrame, prevCanvasFrame);
2970 SetInitialSingleChild(pageContentFrame, aCanvasFrame);
2971 return pageFrame;
2972 }
2973
2974 /* static */
CreatePlaceholderFrameFor(nsIPresShell * aPresShell,nsIContent * aContent,nsIFrame * aFrame,nsContainerFrame * aParentFrame,nsIFrame * aPrevInFlow,nsFrameState aTypeBit)2975 nsIFrame* nsCSSFrameConstructor::CreatePlaceholderFrameFor(
2976 nsIPresShell* aPresShell, nsIContent* aContent, nsIFrame* aFrame,
2977 nsContainerFrame* aParentFrame, nsIFrame* aPrevInFlow,
2978 nsFrameState aTypeBit) {
2979 RefPtr<nsStyleContext> placeholderStyle =
2980 aPresShell->StyleSet()->ResolveStyleForPlaceholder();
2981
2982 // The placeholder frame gets a pseudo style context
2983 nsPlaceholderFrame* placeholderFrame =
2984 (nsPlaceholderFrame*)NS_NewPlaceholderFrame(aPresShell, placeholderStyle,
2985 aTypeBit);
2986
2987 placeholderFrame->Init(aContent, aParentFrame, aPrevInFlow);
2988
2989 // Associate the placeholder/out-of-flow with each other.
2990 placeholderFrame->SetOutOfFlowFrame(aFrame);
2991 aFrame->SetProperty(nsIFrame::PlaceholderFrameProperty(), placeholderFrame);
2992
2993 aFrame->AddStateBits(NS_FRAME_OUT_OF_FLOW);
2994
2995 return placeholderFrame;
2996 }
2997
2998 // Clears any lazy bits set in the range [aStartContent, aEndContent). If
2999 // aEndContent is null, that means to clear bits in all siblings starting with
3000 // aStartContent. aStartContent must not be null unless aEndContent is also
3001 // null. We do this so that when new children are inserted under elements whose
3002 // frame is a leaf the new children don't cause us to try to construct frames
3003 // for the existing children again.
ClearLazyBits(nsIContent * aStartContent,nsIContent * aEndContent)3004 static inline void ClearLazyBits(nsIContent* aStartContent,
3005 nsIContent* aEndContent) {
3006 NS_PRECONDITION(aStartContent || !aEndContent,
3007 "Must have start child if we have an end child");
3008 for (nsIContent* cur = aStartContent; cur != aEndContent;
3009 cur = cur->GetNextSibling()) {
3010 cur->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
3011 }
3012 }
3013
ConstructSelectFrame(nsFrameConstructorState & aState,FrameConstructionItem & aItem,nsContainerFrame * aParentFrame,const nsStyleDisplay * aStyleDisplay,nsFrameItems & aFrameItems)3014 nsIFrame* nsCSSFrameConstructor::ConstructSelectFrame(
3015 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
3016 nsContainerFrame* aParentFrame, const nsStyleDisplay* aStyleDisplay,
3017 nsFrameItems& aFrameItems) {
3018 nsIContent* const content = aItem.mContent;
3019 nsStyleContext* const styleContext = aItem.mStyleContext;
3020
3021 // Construct a frame-based listbox or combobox
3022 dom::HTMLSelectElement* sel = dom::HTMLSelectElement::FromContent(content);
3023 MOZ_ASSERT(sel);
3024 if (sel->IsCombobox()) {
3025 // Construct a frame-based combo box.
3026 // The frame-based combo box is built out of three parts. A display area, a
3027 // button and a dropdown list. The display area and button are created
3028 // through anonymous content. The drop-down list's frame is created
3029 // explicitly. The combobox frame shares its content with the drop-down
3030 // list.
3031 nsFrameState flags = NS_BLOCK_FLOAT_MGR;
3032 nsComboboxControlFrame* comboboxFrame =
3033 NS_NewComboboxControlFrame(mPresShell, styleContext, flags);
3034
3035 // Save the history state so we don't restore during construction
3036 // since the complete tree is required before we restore.
3037 nsILayoutHistoryState* historyState = aState.mFrameState;
3038 aState.mFrameState = nullptr;
3039 // Initialize the combobox frame
3040 InitAndRestoreFrame(aState, content,
3041 aState.GetGeometricParent(aStyleDisplay, aParentFrame),
3042 comboboxFrame);
3043
3044 comboboxFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
3045
3046 aState.AddChild(comboboxFrame, aFrameItems, content, aParentFrame);
3047
3048 // Resolve pseudo element style for the dropdown list
3049 RefPtr<nsStyleContext> listStyle;
3050 listStyle = mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
3051 nsCSSAnonBoxes::dropDownList, styleContext);
3052
3053 // Create a listbox
3054 nsContainerFrame* listFrame = NS_NewListControlFrame(mPresShell, listStyle);
3055
3056 // Notify the listbox that it is being used as a dropdown list.
3057 nsIListControlFrame* listControlFrame = do_QueryFrame(listFrame);
3058 if (listControlFrame) {
3059 listControlFrame->SetComboboxFrame(comboboxFrame);
3060 }
3061 // Notify combobox that it should use the listbox as it's popup
3062 comboboxFrame->SetDropDown(listFrame);
3063
3064 if (!nsLayoutUtils::IsContentSelectEnabled()) {
3065 // TODO(kuoe0) Remove this assertion when content-select is shipped.
3066 NS_ASSERTION(!listFrame->IsAbsPosContainingBlock(),
3067 "Ended up with positioned dropdown list somehow.");
3068 }
3069 NS_ASSERTION(!listFrame->IsFloating(),
3070 "Ended up with floating dropdown list somehow.");
3071
3072 // child frames of combobox frame
3073 nsFrameItems childItems;
3074
3075 // Initialize the scroll frame positioned. Note that it is NOT
3076 // initialized as absolutely positioned.
3077 nsContainerFrame* scrolledFrame =
3078 NS_NewSelectsAreaFrame(mPresShell, styleContext, flags);
3079
3080 InitializeSelectFrame(aState, listFrame, scrolledFrame, content,
3081 comboboxFrame, listStyle, true, aItem.mPendingBinding,
3082 childItems);
3083
3084 if (!nsLayoutUtils::IsContentSelectEnabled()) {
3085 // TODO(kuoe0) Remove this assertion when content-select is shipped.
3086 NS_ASSERTION(listFrame->GetView(), "ListFrame's view is nullptr");
3087 }
3088
3089 // Create display and button frames from the combobox's anonymous content.
3090 // The anonymous content is appended to existing anonymous content for this
3091 // element (the scrollbars).
3092 //
3093 // nsComboboxControlFrame needs special frame creation behavior for its
3094 // first piece of anonymous content, which means that we can't take the
3095 // normal ProcessChildren path.
3096 AutoTArray<nsIAnonymousContentCreator::ContentInfo, 2> newAnonymousItems;
3097 DebugOnly<nsresult> rv =
3098 GetAnonymousContent(content, comboboxFrame, newAnonymousItems);
3099 MOZ_ASSERT(NS_SUCCEEDED(rv));
3100 MOZ_ASSERT(newAnonymousItems.Length() == 2);
3101
3102 // Manually create a frame for the special NAC.
3103 MOZ_ASSERT(newAnonymousItems[0].mContent ==
3104 comboboxFrame->GetDisplayNode());
3105 newAnonymousItems.RemoveElementAt(0);
3106 nsIFrame* customFrame = comboboxFrame->CreateFrameForDisplayNode();
3107 MOZ_ASSERT(customFrame);
3108 customFrame->AddStateBits(NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT);
3109 childItems.AddChild(customFrame);
3110
3111 // The other piece of NAC can take the normal path.
3112 AutoFrameConstructionItemList fcItems(this);
3113 AddFCItemsForAnonymousContent(aState, comboboxFrame, newAnonymousItems,
3114 fcItems);
3115 ConstructFramesFromItemList(aState, fcItems, comboboxFrame,
3116 /* aParentIsWrapperAnonBox = */ false,
3117 childItems);
3118
3119 comboboxFrame->SetInitialChildList(kPrincipalList, childItems);
3120
3121 if (!nsLayoutUtils::IsContentSelectEnabled()) {
3122 // Initialize the additional popup child list which contains the
3123 // dropdown list frame.
3124 nsFrameItems popupItems;
3125 popupItems.AddChild(listFrame);
3126 comboboxFrame->SetInitialChildList(nsIFrame::kSelectPopupList,
3127 popupItems);
3128 }
3129
3130 aState.mFrameState = historyState;
3131 if (aState.mFrameState) {
3132 // Restore frame state for the entire subtree of |comboboxFrame|.
3133 RestoreFrameState(comboboxFrame, aState.mFrameState);
3134 }
3135 return comboboxFrame;
3136 }
3137
3138 // Listbox, not combobox
3139 nsContainerFrame* listFrame =
3140 NS_NewListControlFrame(mPresShell, styleContext);
3141
3142 nsContainerFrame* scrolledFrame =
3143 NS_NewSelectsAreaFrame(mPresShell, styleContext, NS_BLOCK_FLOAT_MGR);
3144
3145 // ******* this code stolen from Initialze ScrollFrame ********
3146 // please adjust this code to use BuildScrollFrame.
3147
3148 InitializeSelectFrame(aState, listFrame, scrolledFrame, content, aParentFrame,
3149 styleContext, false, aItem.mPendingBinding,
3150 aFrameItems);
3151
3152 return listFrame;
3153 }
3154
3155 /**
3156 * Used to be InitializeScrollFrame but now it's only used for the select tag
3157 * But the select tag should really be fixed to use GFX scrollbars that can
3158 * be create with BuildScrollFrame.
3159 */
InitializeSelectFrame(nsFrameConstructorState & aState,nsContainerFrame * scrollFrame,nsContainerFrame * scrolledFrame,nsIContent * aContent,nsContainerFrame * aParentFrame,nsStyleContext * aStyleContext,bool aBuildCombobox,PendingBinding * aPendingBinding,nsFrameItems & aFrameItems)3160 void nsCSSFrameConstructor::InitializeSelectFrame(
3161 nsFrameConstructorState& aState, nsContainerFrame* scrollFrame,
3162 nsContainerFrame* scrolledFrame, nsIContent* aContent,
3163 nsContainerFrame* aParentFrame, nsStyleContext* aStyleContext,
3164 bool aBuildCombobox, PendingBinding* aPendingBinding,
3165 nsFrameItems& aFrameItems) {
3166 // Initialize it
3167 nsContainerFrame* geometricParent =
3168 aState.GetGeometricParent(aStyleContext->StyleDisplay(), aParentFrame);
3169
3170 // We don't call InitAndRestoreFrame for scrollFrame because we can only
3171 // restore the frame state after its parts have been created (in particular,
3172 // the scrollable view). So we have to split Init and Restore.
3173
3174 scrollFrame->Init(aContent, geometricParent, nullptr);
3175
3176 if (!aBuildCombobox || nsLayoutUtils::IsContentSelectEnabled()) {
3177 aState.AddChild(scrollFrame, aFrameItems, aContent, aParentFrame);
3178 }
3179
3180 BuildScrollFrame(aState, aContent, aStyleContext, scrolledFrame,
3181 geometricParent, scrollFrame);
3182
3183 if (aState.mFrameState) {
3184 // Restore frame state for the scroll frame
3185 RestoreFrameStateFor(scrollFrame, aState.mFrameState);
3186 }
3187
3188 // Process children
3189 nsFrameItems childItems;
3190
3191 ProcessChildren(aState, aContent, aStyleContext, scrolledFrame, false,
3192 childItems, false, aPendingBinding);
3193
3194 // Set the scrolled frame's initial child lists
3195 scrolledFrame->SetInitialChildList(kPrincipalList, childItems);
3196 }
3197
ConstructFieldSetFrame(nsFrameConstructorState & aState,FrameConstructionItem & aItem,nsContainerFrame * aParentFrame,const nsStyleDisplay * aStyleDisplay,nsFrameItems & aFrameItems)3198 nsIFrame* nsCSSFrameConstructor::ConstructFieldSetFrame(
3199 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
3200 nsContainerFrame* aParentFrame, const nsStyleDisplay* aStyleDisplay,
3201 nsFrameItems& aFrameItems) {
3202 nsIContent* const content = aItem.mContent;
3203 nsStyleContext* const styleContext = aItem.mStyleContext;
3204
3205 nsContainerFrame* fieldsetFrame =
3206 NS_NewFieldSetFrame(mPresShell, styleContext);
3207
3208 // Initialize it
3209 InitAndRestoreFrame(aState, content,
3210 aState.GetGeometricParent(aStyleDisplay, aParentFrame),
3211 fieldsetFrame);
3212
3213 fieldsetFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
3214
3215 // Resolve style and initialize the frame
3216 RefPtr<nsStyleContext> fieldsetContentStyle;
3217 fieldsetContentStyle =
3218 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
3219 nsCSSAnonBoxes::fieldsetContent, styleContext);
3220
3221 const nsStyleDisplay* fieldsetContentDisplay =
3222 fieldsetContentStyle->StyleDisplay();
3223 bool isScrollable = fieldsetContentDisplay->IsScrollableOverflow();
3224 nsContainerFrame* scrollFrame = nullptr;
3225 if (isScrollable) {
3226 fieldsetContentStyle = BeginBuildingScrollFrame(
3227 aState, content, fieldsetContentStyle, fieldsetFrame,
3228 nsCSSAnonBoxes::scrolledContent, false, scrollFrame);
3229 }
3230
3231 nsContainerFrame* absPosContainer = nullptr;
3232 if (fieldsetFrame->IsAbsPosContainingBlock()) {
3233 absPosContainer = fieldsetFrame;
3234 }
3235
3236 // Create the inner ::-moz-fieldset-content frame.
3237 nsContainerFrame* contentFrameTop;
3238 nsContainerFrame* contentFrame;
3239 auto parent = scrollFrame ? scrollFrame : fieldsetFrame;
3240 switch (fieldsetContentDisplay->mDisplay) {
3241 case StyleDisplay::Flex:
3242 contentFrame = NS_NewFlexContainerFrame(mPresShell, fieldsetContentStyle);
3243 InitAndRestoreFrame(aState, content, parent, contentFrame);
3244 contentFrameTop = contentFrame;
3245 break;
3246 case StyleDisplay::Grid:
3247 contentFrame = NS_NewGridContainerFrame(mPresShell, fieldsetContentStyle);
3248 InitAndRestoreFrame(aState, content, parent, contentFrame);
3249 contentFrameTop = contentFrame;
3250 break;
3251 default: {
3252 MOZ_ASSERT(fieldsetContentDisplay->mDisplay == StyleDisplay::Block,
3253 "bug in nsRuleNode::ComputeDisplayData?");
3254
3255 nsContainerFrame* columnSetFrame = nullptr;
3256 RefPtr<nsStyleContext> innerSC = fieldsetContentStyle;
3257 const nsStyleColumn* columns = fieldsetContentStyle->StyleColumn();
3258 if (columns->mColumnCount != NS_STYLE_COLUMN_COUNT_AUTO ||
3259 columns->mColumnWidth.GetUnit() != eStyleUnit_Auto) {
3260 columnSetFrame =
3261 NS_NewColumnSetFrame(mPresShell, fieldsetContentStyle,
3262 nsFrameState(NS_FRAME_OWNS_ANON_BOXES));
3263 InitAndRestoreFrame(aState, content, parent, columnSetFrame);
3264 innerSC = mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
3265 nsCSSAnonBoxes::columnContent, fieldsetContentStyle);
3266 if (absPosContainer) {
3267 absPosContainer = columnSetFrame;
3268 }
3269 }
3270 contentFrame = NS_NewBlockFormattingContext(mPresShell, innerSC);
3271 if (columnSetFrame) {
3272 InitAndRestoreFrame(aState, content, columnSetFrame, contentFrame);
3273 SetInitialSingleChild(columnSetFrame, contentFrame);
3274 contentFrameTop = columnSetFrame;
3275 } else {
3276 InitAndRestoreFrame(aState, content, parent, contentFrame);
3277 contentFrameTop = contentFrame;
3278 }
3279 break;
3280 }
3281 }
3282
3283 aState.AddChild(fieldsetFrame, aFrameItems, content, aParentFrame);
3284
3285 // Process children
3286 nsFrameConstructorSaveState absoluteSaveState;
3287 nsFrameItems childItems;
3288
3289 contentFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
3290 if (absPosContainer) {
3291 aState.PushAbsoluteContainingBlock(contentFrame, absPosContainer,
3292 absoluteSaveState);
3293 }
3294
3295 ProcessChildren(aState, content, styleContext, contentFrame, true, childItems,
3296 true, aItem.mPendingBinding);
3297
3298 nsFrameItems fieldsetKids;
3299 fieldsetKids.AddChild(scrollFrame ? scrollFrame : contentFrameTop);
3300
3301 for (nsFrameList::Enumerator e(childItems); !e.AtEnd(); e.Next()) {
3302 nsIFrame* child = e.get();
3303 nsContainerFrame* cif = child->GetContentInsertionFrame();
3304 if (cif && cif->IsLegendFrame()) {
3305 // We want the legend to be the first frame in the fieldset child list.
3306 // That way the EventStateManager will do the right thing when tabbing
3307 // from a selection point within the legend (bug 236071), which is
3308 // used for implementing legend access keys (bug 81481).
3309 // GetAdjustedParentFrame() below depends on this frame order.
3310 childItems.RemoveFrame(child);
3311 // Make sure to reparent the legend so it has the fieldset as the parent.
3312 fieldsetKids.InsertFrame(fieldsetFrame, nullptr, child);
3313 if (scrollFrame) {
3314 StickyScrollContainer::NotifyReparentedFrameAcrossScrollFrameBoundary(
3315 child, contentFrame);
3316 }
3317 break;
3318 }
3319 }
3320
3321 if (isScrollable) {
3322 FinishBuildingScrollFrame(scrollFrame, contentFrameTop);
3323 }
3324
3325 // Set the inner frame's initial child lists
3326 contentFrame->SetInitialChildList(kPrincipalList, childItems);
3327
3328 // Set the outer frame's initial child list
3329 fieldsetFrame->SetInitialChildList(kPrincipalList, fieldsetKids);
3330
3331 fieldsetFrame->AddStateBits(NS_FRAME_MAY_HAVE_GENERATED_CONTENT);
3332
3333 // Our new frame returned is the outer frame, which is the fieldset frame.
3334 return fieldsetFrame;
3335 }
3336
ConstructDetailsFrame(nsFrameConstructorState & aState,FrameConstructionItem & aItem,nsContainerFrame * aParentFrame,const nsStyleDisplay * aStyleDisplay,nsFrameItems & aFrameItems)3337 nsIFrame* nsCSSFrameConstructor::ConstructDetailsFrame(
3338 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
3339 nsContainerFrame* aParentFrame, const nsStyleDisplay* aStyleDisplay,
3340 nsFrameItems& aFrameItems) {
3341 if (!aStyleDisplay->IsScrollableOverflow()) {
3342 return ConstructNonScrollableBlockWithConstructor(
3343 aState, aItem, aParentFrame, aStyleDisplay, aFrameItems,
3344 NS_NewDetailsFrame);
3345 }
3346
3347 // Build a scroll frame to wrap details frame if necessary.
3348 return ConstructScrollableBlockWithConstructor(aState, aItem, aParentFrame,
3349 aStyleDisplay, aFrameItems,
3350 NS_NewDetailsFrame);
3351 }
3352
FindAncestorWithGeneratedContentPseudo(nsIFrame * aFrame)3353 static nsIFrame* FindAncestorWithGeneratedContentPseudo(nsIFrame* aFrame) {
3354 for (nsIFrame* f = aFrame->GetParent(); f; f = f->GetParent()) {
3355 NS_ASSERTION(f->IsGeneratedContentFrame(),
3356 "should not have exited generated content");
3357 nsAtom* pseudo = f->StyleContext()->GetPseudo();
3358 if (pseudo == nsCSSPseudoElements::before ||
3359 pseudo == nsCSSPseudoElements::after)
3360 return f;
3361 }
3362 return nullptr;
3363 }
3364
3365 #define SIMPLE_FCDATA(_func) FCDATA_DECL(0, _func)
3366 #define FULL_CTOR_FCDATA(_flags, _func) \
3367 { _flags | FCDATA_FUNC_IS_FULL_CTOR, {nullptr}, _func, nullptr }
3368
3369 /* static */
3370 const nsCSSFrameConstructor::FrameConstructionData*
FindTextData(nsIFrame * aParentFrame)3371 nsCSSFrameConstructor::FindTextData(nsIFrame* aParentFrame) {
3372 if (aParentFrame && IsFrameForSVG(aParentFrame)) {
3373 nsIFrame* ancestorFrame =
3374 nsSVGUtils::GetFirstNonAAncestorFrame(aParentFrame);
3375 if (ancestorFrame) {
3376 static const FrameConstructionData sSVGTextData = FCDATA_DECL(
3377 FCDATA_IS_LINE_PARTICIPANT | FCDATA_IS_SVG_TEXT, NS_NewTextFrame);
3378 if (nsSVGUtils::IsInSVGTextSubtree(ancestorFrame)) {
3379 return &sSVGTextData;
3380 }
3381 }
3382 return nullptr;
3383 }
3384
3385 static const FrameConstructionData sTextData =
3386 FCDATA_DECL(FCDATA_IS_LINE_PARTICIPANT, NS_NewTextFrame);
3387 return &sTextData;
3388 }
3389
ConstructTextFrame(const FrameConstructionData * aData,nsFrameConstructorState & aState,nsIContent * aContent,nsContainerFrame * aParentFrame,nsStyleContext * aStyleContext,nsFrameItems & aFrameItems)3390 void nsCSSFrameConstructor::ConstructTextFrame(
3391 const FrameConstructionData* aData, nsFrameConstructorState& aState,
3392 nsIContent* aContent, nsContainerFrame* aParentFrame,
3393 nsStyleContext* aStyleContext, nsFrameItems& aFrameItems) {
3394 NS_PRECONDITION(aData, "Must have frame construction data");
3395
3396 nsIFrame* newFrame = (*aData->mFunc.mCreationFunc)(mPresShell, aStyleContext);
3397
3398 InitAndRestoreFrame(aState, aContent, aParentFrame, newFrame);
3399
3400 // We never need to create a view for a text frame.
3401
3402 if (newFrame->IsGeneratedContentFrame()) {
3403 nsAutoPtr<nsGenConInitializer> initializer;
3404 initializer = static_cast<nsGenConInitializer*>(
3405 aContent->UnsetProperty(nsGkAtoms::genConInitializerProperty));
3406 if (initializer) {
3407 if (initializer->mNode->InitTextFrame(
3408 initializer->mList,
3409 FindAncestorWithGeneratedContentPseudo(newFrame), newFrame)) {
3410 (this->*(initializer->mDirtyAll))();
3411 }
3412 initializer->mNode.forget();
3413 }
3414 }
3415
3416 // Add the newly constructed frame to the flow
3417 aFrameItems.AddChild(newFrame);
3418
3419 if (!aState.mCreatingExtraFrames) aContent->SetPrimaryFrame(newFrame);
3420 }
3421
3422 /* static */
3423 const nsCSSFrameConstructor::FrameConstructionData*
FindDataByInt(int32_t aInt,Element * aElement,nsStyleContext * aStyleContext,const FrameConstructionDataByInt * aDataPtr,uint32_t aDataLength)3424 nsCSSFrameConstructor::FindDataByInt(int32_t aInt, Element* aElement,
3425 nsStyleContext* aStyleContext,
3426 const FrameConstructionDataByInt* aDataPtr,
3427 uint32_t aDataLength) {
3428 for (const FrameConstructionDataByInt *curData = aDataPtr,
3429 *endData = aDataPtr + aDataLength;
3430 curData != endData; ++curData) {
3431 if (curData->mInt == aInt) {
3432 const FrameConstructionData* data = &curData->mData;
3433 if (data->mBits & FCDATA_FUNC_IS_DATA_GETTER) {
3434 return data->mFunc.mDataGetter(aElement, aStyleContext);
3435 }
3436
3437 return data;
3438 }
3439 }
3440
3441 return nullptr;
3442 }
3443
3444 /* static */
3445 const nsCSSFrameConstructor::FrameConstructionData*
FindDataByTag(nsAtom * aTag,Element * aElement,nsStyleContext * aStyleContext,const FrameConstructionDataByTag * aDataPtr,uint32_t aDataLength,bool * aTagFound)3446 nsCSSFrameConstructor::FindDataByTag(nsAtom* aTag, Element* aElement,
3447 nsStyleContext* aStyleContext,
3448 const FrameConstructionDataByTag* aDataPtr,
3449 uint32_t aDataLength, bool* aTagFound) {
3450 if (aTagFound) {
3451 *aTagFound = false;
3452 }
3453 for (const FrameConstructionDataByTag *curData = aDataPtr,
3454 *endData = aDataPtr + aDataLength;
3455 curData != endData; ++curData) {
3456 if (*curData->mTag == aTag) {
3457 if (aTagFound) {
3458 *aTagFound = true;
3459 }
3460 const FrameConstructionData* data = &curData->mData;
3461 if (data->mBits & FCDATA_FUNC_IS_DATA_GETTER) {
3462 return data->mFunc.mDataGetter(aElement, aStyleContext);
3463 }
3464
3465 return data;
3466 }
3467 }
3468
3469 return nullptr;
3470 }
3471
3472 #define SUPPRESS_FCDATA() FCDATA_DECL(FCDATA_SUPPRESS_FRAME, nullptr)
3473 #define SIMPLE_INT_CREATE(_int, _func) \
3474 { _int, SIMPLE_FCDATA(_func) }
3475 #define SIMPLE_INT_CHAIN(_int, _func) \
3476 { _int, FCDATA_DECL(FCDATA_FUNC_IS_DATA_GETTER, _func) }
3477 #define COMPLEX_INT_CREATE(_int, _func) \
3478 { _int, FULL_CTOR_FCDATA(0, _func) }
3479
3480 #define SIMPLE_TAG_CREATE(_tag, _func) \
3481 { &nsGkAtoms::_tag, SIMPLE_FCDATA(_func) }
3482 #define SIMPLE_TAG_CHAIN(_tag, _func) \
3483 { &nsGkAtoms::_tag, FCDATA_DECL(FCDATA_FUNC_IS_DATA_GETTER, _func) }
3484 #define COMPLEX_TAG_CREATE(_tag, _func) \
3485 { &nsGkAtoms::_tag, FULL_CTOR_FCDATA(0, _func) }
3486
IsFrameForFieldSet(nsIFrame * aFrame)3487 static bool IsFrameForFieldSet(nsIFrame* aFrame) {
3488 nsAtom* pseudo = aFrame->StyleContext()->GetPseudo();
3489 if (pseudo == nsCSSAnonBoxes::fieldsetContent ||
3490 pseudo == nsCSSAnonBoxes::scrolledContent ||
3491 pseudo == nsCSSAnonBoxes::columnContent) {
3492 return IsFrameForFieldSet(aFrame->GetParent());
3493 }
3494 return aFrame->IsFieldSetFrame();
3495 }
3496
3497 /* static */
3498 const nsCSSFrameConstructor::FrameConstructionData*
FindHTMLData(Element * aElement,nsAtom * aTag,int32_t aNameSpaceID,nsIFrame * aParentFrame,nsStyleContext * aStyleContext)3499 nsCSSFrameConstructor::FindHTMLData(Element* aElement, nsAtom* aTag,
3500 int32_t aNameSpaceID,
3501 nsIFrame* aParentFrame,
3502 nsStyleContext* aStyleContext) {
3503 // Ignore the tag if it's not HTML content and if it doesn't extend (via XBL)
3504 // a valid HTML namespace. This check must match the one in
3505 // ShouldHaveFirstLineStyle.
3506 if (aNameSpaceID != kNameSpaceID_XHTML) {
3507 return nullptr;
3508 }
3509
3510 NS_ASSERTION(!aParentFrame ||
3511 aParentFrame->StyleContext()->GetPseudo() !=
3512 nsCSSAnonBoxes::fieldsetContent ||
3513 aParentFrame->GetParent()->IsFieldSetFrame(),
3514 "Unexpected parent for fieldset content anon box");
3515 if (aTag == nsGkAtoms::legend &&
3516 (!aParentFrame || !IsFrameForFieldSet(aParentFrame) ||
3517 aStyleContext->StyleDisplay()->IsFloatingStyle() ||
3518 aStyleContext->StyleDisplay()->IsAbsolutelyPositionedStyle())) {
3519 // <legend> is only special inside fieldset, we only check the frame tree
3520 // parent because the content tree parent may not be a <fieldset> due to
3521 // display:contents, Shadow DOM, or XBL. For floated or absolutely
3522 // positioned legends we want to construct by display type and
3523 // not do special legend stuff.
3524 return nullptr;
3525 }
3526
3527 static const FrameConstructionDataByTag sHTMLData[] = {
3528 SIMPLE_TAG_CHAIN(img, nsCSSFrameConstructor::FindImgData),
3529 SIMPLE_TAG_CHAIN(mozgeneratedcontentimage,
3530 nsCSSFrameConstructor::FindImgData),
3531 {&nsGkAtoms::br,
3532 FCDATA_DECL(FCDATA_IS_LINE_PARTICIPANT | FCDATA_IS_LINE_BREAK,
3533 NS_NewBRFrame)},
3534 SIMPLE_TAG_CREATE(wbr, NS_NewWBRFrame),
3535 SIMPLE_TAG_CHAIN(input, nsCSSFrameConstructor::FindInputData),
3536 SIMPLE_TAG_CREATE(textarea, NS_NewTextControlFrame),
3537 COMPLEX_TAG_CREATE(select, &nsCSSFrameConstructor::ConstructSelectFrame),
3538 SIMPLE_TAG_CHAIN(object, nsCSSFrameConstructor::FindObjectData),
3539 SIMPLE_TAG_CHAIN(embed, nsCSSFrameConstructor::FindObjectData),
3540 COMPLEX_TAG_CREATE(fieldset,
3541 &nsCSSFrameConstructor::ConstructFieldSetFrame),
3542 {&nsGkAtoms::legend,
3543 FCDATA_DECL(FCDATA_ALLOW_BLOCK_STYLES | FCDATA_MAY_NEED_SCROLLFRAME,
3544 NS_NewLegendFrame)},
3545 SIMPLE_TAG_CREATE(frameset, NS_NewHTMLFramesetFrame),
3546 SIMPLE_TAG_CREATE(iframe, NS_NewSubDocumentFrame),
3547 {&nsGkAtoms::button,
3548 FCDATA_WITH_WRAPPING_BLOCK(
3549 FCDATA_ALLOW_BLOCK_STYLES | FCDATA_ALLOW_GRID_FLEX_COLUMNSET,
3550 NS_NewHTMLButtonControlFrame, nsCSSAnonBoxes::buttonContent)},
3551 SIMPLE_TAG_CHAIN(canvas, nsCSSFrameConstructor::FindCanvasData),
3552 SIMPLE_TAG_CREATE(video, NS_NewHTMLVideoFrame),
3553 SIMPLE_TAG_CREATE(audio, NS_NewHTMLVideoFrame),
3554 SIMPLE_TAG_CREATE(progress, NS_NewProgressFrame),
3555 SIMPLE_TAG_CREATE(meter, NS_NewMeterFrame),
3556 COMPLEX_TAG_CREATE(details,
3557 &nsCSSFrameConstructor::ConstructDetailsFrame)};
3558
3559 bool tagFound;
3560 const FrameConstructionData* data =
3561 FindDataByTag(aTag, aElement, aStyleContext, sHTMLData,
3562 ArrayLength(sHTMLData), &tagFound);
3563
3564 // https://drafts.csswg.org/css-display/#unbox-html
3565 if (tagFound && MOZ_UNLIKELY(aStyleContext->StyleDisplay()->mDisplay ==
3566 StyleDisplay::Contents)) {
3567 // <button>, <legend>, <details> and <fieldset> don’t have any special
3568 // behavior; display: contents simply removes their principal box, and their
3569 // contents render as normal.
3570 if (aTag != nsGkAtoms::button && aTag != nsGkAtoms::legend &&
3571 aTag != nsGkAtoms::details && aTag != nsGkAtoms::fieldset) {
3572 // On the rest of unusual HTML elements, display: contents creates no box.
3573 static const FrameConstructionData sSuppressData = SUPPRESS_FCDATA();
3574 return &sSuppressData;
3575 }
3576 }
3577
3578 return data;
3579 }
3580
3581 /* static */
3582 const nsCSSFrameConstructor::FrameConstructionData*
FindImgData(Element * aElement,nsStyleContext * aStyleContext)3583 nsCSSFrameConstructor::FindImgData(Element* aElement,
3584 nsStyleContext* aStyleContext) {
3585 if (!nsImageFrame::ShouldCreateImageFrameFor(aElement, aStyleContext)) {
3586 return nullptr;
3587 }
3588
3589 static const FrameConstructionData sImgData = SIMPLE_FCDATA(NS_NewImageFrame);
3590 return &sImgData;
3591 }
3592
3593 /* static */
3594 const nsCSSFrameConstructor::FrameConstructionData*
FindImgControlData(Element * aElement,nsStyleContext * aStyleContext)3595 nsCSSFrameConstructor::FindImgControlData(Element* aElement,
3596 nsStyleContext* aStyleContext) {
3597 if (!nsImageFrame::ShouldCreateImageFrameFor(aElement, aStyleContext)) {
3598 return nullptr;
3599 }
3600
3601 static const FrameConstructionData sImgControlData =
3602 SIMPLE_FCDATA(NS_NewImageControlFrame);
3603 return &sImgControlData;
3604 }
3605
3606 /* static */
3607 const nsCSSFrameConstructor::FrameConstructionData*
FindInputData(Element * aElement,nsStyleContext * aStyleContext)3608 nsCSSFrameConstructor::FindInputData(Element* aElement,
3609 nsStyleContext* aStyleContext) {
3610 static const FrameConstructionDataByInt sInputData[] = {
3611 SIMPLE_INT_CREATE(NS_FORM_INPUT_CHECKBOX, NS_NewCheckboxRadioFrame),
3612 SIMPLE_INT_CREATE(NS_FORM_INPUT_RADIO, NS_NewCheckboxRadioFrame),
3613 SIMPLE_INT_CREATE(NS_FORM_INPUT_FILE, NS_NewFileControlFrame),
3614 SIMPLE_INT_CHAIN(NS_FORM_INPUT_IMAGE,
3615 nsCSSFrameConstructor::FindImgControlData),
3616 SIMPLE_INT_CREATE(NS_FORM_INPUT_EMAIL, NS_NewTextControlFrame),
3617 SIMPLE_INT_CREATE(NS_FORM_INPUT_SEARCH, NS_NewTextControlFrame),
3618 SIMPLE_INT_CREATE(NS_FORM_INPUT_TEXT, NS_NewTextControlFrame),
3619 SIMPLE_INT_CREATE(NS_FORM_INPUT_TEL, NS_NewTextControlFrame),
3620 SIMPLE_INT_CREATE(NS_FORM_INPUT_URL, NS_NewTextControlFrame),
3621 SIMPLE_INT_CREATE(NS_FORM_INPUT_RANGE, NS_NewRangeFrame),
3622 SIMPLE_INT_CREATE(NS_FORM_INPUT_PASSWORD, NS_NewTextControlFrame),
3623 {NS_FORM_INPUT_COLOR,
3624 FCDATA_WITH_WRAPPING_BLOCK(0, NS_NewColorControlFrame,
3625 nsCSSAnonBoxes::buttonContent)},
3626 // TODO: this is temporary until a frame is written: bug 635240.
3627 SIMPLE_INT_CREATE(NS_FORM_INPUT_NUMBER, NS_NewNumberControlFrame),
3628 SIMPLE_INT_CREATE(NS_FORM_INPUT_TIME, NS_NewDateTimeControlFrame),
3629 SIMPLE_INT_CREATE(NS_FORM_INPUT_DATE, NS_NewDateTimeControlFrame),
3630 // TODO: this is temporary until a frame is written: bug 888320
3631 SIMPLE_INT_CREATE(NS_FORM_INPUT_MONTH, NS_NewTextControlFrame),
3632 // TODO: this is temporary until a frame is written: bug 888320
3633 SIMPLE_INT_CREATE(NS_FORM_INPUT_WEEK, NS_NewTextControlFrame),
3634 // TODO: this is temporary until a frame is written: bug 888320
3635 SIMPLE_INT_CREATE(NS_FORM_INPUT_DATETIME_LOCAL, NS_NewTextControlFrame),
3636 {NS_FORM_INPUT_SUBMIT,
3637 FCDATA_WITH_WRAPPING_BLOCK(0, NS_NewGfxButtonControlFrame,
3638 nsCSSAnonBoxes::buttonContent)},
3639 {NS_FORM_INPUT_RESET,
3640 FCDATA_WITH_WRAPPING_BLOCK(0, NS_NewGfxButtonControlFrame,
3641 nsCSSAnonBoxes::buttonContent)},
3642 {NS_FORM_INPUT_BUTTON,
3643 FCDATA_WITH_WRAPPING_BLOCK(0, NS_NewGfxButtonControlFrame,
3644 nsCSSAnonBoxes::buttonContent)}
3645 // Keeping hidden inputs out of here on purpose for so they get frames by
3646 // display (in practice, none).
3647 };
3648
3649 nsCOMPtr<nsIFormControl> control = do_QueryInterface(aElement);
3650 NS_ASSERTION(control, "input doesn't implement nsIFormControl?");
3651
3652 auto controlType = control->ControlType();
3653
3654 // radio and checkbox inputs with appearance:none should be constructed
3655 // by display type. (Note that we're not checking that appearance is
3656 // not (respectively) NS_THEME_RADIO and NS_THEME_CHECKBOX.)
3657 if ((controlType == NS_FORM_INPUT_CHECKBOX ||
3658 controlType == NS_FORM_INPUT_RADIO) &&
3659 aStyleContext->StyleDisplay()->mAppearance == NS_THEME_NONE) {
3660 return nullptr;
3661 }
3662
3663 return FindDataByInt(controlType, aElement, aStyleContext, sInputData,
3664 ArrayLength(sInputData));
3665 }
3666
3667 /* static */
3668 const nsCSSFrameConstructor::FrameConstructionData*
FindObjectData(Element * aElement,nsStyleContext * aStyleContext)3669 nsCSSFrameConstructor::FindObjectData(Element* aElement,
3670 nsStyleContext* aStyleContext) {
3671 // GetDisplayedType isn't necessarily nsIObjectLoadingContent::TYPE_NULL for
3672 // cases when the object is broken/suppressed/etc (e.g. a broken image), but
3673 // we want to treat those cases as TYPE_NULL
3674 uint32_t type;
3675 if (aElement->State().HasAtLeastOneOfStates(NS_EVENT_STATE_BROKEN |
3676 NS_EVENT_STATE_USERDISABLED |
3677 NS_EVENT_STATE_SUPPRESSED)) {
3678 type = nsIObjectLoadingContent::TYPE_NULL;
3679 } else {
3680 nsCOMPtr<nsIObjectLoadingContent> objContent(do_QueryInterface(aElement));
3681 NS_ASSERTION(objContent,
3682 "embed and object must implement "
3683 "nsIObjectLoadingContent!");
3684
3685 objContent->GetDisplayedType(&type);
3686 }
3687
3688 static const FrameConstructionDataByInt sObjectData[] = {
3689 SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_LOADING,
3690 NS_NewEmptyFrame),
3691 SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_PLUGIN,
3692 NS_NewObjectFrame),
3693 SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_IMAGE, NS_NewImageFrame),
3694 SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_DOCUMENT,
3695 NS_NewSubDocumentFrame),
3696 // Fake plugin handlers load as documents
3697 SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_FAKE_PLUGIN,
3698 NS_NewSubDocumentFrame)
3699 // Nothing for TYPE_NULL so we'll construct frames by display there
3700 };
3701
3702 return FindDataByInt((int32_t)type, aElement, aStyleContext, sObjectData,
3703 ArrayLength(sObjectData));
3704 }
3705
3706 /* static */
3707 const nsCSSFrameConstructor::FrameConstructionData*
FindCanvasData(Element * aElement,nsStyleContext * aStyleContext)3708 nsCSSFrameConstructor::FindCanvasData(Element* aElement,
3709 nsStyleContext* aStyleContext) {
3710 // We want to check whether script is enabled on the document that
3711 // could be painting to the canvas. That's the owner document of
3712 // the canvas, except when the owner document is a static document,
3713 // in which case it's the original document it was cloned from.
3714 nsIDocument* doc = aElement->OwnerDoc();
3715 if (doc->IsStaticDocument()) {
3716 doc = doc->GetOriginalDocument();
3717 }
3718 if (!doc->IsScriptEnabled()) {
3719 return nullptr;
3720 }
3721
3722 static const FrameConstructionData sCanvasData = FCDATA_WITH_WRAPPING_BLOCK(
3723 0, NS_NewHTMLCanvasFrame, nsCSSAnonBoxes::htmlCanvasContent);
3724 return &sCanvasData;
3725 }
3726
ConstructFrameFromItemInternal(FrameConstructionItem & aItem,nsFrameConstructorState & aState,nsContainerFrame * aParentFrame,nsFrameItems & aFrameItems)3727 void nsCSSFrameConstructor::ConstructFrameFromItemInternal(
3728 FrameConstructionItem& aItem, nsFrameConstructorState& aState,
3729 nsContainerFrame* aParentFrame, nsFrameItems& aFrameItems) {
3730 const FrameConstructionData* data = aItem.mFCData;
3731 NS_ASSERTION(data, "Must have frame construction data");
3732
3733 uint32_t bits = data->mBits;
3734
3735 NS_ASSERTION(!(bits & FCDATA_FUNC_IS_DATA_GETTER),
3736 "Should have dealt with this inside the data finder");
3737
3738 // Some sets of bits are not compatible with each other
3739 #define CHECK_ONLY_ONE_BIT(_bit1, _bit2) \
3740 NS_ASSERTION(!(bits & _bit1) || !(bits & _bit2), \
3741 "Only one of these bits should be set")
3742 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR,
3743 FCDATA_FORCE_NULL_ABSPOS_CONTAINER);
3744 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_WRAP_KIDS_IN_BLOCKS);
3745 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_MAY_NEED_SCROLLFRAME);
3746 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_IS_POPUP);
3747 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_SKIP_ABSPOS_PUSH);
3748 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR,
3749 FCDATA_DISALLOW_GENERATED_CONTENT);
3750 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_ALLOW_BLOCK_STYLES);
3751 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR,
3752 FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS);
3753 CHECK_ONLY_ONE_BIT(FCDATA_WRAP_KIDS_IN_BLOCKS,
3754 FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS);
3755 #undef CHECK_ONLY_ONE_BIT
3756 NS_ASSERTION(!(bits & FCDATA_FORCED_NON_SCROLLABLE_BLOCK) ||
3757 ((bits & FCDATA_FUNC_IS_FULL_CTOR) &&
3758 data->mFullConstructor ==
3759 &nsCSSFrameConstructor::ConstructNonScrollableBlock),
3760 "Unexpected FCDATA_FORCED_NON_SCROLLABLE_BLOCK flag");
3761 MOZ_ASSERT(
3762 !(bits & FCDATA_IS_WRAPPER_ANON_BOX) || (bits & FCDATA_USE_CHILD_ITEMS),
3763 "Wrapper anon boxes should always have FCDATA_USE_CHILD_ITEMS");
3764
3765 // Don't create a subdocument frame for iframes if we're creating extra frames
3766 if (aState.mCreatingExtraFrames &&
3767 aItem.mContent->IsHTMLElement(nsGkAtoms::iframe)) {
3768 return;
3769 }
3770
3771 nsIContent* const content = aItem.mContent;
3772 nsIContent* parent = content->GetParent();
3773
3774 // Push display:contents ancestors.
3775 Maybe<AutoDisplayContentsAncestorPusher> adcp;
3776 if (aState.mTreeMatchContext) {
3777 adcp.emplace(*aState.mTreeMatchContext, aState.mPresContext, parent);
3778 } else {
3779 MOZ_ASSERT(content->IsStyledByServo());
3780 }
3781
3782 // Get the parent of the content and check if it is a XBL children element.
3783 // Push the children element as an ancestor here because it does
3784 // not have a frame and would not otherwise be pushed as an ancestor. It is
3785 // necessary to do so in order to correctly handle style resolution on
3786 // descendants. (If !adcp.IsEmpty() then it was already pushed by
3787 // AutoDisplayContentsAncestorPusher above.)
3788 TreeMatchContext::AutoAncestorPusher insertionPointPusher(
3789 aState.mTreeMatchContext);
3790 if (adcp.isSome() && adcp->IsEmpty() && parent &&
3791 parent->IsActiveChildrenElement()) {
3792 if (aState.HasAncestorFilter()) {
3793 insertionPointPusher.PushAncestorAndStyleScope(parent);
3794 } else {
3795 insertionPointPusher.PushStyleScope(parent);
3796 }
3797 }
3798
3799 // Push the content as a style ancestor now, so we don't have to do
3800 // it in our various full-constructor functions. In particular,
3801 // since a number of full-constructor functions don't actually call
3802 // ProcessChildren in some cases (e.g. for CSS anonymous table boxes
3803 // or for situations where only anonymouse children are having
3804 // frames constructed), this is the best place to bottleneck the
3805 // pushing of the content instead of having to do it in multiple
3806 // places.
3807 TreeMatchContext::AutoAncestorPusher ancestorPusher(aState.mTreeMatchContext);
3808 if (aState.HasAncestorFilter()) {
3809 ancestorPusher.PushAncestorAndStyleScope(content);
3810 } else {
3811 ancestorPusher.PushStyleScope(content);
3812 }
3813
3814 nsIFrame* newFrame;
3815 nsIFrame* primaryFrame;
3816 nsStyleContext* const styleContext = aItem.mStyleContext;
3817 const nsStyleDisplay* display = styleContext->StyleDisplay();
3818 if (bits & FCDATA_FUNC_IS_FULL_CTOR) {
3819 newFrame = (this->*(data->mFullConstructor))(aState, aItem, aParentFrame,
3820 display, aFrameItems);
3821 MOZ_ASSERT(newFrame, "Full constructor failed");
3822 primaryFrame = newFrame;
3823 } else {
3824 newFrame = (*data->mFunc.mCreationFunc)(mPresShell, styleContext);
3825
3826 bool allowOutOfFlow = !(bits & FCDATA_DISALLOW_OUT_OF_FLOW);
3827 bool isPopup = aItem.mIsPopup;
3828 NS_ASSERTION(
3829 !isPopup || (aState.mPopupItems.containingBlock &&
3830 aState.mPopupItems.containingBlock->IsPopupSetFrame()),
3831 "Should have a containing block here!");
3832
3833 nsContainerFrame* geometricParent =
3834 isPopup
3835 ? aState.mPopupItems.containingBlock
3836 : (allowOutOfFlow ? aState.GetGeometricParent(display, aParentFrame)
3837 : aParentFrame);
3838
3839 // Must init frameToAddToList to null, since it's inout
3840 nsIFrame* frameToAddToList = nullptr;
3841 if ((bits & FCDATA_MAY_NEED_SCROLLFRAME) &&
3842 display->IsScrollableOverflow()) {
3843 nsContainerFrame* scrollframe = nullptr;
3844 BuildScrollFrame(aState, content, styleContext, newFrame, geometricParent,
3845 scrollframe);
3846 frameToAddToList = scrollframe;
3847 } else {
3848 InitAndRestoreFrame(aState, content, geometricParent, newFrame);
3849 frameToAddToList = newFrame;
3850 }
3851
3852 // Use frameToAddToList as the primary frame. In the non-scrollframe case
3853 // they're equal, but in the scrollframe case newFrame is the scrolled
3854 // frame, while frameToAddToList is the scrollframe (and should be the
3855 // primary frame).
3856 primaryFrame = frameToAddToList;
3857
3858 // If we need to create a block formatting context to wrap our
3859 // kids, do it now.
3860 const nsStyleDisplay* maybeAbsoluteContainingBlockDisplay = display;
3861 nsIFrame* maybeAbsoluteContainingBlockStyleFrame = primaryFrame;
3862 nsIFrame* maybeAbsoluteContainingBlock = newFrame;
3863 nsIFrame* possiblyLeafFrame = newFrame;
3864 if (bits & FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS) {
3865 RefPtr<nsStyleContext> outerSC =
3866 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
3867 *data->mAnonBoxPseudo, styleContext);
3868 #ifdef DEBUG
3869 nsContainerFrame* containerFrame = do_QueryFrame(newFrame);
3870 MOZ_ASSERT(containerFrame);
3871 #endif
3872 nsContainerFrame* container = static_cast<nsContainerFrame*>(newFrame);
3873 nsContainerFrame* outerFrame;
3874 nsContainerFrame* innerFrame;
3875 if (bits & FCDATA_ALLOW_GRID_FLEX_COLUMNSET) {
3876 switch (display->mDisplay) {
3877 case StyleDisplay::Flex:
3878 case StyleDisplay::InlineFlex:
3879 outerFrame = NS_NewFlexContainerFrame(mPresShell, outerSC);
3880 InitAndRestoreFrame(aState, content, container, outerFrame);
3881 innerFrame = outerFrame;
3882 break;
3883 case StyleDisplay::Grid:
3884 case StyleDisplay::InlineGrid:
3885 outerFrame = NS_NewGridContainerFrame(mPresShell, outerSC);
3886 InitAndRestoreFrame(aState, content, container, outerFrame);
3887 innerFrame = outerFrame;
3888 break;
3889 default: {
3890 nsContainerFrame* columnSetFrame = nullptr;
3891 RefPtr<nsStyleContext> innerSC = outerSC;
3892 const nsStyleColumn* columns = outerSC->StyleColumn();
3893 if (columns->mColumnCount != NS_STYLE_COLUMN_COUNT_AUTO ||
3894 columns->mColumnWidth.GetUnit() != eStyleUnit_Auto) {
3895 columnSetFrame = NS_NewColumnSetFrame(
3896 mPresShell, outerSC, nsFrameState(NS_FRAME_OWNS_ANON_BOXES));
3897 InitAndRestoreFrame(aState, content, container, columnSetFrame);
3898 innerSC =
3899 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
3900 nsCSSAnonBoxes::columnContent, outerSC);
3901 }
3902 innerFrame = NS_NewBlockFormattingContext(mPresShell, innerSC);
3903 if (columnSetFrame) {
3904 InitAndRestoreFrame(aState, content, columnSetFrame, innerFrame);
3905 SetInitialSingleChild(columnSetFrame, innerFrame);
3906 outerFrame = columnSetFrame;
3907 } else {
3908 InitAndRestoreFrame(aState, content, container, innerFrame);
3909 outerFrame = innerFrame;
3910 }
3911 break;
3912 }
3913 }
3914 } else {
3915 innerFrame = NS_NewBlockFormattingContext(mPresShell, outerSC);
3916 InitAndRestoreFrame(aState, content, container, innerFrame);
3917 outerFrame = innerFrame;
3918 }
3919
3920 SetInitialSingleChild(container, outerFrame);
3921
3922 container->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
3923
3924 // Now figure out whether newFrame or outerFrame should be the
3925 // absolute container.
3926 auto outerDisplay = outerSC->StyleDisplay();
3927 if (outerDisplay->IsAbsPosContainingBlock(outerFrame)) {
3928 maybeAbsoluteContainingBlockDisplay = outerDisplay;
3929 maybeAbsoluteContainingBlock = outerFrame;
3930 maybeAbsoluteContainingBlockStyleFrame = outerFrame;
3931 innerFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
3932 }
3933
3934 // Our kids should go into the innerFrame.
3935 newFrame = innerFrame;
3936 }
3937
3938 aState.AddChild(frameToAddToList, aFrameItems, content, aParentFrame,
3939 allowOutOfFlow, allowOutOfFlow, isPopup);
3940
3941 nsContainerFrame* newFrameAsContainer = do_QueryFrame(newFrame);
3942 if (newFrameAsContainer) {
3943 #ifdef MOZ_XUL
3944 // Icky XUL stuff, sadly
3945
3946 if (aItem.mIsRootPopupgroup) {
3947 NS_ASSERTION(
3948 nsIRootBox::GetRootBox(mPresShell) &&
3949 nsIRootBox::GetRootBox(mPresShell)->GetPopupSetFrame() ==
3950 newFrame,
3951 "Unexpected PopupSetFrame");
3952 aState.mPopupItems.containingBlock = newFrameAsContainer;
3953 aState.mHavePendingPopupgroup = false;
3954 }
3955 #endif /* MOZ_XUL */
3956
3957 // Process the child content if requested
3958 nsFrameItems childItems;
3959 nsFrameConstructorSaveState absoluteSaveState;
3960
3961 if (bits & FCDATA_FORCE_NULL_ABSPOS_CONTAINER) {
3962 aState.PushAbsoluteContainingBlock(nullptr, nullptr, absoluteSaveState);
3963 } else if (!(bits & FCDATA_SKIP_ABSPOS_PUSH)) {
3964 maybeAbsoluteContainingBlock->AddStateBits(
3965 NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
3966 // This check is identical to nsStyleDisplay::IsAbsPosContainingBlock
3967 // except without the assertion that the style display and frame match.
3968 // When constructing scroll frames we intentionally use the style
3969 // display for the outer, but make the inner the containing block.
3970 if ((maybeAbsoluteContainingBlockDisplay
3971 ->IsAbsolutelyPositionedStyle() ||
3972 maybeAbsoluteContainingBlockDisplay
3973 ->IsRelativelyPositionedStyle() ||
3974 maybeAbsoluteContainingBlockDisplay->IsFixedPosContainingBlock(
3975 maybeAbsoluteContainingBlockStyleFrame)) &&
3976 !nsSVGUtils::IsInSVGTextSubtree(
3977 maybeAbsoluteContainingBlockStyleFrame)) {
3978 nsContainerFrame* cf =
3979 static_cast<nsContainerFrame*>(maybeAbsoluteContainingBlock);
3980 aState.PushAbsoluteContainingBlock(cf, cf, absoluteSaveState);
3981 }
3982 }
3983
3984 if (!aItem.mAnonChildren.IsEmpty()) {
3985 NS_ASSERTION(!(bits & FCDATA_USE_CHILD_ITEMS),
3986 "We should not have both anonymous and non-anonymous "
3987 "children in a given FrameConstructorItem");
3988 AddFCItemsForAnonymousContent(aState, newFrameAsContainer,
3989 aItem.mAnonChildren, aItem.mChildItems);
3990 bits |= FCDATA_USE_CHILD_ITEMS;
3991 }
3992
3993 if (bits & FCDATA_USE_CHILD_ITEMS) {
3994 nsFrameConstructorSaveState floatSaveState;
3995
3996 if (ShouldSuppressFloatingOfDescendants(newFrame)) {
3997 aState.PushFloatContainingBlock(nullptr, floatSaveState);
3998 } else if (newFrame->IsFloatContainingBlock()) {
3999 aState.PushFloatContainingBlock(newFrameAsContainer, floatSaveState);
4000 }
4001 ConstructFramesFromItemList(
4002 aState, aItem.mChildItems, newFrameAsContainer,
4003 bits & FCDATA_IS_WRAPPER_ANON_BOX, childItems);
4004 } else {
4005 // Process the child frames.
4006 ProcessChildren(aState, content, styleContext, newFrameAsContainer,
4007 !(bits & FCDATA_DISALLOW_GENERATED_CONTENT), childItems,
4008 (bits & FCDATA_ALLOW_BLOCK_STYLES) != 0,
4009 aItem.mPendingBinding, possiblyLeafFrame);
4010 }
4011
4012 if (bits & FCDATA_WRAP_KIDS_IN_BLOCKS) {
4013 nsFrameItems newItems;
4014 nsFrameItems currentBlockItems;
4015 nsIFrame* f;
4016 while ((f = childItems.FirstChild()) != nullptr) {
4017 bool wrapFrame = IsInlineFrame(f) || IsFramePartOfIBSplit(f);
4018 if (!wrapFrame) {
4019 FlushAccumulatedBlock(aState, content, newFrameAsContainer,
4020 currentBlockItems, newItems);
4021 }
4022
4023 childItems.RemoveFrame(f);
4024 if (wrapFrame) {
4025 currentBlockItems.AddChild(f);
4026 } else {
4027 newItems.AddChild(f);
4028 }
4029 }
4030 FlushAccumulatedBlock(aState, content, newFrameAsContainer,
4031 currentBlockItems, newItems);
4032
4033 if (childItems.NotEmpty()) {
4034 // an error must have occurred, delete unprocessed frames
4035 childItems.DestroyFrames();
4036 }
4037
4038 childItems = newItems;
4039 }
4040
4041 // Set the frame's initial child list
4042 // Note that MathML depends on this being called even if
4043 // childItems is empty!
4044 newFrameAsContainer->SetInitialChildList(kPrincipalList, childItems);
4045 }
4046 }
4047
4048 NS_ASSERTION(newFrame->IsFrameOfType(nsIFrame::eLineParticipant) ==
4049 ((bits & FCDATA_IS_LINE_PARTICIPANT) != 0),
4050 "Incorrectly set FCDATA_IS_LINE_PARTICIPANT bits");
4051
4052 if (aItem.mIsAnonymousContentCreatorContent) {
4053 primaryFrame->AddStateBits(NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT);
4054 }
4055
4056 // Even if mCreatingExtraFrames is set, we may need to SetPrimaryFrame for
4057 // generated content that doesn't have one yet. Note that we have to examine
4058 // the frame bit, because by this point mIsGeneratedContent has been cleared
4059 // on aItem.
4060 if ((!aState.mCreatingExtraFrames ||
4061 (primaryFrame->HasAnyStateBits(NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT |
4062 NS_FRAME_GENERATED_CONTENT) &&
4063 !aItem.mContent->GetPrimaryFrame())) &&
4064 !(bits & FCDATA_SKIP_FRAMESET)) {
4065 aItem.mContent->SetPrimaryFrame(primaryFrame);
4066 ActiveLayerTracker::TransferActivityToFrame(aItem.mContent, primaryFrame);
4067 }
4068 }
4069
SetFlagsOnSubtree(nsIContent * aNode,uintptr_t aFlagsToSet)4070 static void SetFlagsOnSubtree(nsIContent* aNode, uintptr_t aFlagsToSet) {
4071 #ifdef DEBUG
4072 // Make sure that the node passed to us doesn't have any XBL children
4073 {
4074 FlattenedChildIterator iter(aNode);
4075 NS_ASSERTION(!iter.XBLInvolved() || !iter.GetNextChild(),
4076 "The node should not have any XBL children");
4077 }
4078 #endif
4079
4080 // Set the flag on the node itself
4081 aNode->SetFlags(aFlagsToSet);
4082
4083 // Set the flag on all of its children recursively
4084 for (nsIContent* child = aNode->GetFirstChild(); child;
4085 child = child->GetNextSibling()) {
4086 SetFlagsOnSubtree(child, aFlagsToSet);
4087 }
4088 }
4089
4090 /**
4091 * This function takes a tree of nsIAnonymousContentCreator::ContentInfo
4092 * objects where the nsIContent nodes have just been created, and appends the
4093 * nsIContent children in the tree to their parent. The leaf nsIContent objects
4094 * are appended first to minimize the number of notifications that are sent
4095 * out (i.e. by appending as many descendants as posible while their parent is
4096 * not yet in the document tree).
4097 *
4098 * This function is used simply as a convenience so that implementations of
4099 * nsIAnonymousContentCreator::CreateAnonymousContent don't all have to have
4100 * their own code to connect the elements that they create.
4101 */
ConnectAnonymousTreeDescendants(nsIContent * aParent,nsTArray<nsIAnonymousContentCreator::ContentInfo> & aContent)4102 static void ConnectAnonymousTreeDescendants(
4103 nsIContent* aParent,
4104 nsTArray<nsIAnonymousContentCreator::ContentInfo>& aContent) {
4105 uint32_t count = aContent.Length();
4106 for (uint32_t i = 0; i < count; i++) {
4107 nsIContent* content = aContent[i].mContent;
4108 NS_ASSERTION(content, "null anonymous content?");
4109
4110 ConnectAnonymousTreeDescendants(content, aContent[i].mChildren);
4111
4112 aParent->AppendChildTo(content, false);
4113 }
4114 }
4115
SetNativeAnonymousBitOnDescendants(nsIContent * aRoot)4116 static void SetNativeAnonymousBitOnDescendants(nsIContent* aRoot) {
4117 for (nsIContent* curr = aRoot; curr; curr = curr->GetNextNode(aRoot)) {
4118 curr->SetFlags(NODE_IS_NATIVE_ANONYMOUS);
4119 }
4120 }
4121
GetAnonymousContent(nsIContent * aParent,nsIFrame * aParentFrame,nsTArray<nsIAnonymousContentCreator::ContentInfo> & aContent)4122 nsresult nsCSSFrameConstructor::GetAnonymousContent(
4123 nsIContent* aParent, nsIFrame* aParentFrame,
4124 nsTArray<nsIAnonymousContentCreator::ContentInfo>& aContent) {
4125 nsIAnonymousContentCreator* creator = do_QueryFrame(aParentFrame);
4126 if (!creator) return NS_OK;
4127
4128 nsresult rv = creator->CreateAnonymousContent(aContent);
4129 if (NS_FAILED(rv)) {
4130 // CreateAnonymousContent failed, e.g. because the page has a <use> loop.
4131 return rv;
4132 }
4133
4134 uint32_t count = aContent.Length();
4135 for (uint32_t i = 0; i < count; i++) {
4136 // get our child's content and set its parent to our content
4137 nsIContent* content = aContent[i].mContent;
4138 NS_ASSERTION(content, "null anonymous content?");
4139
4140 ConnectAnonymousTreeDescendants(content, aContent[i].mChildren);
4141
4142 LayoutFrameType parentFrameType = aParentFrame->Type();
4143 if (parentFrameType == LayoutFrameType::SVGUse) {
4144 // least-surprise CSS binding until we do the SVG specified
4145 // cascading rules for <svg:use> - bug 265894
4146 content->SetFlags(NODE_IS_ANONYMOUS_ROOT);
4147 } else {
4148 content->SetIsNativeAnonymousRoot();
4149 // Don't mark descendants of the custom content container
4150 // as native anonymous. When canvas custom content is initially
4151 // created and appended to the custom content container, in
4152 // nsIDocument::InsertAnonymousContent, it is not considered native
4153 // anonymous content. But if we end up reframing the root element,
4154 // we will re-create the nsCanvasFrame, and we would end up in here,
4155 // marking it as NAC. Existing uses of canvas custom content would
4156 // break if it becomes NAC (since each element starts inheriting
4157 // styles from its closest non-NAC ancestor, rather than from its
4158 // parent).
4159 if (!(parentFrameType == LayoutFrameType::Canvas &&
4160 content == static_cast<nsCanvasFrame*>(aParentFrame)
4161 ->GetCustomContentContainer())) {
4162 SetNativeAnonymousBitOnDescendants(content);
4163 }
4164 }
4165
4166 bool anonContentIsEditable = content->HasFlag(NODE_IS_EDITABLE);
4167
4168 // If the parent is in a shadow tree, make sure we don't
4169 // bind with a document because shadow roots and its descendants
4170 // are not in document.
4171 nsIDocument* bindDocument =
4172 aParent->HasFlag(NODE_IS_IN_SHADOW_TREE) ? nullptr : mDocument;
4173 rv = content->BindToTree(bindDocument, aParent, aParent, true);
4174 // If the anonymous content creator requested that the content should be
4175 // editable, honor its request.
4176 // We need to set the flag on the whole subtree, because existing
4177 // children's flags have already been set as part of the BindToTree
4178 // operation.
4179 if (anonContentIsEditable) {
4180 NS_ASSERTION(aParentFrame->IsTextInputFrame(),
4181 "We only expect this for anonymous content under a text "
4182 "control frame");
4183 SetFlagsOnSubtree(content, NODE_IS_EDITABLE);
4184 }
4185 if (NS_FAILED(rv)) {
4186 content->UnbindFromTree();
4187 return rv;
4188 }
4189 }
4190
4191 if (ServoStyleSet* styleSet = mPresShell->StyleSet()->GetAsServo()) {
4192 // Eagerly compute styles for the anonymous content tree.
4193 for (auto& info : aContent) {
4194 if (info.mContent->IsElement()) {
4195 styleSet->StyleNewSubtree(info.mContent->AsElement());
4196 }
4197 }
4198 }
4199
4200 return NS_OK;
4201 }
4202
IsXULDisplayType(const nsStyleDisplay * aDisplay)4203 static bool IsXULDisplayType(const nsStyleDisplay* aDisplay) {
4204 // -moz-{inline-}box is XUL, unless we're emulating it with flexbox.
4205 if (!StylePrefs::sEmulateMozBoxWithFlex &&
4206 (aDisplay->mDisplay == StyleDisplay::MozInlineBox ||
4207 aDisplay->mDisplay == StyleDisplay::MozBox)) {
4208 return true;
4209 }
4210
4211 #ifdef MOZ_XUL
4212 return (aDisplay->mDisplay == StyleDisplay::MozInlineGrid ||
4213 aDisplay->mDisplay == StyleDisplay::MozInlineStack ||
4214 aDisplay->mDisplay == StyleDisplay::MozGrid ||
4215 aDisplay->mDisplay == StyleDisplay::MozStack ||
4216 aDisplay->mDisplay == StyleDisplay::MozGridGroup ||
4217 aDisplay->mDisplay == StyleDisplay::MozGridLine ||
4218 aDisplay->mDisplay == StyleDisplay::MozDeck ||
4219 aDisplay->mDisplay == StyleDisplay::MozPopup ||
4220 aDisplay->mDisplay == StyleDisplay::MozGroupbox);
4221 #else
4222 return false;
4223 #endif
4224 }
4225
4226 // XUL frames are not allowed to be out of flow.
4227 #define SIMPLE_XUL_FCDATA(_func) \
4228 FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_SKIP_ABSPOS_PUSH, _func)
4229 #define SCROLLABLE_XUL_FCDATA(_func) \
4230 FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_SKIP_ABSPOS_PUSH | \
4231 FCDATA_MAY_NEED_SCROLLFRAME, \
4232 _func)
4233 // .. but we allow some XUL frames to be _containers_ for out-of-flow content
4234 // (This is the same as SCROLLABLE_XUL_FCDATA, but w/o FCDATA_SKIP_ABSPOS_PUSH)
4235 #define SCROLLABLE_ABSPOS_CONTAINER_XUL_FCDATA(_func) \
4236 FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_MAY_NEED_SCROLLFRAME, _func)
4237
4238 #define SIMPLE_XUL_CREATE(_tag, _func) \
4239 { &nsGkAtoms::_tag, SIMPLE_XUL_FCDATA(_func) }
4240 #define SCROLLABLE_XUL_CREATE(_tag, _func) \
4241 { &nsGkAtoms::_tag, SCROLLABLE_XUL_FCDATA(_func) }
4242 #define SIMPLE_XUL_DISPLAY_CREATE(_display, _func) \
4243 FCDATA_FOR_DISPLAY(_display, SIMPLE_XUL_FCDATA(_func))
4244 #define SCROLLABLE_XUL_DISPLAY_CREATE(_display, _func) \
4245 FCDATA_FOR_DISPLAY(_display, SCROLLABLE_XUL_FCDATA(_func))
4246 #define SCROLLABLE_ABSPOS_CONTAINER_XUL_DISPLAY_CREATE(_display, _func) \
4247 FCDATA_FOR_DISPLAY(_display, SCROLLABLE_ABSPOS_CONTAINER_XUL_FCDATA(_func))
4248
NS_NewGridBoxFrame(nsIPresShell * aPresShell,nsStyleContext * aStyleContext)4249 static nsIFrame* NS_NewGridBoxFrame(nsIPresShell* aPresShell,
4250 nsStyleContext* aStyleContext) {
4251 nsCOMPtr<nsBoxLayout> layout;
4252 NS_NewGridLayout2(aPresShell, getter_AddRefs(layout));
4253 return NS_NewBoxFrame(aPresShell, aStyleContext, false, layout);
4254 }
4255
4256 /* static */
4257 const nsCSSFrameConstructor::FrameConstructionData*
FindXULTagData(Element * aElement,nsAtom * aTag,int32_t aNameSpaceID,nsStyleContext * aStyleContext)4258 nsCSSFrameConstructor::FindXULTagData(Element* aElement, nsAtom* aTag,
4259 int32_t aNameSpaceID,
4260 nsStyleContext* aStyleContext) {
4261 if (aNameSpaceID != kNameSpaceID_XUL) {
4262 return nullptr;
4263 }
4264
4265 static const FrameConstructionDataByTag sXULTagData[] = {
4266 #ifdef MOZ_XUL
4267 SCROLLABLE_XUL_CREATE(button, NS_NewButtonBoxFrame),
4268 SCROLLABLE_XUL_CREATE(thumb, NS_NewButtonBoxFrame),
4269 SCROLLABLE_XUL_CREATE(checkbox, NS_NewButtonBoxFrame),
4270 SCROLLABLE_XUL_CREATE(radio, NS_NewButtonBoxFrame),
4271 SCROLLABLE_XUL_CREATE(autorepeatbutton, NS_NewAutoRepeatBoxFrame),
4272 SCROLLABLE_XUL_CREATE(titlebar, NS_NewTitleBarFrame),
4273 SCROLLABLE_XUL_CREATE(resizer, NS_NewResizerFrame),
4274 SIMPLE_XUL_CREATE(image, NS_NewImageBoxFrame),
4275 SIMPLE_XUL_CREATE(spring, NS_NewLeafBoxFrame),
4276 SIMPLE_XUL_CREATE(spacer, NS_NewLeafBoxFrame),
4277 SIMPLE_XUL_CREATE(treechildren, NS_NewTreeBodyFrame),
4278 SIMPLE_XUL_CREATE(treecol, NS_NewTreeColFrame),
4279 SIMPLE_XUL_CREATE(text, NS_NewTextBoxFrame),
4280 SIMPLE_TAG_CHAIN(label, nsCSSFrameConstructor::FindXULLabelData),
4281 SIMPLE_TAG_CHAIN(description,
4282 nsCSSFrameConstructor::FindXULDescriptionData),
4283 SIMPLE_XUL_CREATE(menu, NS_NewMenuFrame),
4284 SIMPLE_XUL_CREATE(menubutton, NS_NewMenuFrame),
4285 SIMPLE_XUL_CREATE(menuitem, NS_NewMenuItemFrame),
4286 #ifdef XP_MACOSX
4287 SIMPLE_TAG_CHAIN(menubar, nsCSSFrameConstructor::FindXULMenubarData),
4288 #else
4289 SIMPLE_XUL_CREATE(menubar, NS_NewMenuBarFrame),
4290 #endif /* XP_MACOSX */
4291 SIMPLE_TAG_CHAIN(popupgroup, nsCSSFrameConstructor::FindPopupGroupData),
4292 SIMPLE_XUL_CREATE(iframe, NS_NewSubDocumentFrame),
4293 SIMPLE_XUL_CREATE(editor, NS_NewSubDocumentFrame),
4294 SIMPLE_XUL_CREATE(browser, NS_NewSubDocumentFrame),
4295 SIMPLE_XUL_CREATE(progressmeter, NS_NewProgressMeterFrame),
4296 SIMPLE_XUL_CREATE(splitter, NS_NewSplitterFrame),
4297 SIMPLE_TAG_CHAIN(listboxbody,
4298 nsCSSFrameConstructor::FindXULListBoxBodyData),
4299 SIMPLE_TAG_CHAIN(listitem, nsCSSFrameConstructor::FindXULListItemData),
4300 #endif /* MOZ_XUL */
4301 SIMPLE_XUL_CREATE(slider, NS_NewSliderFrame),
4302 SIMPLE_XUL_CREATE(scrollbar, NS_NewScrollbarFrame),
4303 SIMPLE_XUL_CREATE(scrollbarbutton, NS_NewScrollbarButtonFrame)};
4304
4305 bool tagFound;
4306 const FrameConstructionData* data =
4307 FindDataByTag(aTag, aElement, aStyleContext, sXULTagData,
4308 ArrayLength(sXULTagData), &tagFound);
4309
4310 // There's no spec that says what display: contents means for special XUL
4311 // elements, but we do the same as for HTML "Unusual Elements", i.e. treat it
4312 // as display:none.
4313 if (tagFound && MOZ_UNLIKELY(aStyleContext->StyleDisplay()->mDisplay ==
4314 StyleDisplay::Contents)) {
4315 static const FrameConstructionData sSuppressData = SUPPRESS_FCDATA();
4316 return &sSuppressData;
4317 }
4318
4319 return data;
4320 }
4321
4322 #ifdef MOZ_XUL
4323 /* static */
4324 const nsCSSFrameConstructor::FrameConstructionData*
FindPopupGroupData(Element * aElement,nsStyleContext *)4325 nsCSSFrameConstructor::FindPopupGroupData(Element* aElement,
4326 nsStyleContext* /* unused */) {
4327 if (!aElement->IsRootOfNativeAnonymousSubtree()) {
4328 return nullptr;
4329 }
4330
4331 static const FrameConstructionData sPopupSetData =
4332 SIMPLE_XUL_FCDATA(NS_NewPopupSetFrame);
4333 return &sPopupSetData;
4334 }
4335
4336 /* static */
4337 const nsCSSFrameConstructor::FrameConstructionData
4338 nsCSSFrameConstructor::sXULTextBoxData =
4339 SIMPLE_XUL_FCDATA(NS_NewTextBoxFrame);
4340
4341 /* static */
4342 const nsCSSFrameConstructor::FrameConstructionData*
FindXULLabelData(Element * aElement,nsStyleContext *)4343 nsCSSFrameConstructor::FindXULLabelData(Element* aElement,
4344 nsStyleContext* /* unused */) {
4345 if (aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::value)) {
4346 return &sXULTextBoxData;
4347 }
4348
4349 static const FrameConstructionData sLabelData =
4350 SIMPLE_XUL_FCDATA(NS_NewXULLabelFrame);
4351 return &sLabelData;
4352 }
4353
NS_NewXULDescriptionFrame(nsIPresShell * aPresShell,nsStyleContext * aContext)4354 static nsIFrame* NS_NewXULDescriptionFrame(nsIPresShell* aPresShell,
4355 nsStyleContext* aContext) {
4356 // XXXbz do we really need to set up the block formatting context root? If the
4357 // parent is not a block we'll get it anyway, and if it is, do we want it?
4358 return NS_NewBlockFormattingContext(aPresShell, aContext);
4359 }
4360
4361 /* static */
4362 const nsCSSFrameConstructor::FrameConstructionData*
FindXULDescriptionData(Element * aElement,nsStyleContext *)4363 nsCSSFrameConstructor::FindXULDescriptionData(Element* aElement,
4364 nsStyleContext* /* unused */) {
4365 if (aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::value)) {
4366 return &sXULTextBoxData;
4367 }
4368
4369 static const FrameConstructionData sDescriptionData =
4370 SIMPLE_XUL_FCDATA(NS_NewXULDescriptionFrame);
4371 return &sDescriptionData;
4372 }
4373
4374 #ifdef XP_MACOSX
4375 /* static */
4376 const nsCSSFrameConstructor::FrameConstructionData*
FindXULMenubarData(Element * aElement,nsStyleContext * aStyleContext)4377 nsCSSFrameConstructor::FindXULMenubarData(Element* aElement,
4378 nsStyleContext* aStyleContext) {
4379 nsCOMPtr<nsIDocShell> treeItem = aStyleContext->PresContext()->GetDocShell();
4380 if (treeItem && nsIDocShellTreeItem::typeChrome == treeItem->ItemType()) {
4381 nsCOMPtr<nsIDocShellTreeItem> parent;
4382 treeItem->GetParent(getter_AddRefs(parent));
4383 if (!parent) {
4384 // This is the root. Suppress the menubar, since on Mac
4385 // window menus are not attached to the window.
4386 static const FrameConstructionData sSuppressData = SUPPRESS_FCDATA();
4387 return &sSuppressData;
4388 }
4389 }
4390
4391 static const FrameConstructionData sMenubarData =
4392 SIMPLE_XUL_FCDATA(NS_NewMenuBarFrame);
4393 return &sMenubarData;
4394 }
4395 #endif /* XP_MACOSX */
4396
4397 /* static */
4398 const nsCSSFrameConstructor::FrameConstructionData*
FindXULListBoxBodyData(Element * aElement,nsStyleContext * aStyleContext)4399 nsCSSFrameConstructor::FindXULListBoxBodyData(Element* aElement,
4400 nsStyleContext* aStyleContext) {
4401 if (aStyleContext->StyleDisplay()->mDisplay != StyleDisplay::MozGridGroup) {
4402 return nullptr;
4403 }
4404
4405 static const FrameConstructionData sListBoxBodyData =
4406 SCROLLABLE_XUL_FCDATA(NS_NewListBoxBodyFrame);
4407 return &sListBoxBodyData;
4408 }
4409
4410 /* static */
4411 const nsCSSFrameConstructor::FrameConstructionData*
FindXULListItemData(Element * aElement,nsStyleContext * aStyleContext)4412 nsCSSFrameConstructor::FindXULListItemData(Element* aElement,
4413 nsStyleContext* aStyleContext) {
4414 if (aStyleContext->StyleDisplay()->mDisplay != StyleDisplay::MozGridLine) {
4415 return nullptr;
4416 }
4417
4418 static const FrameConstructionData sListItemData =
4419 SCROLLABLE_XUL_FCDATA(NS_NewListItemFrame);
4420 return &sListItemData;
4421 }
4422
4423 #endif /* MOZ_XUL */
4424
4425 /* static */
4426 const nsCSSFrameConstructor::FrameConstructionData*
FindXULDisplayData(const nsStyleDisplay * aDisplay,Element * aElement,nsStyleContext * aStyleContext)4427 nsCSSFrameConstructor::FindXULDisplayData(const nsStyleDisplay* aDisplay,
4428 Element* aElement,
4429 nsStyleContext* aStyleContext) {
4430 static const FrameConstructionDataByDisplay sXULDisplayData[] = {
4431 SCROLLABLE_ABSPOS_CONTAINER_XUL_DISPLAY_CREATE(StyleDisplay::MozBox,
4432 NS_NewBoxFrame),
4433 SCROLLABLE_ABSPOS_CONTAINER_XUL_DISPLAY_CREATE(StyleDisplay::MozInlineBox,
4434 NS_NewBoxFrame),
4435 #ifdef MOZ_XUL
4436 SCROLLABLE_XUL_DISPLAY_CREATE(StyleDisplay::MozGrid, NS_NewGridBoxFrame),
4437 SCROLLABLE_XUL_DISPLAY_CREATE(StyleDisplay::MozInlineGrid,
4438 NS_NewGridBoxFrame),
4439 SCROLLABLE_XUL_DISPLAY_CREATE(StyleDisplay::MozGridGroup,
4440 NS_NewGridRowGroupFrame),
4441 SCROLLABLE_XUL_DISPLAY_CREATE(StyleDisplay::MozGridLine,
4442 NS_NewGridRowLeafFrame),
4443 SCROLLABLE_XUL_DISPLAY_CREATE(StyleDisplay::MozStack, NS_NewStackFrame),
4444 SCROLLABLE_XUL_DISPLAY_CREATE(StyleDisplay::MozInlineStack,
4445 NS_NewStackFrame),
4446 SIMPLE_XUL_DISPLAY_CREATE(StyleDisplay::MozDeck, NS_NewDeckFrame),
4447 SCROLLABLE_XUL_DISPLAY_CREATE(StyleDisplay::MozGroupbox,
4448 NS_NewGroupBoxFrame),
4449 FCDATA_FOR_DISPLAY(
4450 StyleDisplay::MozPopup,
4451 FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_IS_POPUP |
4452 FCDATA_SKIP_ABSPOS_PUSH,
4453 NS_NewMenuPopupFrame))
4454 #endif /* MOZ_XUL */
4455 };
4456
4457 if (aDisplay->mDisplay < StyleDisplay::MozBox) {
4458 return nullptr;
4459 }
4460
4461 // If we're emulating -moz-box with flexbox, then treat it as non-XUL and
4462 // return null (except for scrollcorners which have to be XUL becuase their
4463 // parent reflows them with BoxReflow() which means they have to get
4464 // actual-XUL frames).
4465 if (StylePrefs::sEmulateMozBoxWithFlex && aElement &&
4466 !aElement->IsXULElement(nsGkAtoms::scrollcorner) &&
4467 (aDisplay->mDisplay == StyleDisplay::MozBox ||
4468 aDisplay->mDisplay == StyleDisplay::MozInlineBox)) {
4469 return nullptr;
4470 }
4471
4472 MOZ_ASSERT(aDisplay->mDisplay <= StyleDisplay::MozPopup,
4473 "Someone added a new display value?");
4474
4475 const FrameConstructionDataByDisplay& data =
4476 sXULDisplayData[size_t(aDisplay->mDisplay) -
4477 size_t(StyleDisplay::MozBox)];
4478 MOZ_ASSERT(aDisplay->mDisplay == data.mDisplay,
4479 "Did someone mess with the order?");
4480
4481 return &data.mData;
4482 }
4483
4484 already_AddRefed<nsStyleContext>
BeginBuildingScrollFrame(nsFrameConstructorState & aState,nsIContent * aContent,nsStyleContext * aContentStyle,nsContainerFrame * aParentFrame,nsAtom * aScrolledPseudo,bool aIsRoot,nsContainerFrame * & aNewFrame)4485 nsCSSFrameConstructor::BeginBuildingScrollFrame(
4486 nsFrameConstructorState& aState, nsIContent* aContent,
4487 nsStyleContext* aContentStyle, nsContainerFrame* aParentFrame,
4488 nsAtom* aScrolledPseudo, bool aIsRoot, nsContainerFrame*& aNewFrame) {
4489 nsContainerFrame* gfxScrollFrame = aNewFrame;
4490
4491 nsFrameItems anonymousItems;
4492
4493 RefPtr<nsStyleContext> contentStyle = aContentStyle;
4494
4495 if (!gfxScrollFrame) {
4496 // Build a XULScrollFrame when the child is a box, otherwise an
4497 // HTMLScrollFrame
4498 // XXXbz this is the lone remaining consumer of IsXULDisplayType.
4499 // I wonder whether we can eliminate that somehow.
4500 const nsStyleDisplay* displayStyle = aContentStyle->StyleDisplay();
4501 if (IsXULDisplayType(displayStyle)) {
4502 gfxScrollFrame = NS_NewXULScrollFrame(
4503 mPresShell, contentStyle, aIsRoot,
4504 displayStyle->mDisplay == StyleDisplay::MozStack ||
4505 displayStyle->mDisplay == StyleDisplay::MozInlineStack);
4506 } else {
4507 gfxScrollFrame = NS_NewHTMLScrollFrame(mPresShell, contentStyle, aIsRoot);
4508 }
4509
4510 InitAndRestoreFrame(aState, aContent, aParentFrame, gfxScrollFrame);
4511 }
4512
4513 // if there are any anonymous children for the scroll frame, create
4514 // frames for them.
4515 //
4516 // We can't take the normal ProcessChildren path, because the NAC needs to
4517 // be parented to the scrollframe, and everything else needs to be parented
4518 // to the scrolledframe.
4519 AutoTArray<nsIAnonymousContentCreator::ContentInfo, 4> scrollNAC;
4520 DebugOnly<nsresult> rv =
4521 GetAnonymousContent(aContent, gfxScrollFrame, scrollNAC);
4522 MOZ_ASSERT(NS_SUCCEEDED(rv));
4523 if (scrollNAC.Length() > 0) {
4524 TreeMatchContext::AutoAncestorPusher ancestorPusher(
4525 aState.mTreeMatchContext);
4526 if (aState.HasAncestorFilter()) {
4527 ancestorPusher.PushAncestorAndStyleScope(aContent->AsElement());
4528 } else {
4529 ancestorPusher.PushStyleScope(aContent->AsElement());
4530 }
4531
4532 AutoFrameConstructionItemList items(this);
4533 AddFCItemsForAnonymousContent(aState, gfxScrollFrame, scrollNAC, items);
4534 ConstructFramesFromItemList(aState, items, gfxScrollFrame,
4535 /* aParentIsWrapperAnonBox = */ false,
4536 anonymousItems);
4537 }
4538
4539 aNewFrame = gfxScrollFrame;
4540 gfxScrollFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
4541
4542 // we used the style that was passed in. So resolve another one.
4543 StyleSetHandle styleSet = mPresShell->StyleSet();
4544 RefPtr<nsStyleContext> scrolledChildStyle =
4545 styleSet->ResolveInheritingAnonymousBoxStyle(aScrolledPseudo,
4546 contentStyle);
4547
4548 if (gfxScrollFrame) {
4549 gfxScrollFrame->SetInitialChildList(kPrincipalList, anonymousItems);
4550 }
4551
4552 return scrolledChildStyle.forget();
4553 }
4554
FinishBuildingScrollFrame(nsContainerFrame * aScrollFrame,nsIFrame * aScrolledFrame)4555 void nsCSSFrameConstructor::FinishBuildingScrollFrame(
4556 nsContainerFrame* aScrollFrame, nsIFrame* aScrolledFrame) {
4557 nsFrameList scrolled(aScrolledFrame, aScrolledFrame);
4558 aScrollFrame->AppendFrames(kPrincipalList, scrolled);
4559 }
4560
4561 /**
4562 * Called to wrap a gfx scrollframe around a frame. The hierarchy will look like
4563 *this
4564 *
4565 * ------- for gfx scrollbars ------
4566 *
4567 *
4568 * ScrollFrame
4569 * ^
4570 * |
4571 * Frame (scrolled frame you passed in)
4572 *
4573 *
4574 *-----------------------------------
4575 * LEGEND:
4576 *
4577 * ScrollFrame: This is a frame that manages gfx cross platform frame based
4578 *scrollbars.
4579 *
4580 * @param aContent the content node of the child to wrap.
4581 * @param aScrolledFrame The frame of the content to wrap. This should not be
4582 * Initialized. This method will initialize it with a
4583 *scrolled pseudo and no nsIContent. The content will be attached to the
4584 *scrollframe returned.
4585 * @param aContentStyle the style context that has already been resolved for the
4586 *content being passed in.
4587 *
4588 * @param aParentFrame The parent to attach the scroll frame to
4589 *
4590 * @param aNewFrame The new scrollframe or gfx scrollframe that we create. It
4591 *will contain the scrolled frame you passed in. (returned) If this is not null,
4592 *we'll just use it
4593 * @param aScrolledContentStyle the style that was resolved for the scrolled
4594 *frame. (returned)
4595 */
BuildScrollFrame(nsFrameConstructorState & aState,nsIContent * aContent,nsStyleContext * aContentStyle,nsIFrame * aScrolledFrame,nsContainerFrame * aParentFrame,nsContainerFrame * & aNewFrame)4596 void nsCSSFrameConstructor::BuildScrollFrame(nsFrameConstructorState& aState,
4597 nsIContent* aContent,
4598 nsStyleContext* aContentStyle,
4599 nsIFrame* aScrolledFrame,
4600 nsContainerFrame* aParentFrame,
4601 nsContainerFrame*& aNewFrame) {
4602 RefPtr<nsStyleContext> scrolledContentStyle = BeginBuildingScrollFrame(
4603 aState, aContent, aContentStyle, aParentFrame,
4604 nsCSSAnonBoxes::scrolledContent, false, aNewFrame);
4605
4606 aScrolledFrame->SetStyleContextWithoutNotification(scrolledContentStyle);
4607 InitAndRestoreFrame(aState, aContent, aNewFrame, aScrolledFrame);
4608
4609 FinishBuildingScrollFrame(aNewFrame, aScrolledFrame);
4610 }
4611
4612 const nsCSSFrameConstructor::FrameConstructionData*
FindDisplayData(const nsStyleDisplay * aDisplay,Element * aElement,nsStyleContext * aStyleContext)4613 nsCSSFrameConstructor::FindDisplayData(const nsStyleDisplay* aDisplay,
4614 Element* aElement,
4615 nsStyleContext* aStyleContext) {
4616 static_assert(eParentTypeCount < (1 << (32 - FCDATA_PARENT_TYPE_OFFSET)),
4617 "Check eParentTypeCount should not overflow");
4618
4619 // The style system ensures that floated and positioned frames are
4620 // block-level.
4621 NS_ASSERTION(!(aDisplay->IsFloatingStyle() ||
4622 aDisplay->IsAbsolutelyPositionedStyle()) ||
4623 aDisplay->IsBlockOutsideStyle() ||
4624 aDisplay->mDisplay == StyleDisplay::Contents,
4625 "Style system did not apply CSS2.1 section 9.7 fixups");
4626
4627 // If this is "body", try propagating its scroll style to the viewport
4628 // Note that we need to do this even if the body is NOT scrollable;
4629 // it might have dynamically changed from scrollable to not scrollable,
4630 // and that might need to be propagated.
4631 // XXXbz is this the right place to do this? If this code moves,
4632 // make this function static.
4633 bool propagatedScrollToViewport = false;
4634 if (aElement->IsHTMLElement(nsGkAtoms::body)) {
4635 if (nsPresContext* presContext = mPresShell->GetPresContext()) {
4636 propagatedScrollToViewport =
4637 presContext->UpdateViewportScrollbarStylesOverride() == aElement;
4638 }
4639 }
4640
4641 NS_ASSERTION(!propagatedScrollToViewport ||
4642 !mPresShell->GetPresContext()->IsPaginated(),
4643 "Shouldn't propagate scroll in paginated contexts");
4644
4645 if (aDisplay->IsBlockInsideStyle()) {
4646 // If the frame is a block-level frame and is scrollable, then wrap it in a
4647 // scroll frame. Except we don't want to do that for paginated contexts for
4648 // frames that are block-outside and aren't frames for native anonymous
4649 // stuff.
4650 // XXX Ignore tables for the time being (except caption)
4651 const uint32_t kCaptionCtorFlags =
4652 FCDATA_IS_TABLE_PART | FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable);
4653 bool caption = aDisplay->mDisplay == StyleDisplay::TableCaption;
4654 bool suppressScrollFrame = false;
4655 bool needScrollFrame =
4656 aDisplay->IsScrollableOverflow() && !propagatedScrollToViewport;
4657 if (needScrollFrame) {
4658 suppressScrollFrame = mPresShell->GetPresContext()->IsPaginated() &&
4659 aDisplay->IsBlockOutsideStyle() &&
4660 !aElement->IsInNativeAnonymousSubtree();
4661 if (!suppressScrollFrame) {
4662 static const FrameConstructionData sScrollableBlockData[2] = {
4663 FULL_CTOR_FCDATA(0,
4664 &nsCSSFrameConstructor::ConstructScrollableBlock),
4665 FULL_CTOR_FCDATA(kCaptionCtorFlags,
4666 &nsCSSFrameConstructor::ConstructScrollableBlock)};
4667 return &sScrollableBlockData[caption];
4668 }
4669
4670 // If the scrollable frame would have propagated its scrolling to the
4671 // viewport, we still want to construct a regular block rather than a
4672 // scrollframe so that it paginates correctly, but we don't want to set
4673 // the bit on the block that tells it to clip at paint time.
4674 if (mPresShell->GetPresContext()->ElementWouldPropagateScrollbarStyles(
4675 aElement)) {
4676 suppressScrollFrame = false;
4677 }
4678 }
4679
4680 // Handle various non-scrollable blocks.
4681 static const FrameConstructionData sNonScrollableBlockData[2][2] = {
4682 {FULL_CTOR_FCDATA(0,
4683 &nsCSSFrameConstructor::ConstructNonScrollableBlock),
4684 FULL_CTOR_FCDATA(kCaptionCtorFlags,
4685 &nsCSSFrameConstructor::ConstructNonScrollableBlock)},
4686 {FULL_CTOR_FCDATA(FCDATA_FORCED_NON_SCROLLABLE_BLOCK,
4687 &nsCSSFrameConstructor::ConstructNonScrollableBlock),
4688 FULL_CTOR_FCDATA(
4689 FCDATA_FORCED_NON_SCROLLABLE_BLOCK | kCaptionCtorFlags,
4690 &nsCSSFrameConstructor::ConstructNonScrollableBlock)}};
4691 return &sNonScrollableBlockData[suppressScrollFrame][caption];
4692 }
4693
4694 // If this is for a <body> node and we've propagated the scroll-frame to the
4695 // viewport, we need to make sure not to add another layer of scrollbars, so
4696 // we use a different FCData struct without FCDATA_MAY_NEED_SCROLLFRAME.
4697 if (propagatedScrollToViewport && aDisplay->IsScrollableOverflow()) {
4698 if (aDisplay->mDisplay == StyleDisplay::Flex ||
4699 aDisplay->mDisplay == StyleDisplay::WebkitBox ||
4700 (StylePrefs::sEmulateMozBoxWithFlex &&
4701 aDisplay->mDisplay == StyleDisplay::MozBox)) {
4702 static const FrameConstructionData sNonScrollableFlexData =
4703 FCDATA_DECL(0, NS_NewFlexContainerFrame);
4704 return &sNonScrollableFlexData;
4705 }
4706 if (aDisplay->mDisplay == StyleDisplay::Grid) {
4707 static const FrameConstructionData sNonScrollableGridData =
4708 FCDATA_DECL(0, NS_NewGridContainerFrame);
4709 return &sNonScrollableGridData;
4710 }
4711 }
4712
4713 // NOTE: Make sure to keep this up to date with the StyleDisplay definition!
4714 static const FrameConstructionDataByDisplay sDisplayData[] = {
4715 FCDATA_FOR_DISPLAY(StyleDisplay::None, UNREACHABLE_FCDATA()),
4716 FCDATA_FOR_DISPLAY(StyleDisplay::Block, UNREACHABLE_FCDATA()),
4717 FCDATA_FOR_DISPLAY(StyleDisplay::FlowRoot, UNREACHABLE_FCDATA()),
4718 // To keep the hash table small don't add inline frames (they're
4719 // typically things like FONT and B), because we can quickly
4720 // find them if we need to.
4721 // XXXbz the "quickly" part is a bald-faced lie!
4722 FCDATA_FOR_DISPLAY(
4723 StyleDisplay::Inline,
4724 FULL_CTOR_FCDATA(FCDATA_IS_INLINE | FCDATA_IS_LINE_PARTICIPANT,
4725 &nsCSSFrameConstructor::ConstructInline)),
4726 FCDATA_FOR_DISPLAY(StyleDisplay::InlineBlock, UNREACHABLE_FCDATA()),
4727 FCDATA_FOR_DISPLAY(StyleDisplay::ListItem, UNREACHABLE_FCDATA()),
4728 FCDATA_FOR_DISPLAY(
4729 StyleDisplay::Table,
4730 FULL_CTOR_FCDATA(0, &nsCSSFrameConstructor::ConstructTable)),
4731 FCDATA_FOR_DISPLAY(
4732 StyleDisplay::InlineTable,
4733 FULL_CTOR_FCDATA(0, &nsCSSFrameConstructor::ConstructTable)),
4734 // NOTE: In the unlikely event that we add another table-part here that
4735 // has a desired-parent-type (& hence triggers table fixup), we'll need to
4736 // also update the flexbox chunk in nsStyleContext::ApplyStyleFixups().
4737 FCDATA_FOR_DISPLAY(
4738 StyleDisplay::TableRowGroup,
4739 FULL_CTOR_FCDATA(
4740 FCDATA_IS_TABLE_PART |
4741 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
4742 &nsCSSFrameConstructor::ConstructTableRowOrRowGroup)),
4743 FCDATA_FOR_DISPLAY(
4744 StyleDisplay::TableColumn,
4745 FULL_CTOR_FCDATA(
4746 FCDATA_IS_TABLE_PART |
4747 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeColGroup),
4748 &nsCSSFrameConstructor::ConstructTableCol)),
4749 FCDATA_FOR_DISPLAY(
4750 StyleDisplay::TableColumnGroup,
4751 FCDATA_DECL(FCDATA_IS_TABLE_PART | FCDATA_DISALLOW_OUT_OF_FLOW |
4752 FCDATA_SKIP_ABSPOS_PUSH |
4753 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
4754 NS_NewTableColGroupFrame)),
4755 FCDATA_FOR_DISPLAY(
4756 StyleDisplay::TableHeaderGroup,
4757 FULL_CTOR_FCDATA(
4758 FCDATA_IS_TABLE_PART |
4759 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
4760 &nsCSSFrameConstructor::ConstructTableRowOrRowGroup)),
4761 FCDATA_FOR_DISPLAY(
4762 StyleDisplay::TableFooterGroup,
4763 FULL_CTOR_FCDATA(
4764 FCDATA_IS_TABLE_PART |
4765 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
4766 &nsCSSFrameConstructor::ConstructTableRowOrRowGroup)),
4767 FCDATA_FOR_DISPLAY(
4768 StyleDisplay::TableRow,
4769 FULL_CTOR_FCDATA(
4770 FCDATA_IS_TABLE_PART |
4771 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRowGroup),
4772 &nsCSSFrameConstructor::ConstructTableRowOrRowGroup)),
4773 FCDATA_FOR_DISPLAY(
4774 StyleDisplay::TableCell,
4775 FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART |
4776 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRow),
4777 &nsCSSFrameConstructor::ConstructTableCell)),
4778 FCDATA_FOR_DISPLAY(StyleDisplay::TableCaption, UNREACHABLE_FCDATA()),
4779 FCDATA_FOR_DISPLAY(
4780 StyleDisplay::Flex,
4781 FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewFlexContainerFrame)),
4782 FCDATA_FOR_DISPLAY(
4783 StyleDisplay::InlineFlex,
4784 FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewFlexContainerFrame)),
4785 FCDATA_FOR_DISPLAY(
4786 StyleDisplay::Grid,
4787 FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewGridContainerFrame)),
4788 FCDATA_FOR_DISPLAY(
4789 StyleDisplay::InlineGrid,
4790 FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewGridContainerFrame)),
4791 FCDATA_FOR_DISPLAY(
4792 StyleDisplay::Ruby,
4793 FCDATA_DECL(FCDATA_IS_LINE_PARTICIPANT, NS_NewRubyFrame)),
4794 FCDATA_FOR_DISPLAY(StyleDisplay::RubyBase,
4795 FCDATA_DECL(FCDATA_IS_LINE_PARTICIPANT |
4796 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(
4797 eTypeRubyBaseContainer),
4798 NS_NewRubyBaseFrame)),
4799 FCDATA_FOR_DISPLAY(
4800 StyleDisplay::RubyBaseContainer,
4801 FCDATA_DECL(FCDATA_IS_LINE_PARTICIPANT |
4802 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRuby),
4803 NS_NewRubyBaseContainerFrame)),
4804 FCDATA_FOR_DISPLAY(StyleDisplay::RubyText,
4805 FCDATA_DECL(FCDATA_IS_LINE_PARTICIPANT |
4806 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(
4807 eTypeRubyTextContainer),
4808 NS_NewRubyTextFrame)),
4809 FCDATA_FOR_DISPLAY(
4810 StyleDisplay::RubyTextContainer,
4811 FCDATA_DECL(FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRuby),
4812 NS_NewRubyTextContainerFrame)),
4813 FCDATA_FOR_DISPLAY(StyleDisplay::Contents, UNREACHABLE_FCDATA()),
4814 FCDATA_FOR_DISPLAY(
4815 StyleDisplay::WebkitBox,
4816 FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewFlexContainerFrame)),
4817 FCDATA_FOR_DISPLAY(
4818 StyleDisplay::WebkitInlineBox,
4819 FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewFlexContainerFrame)),
4820 FCDATA_FOR_DISPLAY(
4821 StyleDisplay::MozBox,
4822 FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewFlexContainerFrame)),
4823 FCDATA_FOR_DISPLAY(
4824 StyleDisplay::MozInlineBox,
4825 FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewFlexContainerFrame)),
4826 };
4827 static_assert(
4828 ArrayLength(sDisplayData) == size_t(StyleDisplay::MozInlineBox) + 1,
4829 "Be sure to update sDisplayData if you touch StyleDisplay");
4830 MOZ_ASSERT(StylePrefs::sEmulateMozBoxWithFlex ||
4831 (aDisplay->mDisplay != StyleDisplay::MozBox &&
4832 aDisplay->mDisplay != StyleDisplay::MozInlineBox),
4833 "-moz-{inline-}box as XUL should have already been handled");
4834 MOZ_ASSERT(size_t(aDisplay->mDisplay) < ArrayLength(sDisplayData),
4835 "XUL display data should have already been handled");
4836
4837 // See the mDisplay fixup code in nsRuleNode::ComputeDisplayData.
4838 MOZ_ASSERT(aDisplay->mDisplay != StyleDisplay::Contents ||
4839 !aElement->IsRootOfNativeAnonymousSubtree(),
4840 "display:contents on anonymous content is unsupported");
4841
4842 const FrameConstructionDataByDisplay& data =
4843 sDisplayData[size_t(aDisplay->mDisplay)];
4844
4845 MOZ_ASSERT(data.mDisplay == aDisplay->mDisplay,
4846 "Someone messed up the order in the display values");
4847
4848 return &data.mData;
4849 }
4850
ConstructScrollableBlock(nsFrameConstructorState & aState,FrameConstructionItem & aItem,nsContainerFrame * aParentFrame,const nsStyleDisplay * aDisplay,nsFrameItems & aFrameItems)4851 nsIFrame* nsCSSFrameConstructor::ConstructScrollableBlock(
4852 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
4853 nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay,
4854 nsFrameItems& aFrameItems) {
4855 return ConstructScrollableBlockWithConstructor(aState, aItem, aParentFrame,
4856 aDisplay, aFrameItems,
4857 NS_NewBlockFormattingContext);
4858 }
4859
ConstructScrollableBlockWithConstructor(nsFrameConstructorState & aState,FrameConstructionItem & aItem,nsContainerFrame * aParentFrame,const nsStyleDisplay * aDisplay,nsFrameItems & aFrameItems,BlockFrameCreationFunc aConstructor)4860 nsIFrame* nsCSSFrameConstructor::ConstructScrollableBlockWithConstructor(
4861 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
4862 nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay,
4863 nsFrameItems& aFrameItems, BlockFrameCreationFunc aConstructor) {
4864 nsIContent* const content = aItem.mContent;
4865 nsStyleContext* const styleContext = aItem.mStyleContext;
4866
4867 nsContainerFrame* newFrame = nullptr;
4868 RefPtr<nsStyleContext> scrolledContentStyle = BeginBuildingScrollFrame(
4869 aState, content, styleContext,
4870 aState.GetGeometricParent(aDisplay, aParentFrame),
4871 nsCSSAnonBoxes::scrolledContent, false, newFrame);
4872
4873 // Create our block frame
4874 // pass a temporary stylecontext, the correct one will be set later
4875 nsContainerFrame* scrolledFrame = aConstructor(mPresShell, styleContext);
4876
4877 // Make sure to AddChild before we call ConstructBlock so that we
4878 // end up before our descendants in fixed-pos lists as needed.
4879 aState.AddChild(newFrame, aFrameItems, content, aParentFrame);
4880
4881 nsFrameItems blockItem;
4882 ConstructBlock(
4883 aState, content, newFrame, newFrame, scrolledContentStyle, &scrolledFrame,
4884 blockItem,
4885 aDisplay->IsAbsPosContainingBlock(newFrame) ? newFrame : nullptr,
4886 aItem.mPendingBinding);
4887
4888 MOZ_ASSERT(blockItem.OnlyChild() == scrolledFrame,
4889 "Scrollframe's frameItems should be exactly the scrolled frame!");
4890 FinishBuildingScrollFrame(newFrame, scrolledFrame);
4891
4892 return newFrame;
4893 }
4894
ConstructNonScrollableBlock(nsFrameConstructorState & aState,FrameConstructionItem & aItem,nsContainerFrame * aParentFrame,const nsStyleDisplay * aDisplay,nsFrameItems & aFrameItems)4895 nsIFrame* nsCSSFrameConstructor::ConstructNonScrollableBlock(
4896 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
4897 nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay,
4898 nsFrameItems& aFrameItems) {
4899 return ConstructNonScrollableBlockWithConstructor(
4900 aState, aItem, aParentFrame, aDisplay, aFrameItems, NS_NewBlockFrame);
4901 }
4902
ConstructNonScrollableBlockWithConstructor(nsFrameConstructorState & aState,FrameConstructionItem & aItem,nsContainerFrame * aParentFrame,const nsStyleDisplay * aDisplay,nsFrameItems & aFrameItems,BlockFrameCreationFunc aConstructor)4903 nsIFrame* nsCSSFrameConstructor::ConstructNonScrollableBlockWithConstructor(
4904 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
4905 nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay,
4906 nsFrameItems& aFrameItems, BlockFrameCreationFunc aConstructor) {
4907 nsStyleContext* const styleContext = aItem.mStyleContext;
4908
4909 // We want a block formatting context root in paginated contexts for
4910 // every block that would be scrollable in a non-paginated context.
4911 // We mark our blocks with a bit here if this condition is true, so
4912 // we can check it later in nsFrame::ApplyPaginatedOverflowClipping.
4913 bool clipPaginatedOverflow =
4914 (aItem.mFCData->mBits & FCDATA_FORCED_NON_SCROLLABLE_BLOCK) != 0;
4915 nsFrameState flags = nsFrameState(0);
4916 if ((aDisplay->IsAbsolutelyPositionedStyle() || aDisplay->IsFloatingStyle() ||
4917 StyleDisplay::InlineBlock == aDisplay->mDisplay ||
4918 clipPaginatedOverflow) &&
4919 !nsSVGUtils::IsInSVGTextSubtree(aParentFrame)) {
4920 flags = NS_BLOCK_FORMATTING_CONTEXT_STATE_BITS;
4921 if (clipPaginatedOverflow) {
4922 flags |= NS_BLOCK_CLIP_PAGINATED_OVERFLOW;
4923 }
4924 }
4925
4926 nsContainerFrame* newFrame = aConstructor(mPresShell, styleContext);
4927 newFrame->AddStateBits(flags);
4928 ConstructBlock(
4929 aState, aItem.mContent, aState.GetGeometricParent(aDisplay, aParentFrame),
4930 aParentFrame, styleContext, &newFrame, aFrameItems,
4931 aDisplay->IsAbsPosContainingBlock(newFrame) ? newFrame : nullptr,
4932 aItem.mPendingBinding);
4933 return newFrame;
4934 }
4935
InitAndRestoreFrame(const nsFrameConstructorState & aState,nsIContent * aContent,nsContainerFrame * aParentFrame,nsIFrame * aNewFrame,bool aAllowCounters)4936 void nsCSSFrameConstructor::InitAndRestoreFrame(
4937 const nsFrameConstructorState& aState, nsIContent* aContent,
4938 nsContainerFrame* aParentFrame, nsIFrame* aNewFrame, bool aAllowCounters) {
4939 MOZ_ASSERT(aNewFrame, "Null frame cannot be initialized");
4940
4941 // Initialize the frame
4942 aNewFrame->Init(aContent, aParentFrame, nullptr);
4943 aNewFrame->AddStateBits(aState.mAdditionalStateBits);
4944
4945 if (aState.mFrameState) {
4946 // Restore frame state for just the newly created frame.
4947 RestoreFrameStateFor(aNewFrame, aState.mFrameState);
4948 }
4949
4950 if (aAllowCounters &&
4951 mCounterManager.AddCounterResetsAndIncrements(aNewFrame)) {
4952 CountersDirty();
4953 }
4954 }
4955
ResolveStyleContext(nsIFrame * aParentFrame,nsIContent * aContainer,nsIContent * aChild,nsFrameConstructorState * aState)4956 already_AddRefed<nsStyleContext> nsCSSFrameConstructor::ResolveStyleContext(
4957 nsIFrame* aParentFrame, nsIContent* aContainer, nsIContent* aChild,
4958 nsFrameConstructorState* aState) {
4959 MOZ_ASSERT(aContainer, "Must have parent here");
4960 // XXX uncomment when bug 1089223 is fixed:
4961 // MOZ_ASSERT(aContainer == aChild->GetFlattenedTreeParent());
4962 nsStyleContext* parentStyleContext = GetDisplayContentsStyleFor(aContainer);
4963 if (MOZ_LIKELY(!parentStyleContext)) {
4964 aParentFrame = nsFrame::CorrectStyleParentFrame(aParentFrame, nullptr);
4965 if (aParentFrame) {
4966 MOZ_ASSERT(aParentFrame->GetContent() == aContainer);
4967 // Resolve the style context based on the content object and the parent
4968 // style context
4969 parentStyleContext = aParentFrame->StyleContext();
4970 } else {
4971 // Perhaps aParentFrame is a canvasFrame and we're replicating
4972 // fixed-pos frames.
4973 // XXX should we create a way to tell ConstructFrame which style
4974 // context to use, and pass it the style context for the
4975 // previous page's fixed-pos frame?
4976 }
4977 }
4978
4979 return ResolveStyleContext(parentStyleContext, aChild, aState);
4980 }
4981
ResolveStyleContext(nsIFrame * aParentFrame,nsIContent * aChild,nsFrameConstructorState * aState)4982 already_AddRefed<nsStyleContext> nsCSSFrameConstructor::ResolveStyleContext(
4983 nsIFrame* aParentFrame, nsIContent* aChild,
4984 nsFrameConstructorState* aState) {
4985 return ResolveStyleContext(aParentFrame, aChild->GetFlattenedTreeParent(),
4986 aChild, aState);
4987 }
4988
ResolveStyleContext(const InsertionPoint & aInsertion,nsIContent * aChild,nsFrameConstructorState * aState)4989 already_AddRefed<nsStyleContext> nsCSSFrameConstructor::ResolveStyleContext(
4990 const InsertionPoint& aInsertion, nsIContent* aChild,
4991 nsFrameConstructorState* aState) {
4992 return ResolveStyleContext(aInsertion.mParentFrame, aInsertion.mContainer,
4993 aChild, aState);
4994 }
4995
ResolveStyleContext(nsStyleContext * aParentStyleContext,nsIContent * aContent,nsFrameConstructorState * aState,Element * aOriginatingElementOrNull)4996 already_AddRefed<nsStyleContext> nsCSSFrameConstructor::ResolveStyleContext(
4997 nsStyleContext* aParentStyleContext, nsIContent* aContent,
4998 nsFrameConstructorState* aState, Element* aOriginatingElementOrNull) {
4999 StyleSetHandle styleSet = mPresShell->StyleSet();
5000
5001 RefPtr<nsStyleContext> result;
5002 if (aContent->IsElement()) {
5003 auto pseudoType = aContent->AsElement()->GetPseudoElementType();
5004 if (pseudoType == CSSPseudoElementType::NotPseudo) {
5005 MOZ_ASSERT(!aOriginatingElementOrNull);
5006 if (aState) {
5007 result = styleSet->ResolveStyleFor(
5008 aContent->AsElement(), aParentStyleContext,
5009 LazyComputeBehavior::Assert, aState->mTreeMatchContext);
5010 } else {
5011 result = styleSet->ResolveStyleFor(aContent->AsElement(),
5012 aParentStyleContext,
5013 LazyComputeBehavior::Assert);
5014 }
5015 } else {
5016 MOZ_ASSERT(aContent->IsInNativeAnonymousSubtree());
5017 if (!aOriginatingElementOrNull) {
5018 // For pseudo-implementing NAC created by JS using the ChromeOnly
5019 // document.createElement(..., { pseudo: ... }) API, we find the
5020 // originating element by lookup the tree until we find a non-NAC
5021 // ancestor. (These are the correct semantics for C++-generated pseudo-
5022 // implementing NAC as well, but for those cases we already have a
5023 // correct originating element passed in.)
5024 MOZ_ASSERT(
5025 nsCSSPseudoElements::PseudoElementIsJSCreatedNAC(pseudoType));
5026 aOriginatingElementOrNull =
5027 nsContentUtils::GetClosestNonNativeAnonymousAncestor(
5028 aContent->AsElement());
5029 }
5030 MOZ_ASSERT(aOriginatingElementOrNull);
5031 result = styleSet->ResolvePseudoElementStyle(
5032 aOriginatingElementOrNull, pseudoType, aParentStyleContext,
5033 aContent->AsElement());
5034 }
5035 } else {
5036 MOZ_ASSERT(!aOriginatingElementOrNull);
5037 NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
5038 "shouldn't waste time creating style contexts for "
5039 "comments and processing instructions");
5040 result = styleSet->ResolveStyleForText(aContent, aParentStyleContext);
5041 }
5042
5043 // ServoRestyleManager does not handle transitions yet, and when it does
5044 // it probably won't need to track reframed style contexts to start
5045 // transitions correctly.
5046 if (RestyleManager()->IsGecko()) {
5047 #ifdef MOZ_OLD_STYLE
5048 mozilla::GeckoRestyleManager* geckoRM = RestyleManager()->AsGecko();
5049 GeckoRestyleManager::ReframingStyleContexts* rsc =
5050 geckoRM->GetReframingStyleContexts();
5051 if (rsc) {
5052 GeckoStyleContext* oldStyleContext =
5053 rsc->Get(aContent, CSSPseudoElementType::NotPseudo);
5054 nsPresContext* presContext = mPresShell->GetPresContext();
5055 if (oldStyleContext) {
5056 RefPtr<GeckoStyleContext> newContext =
5057 GeckoStyleContext::TakeRef(result.forget());
5058 GeckoRestyleManager::TryInitiatingTransition(
5059 presContext, aContent, oldStyleContext, &newContext);
5060 result = newContext.forget();
5061 } else if (aContent->IsElement()) {
5062 presContext->TransitionManager()->PruneCompletedTransitions(
5063 aContent->AsElement(), CSSPseudoElementType::NotPseudo,
5064 result->AsGecko());
5065 }
5066 }
5067 #else
5068 MOZ_CRASH("old style system disabled");
5069 #endif
5070 }
5071
5072 return result.forget();
5073 }
5074
5075 // MathML Mod - RBS
FlushAccumulatedBlock(nsFrameConstructorState & aState,nsIContent * aContent,nsContainerFrame * aParentFrame,nsFrameItems & aBlockItems,nsFrameItems & aNewItems)5076 void nsCSSFrameConstructor::FlushAccumulatedBlock(
5077 nsFrameConstructorState& aState, nsIContent* aContent,
5078 nsContainerFrame* aParentFrame, nsFrameItems& aBlockItems,
5079 nsFrameItems& aNewItems) {
5080 if (aBlockItems.IsEmpty()) {
5081 // Nothing to do
5082 return;
5083 }
5084
5085 nsAtom* anonPseudo = nsCSSAnonBoxes::mozMathMLAnonymousBlock;
5086
5087 nsStyleContext* parentContext =
5088 nsFrame::CorrectStyleParentFrame(aParentFrame, anonPseudo)
5089 ->StyleContext();
5090 StyleSetHandle styleSet = mPresShell->StyleSet();
5091 RefPtr<nsStyleContext> blockContext;
5092 blockContext =
5093 styleSet->ResolveInheritingAnonymousBoxStyle(anonPseudo, parentContext);
5094
5095 // then, create a block frame that will wrap the child frames. Make it a
5096 // MathML frame so that Get(Absolute/Float)ContainingBlockFor know that this
5097 // is not a suitable block.
5098 nsContainerFrame* blockFrame =
5099 NS_NewMathMLmathBlockFrame(mPresShell, blockContext);
5100
5101 InitAndRestoreFrame(aState, aContent, aParentFrame, blockFrame);
5102 ReparentFrames(this, blockFrame, aBlockItems, false);
5103 // We have to walk over aBlockItems before we hand it over to blockFrame.
5104 for (nsIFrame* f : aBlockItems) {
5105 f->SetParentIsWrapperAnonBox();
5106 }
5107 // abs-pos and floats are disabled in MathML children so we don't have to
5108 // worry about messing up those.
5109 blockFrame->SetInitialChildList(kPrincipalList, aBlockItems);
5110 NS_ASSERTION(aBlockItems.IsEmpty(), "What happened?");
5111 aBlockItems.Clear();
5112 aNewItems.AddChild(blockFrame);
5113 }
5114
5115 // Only <math> elements can be floated or positioned. All other MathML
5116 // should be in-flow.
5117 #define SIMPLE_MATHML_CREATE(_tag, _func) \
5118 { \
5119 &nsGkAtoms::_tag, FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW | \
5120 FCDATA_FORCE_NULL_ABSPOS_CONTAINER | \
5121 FCDATA_WRAP_KIDS_IN_BLOCKS, \
5122 _func) \
5123 }
5124
5125 /* static */
5126 const nsCSSFrameConstructor::FrameConstructionData*
FindMathMLData(Element * aElement,nsAtom * aTag,int32_t aNameSpaceID,nsStyleContext * aStyleContext)5127 nsCSSFrameConstructor::FindMathMLData(Element* aElement, nsAtom* aTag,
5128 int32_t aNameSpaceID,
5129 nsStyleContext* aStyleContext) {
5130 // Make sure that we remain confined in the MathML world
5131 if (aNameSpaceID != kNameSpaceID_MathML) return nullptr;
5132
5133 // Handle <math> specially, because it sometimes produces inlines
5134 if (aTag == nsGkAtoms::math) {
5135 // This needs to match the test in EnsureBlockDisplay in
5136 // nsRuleNode.cpp. Though the behavior here for the display:table
5137 // case is pretty weird...
5138 if (aStyleContext->StyleDisplay()->IsBlockOutsideStyle()) {
5139 static const FrameConstructionData sBlockMathData = FCDATA_DECL(
5140 FCDATA_FORCE_NULL_ABSPOS_CONTAINER | FCDATA_WRAP_KIDS_IN_BLOCKS,
5141 NS_NewMathMLmathBlockFrame);
5142 return &sBlockMathData;
5143 }
5144
5145 static const FrameConstructionData sInlineMathData =
5146 FCDATA_DECL(FCDATA_FORCE_NULL_ABSPOS_CONTAINER |
5147 FCDATA_IS_LINE_PARTICIPANT | FCDATA_WRAP_KIDS_IN_BLOCKS,
5148 NS_NewMathMLmathInlineFrame);
5149 return &sInlineMathData;
5150 }
5151
5152 static const FrameConstructionDataByTag sMathMLData[] = {
5153 SIMPLE_MATHML_CREATE(annotation_, NS_NewMathMLTokenFrame),
5154 SIMPLE_MATHML_CREATE(annotation_xml_, NS_NewMathMLmrowFrame),
5155 SIMPLE_MATHML_CREATE(mi_, NS_NewMathMLTokenFrame),
5156 SIMPLE_MATHML_CREATE(mn_, NS_NewMathMLTokenFrame),
5157 SIMPLE_MATHML_CREATE(ms_, NS_NewMathMLTokenFrame),
5158 SIMPLE_MATHML_CREATE(mtext_, NS_NewMathMLTokenFrame),
5159 SIMPLE_MATHML_CREATE(mo_, NS_NewMathMLmoFrame),
5160 SIMPLE_MATHML_CREATE(mfrac_, NS_NewMathMLmfracFrame),
5161 SIMPLE_MATHML_CREATE(msup_, NS_NewMathMLmmultiscriptsFrame),
5162 SIMPLE_MATHML_CREATE(msub_, NS_NewMathMLmmultiscriptsFrame),
5163 SIMPLE_MATHML_CREATE(msubsup_, NS_NewMathMLmmultiscriptsFrame),
5164 SIMPLE_MATHML_CREATE(munder_, NS_NewMathMLmunderoverFrame),
5165 SIMPLE_MATHML_CREATE(mover_, NS_NewMathMLmunderoverFrame),
5166 SIMPLE_MATHML_CREATE(munderover_, NS_NewMathMLmunderoverFrame),
5167 SIMPLE_MATHML_CREATE(mphantom_, NS_NewMathMLmrowFrame),
5168 SIMPLE_MATHML_CREATE(mpadded_, NS_NewMathMLmpaddedFrame),
5169 SIMPLE_MATHML_CREATE(mspace_, NS_NewMathMLmspaceFrame),
5170 SIMPLE_MATHML_CREATE(none, NS_NewMathMLmspaceFrame),
5171 SIMPLE_MATHML_CREATE(mprescripts_, NS_NewMathMLmspaceFrame),
5172 SIMPLE_MATHML_CREATE(mfenced_, NS_NewMathMLmfencedFrame),
5173 SIMPLE_MATHML_CREATE(mmultiscripts_, NS_NewMathMLmmultiscriptsFrame),
5174 SIMPLE_MATHML_CREATE(mstyle_, NS_NewMathMLmrowFrame),
5175 SIMPLE_MATHML_CREATE(msqrt_, NS_NewMathMLmsqrtFrame),
5176 SIMPLE_MATHML_CREATE(mroot_, NS_NewMathMLmrootFrame),
5177 SIMPLE_MATHML_CREATE(maction_, NS_NewMathMLmactionFrame),
5178 SIMPLE_MATHML_CREATE(mrow_, NS_NewMathMLmrowFrame),
5179 SIMPLE_MATHML_CREATE(merror_, NS_NewMathMLmrowFrame),
5180 SIMPLE_MATHML_CREATE(menclose_, NS_NewMathMLmencloseFrame),
5181 SIMPLE_MATHML_CREATE(semantics_, NS_NewMathMLsemanticsFrame)};
5182
5183 return FindDataByTag(aTag, aElement, aStyleContext, sMathMLData,
5184 ArrayLength(sMathMLData));
5185 }
5186
ConstructFrameWithAnonymousChild(nsFrameConstructorState & aState,FrameConstructionItem & aItem,nsContainerFrame * aParentFrame,nsFrameItems & aFrameItems,ContainerFrameCreationFunc aConstructor,ContainerFrameCreationFunc aInnerConstructor,nsICSSAnonBoxPseudo * aInnerPseudo,bool aCandidateRootFrame)5187 nsContainerFrame* nsCSSFrameConstructor::ConstructFrameWithAnonymousChild(
5188 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
5189 nsContainerFrame* aParentFrame, nsFrameItems& aFrameItems,
5190 ContainerFrameCreationFunc aConstructor,
5191 ContainerFrameCreationFunc aInnerConstructor,
5192 nsICSSAnonBoxPseudo* aInnerPseudo, bool aCandidateRootFrame) {
5193 nsIContent* const content = aItem.mContent;
5194 nsStyleContext* const styleContext = aItem.mStyleContext;
5195
5196 // Create the outer frame:
5197 nsContainerFrame* newFrame = aConstructor(mPresShell, styleContext);
5198
5199 InitAndRestoreFrame(aState, content,
5200 aCandidateRootFrame
5201 ? aState.GetGeometricParent(
5202 styleContext->StyleDisplay(), aParentFrame)
5203 : aParentFrame,
5204 newFrame);
5205 newFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
5206
5207 // Create the pseudo SC for the anonymous wrapper child as a child of the SC:
5208 RefPtr<nsStyleContext> scForAnon;
5209 scForAnon = mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
5210 aInnerPseudo, styleContext);
5211
5212 // Create the anonymous inner wrapper frame
5213 nsContainerFrame* innerFrame = aInnerConstructor(mPresShell, scForAnon);
5214
5215 InitAndRestoreFrame(aState, content, newFrame, innerFrame);
5216
5217 // Put the newly created frames into the right child list
5218 SetInitialSingleChild(newFrame, innerFrame);
5219
5220 aState.AddChild(newFrame, aFrameItems, content, aParentFrame,
5221 aCandidateRootFrame, aCandidateRootFrame);
5222
5223 if (!mRootElementFrame && aCandidateRootFrame) {
5224 // The frame we're constructing will be the root element frame.
5225 // Set mRootElementFrame before processing children.
5226 mRootElementFrame = newFrame;
5227 }
5228
5229 nsFrameItems childItems;
5230
5231 // Process children
5232 NS_ASSERTION(aItem.mAnonChildren.IsEmpty(),
5233 "nsIAnonymousContentCreator::CreateAnonymousContent should not "
5234 "be implemented for frames for which we explicitly create an "
5235 "anonymous child to wrap its child frames");
5236 if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) {
5237 ConstructFramesFromItemList(
5238 aState, aItem.mChildItems, innerFrame,
5239 aItem.mFCData->mBits & FCDATA_IS_WRAPPER_ANON_BOX, childItems);
5240 } else {
5241 ProcessChildren(aState, content, styleContext, innerFrame, true, childItems,
5242 false, aItem.mPendingBinding);
5243 }
5244
5245 // Set the inner wrapper frame's initial primary list
5246 innerFrame->SetInitialChildList(kPrincipalList, childItems);
5247
5248 return newFrame;
5249 }
5250
ConstructOuterSVG(nsFrameConstructorState & aState,FrameConstructionItem & aItem,nsContainerFrame * aParentFrame,const nsStyleDisplay * aDisplay,nsFrameItems & aFrameItems)5251 nsIFrame* nsCSSFrameConstructor::ConstructOuterSVG(
5252 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
5253 nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay,
5254 nsFrameItems& aFrameItems) {
5255 return ConstructFrameWithAnonymousChild(
5256 aState, aItem, aParentFrame, aFrameItems, NS_NewSVGOuterSVGFrame,
5257 NS_NewSVGOuterSVGAnonChildFrame, nsCSSAnonBoxes::mozSVGOuterSVGAnonChild,
5258 true);
5259 }
5260
ConstructMarker(nsFrameConstructorState & aState,FrameConstructionItem & aItem,nsContainerFrame * aParentFrame,const nsStyleDisplay * aDisplay,nsFrameItems & aFrameItems)5261 nsIFrame* nsCSSFrameConstructor::ConstructMarker(
5262 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
5263 nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay,
5264 nsFrameItems& aFrameItems) {
5265 return ConstructFrameWithAnonymousChild(
5266 aState, aItem, aParentFrame, aFrameItems, NS_NewSVGMarkerFrame,
5267 NS_NewSVGMarkerAnonChildFrame, nsCSSAnonBoxes::mozSVGMarkerAnonChild,
5268 false);
5269 }
5270
5271 // Only outer <svg> elements can be floated or positioned. All other SVG
5272 // should be in-flow.
5273 #define SIMPLE_SVG_FCDATA(_func) \
5274 FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_SKIP_ABSPOS_PUSH | \
5275 FCDATA_DISALLOW_GENERATED_CONTENT, \
5276 _func)
5277 #define SIMPLE_SVG_CREATE(_tag, _func) \
5278 { &nsGkAtoms::_tag, SIMPLE_SVG_FCDATA(_func) }
5279
IsFilterPrimitiveChildTag(const nsAtom * aTag)5280 static bool IsFilterPrimitiveChildTag(const nsAtom* aTag) {
5281 return aTag == nsGkAtoms::feDistantLight || aTag == nsGkAtoms::fePointLight ||
5282 aTag == nsGkAtoms::feSpotLight || aTag == nsGkAtoms::feFuncR ||
5283 aTag == nsGkAtoms::feFuncG || aTag == nsGkAtoms::feFuncB ||
5284 aTag == nsGkAtoms::feFuncA || aTag == nsGkAtoms::feMergeNode;
5285 }
5286
5287 /* static */
5288 const nsCSSFrameConstructor::FrameConstructionData*
FindSVGData(Element * aElement,nsAtom * aTag,int32_t aNameSpaceID,nsIFrame * aParentFrame,bool aIsWithinSVGText,bool aAllowsTextPathChild,nsStyleContext * aStyleContext)5289 nsCSSFrameConstructor::FindSVGData(Element* aElement, nsAtom* aTag,
5290 int32_t aNameSpaceID, nsIFrame* aParentFrame,
5291 bool aIsWithinSVGText,
5292 bool aAllowsTextPathChild,
5293 nsStyleContext* aStyleContext) {
5294 if (aNameSpaceID != kNameSpaceID_SVG) {
5295 return nullptr;
5296 }
5297
5298 static const FrameConstructionData sSuppressData = SUPPRESS_FCDATA();
5299 static const FrameConstructionData sContainerData =
5300 SIMPLE_SVG_FCDATA(NS_NewSVGContainerFrame);
5301
5302 bool parentIsSVG = aIsWithinSVGText;
5303 nsIContent* parentContent =
5304 aParentFrame ? aParentFrame->GetContent() : nullptr;
5305 // XXXbz should this really be based on the XBL-resolved tag of the parent
5306 // frame's content? Should it not be based on the type of the parent frame
5307 // (e.g. whether it's an SVG frame)?
5308 if (parentContent) {
5309 int32_t parentNSID;
5310 nsAtom* parentTag = parentContent->OwnerDoc()->BindingManager()->ResolveTag(
5311 parentContent, &parentNSID);
5312
5313 // It's not clear whether the SVG spec intends to allow any SVG
5314 // content within svg:foreignObject at all (SVG 1.1, section
5315 // 23.2), but if it does, it better be svg:svg. So given that
5316 // we're allowing it, treat it as a non-SVG parent.
5317 parentIsSVG =
5318 parentNSID == kNameSpaceID_SVG && parentTag != nsGkAtoms::foreignObject;
5319 }
5320
5321 if ((aTag != nsGkAtoms::svg && !parentIsSVG) ||
5322 (aTag == nsGkAtoms::desc || aTag == nsGkAtoms::title ||
5323 aTag == nsGkAtoms::metadata)) {
5324 // Sections 5.1 and G.4 of SVG 1.1 say that SVG elements other than
5325 // svg:svg not contained within svg:svg are incorrect, although they
5326 // don't seem to specify error handling. Ignore them, since many of
5327 // our frame classes can't deal. It *may* be that the document
5328 // should at that point be considered in error according to F.2, but
5329 // it's hard to tell.
5330 //
5331 // Style mutation can't change this situation, so don't bother
5332 // adding to the undisplayed content map.
5333 //
5334 // We don't currently handle any UI for desc/title/metadata
5335 return &sSuppressData;
5336 }
5337
5338 // We don't need frames for animation elements
5339 if (aElement->IsNodeOfType(nsINode::eANIMATION)) {
5340 return &sSuppressData;
5341 }
5342
5343 // https://drafts.csswg.org/css-display/#unbox-svg
5344 if (aStyleContext->StyleDisplay()->mDisplay == StyleDisplay::Contents) {
5345 // For root <svg> elements, display: contents behaves as display: none.
5346 if (aTag == nsGkAtoms::svg && !parentIsSVG) {
5347 return &sSuppressData;
5348 }
5349
5350 // For nested <svg>, <g>, <use> and <tspan> behave normally, but any other
5351 // element behaves as display: none as well.
5352 if (aTag != nsGkAtoms::g && aTag != nsGkAtoms::use &&
5353 aTag != nsGkAtoms::svg && aTag != nsGkAtoms::tspan) {
5354 return &sSuppressData;
5355 }
5356 }
5357
5358 if (aTag == nsGkAtoms::svg && !parentIsSVG) {
5359 // We need outer <svg> elements to have an nsSVGOuterSVGFrame regardless
5360 // of whether they fail conditional processing attributes, since various
5361 // SVG frames assume that one exists. We handle the non-rendering
5362 // of failing outer <svg> element contents like <switch> statements,
5363 // and do the PassesConditionalProcessingTests call in
5364 // nsSVGOuterSVGFrame::Init.
5365 static const FrameConstructionData sOuterSVGData =
5366 FULL_CTOR_FCDATA(0, &nsCSSFrameConstructor::ConstructOuterSVG);
5367 return &sOuterSVGData;
5368 }
5369
5370 if (aTag == nsGkAtoms::marker) {
5371 static const FrameConstructionData sMarkerSVGData =
5372 FULL_CTOR_FCDATA(0, &nsCSSFrameConstructor::ConstructMarker);
5373 return &sMarkerSVGData;
5374 }
5375
5376 nsCOMPtr<SVGTests> tests(do_QueryInterface(aElement));
5377 if (tests && !tests->PassesConditionalProcessingTests()) {
5378 // Elements with failing conditional processing attributes never get
5379 // rendered. Note that this is not where we select which frame in a
5380 // <switch> to render! That happens in nsSVGSwitchFrame::PaintSVG.
5381 if (aIsWithinSVGText) {
5382 // SVGTextFrame doesn't handle conditional processing attributes,
5383 // so don't create frames for descendants of <text> with failing
5384 // attributes. We need frames not to be created so that text layout
5385 // is correct.
5386 return &sSuppressData;
5387 }
5388 // If we're not inside <text>, create an nsSVGContainerFrame (which is a
5389 // frame that doesn't render) so that paint servers can still be referenced,
5390 // even if they live inside an element with failing conditional processing
5391 // attributes.
5392 return &sContainerData;
5393 }
5394
5395 // Ensure that a stop frame is a child of a gradient and that gradients
5396 // can only have stop children.
5397 bool parentIsGradient =
5398 aParentFrame && (aParentFrame->IsSVGLinearGradientFrame() ||
5399 aParentFrame->IsSVGRadialGradientFrame());
5400 bool stop = (aTag == nsGkAtoms::stop);
5401 if ((parentIsGradient && !stop) || (!parentIsGradient && stop)) {
5402 return &sSuppressData;
5403 }
5404
5405 // Prevent bad frame types being children of filters or parents of filter
5406 // primitives. If aParentFrame is null, we know that the frame that will
5407 // be created will be an nsInlineFrame, so it can never be a filter.
5408 bool parentIsFilter = aParentFrame && aParentFrame->IsSVGFilterFrame();
5409 bool filterPrimitive = aElement->IsNodeOfType(nsINode::eFILTER);
5410 if ((parentIsFilter && !filterPrimitive) ||
5411 (!parentIsFilter && filterPrimitive)) {
5412 return &sSuppressData;
5413 }
5414
5415 // Prevent bad frame types being children of filter primitives or parents of
5416 // filter primitive children. If aParentFrame is null, we know that the frame
5417 // that will be created will be an nsInlineFrame, so it can never be a filter
5418 // primitive.
5419 bool parentIsFEContainerFrame =
5420 aParentFrame && aParentFrame->IsSVGFEContainerFrame();
5421 if ((parentIsFEContainerFrame && !IsFilterPrimitiveChildTag(aTag)) ||
5422 (!parentIsFEContainerFrame && IsFilterPrimitiveChildTag(aTag))) {
5423 return &sSuppressData;
5424 }
5425
5426 // Special cases for text/tspan/textPath, because the kind of frame
5427 // they get depends on the parent frame. We ignore 'a' elements when
5428 // determining the parent, however.
5429 if (aIsWithinSVGText) {
5430 // If aIsWithinSVGText is true, then we know that the "SVG text uses
5431 // CSS frames" pref was true when this SVG fragment was first constructed.
5432
5433 // We don't use ConstructInline because we want different behavior
5434 // for generated content.
5435 static const FrameConstructionData sTSpanData = FCDATA_DECL(
5436 FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_SKIP_ABSPOS_PUSH |
5437 FCDATA_DISALLOW_GENERATED_CONTENT | FCDATA_IS_LINE_PARTICIPANT |
5438 FCDATA_IS_INLINE | FCDATA_USE_CHILD_ITEMS,
5439 NS_NewInlineFrame);
5440 if (aTag == nsGkAtoms::textPath) {
5441 if (aAllowsTextPathChild) {
5442 return &sTSpanData;
5443 }
5444 } else if (aTag == nsGkAtoms::tspan || aTag == nsGkAtoms::a) {
5445 return &sTSpanData;
5446 }
5447 return &sSuppressData;
5448 } else if (aTag == nsGkAtoms::tspan || aTag == nsGkAtoms::textPath) {
5449 return &sSuppressData;
5450 }
5451
5452 static const FrameConstructionDataByTag sSVGData[] = {
5453 SIMPLE_SVG_CREATE(svg, NS_NewSVGInnerSVGFrame),
5454 SIMPLE_SVG_CREATE(g, NS_NewSVGGFrame),
5455 SIMPLE_SVG_CREATE(svgSwitch, NS_NewSVGSwitchFrame),
5456 SIMPLE_SVG_CREATE(symbol, NS_NewSVGSymbolFrame),
5457 SIMPLE_SVG_CREATE(polygon, NS_NewSVGGeometryFrame),
5458 SIMPLE_SVG_CREATE(polyline, NS_NewSVGGeometryFrame),
5459 SIMPLE_SVG_CREATE(circle, NS_NewSVGGeometryFrame),
5460 SIMPLE_SVG_CREATE(ellipse, NS_NewSVGGeometryFrame),
5461 SIMPLE_SVG_CREATE(line, NS_NewSVGGeometryFrame),
5462 SIMPLE_SVG_CREATE(rect, NS_NewSVGGeometryFrame),
5463 SIMPLE_SVG_CREATE(path, NS_NewSVGGeometryFrame),
5464 SIMPLE_SVG_CREATE(defs, NS_NewSVGContainerFrame),
5465 SIMPLE_SVG_CREATE(generic_, NS_NewSVGGenericContainerFrame),
5466 {&nsGkAtoms::text,
5467 FCDATA_WITH_WRAPPING_BLOCK(
5468 FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_ALLOW_BLOCK_STYLES,
5469 NS_NewSVGTextFrame, nsCSSAnonBoxes::mozSVGText)},
5470 {&nsGkAtoms::foreignObject,
5471 FCDATA_WITH_WRAPPING_BLOCK(FCDATA_DISALLOW_OUT_OF_FLOW,
5472 NS_NewSVGForeignObjectFrame,
5473 nsCSSAnonBoxes::mozSVGForeignContent)},
5474 SIMPLE_SVG_CREATE(a, NS_NewSVGAFrame),
5475 SIMPLE_SVG_CREATE(linearGradient, NS_NewSVGLinearGradientFrame),
5476 SIMPLE_SVG_CREATE(radialGradient, NS_NewSVGRadialGradientFrame),
5477 SIMPLE_SVG_CREATE(stop, NS_NewSVGStopFrame),
5478 SIMPLE_SVG_CREATE(use, NS_NewSVGUseFrame),
5479 SIMPLE_SVG_CREATE(view, NS_NewSVGViewFrame),
5480 SIMPLE_SVG_CREATE(image, NS_NewSVGImageFrame),
5481 SIMPLE_SVG_CREATE(clipPath, NS_NewSVGClipPathFrame),
5482 SIMPLE_SVG_CREATE(filter, NS_NewSVGFilterFrame),
5483 SIMPLE_SVG_CREATE(pattern, NS_NewSVGPatternFrame),
5484 SIMPLE_SVG_CREATE(mask, NS_NewSVGMaskFrame),
5485 SIMPLE_SVG_CREATE(feDistantLight, NS_NewSVGFEUnstyledLeafFrame),
5486 SIMPLE_SVG_CREATE(fePointLight, NS_NewSVGFEUnstyledLeafFrame),
5487 SIMPLE_SVG_CREATE(feSpotLight, NS_NewSVGFEUnstyledLeafFrame),
5488 SIMPLE_SVG_CREATE(feBlend, NS_NewSVGFELeafFrame),
5489 SIMPLE_SVG_CREATE(feColorMatrix, NS_NewSVGFELeafFrame),
5490 SIMPLE_SVG_CREATE(feFuncR, NS_NewSVGFEUnstyledLeafFrame),
5491 SIMPLE_SVG_CREATE(feFuncG, NS_NewSVGFEUnstyledLeafFrame),
5492 SIMPLE_SVG_CREATE(feFuncB, NS_NewSVGFEUnstyledLeafFrame),
5493 SIMPLE_SVG_CREATE(feFuncA, NS_NewSVGFEUnstyledLeafFrame),
5494 SIMPLE_SVG_CREATE(feComposite, NS_NewSVGFELeafFrame),
5495 SIMPLE_SVG_CREATE(feComponentTransfer, NS_NewSVGFEContainerFrame),
5496 SIMPLE_SVG_CREATE(feConvolveMatrix, NS_NewSVGFELeafFrame),
5497 SIMPLE_SVG_CREATE(feDiffuseLighting, NS_NewSVGFEContainerFrame),
5498 SIMPLE_SVG_CREATE(feDisplacementMap, NS_NewSVGFELeafFrame),
5499 SIMPLE_SVG_CREATE(feDropShadow, NS_NewSVGFELeafFrame),
5500 SIMPLE_SVG_CREATE(feFlood, NS_NewSVGFELeafFrame),
5501 SIMPLE_SVG_CREATE(feGaussianBlur, NS_NewSVGFELeafFrame),
5502 SIMPLE_SVG_CREATE(feImage, NS_NewSVGFEImageFrame),
5503 SIMPLE_SVG_CREATE(feMerge, NS_NewSVGFEContainerFrame),
5504 SIMPLE_SVG_CREATE(feMergeNode, NS_NewSVGFEUnstyledLeafFrame),
5505 SIMPLE_SVG_CREATE(feMorphology, NS_NewSVGFELeafFrame),
5506 SIMPLE_SVG_CREATE(feOffset, NS_NewSVGFELeafFrame),
5507 SIMPLE_SVG_CREATE(feSpecularLighting, NS_NewSVGFEContainerFrame),
5508 SIMPLE_SVG_CREATE(feTile, NS_NewSVGFELeafFrame),
5509 SIMPLE_SVG_CREATE(feTurbulence, NS_NewSVGFELeafFrame)};
5510
5511 const FrameConstructionData* data = FindDataByTag(
5512 aTag, aElement, aStyleContext, sSVGData, ArrayLength(sSVGData));
5513
5514 if (!data) {
5515 data = &sContainerData;
5516 }
5517
5518 return data;
5519 }
5520
AddPageBreakItem(nsIContent * aContent,FrameConstructionItemList & aItems)5521 void nsCSSFrameConstructor::AddPageBreakItem(
5522 nsIContent* aContent, FrameConstructionItemList& aItems) {
5523 RefPtr<nsStyleContext> pseudoStyle =
5524 mPresShell->StyleSet()->ResolveNonInheritingAnonymousBoxStyle(
5525 nsCSSAnonBoxes::pageBreak);
5526
5527 MOZ_ASSERT(pseudoStyle->StyleDisplay()->mDisplay == StyleDisplay::Block,
5528 "Unexpected display");
5529
5530 static const FrameConstructionData sPageBreakData =
5531 FCDATA_DECL(FCDATA_SKIP_FRAMESET, NS_NewPageBreakFrame);
5532
5533 // Lie about the tag and namespace so we don't trigger anything
5534 // interesting during frame construction.
5535 aItems.AppendItem(this, &sPageBreakData, aContent, nsCSSAnonBoxes::pageBreak,
5536 kNameSpaceID_None, nullptr, pseudoStyle.forget(), true,
5537 nullptr);
5538 }
5539
ShouldCreateItemsForChild(nsFrameConstructorState & aState,nsIContent * aContent,nsContainerFrame * aParentFrame)5540 bool nsCSSFrameConstructor::ShouldCreateItemsForChild(
5541 nsFrameConstructorState& aState, nsIContent* aContent,
5542 nsContainerFrame* aParentFrame) {
5543 aContent->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
5544 if (aContent->IsElement() && !aContent->IsStyledByServo()) {
5545 // We can't just remove our pending restyle flags, since we may
5546 // have restyle-later-siblings set on us. But we _can_ remove the
5547 // "is possible restyle root" flags, and need to. Otherwise we can
5548 // end up with stale such flags (e.g. if we used to have a
5549 // display:none parent when our last restyle was posted and
5550 // processed and now no longer do).
5551 aContent->UnsetFlags(ELEMENT_ALL_RESTYLE_FLAGS &
5552 ~ELEMENT_PENDING_RESTYLE_FLAGS);
5553 }
5554
5555 // XXX the GetContent() != aContent check is needed due to bug 135040.
5556 // Remove it once that's fixed.
5557 if (aContent->GetPrimaryFrame() &&
5558 aContent->GetPrimaryFrame()->GetContent() == aContent &&
5559 !aState.mCreatingExtraFrames) {
5560 NS_ERROR(
5561 "asked to create frame construction item for a node that already "
5562 "has a frame");
5563 return false;
5564 }
5565
5566 // don't create a whitespace frame if aParent doesn't want it
5567 if (!NeedFrameFor(aState, aParentFrame, aContent)) {
5568 return false;
5569 }
5570
5571 // never create frames for comments or PIs
5572 if (aContent->IsNodeOfType(nsINode::eCOMMENT) ||
5573 aContent->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION)) {
5574 return false;
5575 }
5576
5577 return true;
5578 }
5579
DoAddFrameConstructionItems(nsFrameConstructorState & aState,nsIContent * aContent,nsStyleContext * aStyleContext,bool aSuppressWhiteSpaceOptimizations,nsContainerFrame * aParentFrame,nsTArray<nsIAnonymousContentCreator::ContentInfo> * aAnonChildren,FrameConstructionItemList & aItems)5580 void nsCSSFrameConstructor::DoAddFrameConstructionItems(
5581 nsFrameConstructorState& aState, nsIContent* aContent,
5582 nsStyleContext* aStyleContext, bool aSuppressWhiteSpaceOptimizations,
5583 nsContainerFrame* aParentFrame,
5584 nsTArray<nsIAnonymousContentCreator::ContentInfo>* aAnonChildren,
5585 FrameConstructionItemList& aItems) {
5586 uint32_t flags = ITEM_ALLOW_XBL_BASE | ITEM_ALLOW_PAGE_BREAK;
5587 if (aParentFrame) {
5588 if (nsSVGUtils::IsInSVGTextSubtree(aParentFrame)) {
5589 flags |= ITEM_IS_WITHIN_SVG_TEXT;
5590 }
5591 if (aParentFrame->IsBlockFrame() && aParentFrame->GetParent() &&
5592 aParentFrame->GetParent()->IsSVGTextFrame()) {
5593 flags |= ITEM_ALLOWS_TEXT_PATH_CHILD;
5594 }
5595 }
5596 AddFrameConstructionItemsInternal(
5597 aState, aContent, aParentFrame, aContent->NodeInfo()->NameAtom(),
5598 aContent->GetNameSpaceID(), aSuppressWhiteSpaceOptimizations,
5599 aStyleContext, flags, aAnonChildren, aItems);
5600 }
5601
AddFrameConstructionItems(nsFrameConstructorState & aState,nsIContent * aContent,bool aSuppressWhiteSpaceOptimizations,const InsertionPoint & aInsertion,FrameConstructionItemList & aItems)5602 void nsCSSFrameConstructor::AddFrameConstructionItems(
5603 nsFrameConstructorState& aState, nsIContent* aContent,
5604 bool aSuppressWhiteSpaceOptimizations, const InsertionPoint& aInsertion,
5605 FrameConstructionItemList& aItems) {
5606 nsContainerFrame* parentFrame = aInsertion.mParentFrame;
5607 if (!ShouldCreateItemsForChild(aState, aContent, parentFrame)) {
5608 return;
5609 }
5610 RefPtr<nsStyleContext> styleContext =
5611 ResolveStyleContext(aInsertion, aContent, &aState);
5612 DoAddFrameConstructionItems(aState, aContent, styleContext,
5613 aSuppressWhiteSpaceOptimizations, parentFrame,
5614 nullptr, aItems);
5615 }
5616
SetAsUndisplayedContent(nsFrameConstructorState & aState,FrameConstructionItemList & aList,nsIContent * aContent,nsStyleContext * aStyleContext,bool aIsGeneratedContent)5617 void nsCSSFrameConstructor::SetAsUndisplayedContent(
5618 nsFrameConstructorState& aState, FrameConstructionItemList& aList,
5619 nsIContent* aContent, nsStyleContext* aStyleContext,
5620 bool aIsGeneratedContent) {
5621 if (aStyleContext->GetPseudo()) {
5622 if (aIsGeneratedContent) {
5623 aContent->UnbindFromTree();
5624 }
5625 return;
5626 }
5627 NS_ASSERTION(!aIsGeneratedContent, "Should have had pseudo type");
5628
5629 if (aState.mCreatingExtraFrames) {
5630 MOZ_ASSERT(GetDisplayNoneStyleFor(aContent),
5631 "should have called RegisterDisplayNoneStyleFor earlier");
5632 return;
5633 }
5634 aList.AppendUndisplayedItem(aContent, aStyleContext);
5635 }
5636
AddFrameConstructionItemsInternal(nsFrameConstructorState & aState,nsIContent * aContent,nsContainerFrame * aParentFrame,nsAtom * aTag,int32_t aNameSpaceID,bool aSuppressWhiteSpaceOptimizations,nsStyleContext * aStyleContext,uint32_t aFlags,nsTArray<nsIAnonymousContentCreator::ContentInfo> * aAnonChildren,FrameConstructionItemList & aItems)5637 void nsCSSFrameConstructor::AddFrameConstructionItemsInternal(
5638 nsFrameConstructorState& aState, nsIContent* aContent,
5639 nsContainerFrame* aParentFrame, nsAtom* aTag, int32_t aNameSpaceID,
5640 bool aSuppressWhiteSpaceOptimizations, nsStyleContext* aStyleContext,
5641 uint32_t aFlags,
5642 nsTArray<nsIAnonymousContentCreator::ContentInfo>* aAnonChildren,
5643 FrameConstructionItemList& aItems) {
5644 NS_PRECONDITION(
5645 aContent->IsNodeOfType(nsINode::eTEXT) || aContent->IsElement(),
5646 "Shouldn't get anything else here!");
5647 MOZ_ASSERT(aContent->IsInComposedDoc());
5648 MOZ_ASSERT(!aContent->GetPrimaryFrame() || aState.mCreatingExtraFrames ||
5649 aContent->NodeInfo()->NameAtom() == nsGkAtoms::area);
5650
5651 // The following code allows the user to specify the base tag
5652 // of an element using XBL. XUL and HTML objects (like boxes, menus, etc.)
5653 // can then be extended arbitrarily.
5654 const nsStyleDisplay* display = aStyleContext->StyleDisplay();
5655 RefPtr<nsStyleContext> styleContext(aStyleContext);
5656 PendingBinding* pendingBinding = nullptr;
5657 if (aFlags & ITEM_ALLOW_XBL_BASE) {
5658 if (display->mBinding) {
5659 // Ensure that our XBL bindings are installed.
5660
5661 nsXBLService* xblService = nsXBLService::GetInstance();
5662 if (!xblService) return;
5663
5664 bool resolveStyle;
5665
5666 nsAutoPtr<PendingBinding> newPendingBinding(new PendingBinding());
5667
5668 nsresult rv = xblService->LoadBindings(
5669 aContent->AsElement(), display->mBinding->GetURI(),
5670 display->mBinding->mExtraData->GetPrincipal(),
5671 getter_AddRefs(newPendingBinding->mBinding), &resolveStyle);
5672 if (NS_FAILED(rv) && rv != NS_ERROR_XBL_BLOCKED) return;
5673
5674 if (newPendingBinding->mBinding) {
5675 pendingBinding = newPendingBinding;
5676 // aState takes over owning newPendingBinding
5677 aState.AddPendingBinding(newPendingBinding.forget());
5678 }
5679
5680 if (resolveStyle) {
5681 // Need to take a different path (Servo directly grabs the style from
5682 // the element, Gecko needs to actually re-resolve it using the parent
5683 // style context).
5684 if (styleContext->IsServo()) {
5685 styleContext = mPresShell->StyleSet()->AsServo()->ResolveServoStyle(
5686 aContent->AsElement());
5687 } else {
5688 #ifdef MOZ_OLD_STYLE
5689 styleContext = ResolveStyleContext(
5690 styleContext->AsGecko()->GetParent(), aContent, &aState);
5691 #else
5692 MOZ_CRASH("old style system disabled");
5693 #endif
5694 }
5695 }
5696
5697 display = styleContext->StyleDisplay();
5698 aStyleContext = styleContext;
5699 aTag = mDocument->BindingManager()->ResolveTag(aContent, &aNameSpaceID);
5700 }
5701 }
5702
5703 const bool isGeneratedContent = !!(aFlags & ITEM_IS_GENERATED_CONTENT);
5704
5705 // Pre-check for display "none" - if we find that, don't create
5706 // any frame at all
5707 if (StyleDisplay::None == display->mDisplay) {
5708 SetAsUndisplayedContent(aState, aItems, aContent, styleContext,
5709 isGeneratedContent);
5710 return;
5711 }
5712
5713 bool isText = !aContent->IsElement();
5714
5715 // never create frames for non-option/optgroup kids of <select> and
5716 // non-option kids of <optgroup> inside a <select>.
5717 // XXXbz it's not clear how this should best work with XBL.
5718 nsIContent* parent = aContent->GetParent();
5719 if (parent) {
5720 // Check tag first, since that check will usually fail
5721 if (parent->IsAnyOfHTMLElements(nsGkAtoms::select, nsGkAtoms::optgroup) &&
5722 // <option> is ok no matter what
5723 !aContent->IsHTMLElement(nsGkAtoms::option) &&
5724 // <optgroup> is OK in <select> but not in <optgroup>
5725 (!aContent->IsHTMLElement(nsGkAtoms::optgroup) ||
5726 !parent->IsHTMLElement(nsGkAtoms::select)) &&
5727 // Allow native anonymous content no matter what
5728 !aContent->IsRootOfNativeAnonymousSubtree()) {
5729 // No frame for aContent
5730 if (!isText) {
5731 SetAsUndisplayedContent(aState, aItems, aContent, styleContext,
5732 isGeneratedContent);
5733 }
5734 return;
5735 }
5736 }
5737
5738 // When constructing a child of a non-open <details>, create only the frame
5739 // for the main <summary> element, and skip other elements. This only applies
5740 // to things that are not roots of native anonymous subtrees (except for
5741 // ::before and ::after); we always want to create "internal" anonymous
5742 // content.
5743 auto* details = HTMLDetailsElement::FromContentOrNull(parent);
5744 if (details && !details->Open() &&
5745 (!aContent->IsRootOfNativeAnonymousSubtree() ||
5746 aContent->IsGeneratedContentContainerForBefore() ||
5747 aContent->IsGeneratedContentContainerForAfter())) {
5748 auto* summary = HTMLSummaryElement::FromContentOrNull(aContent);
5749 if (!summary || !summary->IsMainSummary()) {
5750 SetAsUndisplayedContent(aState, aItems, aContent, styleContext,
5751 isGeneratedContent);
5752 return;
5753 }
5754 }
5755
5756 bool isPopup = false;
5757 bool foundMathMLData = false;
5758 // Try to find frame construction data for this content
5759 const FrameConstructionData* data;
5760 if (isText) {
5761 data = FindTextData(aParentFrame);
5762 if (!data) {
5763 // Nothing to do here; suppressed text inside SVG
5764 return;
5765 }
5766 } else {
5767 Element* element = aContent->AsElement();
5768
5769 // Don't create frames for non-SVG element children of SVG elements.
5770 if (aNameSpaceID != kNameSpaceID_SVG &&
5771 ((aParentFrame && IsFrameForSVG(aParentFrame) &&
5772 !aParentFrame->IsFrameOfType(nsIFrame::eSVGForeignObject)) ||
5773 (aFlags & ITEM_IS_WITHIN_SVG_TEXT))) {
5774 SetAsUndisplayedContent(aState, aItems, element, styleContext,
5775 isGeneratedContent);
5776 return;
5777 }
5778
5779 data =
5780 FindHTMLData(element, aTag, aNameSpaceID, aParentFrame, styleContext);
5781 if (!data) {
5782 data = FindXULTagData(element, aTag, aNameSpaceID, styleContext);
5783 }
5784 if (!data) {
5785 data = FindMathMLData(element, aTag, aNameSpaceID, styleContext);
5786 foundMathMLData = !!data;
5787 }
5788 if (!data) {
5789 data = FindSVGData(element, aTag, aNameSpaceID, aParentFrame,
5790 aFlags & ITEM_IS_WITHIN_SVG_TEXT,
5791 aFlags & ITEM_ALLOWS_TEXT_PATH_CHILD, styleContext);
5792 }
5793
5794 // Now check for XUL display types
5795 if (!data) {
5796 data = FindXULDisplayData(display, element, styleContext);
5797 }
5798
5799 // And general display types
5800 if (!data) {
5801 data = FindDisplayData(display, element, styleContext);
5802 }
5803
5804 NS_ASSERTION(data, "Should have frame construction data now");
5805
5806 if (data->mBits & FCDATA_SUPPRESS_FRAME) {
5807 SetAsUndisplayedContent(aState, aItems, element, styleContext,
5808 isGeneratedContent);
5809 return;
5810 }
5811
5812 #ifdef MOZ_XUL
5813 if ((data->mBits & FCDATA_IS_POPUP) &&
5814 (!aParentFrame || // Parent is inline
5815 !aParentFrame->IsMenuFrame())) {
5816 if (!aState.mPopupItems.containingBlock &&
5817 !aState.mHavePendingPopupgroup) {
5818 SetAsUndisplayedContent(aState, aItems, element, styleContext,
5819 isGeneratedContent);
5820 return;
5821 }
5822
5823 isPopup = true;
5824 }
5825 #endif /* MOZ_XUL */
5826 }
5827
5828 uint32_t bits = data->mBits;
5829
5830 // Inside colgroups, suppress everything except columns.
5831 if (aParentFrame && aParentFrame->IsTableColGroupFrame() &&
5832 (!(bits & FCDATA_IS_TABLE_PART) ||
5833 display->mDisplay != StyleDisplay::TableColumn)) {
5834 SetAsUndisplayedContent(aState, aItems, aContent, styleContext,
5835 isGeneratedContent);
5836 return;
5837 }
5838
5839 bool canHavePageBreak =
5840 (aFlags & ITEM_ALLOW_PAGE_BREAK) && aState.mPresContext->IsPaginated() &&
5841 !display->IsAbsolutelyPositionedStyle() &&
5842 !(aParentFrame && aParentFrame->IsGridContainerFrame()) &&
5843 !(bits & FCDATA_IS_TABLE_PART) && !(bits & FCDATA_IS_SVG_TEXT);
5844
5845 if (canHavePageBreak && display->mBreakBefore) {
5846 AddPageBreakItem(aContent, aItems);
5847 }
5848
5849 // FIXME(emilio, https://github.com/w3c/csswg-drafts/issues/2167):
5850 //
5851 // Figure out what should happen for display: contents in MathML.
5852 if (display->mDisplay == StyleDisplay::Contents && !foundMathMLData) {
5853 if (!GetDisplayContentsStyleFor(aContent)) {
5854 MOZ_ASSERT(styleContext->GetPseudo() || !isGeneratedContent,
5855 "Should have had pseudo type");
5856 aState.mFrameManager->RegisterDisplayContentsStyleFor(aContent,
5857 styleContext);
5858 } else {
5859 aState.mFrameManager->ChangeRegisteredDisplayContentsStyleFor(
5860 aContent, styleContext);
5861 }
5862
5863 TreeMatchContext::AutoAncestorPusher ancestorPusher(
5864 aState.mTreeMatchContext);
5865 if (aState.HasAncestorFilter()) {
5866 ancestorPusher.PushAncestorAndStyleScope(aContent->AsElement());
5867 } else {
5868 ancestorPusher.PushStyleScope(aContent->AsElement());
5869 }
5870
5871 if (aParentFrame) {
5872 aParentFrame->AddStateBits(NS_FRAME_MAY_HAVE_GENERATED_CONTENT);
5873 }
5874 CreateGeneratedContentItem(aState, aParentFrame, aContent->AsElement(),
5875 styleContext, CSSPseudoElementType::before,
5876 aItems);
5877
5878 FlattenedChildIterator iter(aContent);
5879 for (nsIContent* child = iter.GetNextChild(); child;
5880 child = iter.GetNextChild()) {
5881 if (!ShouldCreateItemsForChild(aState, child, aParentFrame)) {
5882 continue;
5883 }
5884
5885 // Get the parent of the content and check if it is a XBL children element
5886 // (if the content is a children element then parent != aContent because
5887 // the FlattenedChildIterator will transitively iterate through
5888 // <xbl:children> for default content). Push the children element as an
5889 // ancestor here because it does not have a frame and would not otherwise
5890 // be pushed as an ancestor.
5891 nsIContent* parent = child->GetParent();
5892 MOZ_ASSERT(parent,
5893 "Parent must be non-null because we are iterating children.");
5894 TreeMatchContext::AutoAncestorPusher ancestorPusher(
5895 aState.mTreeMatchContext);
5896 if (parent != aContent && parent->IsElement()) {
5897 if (aState.HasAncestorFilter()) {
5898 ancestorPusher.PushAncestorAndStyleScope(parent->AsElement());
5899 } else {
5900 ancestorPusher.PushStyleScope(parent->AsElement());
5901 }
5902 }
5903
5904 RefPtr<nsStyleContext> childContext =
5905 ResolveStyleContext(styleContext, child, &aState);
5906 DoAddFrameConstructionItems(aState, child, childContext,
5907 aSuppressWhiteSpaceOptimizations,
5908 aParentFrame, aAnonChildren, aItems);
5909 }
5910 aItems.SetParentHasNoXBLChildren(!iter.XBLInvolved());
5911
5912 CreateGeneratedContentItem(aState, aParentFrame, aContent->AsElement(),
5913 styleContext, CSSPseudoElementType::after,
5914 aItems);
5915 if (canHavePageBreak && display->mBreakAfter) {
5916 AddPageBreakItem(aContent, aItems);
5917 }
5918 return;
5919 }
5920
5921 FrameConstructionItem* item = nullptr;
5922 if (details && details->Open()) {
5923 auto* summary = HTMLSummaryElement::FromContentOrNull(aContent);
5924 if (summary && summary->IsMainSummary()) {
5925 // If details is open, the main summary needs to be rendered as if it is
5926 // the first child, so add the item to the front of the item list.
5927 item =
5928 aItems.PrependItem(this, data, aContent, aTag, aNameSpaceID,
5929 pendingBinding, styleContext.forget(),
5930 aSuppressWhiteSpaceOptimizations, aAnonChildren);
5931 }
5932 }
5933
5934 if (!item) {
5935 item = aItems.AppendItem(this, data, aContent, aTag, aNameSpaceID,
5936 pendingBinding, styleContext.forget(),
5937 aSuppressWhiteSpaceOptimizations, aAnonChildren);
5938 }
5939 item->mIsText = isText;
5940 item->mIsGeneratedContent = isGeneratedContent;
5941 item->mIsAnonymousContentCreatorContent =
5942 aFlags & ITEM_IS_ANONYMOUSCONTENTCREATOR_CONTENT;
5943 if (isGeneratedContent) {
5944 // We need to keep this alive until the frame takes ownership.
5945 // This corresponds to the Release in ConstructFramesFromItem.
5946 item->mContent->AddRef();
5947 }
5948 item->mIsRootPopupgroup = aNameSpaceID == kNameSpaceID_XUL &&
5949 aTag == nsGkAtoms::popupgroup &&
5950 aContent->IsRootOfNativeAnonymousSubtree();
5951 if (item->mIsRootPopupgroup) {
5952 aState.mHavePendingPopupgroup = true;
5953 }
5954 item->mIsPopup = isPopup;
5955 item->mIsForSVGAElement =
5956 aNameSpaceID == kNameSpaceID_SVG && aTag == nsGkAtoms::a;
5957
5958 if (canHavePageBreak && display->mBreakAfter) {
5959 AddPageBreakItem(aContent, aItems);
5960 }
5961
5962 if (bits & FCDATA_IS_INLINE) {
5963 // To correctly set item->mIsAllInline we need to build up our child items
5964 // right now.
5965 BuildInlineChildItems(aState, *item, aFlags & ITEM_IS_WITHIN_SVG_TEXT,
5966 aFlags & ITEM_ALLOWS_TEXT_PATH_CHILD);
5967 item->mHasInlineEnds = true;
5968 item->mIsBlock = false;
5969 } else {
5970 // Compute a boolean isInline which is guaranteed to be false for blocks
5971 // (but may also be false for some inlines).
5972 bool isInline =
5973 // Table-internal things are inline-outside if and only if they're kids
5974 // of inlines, since they'll trigger construction of inline-table
5975 // pseudos.
5976 ((bits & FCDATA_IS_TABLE_PART) &&
5977 (!aParentFrame || // No aParentFrame means inline
5978 aParentFrame->StyleDisplay()->mDisplay == StyleDisplay::Inline)) ||
5979 // Things that are inline-outside but aren't inline frames are inline
5980 display->IsInlineOutsideStyle() ||
5981 // Popups that are certainly out of flow.
5982 isPopup;
5983
5984 // Set mIsAllInline conservatively. It just might be that even an inline
5985 // that has mIsAllInline false doesn't need an {ib} split. So this is just
5986 // an optimization to keep from doing too much work in cases when we can
5987 // show that mIsAllInline is true..
5988 item->mIsAllInline = item->mHasInlineEnds =
5989 isInline ||
5990 // Figure out whether we're guaranteed this item will be out of flow.
5991 // This is not a precise test, since one of our ancestor inlines might
5992 // add an absolute containing block (if it's relatively positioned) when
5993 // there wasn't such a containing block before. But it's conservative
5994 // in the sense that anything that will really end up as an in-flow
5995 // non-inline will test false here. In other words, if this test is
5996 // true we're guaranteed to be inline; if it's false we don't know what
5997 // we'll end up as.
5998 //
5999 // If we make this test precise, we can remove some of the code dealing
6000 // with the imprecision in ConstructInline and adjust the comments on
6001 // mIsAllInline and mIsBlock in the header. And probably remove
6002 // mIsBlock altogether, since then it will always be equal to
6003 // !mHasInlineEnds.
6004 (!(bits & FCDATA_DISALLOW_OUT_OF_FLOW) &&
6005 aState.GetGeometricParent(display, nullptr));
6006
6007 // Set mIsBlock conservatively. It's OK to set it false for some real
6008 // blocks, but not OK to set it true for things that aren't blocks. Since
6009 // isOutOfFlow might be false even in cases when the frame will end up
6010 // out-of-flow, we can't use it here. But we _can_ say that the frame will
6011 // for sure end up in-flow if it's not floated or absolutely positioned.
6012 item->mIsBlock = !isInline && !display->IsAbsolutelyPositionedStyle() &&
6013 !display->IsFloatingStyle() &&
6014 !(bits & FCDATA_IS_SVG_TEXT);
6015 }
6016
6017 if (item->mIsAllInline) {
6018 aItems.InlineItemAdded();
6019 } else if (item->mIsBlock) {
6020 aItems.BlockItemAdded();
6021 }
6022
6023 // Our item should be treated as a line participant if we have the relevant
6024 // bit and are going to be in-flow. Note that this really only matters if
6025 // our ancestor is a box or some such, so the fact that we might have an
6026 // inline ancestor that might become a containing block is not relevant here.
6027 if ((bits & FCDATA_IS_LINE_PARTICIPANT) &&
6028 ((bits & FCDATA_DISALLOW_OUT_OF_FLOW) ||
6029 !aState.GetGeometricParent(display, nullptr))) {
6030 item->mIsLineParticipant = true;
6031 aItems.LineParticipantItemAdded();
6032 }
6033 }
6034
6035 /**
6036 * Return true if the frame construction item pointed to by aIter will
6037 * create a frame adjacent to a line boundary in the frame tree, and that
6038 * line boundary is induced by a content node adjacent to the frame's
6039 * content node in the content tree. The latter condition is necessary so
6040 * that ContentAppended/ContentInserted/ContentRemoved can easily find any
6041 * text nodes that were suppressed here.
6042 */
AtLineBoundary(FCItemIterator & aIter)6043 bool nsCSSFrameConstructor::AtLineBoundary(FCItemIterator& aIter) {
6044 if (aIter.item().mSuppressWhiteSpaceOptimizations) {
6045 return false;
6046 }
6047
6048 if (aIter.AtStart()) {
6049 if (aIter.List()->HasLineBoundaryAtStart() &&
6050 !aIter.item().mContent->GetPreviousSibling())
6051 return true;
6052 } else {
6053 FCItemIterator prev = aIter;
6054 prev.Prev();
6055 if (prev.item().IsLineBoundary() &&
6056 !prev.item().mSuppressWhiteSpaceOptimizations &&
6057 aIter.item().mContent->GetPreviousSibling() == prev.item().mContent)
6058 return true;
6059 }
6060
6061 FCItemIterator next = aIter;
6062 next.Next();
6063 if (next.IsDone()) {
6064 if (aIter.List()->HasLineBoundaryAtEnd() &&
6065 !aIter.item().mContent->GetNextSibling())
6066 return true;
6067 } else {
6068 if (next.item().IsLineBoundary() &&
6069 !next.item().mSuppressWhiteSpaceOptimizations &&
6070 aIter.item().mContent->GetNextSibling() == next.item().mContent)
6071 return true;
6072 }
6073
6074 return false;
6075 }
6076
ConstructFramesFromItem(nsFrameConstructorState & aState,FCItemIterator & aIter,nsContainerFrame * aParentFrame,nsFrameItems & aFrameItems)6077 void nsCSSFrameConstructor::ConstructFramesFromItem(
6078 nsFrameConstructorState& aState, FCItemIterator& aIter,
6079 nsContainerFrame* aParentFrame, nsFrameItems& aFrameItems) {
6080 nsContainerFrame* adjParentFrame = aParentFrame;
6081 FrameConstructionItem& item = aIter.item();
6082 nsStyleContext* styleContext = item.mStyleContext;
6083 AdjustParentFrame(&adjParentFrame, item.mFCData, styleContext);
6084
6085 if (item.mIsText) {
6086 // If this is collapsible whitespace next to a line boundary,
6087 // don't create a frame. item.IsWhitespace() also sets the
6088 // NS_CREATE_FRAME_IF_NON_WHITESPACE flag in the text node. (If we
6089 // end up creating a frame, nsTextFrame::Init will clear the flag.)
6090 // We don't do this for generated content, because some generated
6091 // text content is empty text nodes that are about to be initialized.
6092 // (We check mAdditionalStateBits because only the generated content
6093 // container's frame construction item is marked with
6094 // mIsGeneratedContent, and we might not have an aParentFrame.)
6095 // We don't do it for content that may have XBL anonymous siblings,
6096 // because they make it difficult to correctly create the frame
6097 // due to dynamic changes.
6098 // We don't do it for SVG text, since we might need to position and
6099 // measure the white space glyphs due to x/y/dx/dy attributes.
6100 if (AtLineBoundary(aIter) &&
6101 !styleContext->StyleText()->WhiteSpaceOrNewlineIsSignificant() &&
6102 aIter.List()->ParentHasNoXBLChildren() &&
6103 !(aState.mAdditionalStateBits & NS_FRAME_GENERATED_CONTENT) &&
6104 (item.mFCData->mBits & FCDATA_IS_LINE_PARTICIPANT) &&
6105 !(item.mFCData->mBits & FCDATA_IS_SVG_TEXT) &&
6106 !mAlwaysCreateFramesForIgnorableWhitespace && item.IsWhitespace(aState))
6107 return;
6108
6109 ConstructTextFrame(item.mFCData, aState, item.mContent, adjParentFrame,
6110 styleContext, aFrameItems);
6111 return;
6112 }
6113
6114 // Start background loads during frame construction so that we're
6115 // guaranteed that they will be started before onload fires.
6116 styleContext->StartBackgroundImageLoads();
6117
6118 nsFrameState savedStateBits = aState.mAdditionalStateBits;
6119 if (item.mIsGeneratedContent) {
6120 // Ensure that frames created here are all tagged with
6121 // NS_FRAME_GENERATED_CONTENT.
6122 aState.mAdditionalStateBits |= NS_FRAME_GENERATED_CONTENT;
6123 }
6124
6125 // XXXbz maybe just inline ConstructFrameFromItemInternal here or something?
6126 ConstructFrameFromItemInternal(item, aState, adjParentFrame, aFrameItems);
6127
6128 if (item.mIsGeneratedContent) {
6129 // This corresponds to the AddRef in AddFrameConstructionItemsInternal.
6130 // The frame owns the generated content now.
6131 item.mContent->Release();
6132
6133 // Now that we've passed ownership of item.mContent to the frame, unset
6134 // our generated content flag so we don't release or unbind it ourselves.
6135 item.mIsGeneratedContent = false;
6136 }
6137
6138 aState.mAdditionalStateBits = savedStateBits;
6139 }
6140
IsRootBoxFrame(nsIFrame * aFrame)6141 inline bool IsRootBoxFrame(nsIFrame* aFrame) { return (aFrame->IsRootFrame()); }
6142
ReconstructDocElementHierarchy(InsertionKind aInsertionKind)6143 void nsCSSFrameConstructor::ReconstructDocElementHierarchy(
6144 InsertionKind aInsertionKind) {
6145 Element* rootElement = mDocument->GetRootElement();
6146 if (!rootElement) {
6147 /* nothing to do */
6148 return;
6149 }
6150 RecreateFramesForContent(rootElement, aInsertionKind);
6151 }
6152
GetAbsoluteContainingBlock(nsIFrame * aFrame,ContainingBlockType aType)6153 nsContainerFrame* nsCSSFrameConstructor::GetAbsoluteContainingBlock(
6154 nsIFrame* aFrame, ContainingBlockType aType) {
6155 // Starting with aFrame, look for a frame that is absolutely positioned or
6156 // relatively positioned (and transformed, if aType is FIXED)
6157 for (nsIFrame* frame = aFrame; frame; frame = frame->GetParent()) {
6158 if (frame->IsFrameOfType(nsIFrame::eMathML)) {
6159 // If it's mathml, bail out -- no absolute positioning out from inside
6160 // mathml frames. Note that we don't make this part of the loop
6161 // condition because of the stuff at the end of this method...
6162 return nullptr;
6163 }
6164
6165 // Look for the ICB.
6166 if (aType == FIXED_POS) {
6167 LayoutFrameType t = frame->Type();
6168 if (t == LayoutFrameType::Viewport || t == LayoutFrameType::PageContent) {
6169 return static_cast<nsContainerFrame*>(frame);
6170 }
6171 }
6172
6173 // If the frame is positioned, we will probably return it as the containing
6174 // block (see the exceptions below). Otherwise, we'll start looking at the
6175 // parent frame, unless we're dealing with a scrollframe.
6176 // Scrollframes are special since they're not positioned, but their
6177 // scrolledframe might be. So, we need to check this special case to return
6178 // the correct containing block (the scrolledframe) in that case.
6179 // If we're looking for a fixed-pos containing block and the frame is
6180 // not transformed, skip it.
6181 if (!frame->IsAbsPosContainingBlock() ||
6182 (aType == FIXED_POS && !frame->IsFixedPosContainingBlock())) {
6183 continue;
6184 }
6185 nsIFrame* absPosCBCandidate = frame;
6186 LayoutFrameType type = absPosCBCandidate->Type();
6187 if (type == LayoutFrameType::FieldSet) {
6188 absPosCBCandidate =
6189 static_cast<nsFieldSetFrame*>(absPosCBCandidate)->GetInner();
6190 if (!absPosCBCandidate) {
6191 continue;
6192 }
6193 type = absPosCBCandidate->Type();
6194 }
6195 if (type == LayoutFrameType::Scroll) {
6196 nsIScrollableFrame* scrollFrame = do_QueryFrame(absPosCBCandidate);
6197 absPosCBCandidate = scrollFrame->GetScrolledFrame();
6198 if (!absPosCBCandidate) {
6199 continue;
6200 }
6201 type = absPosCBCandidate->Type();
6202 }
6203 // Only first continuations can be containing blocks.
6204 absPosCBCandidate = absPosCBCandidate->FirstContinuation();
6205 // Is the frame really an absolute container?
6206 if (!absPosCBCandidate->IsAbsoluteContainer()) {
6207 continue;
6208 }
6209
6210 // For tables, skip the inner frame and consider the table wrapper frame.
6211 if (type == LayoutFrameType::Table) {
6212 continue;
6213 }
6214 // For table wrapper frames, we can just return absPosCBCandidate.
6215 MOZ_ASSERT((nsContainerFrame*)do_QueryFrame(absPosCBCandidate),
6216 "abs.pos. containing block must be nsContainerFrame sub-class");
6217 return static_cast<nsContainerFrame*>(absPosCBCandidate);
6218 }
6219
6220 MOZ_ASSERT(aType != FIXED_POS, "no ICB in this frame tree?");
6221
6222 // It is possible for the search for the containing block to fail, because
6223 // no absolute container can be found in the parent chain. In those cases,
6224 // we fall back to the document element's containing block.
6225 return mHasRootAbsPosContainingBlock ? mDocElementContainingBlock : nullptr;
6226 }
6227
GetFloatContainingBlock(nsIFrame * aFrame)6228 nsContainerFrame* nsCSSFrameConstructor::GetFloatContainingBlock(
6229 nsIFrame* aFrame) {
6230 // Starting with aFrame, look for a frame that is a float containing block.
6231 // IF we hit a mathml frame, bail out; we don't allow floating out of mathml
6232 // frames, because they don't seem to be able to deal.
6233 // The logic here needs to match the logic in ProcessChildren()
6234 for (nsIFrame* containingBlock = aFrame;
6235 containingBlock && !ShouldSuppressFloatingOfDescendants(containingBlock);
6236 containingBlock = containingBlock->GetParent()) {
6237 if (containingBlock->IsFloatContainingBlock()) {
6238 MOZ_ASSERT((nsContainerFrame*)do_QueryFrame(containingBlock),
6239 "float containing block must be nsContainerFrame sub-class");
6240 return static_cast<nsContainerFrame*>(containingBlock);
6241 }
6242 }
6243
6244 // If we didn't find a containing block, then there just isn't
6245 // one.... return null
6246 return nullptr;
6247 }
6248
6249 /**
6250 * This function will get the previous sibling to use for an append operation.
6251 *
6252 * It takes a parent frame (must not be null) and the next insertion sibling, if
6253 * the parent content is display: contents or has ::after content (may be null).
6254 */
FindAppendPrevSibling(nsIFrame * aParentFrame,nsIFrame * aNextSibling)6255 static nsIFrame* FindAppendPrevSibling(nsIFrame* aParentFrame,
6256 nsIFrame* aNextSibling) {
6257 aParentFrame->DrainSelfOverflowList();
6258
6259 if (aNextSibling) {
6260 MOZ_ASSERT(aNextSibling->GetParent() == aParentFrame, "Wrong parent");
6261 return aNextSibling->GetPrevSibling();
6262 }
6263
6264 return aParentFrame->GetChildList(kPrincipalList).LastChild();
6265 }
6266
6267 /**
6268 * Finds the right parent frame to append content to aParentFrame.
6269 *
6270 * Cannot return or receive null.
6271 */
ContinuationToAppendTo(nsContainerFrame * aParentFrame)6272 static nsContainerFrame* ContinuationToAppendTo(
6273 nsContainerFrame* aParentFrame) {
6274 MOZ_ASSERT(aParentFrame);
6275
6276 if (IsFramePartOfIBSplit(aParentFrame)) {
6277 // If the frame we are manipulating is a ib-split frame (that is, one that's
6278 // been created as a result of a block-in-inline situation) then we need to
6279 // append to the last ib-split sibling, not to the frame itself.
6280 //
6281 // Always make sure to look at the last continuation of the frame for the
6282 // {ib} case, even if that continuation is empty.
6283 //
6284 // We don't do this for the non-ib-split-frame case, since in the other
6285 // cases appending to the last nonempty continuation is fine and in fact not
6286 // doing that can confuse code that doesn't know to pull kids from
6287 // continuations other than its next one.
6288 return static_cast<nsContainerFrame*>(
6289 GetLastIBSplitSibling(aParentFrame)->LastContinuation());
6290 }
6291
6292 return nsLayoutUtils::LastContinuationWithChild(aParentFrame);
6293 }
6294
6295 /**
6296 * This function will get the next sibling for a frame insert operation given
6297 * the parent and previous sibling. aPrevSibling may be null.
6298 */
GetInsertNextSibling(nsIFrame * aParentFrame,nsIFrame * aPrevSibling)6299 static nsIFrame* GetInsertNextSibling(nsIFrame* aParentFrame,
6300 nsIFrame* aPrevSibling) {
6301 if (aPrevSibling) {
6302 return aPrevSibling->GetNextSibling();
6303 }
6304
6305 return aParentFrame->PrincipalChildList().FirstChild();
6306 }
6307
6308 /**
6309 * This function is called by ContentAppended() and ContentInserted() when
6310 * appending flowed frames to a parent's principal child list. It handles the
6311 * case where the parent is the trailing inline of an {ib} split.
6312 */
AppendFramesToParent(nsFrameConstructorState & aState,nsContainerFrame * aParentFrame,nsFrameItems & aFrameList,nsIFrame * aPrevSibling,bool aIsRecursiveCall)6313 void nsCSSFrameConstructor::AppendFramesToParent(
6314 nsFrameConstructorState& aState, nsContainerFrame* aParentFrame,
6315 nsFrameItems& aFrameList, nsIFrame* aPrevSibling, bool aIsRecursiveCall) {
6316 NS_PRECONDITION(
6317 !IsFramePartOfIBSplit(aParentFrame) || !GetIBSplitSibling(aParentFrame) ||
6318 !GetIBSplitSibling(aParentFrame)->PrincipalChildList().FirstChild(),
6319 "aParentFrame has a ib-split sibling with kids?");
6320 NS_PRECONDITION(!aPrevSibling || aPrevSibling->GetParent() == aParentFrame,
6321 "Parent and prevsibling don't match");
6322
6323 nsIFrame* nextSibling = ::GetInsertNextSibling(aParentFrame, aPrevSibling);
6324
6325 NS_ASSERTION(nextSibling || !aParentFrame->GetNextContinuation() ||
6326 !aParentFrame->GetNextContinuation()
6327 ->PrincipalChildList()
6328 .FirstChild() ||
6329 aIsRecursiveCall,
6330 "aParentFrame has later continuations with kids?");
6331 NS_ASSERTION(
6332 nextSibling || !IsFramePartOfIBSplit(aParentFrame) ||
6333 (IsInlineFrame(aParentFrame) && !GetIBSplitSibling(aParentFrame) &&
6334 !aParentFrame->GetNextContinuation()) ||
6335 aIsRecursiveCall,
6336 "aParentFrame is not last?");
6337
6338 // If we're inserting a list of frames at the end of the trailing inline
6339 // of an {ib} split, we may need to create additional {ib} siblings to parent
6340 // them.
6341 if (!nextSibling && IsFramePartOfIBSplit(aParentFrame)) {
6342 // When we get here, our frame list might start with a block. If it does
6343 // so, and aParentFrame is an inline, and it and all its previous
6344 // continuations have no siblings, then put the initial blocks from the
6345 // frame list into the previous block of the {ib} split. Note that we
6346 // didn't want to stop at the block part of the split when figuring out
6347 // initial parent, because that could screw up float parenting; it's easier
6348 // to do this little fixup here instead.
6349 if (aFrameList.NotEmpty() && !aFrameList.FirstChild()->IsInlineOutside()) {
6350 // See whether our trailing inline is empty
6351 nsIFrame* firstContinuation = aParentFrame->FirstContinuation();
6352 if (firstContinuation->PrincipalChildList().IsEmpty()) {
6353 // Our trailing inline is empty. Collect our starting blocks from
6354 // aFrameList, get the right parent frame for them, and put them in.
6355 nsFrameList::FrameLinkEnumerator firstNonBlockEnumerator =
6356 FindFirstNonBlock(aFrameList);
6357 nsFrameList blockKids = aFrameList.ExtractHead(firstNonBlockEnumerator);
6358 NS_ASSERTION(blockKids.NotEmpty(), "No blocks?");
6359
6360 nsContainerFrame* prevBlock = GetIBSplitPrevSibling(firstContinuation);
6361 prevBlock =
6362 static_cast<nsContainerFrame*>(prevBlock->LastContinuation());
6363 NS_ASSERTION(prevBlock, "Should have previous block here");
6364
6365 MoveChildrenTo(aParentFrame, prevBlock, blockKids);
6366 }
6367 }
6368
6369 // We want to put some of the frames into this inline frame.
6370 nsFrameList::FrameLinkEnumerator firstBlockEnumerator(aFrameList);
6371 FindFirstBlock(firstBlockEnumerator);
6372
6373 nsFrameList inlineKids = aFrameList.ExtractHead(firstBlockEnumerator);
6374 if (!inlineKids.IsEmpty()) {
6375 AppendFrames(aParentFrame, kPrincipalList, inlineKids);
6376 }
6377
6378 if (!aFrameList.IsEmpty()) {
6379 bool positioned = aParentFrame->IsRelativelyPositioned();
6380 nsFrameItems ibSiblings;
6381 CreateIBSiblings(aState, aParentFrame, positioned, aFrameList,
6382 ibSiblings);
6383
6384 // Make sure to trigger reflow of the inline that used to be our
6385 // last one and now isn't anymore, since its GetSkipSides() has
6386 // changed.
6387 mPresShell->FrameNeedsReflow(aParentFrame, nsIPresShell::eTreeChange,
6388 NS_FRAME_HAS_DIRTY_CHILDREN);
6389
6390 // Recurse so we create new ib siblings as needed for aParentFrame's
6391 // parent
6392 return AppendFramesToParent(aState, aParentFrame->GetParent(), ibSiblings,
6393 aParentFrame, true);
6394 }
6395 return;
6396 }
6397
6398 // Insert the frames after our aPrevSibling
6399 InsertFrames(aParentFrame, kPrincipalList, aPrevSibling, aFrameList);
6400 }
6401
6402 #define UNSET_DISPLAY static_cast<StyleDisplay>(255)
6403
6404 // This gets called to see if the frames corresponding to aSibling and aContent
6405 // should be siblings in the frame tree. Although (1) rows and cols, (2) row
6406 // groups and col groups, (3) row groups and captions, (4) legends and content
6407 // inside fieldsets, (5) popups and other kids of the menu are siblings from a
6408 // content perspective, they are not considered siblings in the frame tree.
IsValidSibling(nsIFrame * aSibling,nsIContent * aContent,StyleDisplay & aDisplay)6409 bool nsCSSFrameConstructor::IsValidSibling(nsIFrame* aSibling,
6410 nsIContent* aContent,
6411 StyleDisplay& aDisplay) {
6412 nsIFrame* parentFrame = aSibling->GetParent();
6413 LayoutFrameType parentType = parentFrame->Type();
6414
6415 StyleDisplay siblingDisplay = aSibling->GetDisplay();
6416 if (StyleDisplay::TableColumnGroup == siblingDisplay ||
6417 StyleDisplay::TableColumn == siblingDisplay ||
6418 StyleDisplay::TableCaption == siblingDisplay ||
6419 StyleDisplay::TableHeaderGroup == siblingDisplay ||
6420 StyleDisplay::TableRowGroup == siblingDisplay ||
6421 StyleDisplay::TableFooterGroup == siblingDisplay ||
6422 LayoutFrameType::Menu == parentType) {
6423 // if we haven't already, construct a style context to find the display type
6424 // of aContent
6425 if (UNSET_DISPLAY == aDisplay) {
6426 nsIFrame* styleParent;
6427 aSibling->GetParentStyleContext(&styleParent);
6428 if (!styleParent) {
6429 styleParent = aSibling->GetParent();
6430 }
6431 if (!styleParent) {
6432 NS_NOTREACHED("Shouldn't happen");
6433 return false;
6434 }
6435 if (aContent->IsNodeOfType(nsINode::eCOMMENT) ||
6436 aContent->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION)) {
6437 // Comments and processing instructions never have frames, so we
6438 // should not try to generate style contexts for them.
6439 return false;
6440 }
6441 // XXXbz when this code is killed, the state argument to
6442 // ResolveStyleContext can be made non-optional.
6443 RefPtr<nsStyleContext> styleContext =
6444 ResolveStyleContext(styleParent, aContent, nullptr);
6445 const nsStyleDisplay* display = styleContext->StyleDisplay();
6446 aDisplay = display->mDisplay;
6447 }
6448 if (LayoutFrameType::Menu == parentType) {
6449 return (StyleDisplay::MozPopup == aDisplay) ==
6450 (StyleDisplay::MozPopup == siblingDisplay);
6451 }
6452 // To have decent performance we want to return false in cases in which
6453 // reordering the two siblings has no effect on display. To ensure
6454 // correctness, we MUST return false in cases where the two siblings have
6455 // the same desired parent type and live on different display lists.
6456 // Specificaly, columns and column groups should only consider columns and
6457 // column groups as valid siblings. Captions should only consider other
6458 // captions. All other things should consider each other as valid
6459 // siblings. The restriction in the |if| above on siblingDisplay is ok,
6460 // because for correctness the only part that really needs to happen is to
6461 // not consider captions, column groups, and row/header/footer groups
6462 // siblings of each other. Treating a column or colgroup as a valid
6463 // sibling of a non-table-related frame will just mean we end up reframing.
6464 if ((siblingDisplay == StyleDisplay::TableCaption) !=
6465 (aDisplay == StyleDisplay::TableCaption)) {
6466 // One's a caption and the other is not. Not valid siblings.
6467 return false;
6468 }
6469
6470 if ((siblingDisplay == StyleDisplay::TableColumnGroup ||
6471 siblingDisplay == StyleDisplay::TableColumn) !=
6472 (aDisplay == StyleDisplay::TableColumnGroup ||
6473 aDisplay == StyleDisplay::TableColumn)) {
6474 // One's a column or column group and the other is not. Not valid
6475 // siblings.
6476 return false;
6477 }
6478 // Fall through; it's possible that the display type was overridden and
6479 // a different sort of frame was constructed, so we may need to return false
6480 // below.
6481 }
6482
6483 if (IsFrameForFieldSet(parentFrame)) {
6484 // Legends can be sibling of legends but not of other content in the
6485 // fieldset
6486 if (nsContainerFrame* cif = aSibling->GetContentInsertionFrame()) {
6487 aSibling = cif;
6488 }
6489 LayoutFrameType sibType = aSibling->Type();
6490 bool legendContent = aContent->IsHTMLElement(nsGkAtoms::legend);
6491
6492 if ((legendContent && (LayoutFrameType::Legend != sibType)) ||
6493 (!legendContent && (LayoutFrameType::Legend == sibType)))
6494 return false;
6495 }
6496
6497 return true;
6498 }
6499
6500 // FIXME(emilio): If we ever kill IsValidSibling() we can simplify this quite a
6501 // bit (no need to pass aTargetContent or aTargetContentDisplay, and the
6502 // adjust() calls can be responsibility of the caller).
6503 template <nsCSSFrameConstructor::SiblingDirection aDirection>
FindSiblingInternal(FlattenedChildIterator & aIter,nsIContent * aTargetContent,StyleDisplay & aTargetContentDisplay)6504 nsIFrame* nsCSSFrameConstructor::FindSiblingInternal(
6505 FlattenedChildIterator& aIter, nsIContent* aTargetContent,
6506 StyleDisplay& aTargetContentDisplay) {
6507 auto adjust = [&](nsIFrame* aPotentialSiblingFrame) -> nsIFrame* {
6508 return AdjustSiblingFrame(aPotentialSiblingFrame, aTargetContent,
6509 aTargetContentDisplay, aDirection);
6510 };
6511
6512 auto nextDomSibling = [](FlattenedChildIterator& aIter) -> nsIContent* {
6513 return aDirection == SiblingDirection::Forward ? aIter.GetNextChild()
6514 : aIter.GetPreviousChild();
6515 };
6516
6517 auto getNearPseudo = [](const nsIContent* aContent) -> nsIFrame* {
6518 return aDirection == SiblingDirection::Forward
6519 ? nsLayoutUtils::GetBeforeFrame(aContent)
6520 : nsLayoutUtils::GetAfterFrame(aContent);
6521 };
6522
6523 auto getFarPseudo = [](const nsIContent* aContent) -> nsIFrame* {
6524 return aDirection == SiblingDirection::Forward
6525 ? nsLayoutUtils::GetAfterFrame(aContent)
6526 : nsLayoutUtils::GetBeforeFrame(aContent);
6527 };
6528
6529 while (nsIContent* sibling = nextDomSibling(aIter)) {
6530 if (nsIFrame* primaryFrame = sibling->GetPrimaryFrame()) {
6531 // XXX the GetContent() == sibling check is needed due to bug 135040.
6532 // Remove it once that's fixed.
6533 if (primaryFrame->GetContent() == sibling) {
6534 if (nsIFrame* frame = adjust(primaryFrame)) {
6535 return frame;
6536 }
6537 }
6538 }
6539
6540 if (GetDisplayContentsStyleFor(sibling)) {
6541 if (nsIFrame* frame = adjust(getNearPseudo(sibling))) {
6542 return frame;
6543 }
6544
6545 const bool startFromBeginning = aDirection == SiblingDirection::Forward;
6546 FlattenedChildIterator iter(sibling, startFromBeginning);
6547 nsIFrame* sibling = FindSiblingInternal<aDirection>(
6548 iter, aTargetContent, aTargetContentDisplay);
6549 if (sibling) {
6550 return sibling;
6551 }
6552 }
6553 }
6554
6555 return adjust(getFarPseudo(aIter.Parent()));
6556 }
6557
AdjustSiblingFrame(nsIFrame * aSibling,nsIContent * aTargetContent,mozilla::StyleDisplay & aTargetContentDisplay,SiblingDirection aDirection)6558 nsIFrame* nsCSSFrameConstructor::AdjustSiblingFrame(
6559 nsIFrame* aSibling, nsIContent* aTargetContent,
6560 mozilla::StyleDisplay& aTargetContentDisplay, SiblingDirection aDirection) {
6561 if (!aSibling) {
6562 return nullptr;
6563 }
6564
6565 if (aSibling->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
6566 aSibling = aSibling->GetPlaceholderFrame();
6567 MOZ_ASSERT(aSibling);
6568 }
6569
6570 MOZ_ASSERT(!aSibling->GetPrevContinuation(), "How?");
6571 if (aDirection == SiblingDirection::Backward) {
6572 // The frame may be a ib-split frame (a split inline frame that contains a
6573 // block). Get the last part of that split.
6574 if (IsFramePartOfIBSplit(aSibling)) {
6575 aSibling = GetLastIBSplitSibling(aSibling);
6576 }
6577
6578 // The frame may have a continuation. If so, we want the last
6579 // non-overflow-container continuation as our previous sibling.
6580 aSibling = aSibling->GetTailContinuation();
6581 }
6582
6583 if (!IsValidSibling(aSibling, aTargetContent, aTargetContentDisplay)) {
6584 return nullptr;
6585 }
6586
6587 return aSibling;
6588 }
6589
FindPreviousSibling(const FlattenedChildIterator & aIter,StyleDisplay & aTargetContentDisplay)6590 nsIFrame* nsCSSFrameConstructor::FindPreviousSibling(
6591 const FlattenedChildIterator& aIter, StyleDisplay& aTargetContentDisplay) {
6592 return FindSibling<SiblingDirection::Backward>(aIter, aTargetContentDisplay);
6593 }
6594
FindNextSibling(const FlattenedChildIterator & aIter,StyleDisplay & aTargetContentDisplay)6595 nsIFrame* nsCSSFrameConstructor::FindNextSibling(
6596 const FlattenedChildIterator& aIter, StyleDisplay& aTargetContentDisplay) {
6597 return FindSibling<SiblingDirection::Forward>(aIter, aTargetContentDisplay);
6598 }
6599
6600 template <nsCSSFrameConstructor::SiblingDirection aDirection>
FindSibling(const FlattenedChildIterator & aIter,StyleDisplay & aTargetContentDisplay)6601 nsIFrame* nsCSSFrameConstructor::FindSibling(
6602 const FlattenedChildIterator& aIter, StyleDisplay& aTargetContentDisplay) {
6603 nsIContent* targetContent = aIter.Get();
6604 FlattenedChildIterator siblingIter = aIter;
6605 nsIFrame* sibling = FindSiblingInternal<aDirection>(
6606 siblingIter, targetContent, aTargetContentDisplay);
6607 if (sibling) {
6608 return sibling;
6609 }
6610
6611 // Our siblings (if any) do not have a frame to guide us. The frame for the
6612 // target content should be inserted whereever a frame for the container would
6613 // be inserted. This is needed when inserting into display: contents nodes.
6614 const nsIContent* current = aIter.Parent();
6615 while (GetDisplayContentsStyleFor(current)) {
6616 const nsIContent* parent = current->GetFlattenedTreeParent();
6617 MOZ_ASSERT(parent, "No display: contents on the root");
6618
6619 FlattenedChildIterator iter(parent);
6620 iter.Seek(current);
6621 sibling = FindSiblingInternal<aDirection>(iter, targetContent,
6622 aTargetContentDisplay);
6623 if (sibling) {
6624 return sibling;
6625 }
6626
6627 current = parent;
6628 }
6629
6630 return nullptr;
6631 }
6632
6633 // For fieldsets, returns the area frame, if the child is not a legend.
GetAdjustedParentFrame(nsContainerFrame * aParentFrame,nsIContent * aChildContent)6634 static nsContainerFrame* GetAdjustedParentFrame(nsContainerFrame* aParentFrame,
6635 nsIContent* aChildContent) {
6636 NS_PRECONDITION(!aParentFrame->IsTableWrapperFrame(),
6637 "Shouldn't be happening!");
6638
6639 nsContainerFrame* newParent = nullptr;
6640
6641 if (aParentFrame->IsFieldSetFrame()) {
6642 // If the parent is a fieldSet, use the fieldSet's area frame as the
6643 // parent unless the new content is a legend.
6644 if (!aChildContent->IsHTMLElement(nsGkAtoms::legend)) {
6645 newParent = GetFieldSetBlockFrame(aParentFrame);
6646 }
6647 }
6648 return newParent ? newParent : aParentFrame;
6649 }
6650
GetInsertionPrevSibling(InsertionPoint * aInsertion,nsIContent * aChild,bool * aIsAppend,bool * aIsRangeInsertSafe,nsIContent * aStartSkipChild,nsIContent * aEndSkipChild)6651 nsIFrame* nsCSSFrameConstructor::GetInsertionPrevSibling(
6652 InsertionPoint* aInsertion, nsIContent* aChild, bool* aIsAppend,
6653 bool* aIsRangeInsertSafe, nsIContent* aStartSkipChild,
6654 nsIContent* aEndSkipChild) {
6655 NS_PRECONDITION(aInsertion->mParentFrame,
6656 "Must have parent frame to start with");
6657
6658 *aIsAppend = false;
6659
6660 // Find the frame that precedes the insertion point. Walk backwards
6661 // from the parent frame to get the parent content, because if an
6662 // XBL insertion point is involved, we'll need to use _that_ to find
6663 // the preceding frame.
6664 FlattenedChildIterator iter(aInsertion->mContainer);
6665 bool xblCase = iter.XBLInvolved() || aInsertion->mParentFrame->GetContent() !=
6666 aInsertion->mContainer;
6667 if (xblCase || !aChild->IsRootOfAnonymousSubtree()) {
6668 // The check for IsRootOfAnonymousSubtree() is because editor is
6669 // severely broken and calls us directly for native anonymous
6670 // nodes that it creates.
6671 if (aStartSkipChild) {
6672 iter.Seek(aStartSkipChild);
6673 } else {
6674 iter.Seek(aChild);
6675 }
6676 } else {
6677 // Prime the iterator for the call to FindPreviousSibling.
6678 iter.GetNextChild();
6679 MOZ_ASSERT(aChild->GetProperty(nsGkAtoms::restylableAnonymousNode),
6680 "Someone passed native anonymous content directly into frame "
6681 "construction. Stop doing that!");
6682 }
6683
6684 // Note that FindPreviousSibling is passed the iterator by value, so that
6685 // the later usage of the iterator starts from the same place.
6686 StyleDisplay childDisplay = UNSET_DISPLAY;
6687 nsIFrame* prevSibling = FindPreviousSibling(iter, childDisplay);
6688
6689 // Now, find the geometric parent so that we can handle
6690 // continuations properly. Use the prev sibling if we have it;
6691 // otherwise use the next sibling.
6692 if (prevSibling) {
6693 aInsertion->mParentFrame =
6694 prevSibling->GetParent()->GetContentInsertionFrame();
6695 } else {
6696 // If there is no previous sibling, then find the frame that follows
6697 //
6698 // FIXME(emilio): This is really complex and probably shouldn't be.
6699 if (aEndSkipChild) {
6700 iter.Seek(aEndSkipChild);
6701 iter.GetPreviousChild();
6702 }
6703 if (nsIFrame* nextSibling = FindNextSibling(iter, childDisplay)) {
6704 aInsertion->mParentFrame =
6705 nextSibling->GetParent()->GetContentInsertionFrame();
6706 } else {
6707 // No previous or next sibling, so treat this like an appended frame.
6708 *aIsAppend = true;
6709 aInsertion->mParentFrame =
6710 ::ContinuationToAppendTo(aInsertion->mParentFrame);
6711
6712 // Deal with fieldsets.
6713 aInsertion->mParentFrame =
6714 ::GetAdjustedParentFrame(aInsertion->mParentFrame, aChild);
6715 prevSibling = ::FindAppendPrevSibling(aInsertion->mParentFrame, nullptr);
6716 }
6717 }
6718
6719 *aIsRangeInsertSafe = (childDisplay == UNSET_DISPLAY);
6720 return prevSibling;
6721 }
6722
GetContentInsertionFrameFor(nsIContent * aContent)6723 nsContainerFrame* nsCSSFrameConstructor::GetContentInsertionFrameFor(
6724 nsIContent* aContent) {
6725 nsIFrame* frame;
6726 while (!(frame = aContent->GetPrimaryFrame())) {
6727 if (!GetDisplayContentsStyleFor(aContent)) {
6728 return nullptr;
6729 }
6730
6731 aContent = aContent->GetFlattenedTreeParent();
6732 if (!aContent) {
6733 return nullptr;
6734 }
6735 }
6736
6737 // If the content of the frame is not the desired content then this is not
6738 // really a frame for the desired content.
6739 // XXX This check is needed due to bug 135040. Remove it once that's fixed.
6740 if (frame->GetContent() != aContent) {
6741 return nullptr;
6742 }
6743
6744 nsContainerFrame* insertionFrame = frame->GetContentInsertionFrame();
6745
6746 NS_ASSERTION(!insertionFrame || insertionFrame == frame || !frame->IsLeaf(),
6747 "The insertion frame is the primary frame or the primary frame "
6748 "isn't a leaf");
6749
6750 return insertionFrame;
6751 }
6752
IsSpecialFramesetChild(nsIContent * aContent)6753 static bool IsSpecialFramesetChild(nsIContent* aContent) {
6754 // IMPORTANT: This must match the conditions in nsHTMLFramesetFrame::Init.
6755 return aContent->IsAnyOfHTMLElements(nsGkAtoms::frameset, nsGkAtoms::frame);
6756 }
6757
6758 static void InvalidateCanvasIfNeeded(nsIPresShell* presShell, nsIContent* node);
6759
6760 #ifdef MOZ_XUL
6761
IsXULListBox(nsIContent * aContainer)6762 static bool IsXULListBox(nsIContent* aContainer) {
6763 return (aContainer->IsXULElement(nsGkAtoms::listbox));
6764 }
6765
MaybeGetListBoxBodyFrame(nsIContent * aContainer,nsIContent * aChild)6766 static nsListBoxBodyFrame* MaybeGetListBoxBodyFrame(nsIContent* aContainer,
6767 nsIContent* aChild) {
6768 if (!aContainer) return nullptr;
6769
6770 if (IsXULListBox(aContainer) && aChild->IsXULElement(nsGkAtoms::listitem)) {
6771 RefPtr<nsXULElement> xulElement = nsXULElement::FromContent(aContainer);
6772 nsCOMPtr<nsIBoxObject> boxObject = xulElement->GetBoxObject(IgnoreErrors());
6773 nsCOMPtr<nsPIListBoxObject> listBoxObject = do_QueryInterface(boxObject);
6774 if (listBoxObject) {
6775 return listBoxObject->GetListBoxBody(false);
6776 }
6777 }
6778
6779 return nullptr;
6780 }
6781 #endif // MOZ_XUL
6782
AddTextItemIfNeeded(nsFrameConstructorState & aState,const InsertionPoint & aInsertion,nsIContent * aPossibleTextContent,FrameConstructionItemList & aItems)6783 void nsCSSFrameConstructor::AddTextItemIfNeeded(
6784 nsFrameConstructorState& aState, const InsertionPoint& aInsertion,
6785 nsIContent* aPossibleTextContent, FrameConstructionItemList& aItems) {
6786 NS_PRECONDITION(aPossibleTextContent, "Must have node");
6787 if (!aPossibleTextContent->IsNodeOfType(nsINode::eTEXT) ||
6788 !aPossibleTextContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE) ||
6789 aPossibleTextContent->HasFlag(NODE_NEEDS_FRAME)) {
6790 // Not text, or not suppressed due to being all-whitespace (if it were being
6791 // suppressed, it would have the NS_CREATE_FRAME_IF_NON_WHITESPACE flag), or
6792 // going to be reframed anyway.
6793 return;
6794 }
6795 MOZ_ASSERT(!aPossibleTextContent->GetPrimaryFrame(),
6796 "Text node has a frame and NS_CREATE_FRAME_IF_NON_WHITESPACE");
6797 AddFrameConstructionItems(aState, aPossibleTextContent, false, aInsertion,
6798 aItems);
6799 }
6800
ReframeTextIfNeeded(nsIContent * aParentContent,nsIContent * aContent)6801 void nsCSSFrameConstructor::ReframeTextIfNeeded(nsIContent* aParentContent,
6802 nsIContent* aContent) {
6803 if (!aContent->IsNodeOfType(nsINode::eTEXT) ||
6804 !aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE) ||
6805 aContent->HasFlag(NODE_NEEDS_FRAME)) {
6806 // Not text, or not suppressed due to being all-whitespace (if it were being
6807 // suppressed, it would have the NS_CREATE_FRAME_IF_NON_WHITESPACE flag), or
6808 // going to be reframed anyway.
6809 return;
6810 }
6811 MOZ_ASSERT(!aContent->GetPrimaryFrame(),
6812 "Text node has a frame and NS_CREATE_FRAME_IF_NON_WHITESPACE");
6813 ContentInserted(aParentContent, aContent, nullptr, InsertionKind::Async);
6814 }
6815
6816 #ifdef DEBUG
CheckBitsForLazyFrameConstruction(nsIContent * aParent)6817 void nsCSSFrameConstructor::CheckBitsForLazyFrameConstruction(
6818 nsIContent* aParent) {
6819 // If we hit a node with no primary frame, or the NODE_NEEDS_FRAME bit set
6820 // we want to assert, but leaf frames that process their own children and may
6821 // ignore anonymous children (eg framesets) make this complicated. So we set
6822 // these two booleans if we encounter these situations and unset them if we
6823 // hit a node with a leaf frame.
6824 //
6825 // It's fine if one of node without primary frame is in a display:none
6826 // subtree.
6827 //
6828 // Also, it's fine if one of the nodes without primary frame is a display:
6829 // contents node.
6830 bool noPrimaryFrame = false;
6831 bool needsFrameBitSet = false;
6832 nsIContent* content = aParent;
6833 while (content && !content->HasFlag(NODE_DESCENDANTS_NEED_FRAMES)) {
6834 if (content->GetPrimaryFrame() && content->GetPrimaryFrame()->IsLeaf()) {
6835 noPrimaryFrame = needsFrameBitSet = false;
6836 }
6837 if (!noPrimaryFrame && !content->GetPrimaryFrame()) {
6838 nsStyleContext* sc = GetDisplayNoneStyleFor(content);
6839 noPrimaryFrame = !GetDisplayContentsStyleFor(content) &&
6840 (sc && !sc->IsInDisplayNoneSubtree());
6841 }
6842 if (!needsFrameBitSet && content->HasFlag(NODE_NEEDS_FRAME)) {
6843 needsFrameBitSet = true;
6844 }
6845
6846 content = content->GetFlattenedTreeParent();
6847 }
6848 if (content && content->GetPrimaryFrame() &&
6849 content->GetPrimaryFrame()->IsLeaf()) {
6850 noPrimaryFrame = needsFrameBitSet = false;
6851 }
6852 NS_ASSERTION(!noPrimaryFrame,
6853 "Ancestors of nodes with frames to be "
6854 "constructed lazily should have frames");
6855 NS_ASSERTION(!needsFrameBitSet,
6856 "Ancestors of nodes with frames to be "
6857 "constructed lazily should not have NEEDS_FRAME bit set");
6858 }
6859 #endif
6860
6861 // For inserts aChild should be valid, for appends it should be null.
6862 // Returns true if this operation can be lazy, false if not.
6863 //
6864 // FIXME(emilio, bug 1410020): This function assumes that the flattened tree
6865 // parent of all the appended children is the same, which, afaict, is not
6866 // necessarily true.
6867 //
6868 // But we disable lazy frame construction for shadow trees... We should fix
6869 // that, too.
6870 //
6871 // NOTE(emilio): The IsXULElement check is pretty unfortunate, but there's tons
6872 // of browser chrome code that rely on XBL bindings getting synchronously loaded
6873 // as soon as the elements get inserted in the DOM.
MaybeConstructLazily(Operation aOperation,nsIContent * aContainer,nsIContent * aChild)6874 bool nsCSSFrameConstructor::MaybeConstructLazily(Operation aOperation,
6875 nsIContent* aContainer,
6876 nsIContent* aChild) {
6877 if (!aContainer || aContainer->IsInNativeAnonymousSubtree() ||
6878 aContainer->IsXULElement()) {
6879 return false;
6880 }
6881
6882 if (aOperation == CONTENTINSERT) {
6883 if (aChild->IsRootOfAnonymousSubtree() || aChild->IsXULElement()) {
6884 return false;
6885 }
6886 } else { // CONTENTAPPEND
6887 NS_ASSERTION(aOperation == CONTENTAPPEND,
6888 "operation should be either insert or append");
6889 for (nsIContent* child = aChild; child; child = child->GetNextSibling()) {
6890 NS_ASSERTION(!child->IsRootOfAnonymousSubtree(),
6891 "Should be coming through the CONTENTINSERT case");
6892 if (child->IsXULElement()) {
6893 return false;
6894 }
6895 }
6896 }
6897
6898 // We can construct lazily; just need to set suitable bits in the content
6899 // tree.
6900 nsIContent* parent = aChild->GetFlattenedTreeParent();
6901 if (!parent) {
6902 // Not part of the flat tree, nothing to do.
6903 return true;
6904 }
6905
6906 // Set NODE_NEEDS_FRAME on the new nodes.
6907 if (aOperation == CONTENTINSERT) {
6908 NS_ASSERTION(!aChild->GetPrimaryFrame() ||
6909 aChild->GetPrimaryFrame()->GetContent() != aChild,
6910 // XXX the aChild->GetPrimaryFrame()->GetContent() != aChild
6911 // check is needed due to bug 135040. Remove it once that's
6912 // fixed.
6913 "setting NEEDS_FRAME on a node that already has a frame?");
6914 aChild->SetFlags(NODE_NEEDS_FRAME);
6915 } else { // CONTENTAPPEND
6916 for (nsIContent* child = aChild; child; child = child->GetNextSibling()) {
6917 NS_ASSERTION(!child->GetPrimaryFrame() ||
6918 child->GetPrimaryFrame()->GetContent() != child,
6919 // XXX the child->GetPrimaryFrame()->GetContent() != child
6920 // check is needed due to bug 135040. Remove it once that's
6921 // fixed.
6922 "setting NEEDS_FRAME on a node that already has a frame?");
6923 child->SetFlags(NODE_NEEDS_FRAME);
6924 }
6925 }
6926
6927 // Walk up the tree setting the NODE_DESCENDANTS_NEED_FRAMES bit as we go.
6928 // We need different handling for servo given the scoped restyle roots.
6929 CheckBitsForLazyFrameConstruction(parent);
6930
6931 if (RestyleManager()->IsGecko()) {
6932 #ifdef MOZ_OLD_STYLE
6933 mozilla::GeckoRestyleManager* geckoRM = RestyleManager()->AsGecko();
6934 while (parent && !parent->HasFlag(NODE_DESCENDANTS_NEED_FRAMES)) {
6935 parent->SetFlags(NODE_DESCENDANTS_NEED_FRAMES);
6936 parent = parent->GetFlattenedTreeParent();
6937 }
6938 geckoRM->PostRestyleEventForLazyConstruction();
6939 #else
6940 MOZ_CRASH("old style system disabled");
6941 #endif
6942 } else {
6943 parent->AsElement()->NoteDescendantsNeedFramesForServo();
6944 }
6945
6946 return true;
6947 }
6948
6949 #ifdef MOZ_OLD_STYLE
CreateNeededFrames(nsIContent * aContent,TreeMatchContext & aTreeMatchContext)6950 void nsCSSFrameConstructor::CreateNeededFrames(
6951 nsIContent* aContent, TreeMatchContext& aTreeMatchContext) {
6952 MOZ_ASSERT(!aContent->IsStyledByServo());
6953 NS_ASSERTION(
6954 !aContent->HasFlag(NODE_NEEDS_FRAME),
6955 "shouldn't get here with a content node that has needs frame bit set");
6956 NS_ASSERTION(aContent->HasFlag(NODE_DESCENDANTS_NEED_FRAMES),
6957 "should only get here with a content node that has descendants "
6958 "needing frames");
6959 MOZ_ASSERT(aTreeMatchContext.mAncestorFilter.HasFilter(),
6960 "The whole point of having the tree match context is optimizing "
6961 "the ancestor filter usage!");
6962
6963 aContent->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES);
6964
6965 // We could either descend first (on nodes that don't have NODE_NEEDS_FRAME
6966 // set) or issue content notifications for our kids first. In absence of
6967 // anything definitive either way we'll go with the latter.
6968
6969 // It might be better to use GetChildArray and scan it completely first and
6970 // then issue all notifications. (We have to scan it completely first because
6971 // constructing frames can set attributes, which can change the storage of
6972 // child lists).
6973
6974 // Scan the children of aContent to see what operations (if any) we need to
6975 // perform.
6976 bool inRun = false;
6977 nsIContent* firstChildInRun = nullptr;
6978 for (nsIContent* child = aContent->GetFirstChild(); child;
6979 child = child->GetNextSibling()) {
6980 if (child->HasFlag(NODE_NEEDS_FRAME)) {
6981 NS_ASSERTION(!child->GetPrimaryFrame() ||
6982 child->GetPrimaryFrame()->GetContent() != child,
6983 // XXX the child->GetPrimaryFrame()->GetContent() != child
6984 // check is needed due to bug 135040. Remove it once that's
6985 // fixed.
6986 "NEEDS_FRAME set on a node that already has a frame?");
6987 if (!inRun) {
6988 inRun = true;
6989 firstChildInRun = child;
6990 }
6991 } else {
6992 if (inRun) {
6993 inRun = false;
6994 // generate a ContentRangeInserted for [startOfRun,i)
6995 ContentRangeInserted(aContent, firstChildInRun, child, nullptr,
6996 InsertionKind::Sync, &aTreeMatchContext);
6997 }
6998 }
6999 }
7000
7001 if (inRun) {
7002 ContentAppended(aContent, firstChildInRun, InsertionKind::Sync,
7003 &aTreeMatchContext);
7004 }
7005
7006 // Now descend.
7007 FlattenedChildIterator iter(aContent);
7008 for (nsIContent* child = iter.GetNextChild(); child;
7009 child = iter.GetNextChild()) {
7010 if (child->HasFlag(NODE_DESCENDANTS_NEED_FRAMES)) {
7011 TreeMatchContext::AutoAncestorPusher insertionPointPusher(
7012 &aTreeMatchContext);
7013
7014 // Handle stuff like xbl:children.
7015 if (child->GetParent() != aContent && child->GetParent()->IsElement()) {
7016 insertionPointPusher.PushAncestorAndStyleScope(
7017 child->GetParent()->AsElement());
7018 }
7019
7020 TreeMatchContext::AutoAncestorPusher pusher(&aTreeMatchContext);
7021 pusher.PushAncestorAndStyleScope(child);
7022
7023 CreateNeededFrames(child, aTreeMatchContext);
7024 }
7025 }
7026 }
7027
CreateNeededFrames()7028 void nsCSSFrameConstructor::CreateNeededFrames() {
7029 NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
7030 "Someone forgot a script blocker");
7031
7032 Element* rootElement = mDocument->GetRootElement();
7033 NS_ASSERTION(!rootElement || !rootElement->HasFlag(NODE_NEEDS_FRAME),
7034 "root element should not have frame created lazily");
7035 if (rootElement && rootElement->HasFlag(NODE_DESCENDANTS_NEED_FRAMES)) {
7036 TreeMatchContext treeMatchContext(mDocument,
7037 TreeMatchContext::ForFrameConstruction);
7038 treeMatchContext.InitAncestors(rootElement);
7039 CreateNeededFrames(rootElement, treeMatchContext);
7040 }
7041 }
7042 #endif
7043
IssueSingleInsertNofications(nsIContent * aContainer,nsIContent * aStartChild,nsIContent * aEndChild)7044 void nsCSSFrameConstructor::IssueSingleInsertNofications(
7045 nsIContent* aContainer, nsIContent* aStartChild, nsIContent* aEndChild) {
7046 for (nsIContent* child = aStartChild; child != aEndChild;
7047 child = child->GetNextSibling()) {
7048 // listboxes suck.
7049 MOZ_ASSERT(MaybeGetListBoxBodyFrame(aContainer, child) ||
7050 (!child->GetPrimaryFrame() && !GetDisplayNoneStyleFor(child) &&
7051 !GetDisplayContentsStyleFor(child)));
7052
7053 // Call ContentRangeInserted with this node.
7054 ContentRangeInserted(aContainer, child, child->GetNextSibling(),
7055 mTempFrameTreeState, InsertionKind::Sync, nullptr);
7056 }
7057 }
7058
IsMultiple() const7059 bool nsCSSFrameConstructor::InsertionPoint::IsMultiple() const {
7060 if (!mParentFrame) {
7061 return false;
7062 }
7063
7064 // Fieldset frames have multiple normal flow child frame lists so handle it
7065 // the same as if it had multiple content insertion points.
7066 if (mParentFrame->IsFieldSetFrame()) {
7067 return true;
7068 }
7069
7070 // A details frame moves the first summary frame to be its first child, so we
7071 // treat it as if it has multiple content insertion points.
7072 if (mParentFrame->IsDetailsFrame()) {
7073 return true;
7074 }
7075
7076 return false;
7077 }
7078
7079 nsCSSFrameConstructor::InsertionPoint
GetRangeInsertionPoint(nsIContent * aContainer,nsIContent * aStartChild,nsIContent * aEndChild)7080 nsCSSFrameConstructor::GetRangeInsertionPoint(nsIContent* aContainer,
7081 nsIContent* aStartChild,
7082 nsIContent* aEndChild) {
7083 MOZ_ASSERT(aStartChild);
7084
7085 // If the children of the container may be distributed to different insertion
7086 // points, insert them separately and bail out, letting ContentInserted handle
7087 // the mess.
7088 if (aContainer->GetShadowRoot() || aContainer->GetXBLBinding()) {
7089 IssueSingleInsertNofications(aContainer, aStartChild, aEndChild);
7090 return {};
7091 }
7092
7093 #ifdef DEBUG
7094 {
7095 nsIContent* expectedParent = aStartChild->GetFlattenedTreeParent();
7096 for (nsIContent* child = aStartChild->GetNextSibling(); child;
7097 child = child->GetNextSibling()) {
7098 MOZ_ASSERT(child->GetFlattenedTreeParent() == expectedParent);
7099 }
7100 }
7101 #endif
7102
7103 // Now the flattened tree parent of all the siblings is the same, just use the
7104 // same insertion point and take the fast path, unless it's a multiple
7105 // insertion point.
7106 InsertionPoint ip = GetInsertionPoint(aStartChild);
7107 if (ip.IsMultiple()) {
7108 IssueSingleInsertNofications(aContainer, aStartChild, aEndChild);
7109 return {};
7110 }
7111
7112 return ip;
7113 }
7114
MaybeRecreateForFrameset(nsIFrame * aParentFrame,nsIContent * aStartChild,nsIContent * aEndChild)7115 bool nsCSSFrameConstructor::MaybeRecreateForFrameset(nsIFrame* aParentFrame,
7116 nsIContent* aStartChild,
7117 nsIContent* aEndChild) {
7118 if (aParentFrame->IsFrameSetFrame()) {
7119 // Check whether we have any kids we care about.
7120 for (nsIContent* cur = aStartChild; cur != aEndChild;
7121 cur = cur->GetNextSibling()) {
7122 if (IsSpecialFramesetChild(cur)) {
7123 // Just reframe the parent, since framesets are weird like that.
7124 RecreateFramesForContent(aParentFrame->GetContent(),
7125 InsertionKind::Async);
7126 return true;
7127 }
7128 }
7129 }
7130 return false;
7131 }
7132
LazilyStyleNewChildRange(nsIContent * aStartChild,nsIContent * aEndChild)7133 void nsCSSFrameConstructor::LazilyStyleNewChildRange(nsIContent* aStartChild,
7134 nsIContent* aEndChild) {
7135 for (nsIContent* child = aStartChild; child != aEndChild;
7136 child = child->GetNextSibling()) {
7137 if (child->IsElement()) {
7138 child->AsElement()->NoteDirtyForServo();
7139 }
7140 }
7141 }
7142
7143 #ifdef DEBUG
IsFlattenedTreeChild(nsIContent * aParent,nsIContent * aChild)7144 static bool IsFlattenedTreeChild(nsIContent* aParent, nsIContent* aChild) {
7145 FlattenedChildIterator iter(aParent);
7146 for (nsIContent* node = iter.GetNextChild(); node;
7147 node = iter.GetNextChild()) {
7148 if (node == aChild) {
7149 return true;
7150 }
7151 }
7152 return false;
7153 }
7154 #endif
7155
StyleNewChildRange(nsIContent * aStartChild,nsIContent * aEndChild)7156 void nsCSSFrameConstructor::StyleNewChildRange(nsIContent* aStartChild,
7157 nsIContent* aEndChild) {
7158 ServoStyleSet* styleSet = mPresShell->StyleSet()->AsServo();
7159
7160 for (nsIContent* child = aStartChild; child != aEndChild;
7161 child = child->GetNextSibling()) {
7162 if (child->IsElement() && !child->AsElement()->HasServoData()) {
7163 Element* parent = child->AsElement()->GetFlattenedTreeParentElement();
7164 // NB: Parent may be null if the content is appended to a shadow root, and
7165 // isn't assigned to any insertion point.
7166 if (MOZ_LIKELY(parent) && parent->HasServoData()) {
7167 MOZ_ASSERT(
7168 IsFlattenedTreeChild(parent, child),
7169 "GetFlattenedTreeParent and ChildIterator don't agree, fix this!");
7170 styleSet->StyleNewSubtree(child->AsElement());
7171 }
7172 }
7173 }
7174 }
7175
ContentAppended(nsIContent * aContainer,nsIContent * aFirstNewContent,InsertionKind aInsertionKind,TreeMatchContext * aProvidedTreeMatchContext)7176 void nsCSSFrameConstructor::ContentAppended(
7177 nsIContent* aContainer, nsIContent* aFirstNewContent,
7178 InsertionKind aInsertionKind, TreeMatchContext* aProvidedTreeMatchContext) {
7179 MOZ_ASSERT(!aProvidedTreeMatchContext ||
7180 aInsertionKind == InsertionKind::Sync);
7181 MOZ_ASSERT(aInsertionKind == InsertionKind::Sync ||
7182 !RestyleManager()->IsInStyleRefresh());
7183
7184 AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
7185
7186 #ifdef DEBUG
7187 if (gNoisyContentUpdates) {
7188 printf(
7189 "nsCSSFrameConstructor::ContentAppended container=%p "
7190 "first-child=%p lazy=%d\n",
7191 static_cast<void*>(aContainer), aFirstNewContent,
7192 aInsertionKind == InsertionKind::Async);
7193 if (gReallyNoisyContentUpdates && aContainer) {
7194 aContainer->List(stdout, 0);
7195 }
7196 }
7197 #endif
7198
7199 #ifdef DEBUG
7200 for (nsIContent* child = aFirstNewContent; child;
7201 child = child->GetNextSibling()) {
7202 // XXX the GetContent() != child check is needed due to bug 135040.
7203 // Remove it once that's fixed.
7204 NS_ASSERTION(
7205 !child->GetPrimaryFrame() ||
7206 child->GetPrimaryFrame()->GetContent() != child,
7207 "asked to construct a frame for a node that already has a frame");
7208 }
7209 #endif
7210
7211 #ifdef MOZ_XUL
7212 if (aContainer) {
7213 int32_t namespaceID;
7214 nsAtom* tag =
7215 mDocument->BindingManager()->ResolveTag(aContainer, &namespaceID);
7216
7217 // Just ignore tree tags, anyway we don't create any frames for them.
7218 if (tag == nsGkAtoms::treechildren || tag == nsGkAtoms::treeitem ||
7219 tag == nsGkAtoms::treerow)
7220 return;
7221 }
7222 #endif // MOZ_XUL
7223
7224 // See comment in ContentRangeInserted for why this is necessary.
7225 if (!GetContentInsertionFrameFor(aContainer) &&
7226 !aContainer->IsActiveChildrenElement()) {
7227 // We're punting on frame construction because there's no container frame.
7228 // The Servo-backed style system handles this case like the lazy frame
7229 // construction case, except when we're already constructing frames, in
7230 // which case we shouldn't need to do anything else.
7231 if (aContainer->IsStyledByServo() &&
7232 aInsertionKind == InsertionKind::Async) {
7233 LazilyStyleNewChildRange(aFirstNewContent, nullptr);
7234 }
7235 return;
7236 }
7237
7238 if (aInsertionKind == InsertionKind::Async &&
7239 MaybeConstructLazily(CONTENTAPPEND, aContainer, aFirstNewContent)) {
7240 if (aContainer->IsStyledByServo()) {
7241 LazilyStyleNewChildRange(aFirstNewContent, nullptr);
7242 }
7243 return;
7244 }
7245
7246 // We couldn't construct lazily. Make Servo eagerly traverse the new content
7247 // if needed (when aInsertionKind == InsertionKind::Sync, we know that the
7248 // styles are up-to-date already).
7249 if (aInsertionKind == InsertionKind::Async && aContainer->IsStyledByServo()) {
7250 StyleNewChildRange(aFirstNewContent, nullptr);
7251 }
7252
7253 LAYOUT_PHASE_TEMP_EXIT();
7254 InsertionPoint insertion =
7255 GetRangeInsertionPoint(aContainer, aFirstNewContent, nullptr);
7256 nsContainerFrame*& parentFrame = insertion.mParentFrame;
7257 LAYOUT_PHASE_TEMP_REENTER();
7258 if (!parentFrame) {
7259 return;
7260 }
7261
7262 LAYOUT_PHASE_TEMP_EXIT();
7263 if (MaybeRecreateForFrameset(parentFrame, aFirstNewContent, nullptr)) {
7264 LAYOUT_PHASE_TEMP_REENTER();
7265 return;
7266 }
7267 LAYOUT_PHASE_TEMP_REENTER();
7268
7269 if (parentFrame->IsLeaf()) {
7270 // Nothing to do here; we shouldn't be constructing kids of leaves
7271 // Clear lazy bits so we don't try to construct again.
7272 ClearLazyBits(aFirstNewContent, nullptr);
7273 return;
7274 }
7275
7276 if (parentFrame->IsFrameOfType(nsIFrame::eMathML)) {
7277 LAYOUT_PHASE_TEMP_EXIT();
7278 RecreateFramesForContent(parentFrame->GetContent(), InsertionKind::Async);
7279 LAYOUT_PHASE_TEMP_REENTER();
7280 return;
7281 }
7282
7283 #ifdef DEBUG
7284 if (gNoisyContentUpdates && IsFramePartOfIBSplit(parentFrame)) {
7285 printf("nsCSSFrameConstructor::ContentAppended: parentFrame=");
7286 nsFrame::ListTag(stdout, parentFrame);
7287 printf(" is ib-split\n");
7288 }
7289 #endif
7290
7291 // We should never get here with fieldsets or details, since they have
7292 // multiple insertion points.
7293 MOZ_ASSERT(!parentFrame->IsFieldSetFrame() && !parentFrame->IsDetailsFrame(),
7294 "Parent frame should not be fieldset or details!");
7295
7296 // Deal with possible :after generated content on the parent, or display:
7297 // contents.
7298 nsIFrame* nextSibling = nullptr;
7299 if (GetDisplayContentsStyleFor(insertion.mContainer) ||
7300 nsLayoutUtils::GetAfterFrame(insertion.mContainer)) {
7301 FlattenedChildIterator iter(insertion.mContainer);
7302 iter.Seek(insertion.mContainer->GetLastChild());
7303 StyleDisplay unused = UNSET_DISPLAY;
7304 nextSibling = FindNextSibling(iter, unused);
7305 }
7306
7307 if (nextSibling) {
7308 parentFrame = nextSibling->GetParent()->GetContentInsertionFrame();
7309 } else {
7310 parentFrame = ::ContinuationToAppendTo(parentFrame);
7311 }
7312
7313 nsContainerFrame* containingBlock = GetFloatContainingBlock(parentFrame);
7314
7315 // See if the containing block has :first-letter style applied.
7316 const bool haveFirstLetterStyle =
7317 containingBlock && HasFirstLetterStyle(containingBlock);
7318
7319 const bool haveFirstLineStyle =
7320 containingBlock &&
7321 ShouldHaveFirstLineStyle(containingBlock->GetContent(),
7322 containingBlock->StyleContext());
7323
7324 if (haveFirstLetterStyle) {
7325 AutoWeakFrame wf(nextSibling);
7326
7327 // Before we get going, remove the current letter frames
7328 RemoveLetterFrames(mPresShell, containingBlock);
7329
7330 // Reget nextSibling, since we may have killed it.
7331 //
7332 // FIXME(emilio): This kinda sucks! :(
7333 if (nextSibling && !wf) {
7334 FlattenedChildIterator iter(insertion.mContainer);
7335 iter.Seek(insertion.mContainer->GetLastChild());
7336 StyleDisplay unused = UNSET_DISPLAY;
7337 nextSibling = FindNextSibling(iter, unused);
7338 if (nextSibling) {
7339 parentFrame = nextSibling->GetParent()->GetContentInsertionFrame();
7340 containingBlock = GetFloatContainingBlock(parentFrame);
7341 }
7342 }
7343 }
7344
7345 // Create some new frames
7346 //
7347 // We use the provided tree match context, or create a new one on the fly
7348 // otherwise.
7349 Maybe<TreeMatchContext> matchContext;
7350 if (!aProvidedTreeMatchContext && !aContainer->IsStyledByServo()) {
7351 // We use GetParentElementCrossingShadowRoot to handle the case where
7352 // aContainer is a ShadowRoot.
7353 matchContext.emplace(mDocument, TreeMatchContext::ForFrameConstruction);
7354 matchContext->InitAncestors(
7355 aFirstNewContent->GetParentElementCrossingShadowRoot());
7356 }
7357 nsFrameConstructorState state(
7358 mPresShell, matchContext.ptrOr(aProvidedTreeMatchContext),
7359 GetAbsoluteContainingBlock(parentFrame, FIXED_POS),
7360 GetAbsoluteContainingBlock(parentFrame, ABS_POS), containingBlock);
7361
7362 LayoutFrameType frameType = parentFrame->Type();
7363
7364 FlattenedChildIterator iter(aContainer);
7365 const bool haveNoXBLChildren = !iter.XBLInvolved() || !iter.GetNextChild();
7366
7367 AutoFrameConstructionItemList items(this);
7368 if (aFirstNewContent->GetPreviousSibling() &&
7369 GetParentType(frameType) == eTypeBlock && haveNoXBLChildren) {
7370 // If there's a text node in the normal content list just before the new
7371 // items, and it has no frame, make a frame construction item for it. If it
7372 // doesn't need a frame, ConstructFramesFromItemList below won't give it
7373 // one. No need to do all this if our parent type is not block, though,
7374 // since WipeContainingBlock already handles that situation.
7375 //
7376 // Because we're appending, we don't need to worry about any text
7377 // after the appended content; there can only be XBL anonymous content
7378 // (text in an XBL binding is not suppressed) or generated content
7379 // (and bare text nodes are not generated). Native anonymous content
7380 // generated by frames never participates in inline layout.
7381 AddTextItemIfNeeded(state, insertion,
7382 aFirstNewContent->GetPreviousSibling(), items);
7383 }
7384 for (nsIContent* child = aFirstNewContent; child;
7385 child = child->GetNextSibling()) {
7386 AddFrameConstructionItems(state, child, false, insertion, items);
7387 }
7388
7389 nsIFrame* prevSibling = ::FindAppendPrevSibling(parentFrame, nextSibling);
7390
7391 // Perform special check for diddling around with the frames in
7392 // a ib-split inline frame.
7393 // If we're appending before :after content, then we're not really
7394 // appending, so let WipeContainingBlock know that.
7395 LAYOUT_PHASE_TEMP_EXIT();
7396 if (WipeContainingBlock(state, containingBlock, parentFrame, items, true,
7397 prevSibling)) {
7398 LAYOUT_PHASE_TEMP_REENTER();
7399 return;
7400 }
7401 LAYOUT_PHASE_TEMP_REENTER();
7402
7403 // If the parent is a block frame, and we're not in a special case
7404 // where frames can be moved around, determine if the list is for the
7405 // start or end of the block.
7406 if (nsLayoutUtils::GetAsBlock(parentFrame) && !haveFirstLetterStyle &&
7407 !haveFirstLineStyle && !IsFramePartOfIBSplit(parentFrame)) {
7408 items.SetLineBoundaryAtStart(!prevSibling ||
7409 !prevSibling->IsInlineOutside() ||
7410 prevSibling->IsBrFrame());
7411 // :after content can't be <br> so no need to check it
7412 //
7413 // FIXME(emilio): A display: contents sibling could! Write a test-case and
7414 // fix.
7415 items.SetLineBoundaryAtEnd(!nextSibling || !nextSibling->IsInlineOutside());
7416 }
7417 // To suppress whitespace-only text frames, we have to verify that
7418 // our container's DOM child list matches its flattened tree child list.
7419 items.SetParentHasNoXBLChildren(haveNoXBLChildren);
7420
7421 nsFrameItems frameItems;
7422 ConstructFramesFromItemList(state, items, parentFrame,
7423 ParentIsWrapperAnonBox(parentFrame), frameItems);
7424
7425 for (nsIContent* child = aFirstNewContent; child;
7426 child = child->GetNextSibling()) {
7427 // Invalidate now instead of before the WipeContainingBlock call, just in
7428 // case we do wipe; in that case we don't need to do this walk at all.
7429 // XXXbz does that matter? Would it make more sense to save some virtual
7430 // GetChildAt_Deprecated calls instead and do this during construction of
7431 // our FrameConstructionItemList?
7432 InvalidateCanvasIfNeeded(mPresShell, child);
7433 }
7434
7435 // If the container is a table and a caption was appended, it needs to be put
7436 // in the table wrapper frame's additional child list.
7437 nsFrameItems captionItems;
7438 if (LayoutFrameType::Table == frameType) {
7439 // Pull out the captions. Note that we don't want to do that as we go,
7440 // because processing a single caption can add a whole bunch of things to
7441 // the frame items due to pseudoframe processing. So we'd have to pull
7442 // captions from a list anyway; might as well do that here.
7443 // XXXbz this is no longer true; we could pull captions directly out of the
7444 // FrameConstructionItemList now.
7445 PullOutCaptionFrames(frameItems, captionItems);
7446 }
7447
7448 if (haveFirstLineStyle && parentFrame == containingBlock) {
7449 // It's possible that some of the new frames go into a
7450 // first-line frame. Look at them and see...
7451 AppendFirstLineFrames(state, containingBlock->GetContent(), containingBlock,
7452 frameItems);
7453 // That moved things into line frames as needed, reparenting their
7454 // styles. Nothing else needs to be done.
7455 } else if (parentFrame->StyleContext()->HasPseudoElementData()) {
7456 // parentFrame might be inside a ::first-line frame. Check whether it is,
7457 // and if so fix up our styles.
7458 CheckForFirstLineInsertion(parentFrame, frameItems);
7459 CheckForFirstLineInsertion(parentFrame, captionItems);
7460 }
7461
7462 // Notify the parent frame passing it the list of new frames
7463 // Append the flowed frames to the principal child list; captions
7464 // need special treatment
7465 if (captionItems.NotEmpty()) { // append the caption to the table wrapper
7466 NS_ASSERTION(LayoutFrameType::Table == frameType, "how did that happen?");
7467 nsContainerFrame* outerTable = parentFrame->GetParent();
7468 AppendFrames(outerTable, nsIFrame::kCaptionList, captionItems);
7469 }
7470
7471 if (frameItems.NotEmpty()) { // append the in-flow kids
7472 AppendFramesToParent(state, parentFrame, frameItems, prevSibling);
7473 }
7474
7475 // Recover first-letter frames
7476 if (haveFirstLetterStyle) {
7477 RecoverLetterFrames(containingBlock);
7478 }
7479
7480 #ifdef DEBUG
7481 if (gReallyNoisyContentUpdates) {
7482 printf("nsCSSFrameConstructor::ContentAppended: resulting frame model:\n");
7483 parentFrame->List(stdout, 0);
7484 }
7485 #endif
7486
7487 #ifdef ACCESSIBILITY
7488 if (nsAccessibilityService* accService = nsIPresShell::AccService()) {
7489 accService->ContentRangeInserted(mPresShell, aContainer, aFirstNewContent,
7490 nullptr);
7491 }
7492 #endif
7493 }
7494
7495 #ifdef MOZ_XUL
7496
7497 enum content_operation { CONTENT_INSERTED, CONTENT_REMOVED };
7498
7499 // Helper function to lookup the listbox body frame and send a notification
7500 // for insertion or removal of content
NotifyListBoxBody(nsPresContext * aPresContext,nsIContent * aContainer,nsIContent * aChild,nsIContent * aOldNextSibling,nsIFrame * aChildFrame,content_operation aOperation)7501 static bool NotifyListBoxBody(nsPresContext* aPresContext,
7502 nsIContent* aContainer, nsIContent* aChild,
7503 // Only used for the removed notification
7504 nsIContent* aOldNextSibling,
7505 nsIFrame* aChildFrame,
7506 content_operation aOperation) {
7507 nsListBoxBodyFrame* listBoxBodyFrame =
7508 MaybeGetListBoxBodyFrame(aContainer, aChild);
7509 if (listBoxBodyFrame) {
7510 if (aOperation == CONTENT_REMOVED) {
7511 // Except if we have an aChildFrame and its parent is not the right
7512 // thing, then we don't do this. Pseudo frames are so much fun....
7513 if (!aChildFrame || aChildFrame->GetParent() == listBoxBodyFrame) {
7514 listBoxBodyFrame->OnContentRemoved(aPresContext, aContainer,
7515 aChildFrame, aOldNextSibling);
7516 return true;
7517 }
7518 } else {
7519 listBoxBodyFrame->OnContentInserted(aChild);
7520 return true;
7521 }
7522 }
7523
7524 return false;
7525 }
7526 #endif // MOZ_XUL
7527
ContentInserted(nsIContent * aContainer,nsIContent * aChild,nsILayoutHistoryState * aFrameState,InsertionKind aInsertionKind)7528 void nsCSSFrameConstructor::ContentInserted(nsIContent* aContainer,
7529 nsIContent* aChild,
7530 nsILayoutHistoryState* aFrameState,
7531 InsertionKind aInsertionKind) {
7532 ContentRangeInserted(aContainer, aChild, aChild->GetNextSibling(),
7533 aFrameState, aInsertionKind);
7534 }
7535
7536 // ContentRangeInserted handles creating frames for a range of nodes that
7537 // aren't at the end of their childlist. ContentRangeInserted isn't a real
7538 // content notification, but rather it handles regular ContentInserted calls
7539 // for a single node as well as the lazy construction of frames for a range of
7540 // nodes when called from CreateNeededFrames. For a range of nodes to be
7541 // suitable to have its frames constructed all at once they must meet the same
7542 // conditions that ContentAppended imposes (GetRangeInsertionPoint checks
7543 // these), plus more. Namely when finding the insertion prevsibling we must not
7544 // need to consult something specific to any one node in the range, so that the
7545 // insertion prevsibling would be the same for each node in the range. So we
7546 // pass the first node in the range to GetInsertionPrevSibling, and if
7547 // IsValidSibling (the only place GetInsertionPrevSibling might look at the
7548 // passed in node itself) needs to resolve style on the node we record this and
7549 // return that this range needs to be split up and inserted separately. Table
7550 // captions need extra attention as we need to determine where to insert them
7551 // in the caption list, while skipping any nodes in the range being inserted
7552 // (because when we treat the caption frames the other nodes have had their
7553 // frames constructed but not yet inserted into the frame tree).
ContentRangeInserted(nsIContent * aContainer,nsIContent * aStartChild,nsIContent * aEndChild,nsILayoutHistoryState * aFrameState,InsertionKind aInsertionKind,TreeMatchContext * aProvidedTreeMatchContext)7554 void nsCSSFrameConstructor::ContentRangeInserted(
7555 nsIContent* aContainer, nsIContent* aStartChild, nsIContent* aEndChild,
7556 nsILayoutHistoryState* aFrameState, InsertionKind aInsertionKind,
7557 TreeMatchContext* aProvidedTreeMatchContext) {
7558 MOZ_ASSERT(!aProvidedTreeMatchContext ||
7559 aInsertionKind == InsertionKind::Sync);
7560 MOZ_ASSERT(aInsertionKind == InsertionKind::Sync ||
7561 !RestyleManager()->IsInStyleRefresh());
7562
7563 AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
7564
7565 NS_PRECONDITION(aStartChild, "must always pass a child");
7566
7567 #ifdef DEBUG
7568 if (gNoisyContentUpdates) {
7569 printf(
7570 "nsCSSFrameConstructor::ContentRangeInserted container=%p "
7571 "start-child=%p end-child=%p lazy=%d\n",
7572 static_cast<void*>(aContainer), static_cast<void*>(aStartChild),
7573 static_cast<void*>(aEndChild), aInsertionKind == InsertionKind::Async);
7574 if (gReallyNoisyContentUpdates) {
7575 if (aContainer) {
7576 aContainer->List(stdout, 0);
7577 } else {
7578 aStartChild->List(stdout, 0);
7579 }
7580 }
7581 }
7582
7583 for (nsIContent* child = aStartChild; child != aEndChild;
7584 child = child->GetNextSibling()) {
7585 // XXX the GetContent() != child check is needed due to bug 135040.
7586 // Remove it once that's fixed.
7587 NS_ASSERTION(
7588 !child->GetPrimaryFrame() ||
7589 child->GetPrimaryFrame()->GetContent() != child,
7590 "asked to construct a frame for a node that already has a frame");
7591 }
7592 #endif
7593
7594 auto styleNewChildRangeEagerly = [this, aInsertionKind, aContainer,
7595 aStartChild, aEndChild]() {
7596 // When aInsertionKind == InsertionKind::Sync, we know that the
7597 // styles are up-to-date already.
7598 if (aInsertionKind == InsertionKind::Async &&
7599 aContainer->IsStyledByServo()) {
7600 StyleNewChildRange(aStartChild, aEndChild);
7601 }
7602 };
7603
7604 bool isSingleInsert = (aStartChild->GetNextSibling() == aEndChild);
7605 NS_ASSERTION(isSingleInsert || aInsertionKind == InsertionKind::Sync,
7606 "range insert shouldn't be lazy");
7607 NS_ASSERTION(isSingleInsert || aEndChild,
7608 "range should not include all nodes after aStartChild");
7609
7610 #ifdef MOZ_XUL
7611 if (aContainer && IsXULListBox(aContainer)) {
7612 // For XUL list box, we need to style the new children eagerly.
7613 styleNewChildRangeEagerly();
7614 if (isSingleInsert) {
7615 if (NotifyListBoxBody(mPresShell->GetPresContext(), aContainer,
7616 // The insert case in NotifyListBoxBody
7617 // doesn't use "old next sibling".
7618 aStartChild, nullptr, nullptr, CONTENT_INSERTED)) {
7619 return;
7620 }
7621 } else {
7622 // We don't handle a range insert to a listbox parent, issue single
7623 // ContertInserted calls for each node inserted.
7624 LAYOUT_PHASE_TEMP_EXIT();
7625 IssueSingleInsertNofications(aContainer, aStartChild, aEndChild);
7626 LAYOUT_PHASE_TEMP_REENTER();
7627 return;
7628 }
7629 }
7630 #endif // MOZ_XUL
7631
7632 // If we have a null parent, then this must be the document element being
7633 // inserted, or some other child of the document in the DOM (might be a PI,
7634 // say).
7635 if (!aContainer) {
7636 NS_ASSERTION(isSingleInsert,
7637 "root node insertion should be a single insertion");
7638 Element* docElement = mDocument->GetRootElement();
7639
7640 if (aStartChild != docElement) {
7641 // Not the root element; just bail out
7642 return;
7643 }
7644
7645 NS_PRECONDITION(!mRootElementFrame, "root element frame already created");
7646
7647 // Create frames for the document element and its child elements
7648 if (ConstructDocElementFrame(docElement, aFrameState)) {
7649 InvalidateCanvasIfNeeded(mPresShell, aStartChild);
7650 #ifdef DEBUG
7651 if (gReallyNoisyContentUpdates) {
7652 printf(
7653 "nsCSSFrameConstructor::ContentRangeInserted: resulting frame "
7654 "model:\n");
7655 mRootElementFrame->List(stdout, 0);
7656 }
7657 #endif
7658 }
7659
7660 if (aFrameState) {
7661 // Restore frame state for the root scroll frame if there is one
7662 if (nsIFrame* rootScrollFrame = mPresShell->GetRootScrollFrame()) {
7663 RestoreFrameStateFor(rootScrollFrame, aFrameState);
7664 }
7665 }
7666
7667 #ifdef ACCESSIBILITY
7668 if (nsAccessibilityService* accService = nsIPresShell::AccService()) {
7669 accService->ContentRangeInserted(mPresShell, aContainer, aStartChild,
7670 aEndChild);
7671 }
7672 #endif
7673
7674 return;
7675 }
7676
7677 nsContainerFrame* parentFrame = GetContentInsertionFrameFor(aContainer);
7678 // The xbl:children element won't have a frame, but default content can have
7679 // the children as a parent. While its uncommon to change the structure of the
7680 // default content itself, a label, for example, can be reframed by having its
7681 // value attribute set or removed.
7682 if (!parentFrame &&
7683 !(aContainer->IsActiveChildrenElement() || aContainer->IsShadowRoot())) {
7684 // We're punting on frame construction because there's no container frame.
7685 // The Servo-backed style system handles this case like the lazy frame
7686 // construction case, except when we're already constructing frames, in
7687 // which case we shouldn't need to do anything else.
7688 if (aContainer->IsStyledByServo() &&
7689 aInsertionKind == InsertionKind::Async) {
7690 LazilyStyleNewChildRange(aStartChild, aEndChild);
7691 }
7692 return;
7693 }
7694
7695 MOZ_ASSERT_IF(aContainer->IsShadowRoot(), !parentFrame);
7696
7697 // Otherwise, we've got parent content. Find its frame.
7698 NS_ASSERTION(!parentFrame || parentFrame->GetContent() == aContainer ||
7699 GetDisplayContentsStyleFor(aContainer),
7700 "New XBL code is possibly wrong!");
7701
7702 if (aInsertionKind == InsertionKind::Async &&
7703 MaybeConstructLazily(CONTENTINSERT, aContainer, aStartChild)) {
7704 if (aContainer->IsStyledByServo()) {
7705 LazilyStyleNewChildRange(aStartChild, aEndChild);
7706 }
7707 return;
7708 }
7709
7710 // We couldn't construct lazily. Make Servo eagerly traverse the new content
7711 // if needed.
7712 styleNewChildRangeEagerly();
7713
7714 InsertionPoint insertion;
7715 if (isSingleInsert) {
7716 // See if we have an XBL insertion point. If so, then that's our
7717 // real parent frame; if not, then the frame hasn't been built yet
7718 // and we just bail.
7719 insertion = GetInsertionPoint(aStartChild);
7720 } else {
7721 // Get our insertion point. If we need to issue single ContentInserted's
7722 // GetRangeInsertionPoint will take care of that for us.
7723 LAYOUT_PHASE_TEMP_EXIT();
7724 insertion = GetRangeInsertionPoint(aContainer, aStartChild, aEndChild);
7725 LAYOUT_PHASE_TEMP_REENTER();
7726 }
7727
7728 if (!insertion.mParentFrame) {
7729 return;
7730 }
7731
7732 bool isAppend, isRangeInsertSafe;
7733 nsIFrame* prevSibling = GetInsertionPrevSibling(
7734 &insertion, aStartChild, &isAppend, &isRangeInsertSafe);
7735
7736 // check if range insert is safe
7737 if (!isSingleInsert && !isRangeInsertSafe) {
7738 // must fall back to a single ContertInserted for each child in the range
7739 LAYOUT_PHASE_TEMP_EXIT();
7740 IssueSingleInsertNofications(aContainer, aStartChild, aEndChild);
7741 LAYOUT_PHASE_TEMP_REENTER();
7742 return;
7743 }
7744
7745 LayoutFrameType frameType = insertion.mParentFrame->Type();
7746 LAYOUT_PHASE_TEMP_EXIT();
7747 if (MaybeRecreateForFrameset(insertion.mParentFrame, aStartChild,
7748 aEndChild)) {
7749 LAYOUT_PHASE_TEMP_REENTER();
7750 return;
7751 }
7752 LAYOUT_PHASE_TEMP_REENTER();
7753
7754 // We should only get here with fieldsets when doing a single insert, because
7755 // fieldsets have multiple insertion points.
7756 NS_ASSERTION(isSingleInsert || frameType != LayoutFrameType::FieldSet,
7757 "Unexpected parent");
7758 if (IsFrameForFieldSet(insertion.mParentFrame) &&
7759 aStartChild->NodeInfo()->NameAtom() == nsGkAtoms::legend) {
7760 // Just reframe the parent, since figuring out whether this
7761 // should be the new legend and then handling it is too complex.
7762 // We could do a little better here --- check if the fieldset already
7763 // has a legend which occurs earlier in its child list than this node,
7764 // and if so, proceed. But we'd have to extend nsFieldSetFrame
7765 // to locate this legend in the inserted frames and extract it.
7766 LAYOUT_PHASE_TEMP_EXIT();
7767 RecreateFramesForContent(insertion.mParentFrame->GetContent(),
7768 InsertionKind::Async);
7769 LAYOUT_PHASE_TEMP_REENTER();
7770 return;
7771 }
7772
7773 // We should only get here with details when doing a single insertion because
7774 // we treat details frame as if it has multiple insertion points.
7775 MOZ_ASSERT(isSingleInsert || frameType != LayoutFrameType::Details);
7776 if (frameType == LayoutFrameType::Details) {
7777 // When inserting an element into <details>, just reframe the details frame
7778 // and let it figure out where the element should be laid out. It might seem
7779 // expensive to recreate the entire details frame, but it's the simplest way
7780 // to handle the insertion.
7781 LAYOUT_PHASE_TEMP_EXIT();
7782 RecreateFramesForContent(insertion.mParentFrame->GetContent(),
7783 InsertionKind::Async);
7784 LAYOUT_PHASE_TEMP_REENTER();
7785 return;
7786 }
7787
7788 // Don't construct kids of leaves
7789 if (insertion.mParentFrame->IsLeaf()) {
7790 // Clear lazy bits so we don't try to construct again.
7791 ClearLazyBits(aStartChild, aEndChild);
7792 return;
7793 }
7794
7795 // FIXME(emilio): This looks terribly inefficient if you insert elements deep
7796 // in a MathML subtree.
7797 if (insertion.mParentFrame->IsFrameOfType(nsIFrame::eMathML)) {
7798 LAYOUT_PHASE_TEMP_EXIT();
7799 RecreateFramesForContent(insertion.mParentFrame->GetContent(),
7800 InsertionKind::Async);
7801 LAYOUT_PHASE_TEMP_REENTER();
7802 return;
7803 }
7804
7805 Maybe<TreeMatchContext> matchContext;
7806 if (!aProvidedTreeMatchContext && !aContainer->IsStyledByServo()) {
7807 // We use GetParentElementCrossingShadowRoot to handle the case where
7808 // aContainer is a ShadowRoot.
7809 matchContext.emplace(mDocument, TreeMatchContext::ForFrameConstruction);
7810 matchContext->InitAncestors(
7811 aStartChild->GetParentElementCrossingShadowRoot());
7812 }
7813 nsFrameConstructorState state(
7814 mPresShell, matchContext.ptrOr(aProvidedTreeMatchContext),
7815 GetAbsoluteContainingBlock(insertion.mParentFrame, FIXED_POS),
7816 GetAbsoluteContainingBlock(insertion.mParentFrame, ABS_POS),
7817 GetFloatContainingBlock(insertion.mParentFrame), do_AddRef(aFrameState));
7818
7819 // Recover state for the containing block - we need to know if
7820 // it has :first-letter or :first-line style applied to it. The
7821 // reason we care is that the internal structure in these cases
7822 // is not the normal structure and requires custom updating
7823 // logic.
7824 nsContainerFrame* containingBlock = state.mFloatedItems.containingBlock;
7825 bool haveFirstLetterStyle = false;
7826 bool haveFirstLineStyle = false;
7827
7828 // In order to shave off some cycles, we only dig up the
7829 // containing block haveFirst* flags if the parent frame where
7830 // the insertion/append is occurring is an inline or block
7831 // container. For other types of containers this isn't relevant.
7832 StyleDisplay parentDisplay = insertion.mParentFrame->GetDisplay();
7833
7834 // Examine the insertion.mParentFrame where the insertion is taking
7835 // place. If it's a certain kind of container then some special
7836 // processing is done.
7837 if ((StyleDisplay::Block == parentDisplay) ||
7838 (StyleDisplay::ListItem == parentDisplay) ||
7839 (StyleDisplay::Inline == parentDisplay) ||
7840 (StyleDisplay::InlineBlock == parentDisplay)) {
7841 // Recover the special style flags for the containing block
7842 if (containingBlock) {
7843 haveFirstLetterStyle = HasFirstLetterStyle(containingBlock);
7844 haveFirstLineStyle = ShouldHaveFirstLineStyle(
7845 containingBlock->GetContent(), containingBlock->StyleContext());
7846 }
7847
7848 if (haveFirstLetterStyle) {
7849 // If our current insertion.mParentFrame is a Letter frame, use its parent
7850 // as our new parent hint
7851 if (insertion.mParentFrame->IsLetterFrame()) {
7852 // If insertion.mParentFrame is out of flow, then we actually want the
7853 // parent of the placeholder frame.
7854 if (insertion.mParentFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
7855 nsPlaceholderFrame* placeholderFrame =
7856 insertion.mParentFrame->GetPlaceholderFrame();
7857 NS_ASSERTION(placeholderFrame, "No placeholder for out-of-flow?");
7858 insertion.mParentFrame = placeholderFrame->GetParent();
7859 } else {
7860 insertion.mParentFrame = insertion.mParentFrame->GetParent();
7861 }
7862 }
7863
7864 // Remove the old letter frames before doing the insertion
7865 RemoveLetterFrames(mPresShell, state.mFloatedItems.containingBlock);
7866
7867 // Removing the letterframes messes around with the frame tree, removing
7868 // and creating frames. We need to reget our prevsibling, parent frame,
7869 // etc.
7870 prevSibling = GetInsertionPrevSibling(&insertion, aStartChild, &isAppend,
7871 &isRangeInsertSafe);
7872
7873 // Need check whether a range insert is still safe.
7874 if (!isSingleInsert && !isRangeInsertSafe) {
7875 // Need to recover the letter frames first.
7876 RecoverLetterFrames(state.mFloatedItems.containingBlock);
7877
7878 // must fall back to a single ContertInserted for each child in the
7879 // range
7880 LAYOUT_PHASE_TEMP_EXIT();
7881 IssueSingleInsertNofications(aContainer, aStartChild, aEndChild);
7882 LAYOUT_PHASE_TEMP_REENTER();
7883 return;
7884 }
7885
7886 frameType = insertion.mParentFrame->Type();
7887 }
7888 }
7889
7890 AutoFrameConstructionItemList items(this);
7891 ParentType parentType = GetParentType(frameType);
7892 FlattenedChildIterator iter(aContainer);
7893 bool haveNoXBLChildren = (!iter.XBLInvolved() || !iter.GetNextChild());
7894 if (aStartChild->GetPreviousSibling() && parentType == eTypeBlock &&
7895 haveNoXBLChildren) {
7896 // If there's a text node in the normal content list just before the
7897 // new nodes, and it has no frame, make a frame construction item for
7898 // it, because it might need a frame now. No need to do this if our
7899 // parent type is not block, though, since WipeContainingBlock
7900 // already handles that sitation.
7901 AddTextItemIfNeeded(state, insertion, aStartChild->GetPreviousSibling(),
7902 items);
7903 }
7904
7905 if (isSingleInsert) {
7906 AddFrameConstructionItems(state, aStartChild,
7907 aStartChild->IsRootOfAnonymousSubtree(),
7908 insertion, items);
7909 } else {
7910 for (nsIContent* child = aStartChild; child != aEndChild;
7911 child = child->GetNextSibling()) {
7912 AddFrameConstructionItems(state, child, false, insertion, items);
7913 }
7914 }
7915
7916 if (aEndChild && parentType == eTypeBlock && haveNoXBLChildren) {
7917 // If there's a text node in the normal content list just after the
7918 // new nodes, and it has no frame, make a frame construction item for
7919 // it, because it might need a frame now. No need to do this if our
7920 // parent type is not block, though, since WipeContainingBlock
7921 // already handles that sitation.
7922 AddTextItemIfNeeded(state, insertion, aEndChild, items);
7923 }
7924
7925 // Perform special check for diddling around with the frames in
7926 // a special inline frame.
7927 // If we're appending before :after content, then we're not really
7928 // appending, so let WipeContainingBlock know that.
7929 LAYOUT_PHASE_TEMP_EXIT();
7930 if (WipeContainingBlock(state, containingBlock, insertion.mParentFrame, items,
7931 isAppend, prevSibling)) {
7932 LAYOUT_PHASE_TEMP_REENTER();
7933 return;
7934 }
7935 LAYOUT_PHASE_TEMP_REENTER();
7936
7937 // If the container is a table and a caption will be appended, it needs to be
7938 // put in the table wrapper frame's additional child list.
7939 // We make no attempt here to set flags to indicate whether the list
7940 // will be at the start or end of a block. It doesn't seem worthwhile.
7941 nsFrameItems frameItems, captionItems;
7942 ConstructFramesFromItemList(state, items, insertion.mParentFrame,
7943 ParentIsWrapperAnonBox(insertion.mParentFrame),
7944 frameItems);
7945
7946 if (frameItems.NotEmpty()) {
7947 for (nsIContent* child = aStartChild; child != aEndChild;
7948 child = child->GetNextSibling()) {
7949 InvalidateCanvasIfNeeded(mPresShell, child);
7950 }
7951
7952 if (LayoutFrameType::Table == frameType ||
7953 LayoutFrameType::TableWrapper == frameType) {
7954 PullOutCaptionFrames(frameItems, captionItems);
7955 }
7956 }
7957
7958 if (haveFirstLineStyle && insertion.mParentFrame == containingBlock &&
7959 isAppend) {
7960 // It's possible that the new frame goes into a first-line
7961 // frame. Look at it and see...
7962 AppendFirstLineFrames(state, containingBlock->GetContent(), containingBlock,
7963 frameItems);
7964 } else if (insertion.mParentFrame->StyleContext()->HasPseudoElementData()) {
7965 CheckForFirstLineInsertion(insertion.mParentFrame, frameItems);
7966 CheckForFirstLineInsertion(insertion.mParentFrame, captionItems);
7967 }
7968
7969 // We might have captions; put them into the caption list of the
7970 // table wrapper frame.
7971 if (captionItems.NotEmpty()) {
7972 NS_ASSERTION(LayoutFrameType::Table == frameType ||
7973 LayoutFrameType::TableWrapper == frameType,
7974 "parent for caption is not table?");
7975 // We need to determine where to put the caption items; start with the
7976 // the parent frame that has already been determined and get the insertion
7977 // prevsibling of the first caption item.
7978 bool captionIsAppend;
7979 nsIFrame* captionPrevSibling = nullptr;
7980
7981 // aIsRangeInsertSafe is ignored on purpose because it is irrelevant here.
7982 bool ignored;
7983 InsertionPoint captionInsertion(insertion.mParentFrame,
7984 insertion.mContainer);
7985 if (isSingleInsert) {
7986 captionPrevSibling = GetInsertionPrevSibling(
7987 &captionInsertion, aStartChild, &captionIsAppend, &ignored);
7988 } else {
7989 nsIContent* firstCaption = captionItems.FirstChild()->GetContent();
7990 // It is very important here that we skip the children in
7991 // [aStartChild,aEndChild) when looking for a
7992 // prevsibling.
7993 captionPrevSibling = GetInsertionPrevSibling(
7994 &captionInsertion, firstCaption, &captionIsAppend, &ignored,
7995 aStartChild, aEndChild);
7996 }
7997
7998 nsContainerFrame* outerTable = nullptr;
7999 if (GetCaptionAdjustedParent(captionInsertion.mParentFrame,
8000 captionItems.FirstChild(), &outerTable)) {
8001 // If the parent is not a table wrapper frame we will try to add frames
8002 // to a named child list that the parent does not honor and the frames
8003 // will get lost.
8004 NS_ASSERTION(outerTable->IsTableWrapperFrame(),
8005 "Pseudo frame construction failure; "
8006 "a caption can be only a child of a table wrapper frame");
8007
8008 // If the parent of our current prevSibling is different from the frame
8009 // we'll actually use as the parent, then the calculated insertion
8010 // point is now invalid (bug 341382).
8011 if (captionPrevSibling && captionPrevSibling->GetParent() != outerTable) {
8012 captionPrevSibling = nullptr;
8013 }
8014 if (captionIsAppend) {
8015 AppendFrames(outerTable, nsIFrame::kCaptionList, captionItems);
8016 } else {
8017 InsertFrames(outerTable, nsIFrame::kCaptionList, captionPrevSibling,
8018 captionItems);
8019 }
8020 }
8021 }
8022
8023 if (frameItems.NotEmpty()) {
8024 // Notify the parent frame
8025 if (isAppend) {
8026 AppendFramesToParent(state, insertion.mParentFrame, frameItems,
8027 prevSibling);
8028 } else {
8029 InsertFrames(insertion.mParentFrame, kPrincipalList, prevSibling,
8030 frameItems);
8031 }
8032 }
8033
8034 if (haveFirstLetterStyle) {
8035 // Recover the letter frames for the containing block when
8036 // it has first-letter style.
8037 RecoverLetterFrames(state.mFloatedItems.containingBlock);
8038 }
8039
8040 #ifdef DEBUG
8041 if (gReallyNoisyContentUpdates && insertion.mParentFrame) {
8042 printf(
8043 "nsCSSFrameConstructor::ContentRangeInserted: resulting frame "
8044 "model:\n");
8045 insertion.mParentFrame->List(stdout, 0);
8046 }
8047 #endif
8048
8049 #ifdef ACCESSIBILITY
8050 if (nsAccessibilityService* accService = nsIPresShell::AccService()) {
8051 accService->ContentRangeInserted(mPresShell, aContainer, aStartChild,
8052 aEndChild);
8053 }
8054 #endif
8055 }
8056
ContentRemoved(nsIContent * aContainer,nsIContent * aChild,nsIContent * aOldNextSibling,RemoveFlags aFlags)8057 bool nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer,
8058 nsIContent* aChild,
8059 nsIContent* aOldNextSibling,
8060 RemoveFlags aFlags) {
8061 MOZ_ASSERT(aChild);
8062 MOZ_ASSERT(!aChild->IsRootOfAnonymousSubtree() || !aOldNextSibling,
8063 "Anonymous roots don't have siblings");
8064 AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
8065 nsPresContext* presContext = mPresShell->GetPresContext();
8066 MOZ_ASSERT(presContext, "Our presShell should have a valid presContext");
8067
8068 // We want to detect when the viewport override element stored in the
8069 // prescontext is in the subtree being removed. Except in fullscreen cases
8070 // (which are handled in Element::UnbindFromTree and do not get stored on the
8071 // prescontext), the override element is always either the root element or a
8072 // <body> child of the root element. So we can only be removing the stored
8073 // override element if the thing being removed is either the override element
8074 // itself or the root element (which can be a parent of the override element).
8075 if (aChild == presContext->GetViewportScrollbarStylesOverrideElement() ||
8076 (!aContainer && aChild->IsElement())) {
8077 // We might be removing the element that we propagated viewport scrollbar
8078 // styles from. Recompute those. (This clause covers two of the three
8079 // possible scrollbar-propagation sources: the <body> [as aChild or a
8080 // descendant] and the root node. The other possible scrollbar-propagation
8081 // source is a fullscreen element, and we have code elsewhere to update
8082 // scrollbars after fullscreen elements are removed -- specifically, it's
8083 // part of the fullscreen cleanup code called by Element::UnbindFromTree.
8084 // We don't handle the fullscreen case here, because it doesn't change the
8085 // scrollbar styles override element stored on the prescontext.)
8086 Element* newOverrideElement =
8087 presContext->UpdateViewportScrollbarStylesOverride();
8088
8089 // If aChild is the root (i.e. aContainer is null), then we don't
8090 // need to do any reframing of newOverrideElement, because we're
8091 // about to tear down the whole frame tree anyway. And we need to
8092 // make sure we don't do any such reframing, because reframing the
8093 // <body> can trigger a reframe of the <html> and then reenter
8094 // here.
8095 //
8096 // But if aChild is not the root, and if newOverrideElement is not
8097 // the root and isn't aChild (which it could be if all we're doing
8098 // here is reframing the current override element), it needs
8099 // reframing. In particular, it used to have a scrollframe
8100 // (because its overflow was not "visible"), but now it will
8101 // propagate its overflow to the viewport, so it should not need a
8102 // scrollframe anymore.
8103 if (aContainer && newOverrideElement && newOverrideElement->GetParent() &&
8104 newOverrideElement != aChild) {
8105 LAYOUT_PHASE_TEMP_EXIT();
8106 RecreateFramesForContent(newOverrideElement, InsertionKind::Async);
8107 LAYOUT_PHASE_TEMP_REENTER();
8108 }
8109 }
8110
8111 #ifdef DEBUG
8112 if (gNoisyContentUpdates) {
8113 printf(
8114 "nsCSSFrameConstructor::ContentRemoved container=%p child=%p "
8115 "old-next-sibling=%p\n",
8116 static_cast<void*>(aContainer), static_cast<void*>(aChild),
8117 static_cast<void*>(aOldNextSibling));
8118 if (gReallyNoisyContentUpdates) {
8119 aContainer->List(stdout, 0);
8120 }
8121 }
8122 #endif
8123
8124 nsIFrame* childFrame = aChild->GetPrimaryFrame();
8125 if (!childFrame || childFrame->GetContent() != aChild) {
8126 // XXXbz the GetContent() != aChild check is needed due to bug 135040.
8127 // Remove it once that's fixed.
8128 childFrame = nullptr;
8129 UnregisterDisplayNoneStyleFor(aChild, aContainer);
8130 }
8131 MOZ_ASSERT(!childFrame || !GetDisplayContentsStyleFor(aChild),
8132 "display:contents nodes shouldn't have a frame");
8133 if (!childFrame && GetDisplayContentsStyleFor(aChild)) {
8134 // NOTE(emilio): We may iterate through ::before and ::after here and they
8135 // may be gone after the respective ContentRemoved call. Right now
8136 // StyleChildrenIterator handles that properly, so it's not an issue.
8137 StyleChildrenIterator iter(aChild);
8138 for (nsIContent* c = iter.GetNextChild(); c; c = iter.GetNextChild()) {
8139 if (c->GetPrimaryFrame() || GetDisplayContentsStyleFor(c)) {
8140 LAYOUT_PHASE_TEMP_EXIT();
8141 bool didReconstruct =
8142 ContentRemoved(aChild, c, nullptr, REMOVE_FOR_RECONSTRUCTION);
8143 LAYOUT_PHASE_TEMP_REENTER();
8144 if (didReconstruct) {
8145 return true;
8146 }
8147 }
8148 }
8149 UnregisterDisplayContentsStyleFor(aChild, aContainer);
8150 return false;
8151 }
8152
8153 #ifdef MOZ_XUL
8154 if (NotifyListBoxBody(presContext, aContainer, aChild, aOldNextSibling,
8155 childFrame, CONTENT_REMOVED)) {
8156 return false;
8157 }
8158 #endif // MOZ_XUL
8159
8160 // If we're removing the root, then make sure to remove things starting at
8161 // the viewport's child instead of the primary frame (which might even be
8162 // null if the root had an XBL binding or display:none, even though the
8163 // frames above it got created). We do the adjustment after the childFrame
8164 // check above, because we do want to clear any undisplayed content we might
8165 // have for the root. Detecting removal of a root is a little exciting; in
8166 // particular, having a null aContainer is necessary but NOT sufficient. Due
8167 // to how we process reframes, the content node might not even be in our
8168 // document by now. So explicitly check whether the viewport's first kid's
8169 // content node is aChild.
8170 bool isRoot = false;
8171 if (!aContainer) {
8172 nsIFrame* viewport = GetRootFrame();
8173 if (viewport) {
8174 nsIFrame* firstChild = viewport->PrincipalChildList().FirstChild();
8175 if (firstChild && firstChild->GetContent() == aChild) {
8176 isRoot = true;
8177 childFrame = firstChild;
8178 NS_ASSERTION(!childFrame->GetNextSibling(), "How did that happen?");
8179 }
8180 }
8181 }
8182
8183 if (childFrame) {
8184 InvalidateCanvasIfNeeded(mPresShell, aChild);
8185
8186 // See whether we need to remove more than just childFrame
8187 LAYOUT_PHASE_TEMP_EXIT();
8188 if (MaybeRecreateContainerForFrameRemoval(childFrame)) {
8189 LAYOUT_PHASE_TEMP_REENTER();
8190 return true;
8191 }
8192 LAYOUT_PHASE_TEMP_REENTER();
8193
8194 // Get the childFrame's parent frame
8195 nsIFrame* parentFrame = childFrame->GetParent();
8196 LayoutFrameType parentType = parentFrame->Type();
8197
8198 if (parentType == LayoutFrameType::FrameSet &&
8199 IsSpecialFramesetChild(aChild)) {
8200 // Just reframe the parent, since framesets are weird like that.
8201 LAYOUT_PHASE_TEMP_EXIT();
8202 RecreateFramesForContent(parentFrame->GetContent(), InsertionKind::Async);
8203 LAYOUT_PHASE_TEMP_REENTER();
8204 return true;
8205 }
8206
8207 // If we're a child of MathML, then we should reframe the MathML content.
8208 // If we're non-MathML, then we would be wrapped in a block so we need to
8209 // check our grandparent in that case.
8210 nsIFrame* possibleMathMLAncestor = parentType == LayoutFrameType::Block
8211 ? parentFrame->GetParent()
8212 : parentFrame;
8213 if (possibleMathMLAncestor->IsFrameOfType(nsIFrame::eMathML)) {
8214 LAYOUT_PHASE_TEMP_EXIT();
8215 RecreateFramesForContent(parentFrame->GetContent(), InsertionKind::Async);
8216 LAYOUT_PHASE_TEMP_REENTER();
8217 return true;
8218 }
8219
8220 // Undo XUL wrapping if it's no longer needed.
8221 // (If we're in the XUL block-wrapping situation, parentFrame is the
8222 // wrapper frame.)
8223 nsIFrame* grandparentFrame = parentFrame->GetParent();
8224 if (grandparentFrame && grandparentFrame->IsXULBoxFrame() &&
8225 (grandparentFrame->GetStateBits() & NS_STATE_BOX_WRAPS_KIDS_IN_BLOCK) &&
8226 // check if this frame is the only one needing wrapping
8227 aChild == AnyKidsNeedBlockParent(
8228 parentFrame->PrincipalChildList().FirstChild()) &&
8229 !AnyKidsNeedBlockParent(childFrame->GetNextSibling())) {
8230 LAYOUT_PHASE_TEMP_EXIT();
8231 RecreateFramesForContent(grandparentFrame->GetContent(),
8232 InsertionKind::Async);
8233 LAYOUT_PHASE_TEMP_REENTER();
8234 return true;
8235 }
8236
8237 #ifdef ACCESSIBILITY
8238 if (nsAccessibilityService* accService = nsIPresShell::AccService()) {
8239 accService->ContentRemoved(mPresShell, aChild);
8240 }
8241 #endif
8242
8243 // Examine the containing-block for the removed content and see if
8244 // :first-letter style applies.
8245 nsIFrame* inflowChild = childFrame;
8246 if (childFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
8247 inflowChild = childFrame->GetPlaceholderFrame();
8248 NS_ASSERTION(inflowChild, "No placeholder for out-of-flow?");
8249 }
8250 nsContainerFrame* containingBlock =
8251 GetFloatContainingBlock(inflowChild->GetParent());
8252 bool haveFLS = containingBlock && HasFirstLetterStyle(containingBlock);
8253 if (haveFLS) {
8254 // Trap out to special routine that handles adjusting a blocks
8255 // frame tree when first-letter style is present.
8256 #ifdef NOISY_FIRST_LETTER
8257 printf("ContentRemoved: containingBlock=");
8258 nsFrame::ListTag(stdout, containingBlock);
8259 printf(" parentFrame=");
8260 nsFrame::ListTag(stdout, parentFrame);
8261 printf(" childFrame=");
8262 nsFrame::ListTag(stdout, childFrame);
8263 printf("\n");
8264 #endif
8265
8266 // First update the containing blocks structure by removing the
8267 // existing letter frames. This makes the subsequent logic
8268 // simpler.
8269 RemoveLetterFrames(mPresShell, containingBlock);
8270
8271 // Recover childFrame and parentFrame
8272 childFrame = aChild->GetPrimaryFrame();
8273 if (!childFrame || childFrame->GetContent() != aChild) {
8274 // XXXbz the GetContent() != aChild check is needed due to bug 135040.
8275 // Remove it once that's fixed.
8276 UnregisterDisplayNoneStyleFor(aChild, aContainer);
8277 return false;
8278 }
8279 parentFrame = childFrame->GetParent();
8280 parentType = parentFrame->Type();
8281
8282 #ifdef NOISY_FIRST_LETTER
8283 printf(" ==> revised parentFrame=");
8284 nsFrame::ListTag(stdout, parentFrame);
8285 printf(" childFrame=");
8286 nsFrame::ListTag(stdout, childFrame);
8287 printf("\n");
8288 #endif
8289 }
8290
8291 #ifdef DEBUG
8292 if (gReallyNoisyContentUpdates) {
8293 printf("nsCSSFrameConstructor::ContentRemoved: childFrame=");
8294 nsFrame::ListTag(stdout, childFrame);
8295 putchar('\n');
8296 parentFrame->List(stdout, 0);
8297 }
8298 #endif
8299
8300 // Notify the parent frame that it should delete the frame
8301 if (childFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
8302 childFrame = childFrame->GetPlaceholderFrame();
8303 NS_ASSERTION(childFrame, "Missing placeholder frame for out of flow.");
8304 parentFrame = childFrame->GetParent();
8305 }
8306
8307 RemoveFrame(nsLayoutUtils::GetChildListNameFor(childFrame), childFrame);
8308
8309 // NOTE(emilio): aChild could be dead here already if it is a ::before or
8310 // ::after pseudo-element (since in that case it was owned by childFrame,
8311 // which we just destroyed).
8312
8313 if (isRoot) {
8314 mRootElementFrame = nullptr;
8315 mRootElementStyleFrame = nullptr;
8316 mDocElementContainingBlock = nullptr;
8317 mPageSequenceFrame = nullptr;
8318 mHasRootAbsPosContainingBlock = false;
8319 }
8320
8321 if (haveFLS && mRootElementFrame) {
8322 RecoverLetterFrames(containingBlock);
8323 }
8324
8325 // If we're just reconstructing frames for the element, then the
8326 // following ContentInserted notification on the element will
8327 // take care of fixing up any adjacent text nodes. We don't need
8328 // to do this if the table parent type of our parent type is not
8329 // eTypeBlock, though, because in that case the whitespace isn't
8330 // being suppressed due to us anyway.
8331 if (aContainer && aOldNextSibling && aFlags == REMOVE_CONTENT &&
8332 GetParentType(parentType) == eTypeBlock) {
8333 // Adjacent whitespace-only text nodes might have been suppressed if
8334 // this node does not have inline ends. Create frames for them now
8335 // if necessary.
8336 // Reframe any text node just before the node being removed, if there is
8337 // one, and if it's not the last child or the first child. If a whitespace
8338 // textframe was being suppressed and it's now the last child or first
8339 // child then it can stay suppressed since the parent must be a block
8340 // and hence it's adjacent to a block end.
8341 // If aOldNextSibling is null, then the text node before the node being
8342 // removed is the last node, and we don't need to worry about it.
8343 //
8344 // FIXME(emilio): This should probably use the lazy frame construction
8345 // bits if possible instead of reframing it in place.
8346 nsIContent* prevSibling = aOldNextSibling->GetPreviousSibling();
8347 if (prevSibling && prevSibling->GetPreviousSibling()) {
8348 LAYOUT_PHASE_TEMP_EXIT();
8349 ReframeTextIfNeeded(aContainer, prevSibling);
8350 LAYOUT_PHASE_TEMP_REENTER();
8351 }
8352 // Reframe any text node just after the node being removed, if there is
8353 // one, and if it's not the last child or the first child.
8354 if (aOldNextSibling->GetNextSibling() &&
8355 aOldNextSibling->GetPreviousSibling()) {
8356 LAYOUT_PHASE_TEMP_EXIT();
8357 ReframeTextIfNeeded(aContainer, aOldNextSibling);
8358 LAYOUT_PHASE_TEMP_REENTER();
8359 }
8360 }
8361
8362 #ifdef DEBUG
8363 if (gReallyNoisyContentUpdates && parentFrame) {
8364 printf("nsCSSFrameConstructor::ContentRemoved: resulting frame model:\n");
8365 parentFrame->List(stdout, 0);
8366 }
8367 #endif
8368 }
8369
8370 return false;
8371 }
8372
8373 /**
8374 * This method invalidates the canvas when frames are removed or added for a
8375 * node that might have its background propagated to the canvas, i.e., a
8376 * document root node or an HTML BODY which is a child of the root node.
8377 *
8378 * @param aFrame a frame for a content node about to be removed or a frame that
8379 * was just created for a content node that was inserted.
8380 */
InvalidateCanvasIfNeeded(nsIPresShell * presShell,nsIContent * node)8381 static void InvalidateCanvasIfNeeded(nsIPresShell* presShell,
8382 nsIContent* node) {
8383 NS_PRECONDITION(presShell->GetRootFrame(), "What happened here?");
8384 NS_PRECONDITION(presShell->GetPresContext(), "Say what?");
8385
8386 // Note that both in ContentRemoved and ContentInserted the content node
8387 // will still have the right parent pointer, so looking at that is ok.
8388
8389 nsIContent* parent = node->GetParent();
8390 if (parent) {
8391 // Has a parent; might not be what we want
8392 nsIContent* grandParent = parent->GetParent();
8393 if (grandParent) {
8394 // Has a grandparent, so not what we want
8395 return;
8396 }
8397
8398 // Check whether it's an HTML body
8399 if (!node->IsHTMLElement(nsGkAtoms::body)) {
8400 return;
8401 }
8402 }
8403
8404 // At this point the node has no parent or it's an HTML <body> child of the
8405 // root. We might not need to invalidate in this case (eg we might be in
8406 // XHTML or something), but chances are we want to. Play it safe.
8407 // Invalidate the viewport.
8408
8409 nsIFrame* rootFrame = presShell->GetRootFrame();
8410 rootFrame->InvalidateFrameSubtree();
8411 }
8412
EnsureFrameForTextNodeIsCreatedAfterFlush(nsGenericDOMDataNode * aContent)8413 bool nsCSSFrameConstructor::EnsureFrameForTextNodeIsCreatedAfterFlush(
8414 nsGenericDOMDataNode* aContent) {
8415 if (!aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE)) {
8416 return false;
8417 }
8418
8419 if (mAlwaysCreateFramesForIgnorableWhitespace) {
8420 return false;
8421 }
8422
8423 // Text frame may have been suppressed. Disable suppression and signal that a
8424 // flush should be performed. We do this on a document-wide basis so that
8425 // pages that repeatedly query metrics for collapsed-whitespace text nodes
8426 // don't trigger pathological behavior.
8427 mAlwaysCreateFramesForIgnorableWhitespace = true;
8428 Element* root = mDocument->GetRootElement();
8429 if (!root) {
8430 return false;
8431 }
8432
8433 RestyleManager()->PostRestyleEvent(root, nsRestyleHint(0),
8434 nsChangeHint_ReconstructFrame);
8435 return true;
8436 }
8437
CharacterDataChanged(nsIContent * aContent,const CharacterDataChangeInfo & aInfo)8438 void nsCSSFrameConstructor::CharacterDataChanged(
8439 nsIContent* aContent, const CharacterDataChangeInfo& aInfo) {
8440 AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
8441
8442 if ((aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE) &&
8443 !aContent->TextIsOnlyWhitespace()) ||
8444 (aContent->HasFlag(NS_REFRAME_IF_WHITESPACE) &&
8445 aContent->TextIsOnlyWhitespace())) {
8446 #ifdef DEBUG
8447 nsIFrame* frame = aContent->GetPrimaryFrame();
8448 NS_ASSERTION(!frame || !frame->IsGeneratedContentFrame(),
8449 "Bit should never be set on generated content");
8450 #endif
8451 LAYOUT_PHASE_TEMP_EXIT();
8452 RecreateFramesForContent(aContent, InsertionKind::Async);
8453 LAYOUT_PHASE_TEMP_REENTER();
8454 return;
8455 }
8456
8457 // It's possible the frame whose content changed isn't inserted into the
8458 // frame hierarchy yet, or that there is no frame that maps the content
8459 if (nsIFrame* frame = aContent->GetPrimaryFrame()) {
8460 #if 0
8461 NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
8462 ("nsCSSFrameConstructor::CharacterDataChanged: content=%p[%s] subcontent=%p frame=%p",
8463 aContent, ContentTag(aContent, 0),
8464 aSubContent, frame));
8465 #endif
8466
8467 // Special check for text content that is a child of a letter frame. If
8468 // this happens, we should remove the letter frame, do whatever we're
8469 // planning to do with this notification, then put the letter frame back.
8470 // Note that this is basically what RecreateFramesForContent ends up doing;
8471 // the reason we dont' want to call that here is that our text content
8472 // could be native anonymous, in which case RecreateFramesForContent would
8473 // completely barf on it. And recreating the non-anonymous ancestor would
8474 // just lead us to come back into this notification (e.g. if quotes or
8475 // counters are involved), leading to a loop.
8476 nsContainerFrame* block = GetFloatContainingBlock(frame);
8477 bool haveFirstLetterStyle = false;
8478 if (block) {
8479 // See if the block has first-letter style applied to it.
8480 haveFirstLetterStyle = HasFirstLetterStyle(block);
8481 if (haveFirstLetterStyle) {
8482 RemoveLetterFrames(mPresShell, block);
8483 // Reget |frame|, since we might have killed it.
8484 // Do we really need to call CharacterDataChanged in this case, though?
8485 frame = aContent->GetPrimaryFrame();
8486 NS_ASSERTION(frame, "Should have frame here!");
8487 }
8488 }
8489
8490 // Notify the first frame that maps the content. It will generate a reflow
8491 // command
8492 frame->CharacterDataChanged(aInfo);
8493
8494 if (haveFirstLetterStyle) {
8495 RecoverLetterFrames(block);
8496 }
8497 }
8498 }
8499
RecalcQuotesAndCounters()8500 void nsCSSFrameConstructor::RecalcQuotesAndCounters() {
8501 nsAutoScriptBlocker scriptBlocker;
8502
8503 if (mQuotesDirty) {
8504 mQuotesDirty = false;
8505 mQuoteList.RecalcAll();
8506 }
8507
8508 if (mCountersDirty) {
8509 mCountersDirty = false;
8510 mCounterManager.RecalcAll();
8511 }
8512
8513 NS_ASSERTION(!mQuotesDirty, "Quotes updates will be lost");
8514 NS_ASSERTION(!mCountersDirty, "Counter updates will be lost");
8515 }
8516
NotifyCounterStylesAreDirty()8517 void nsCSSFrameConstructor::NotifyCounterStylesAreDirty() {
8518 mCounterManager.SetAllDirty();
8519 CountersDirty();
8520 }
8521
WillDestroyFrameTree()8522 void nsCSSFrameConstructor::WillDestroyFrameTree() {
8523 #if defined(DEBUG_dbaron_off)
8524 mCounterManager.Dump();
8525 #endif
8526
8527 mIsDestroyingFrameTree = true;
8528
8529 // Prevent frame tree destruction from being O(N^2)
8530 mQuoteList.Clear();
8531 mCounterManager.Clear();
8532
8533 // Remove our presshell as a style flush observer. But leave
8534 // RestyleManager::mObservingRefreshDriver true so we don't readd to
8535 // it even if someone tries to post restyle events on us from this
8536 // point on for some reason.
8537 mPresShell->GetPresContext()->RefreshDriver()->RemoveStyleFlushObserver(
8538 mPresShell);
8539
8540 nsFrameManager::Destroy();
8541 }
8542
8543 // STATIC
8544
8545 // XXXbz I'd really like this method to go away. Once we have inline-block and
8546 // I can just use that for sized broken images, that can happen, maybe.
GetAlternateTextFor(Element * aElement,nsAtom * aTag,nsAString & aAltText)8547 void nsCSSFrameConstructor::GetAlternateTextFor(Element* aElement, nsAtom* aTag,
8548 nsAString& aAltText) {
8549 // The "alt" attribute specifies alternate text that is rendered
8550 // when the image can not be displayed.
8551 if (aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::alt, aAltText)) {
8552 return;
8553 }
8554
8555 if (nsGkAtoms::input == aTag) {
8556 // If there's no "alt" attribute, and aContent is an input element, then use
8557 // the value of the "value" attribute
8558 if (aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::value, aAltText)) {
8559 return;
8560 }
8561
8562 // If there's no "value" attribute either, then use the localized string for
8563 // "Submit" as the alternate text.
8564 nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
8565 "Submit", aAltText);
8566 }
8567 }
8568
CreateContinuingOuterTableFrame(nsIPresShell * aPresShell,nsPresContext * aPresContext,nsIFrame * aFrame,nsContainerFrame * aParentFrame,nsIContent * aContent,nsStyleContext * aStyleContext)8569 nsIFrame* nsCSSFrameConstructor::CreateContinuingOuterTableFrame(
8570 nsIPresShell* aPresShell, nsPresContext* aPresContext, nsIFrame* aFrame,
8571 nsContainerFrame* aParentFrame, nsIContent* aContent,
8572 nsStyleContext* aStyleContext) {
8573 nsTableWrapperFrame* newFrame =
8574 NS_NewTableWrapperFrame(aPresShell, aStyleContext);
8575
8576 newFrame->Init(aContent, aParentFrame, aFrame);
8577
8578 // Create a continuing inner table frame, and if there's a caption then
8579 // replicate the caption
8580 nsFrameItems newChildFrames;
8581
8582 nsIFrame* childFrame = aFrame->PrincipalChildList().FirstChild();
8583 if (childFrame) {
8584 nsIFrame* continuingTableFrame =
8585 CreateContinuingFrame(aPresContext, childFrame, newFrame);
8586 newChildFrames.AddChild(continuingTableFrame);
8587
8588 NS_ASSERTION(!childFrame->GetNextSibling(),
8589 "there can be only one inner table frame");
8590 }
8591
8592 // Set the table wrapper's initial child list
8593 newFrame->SetInitialChildList(kPrincipalList, newChildFrames);
8594
8595 return newFrame;
8596 }
8597
CreateContinuingTableFrame(nsIPresShell * aPresShell,nsIFrame * aFrame,nsContainerFrame * aParentFrame,nsIContent * aContent,nsStyleContext * aStyleContext)8598 nsIFrame* nsCSSFrameConstructor::CreateContinuingTableFrame(
8599 nsIPresShell* aPresShell, nsIFrame* aFrame, nsContainerFrame* aParentFrame,
8600 nsIContent* aContent, nsStyleContext* aStyleContext) {
8601 nsTableFrame* newFrame = NS_NewTableFrame(aPresShell, aStyleContext);
8602
8603 newFrame->Init(aContent, aParentFrame, aFrame);
8604
8605 // Replicate any header/footer frames
8606 nsFrameItems childFrames;
8607 for (nsIFrame* childFrame : aFrame->PrincipalChildList()) {
8608 // See if it's a header/footer, possibly wrapped in a scroll frame.
8609 nsTableRowGroupFrame* rowGroupFrame =
8610 static_cast<nsTableRowGroupFrame*>(childFrame);
8611 // If the row group was continued, then don't replicate it.
8612 nsIFrame* rgNextInFlow = rowGroupFrame->GetNextInFlow();
8613 if (rgNextInFlow) {
8614 rowGroupFrame->SetRepeatable(false);
8615 } else if (rowGroupFrame->IsRepeatable()) {
8616 // Replicate the header/footer frame.
8617 nsTableRowGroupFrame* headerFooterFrame;
8618 nsFrameItems childItems;
8619
8620 TreeMatchContextHolder matchContext(mDocument);
8621 nsFrameConstructorState state(
8622 mPresShell, matchContext,
8623 GetAbsoluteContainingBlock(newFrame, FIXED_POS),
8624 GetAbsoluteContainingBlock(newFrame, ABS_POS), nullptr);
8625 state.mCreatingExtraFrames = true;
8626
8627 nsStyleContext* const headerFooterStyleContext =
8628 rowGroupFrame->StyleContext();
8629 headerFooterFrame = static_cast<nsTableRowGroupFrame*>(
8630 NS_NewTableRowGroupFrame(aPresShell, headerFooterStyleContext));
8631
8632 nsIContent* headerFooter = rowGroupFrame->GetContent();
8633 headerFooterFrame->Init(headerFooter, newFrame, nullptr);
8634
8635 nsFrameConstructorSaveState absoluteSaveState;
8636 MakeTablePartAbsoluteContainingBlockIfNeeded(
8637 state, headerFooterStyleContext->StyleDisplay(), absoluteSaveState,
8638 headerFooterFrame);
8639
8640 ProcessChildren(state, headerFooter, rowGroupFrame->StyleContext(),
8641 headerFooterFrame, true, childItems, false, nullptr);
8642 NS_ASSERTION(state.mFloatedItems.IsEmpty(), "unexpected floated element");
8643 headerFooterFrame->SetInitialChildList(kPrincipalList, childItems);
8644 headerFooterFrame->SetRepeatable(true);
8645
8646 // Table specific initialization
8647 headerFooterFrame->InitRepeatedFrame(rowGroupFrame);
8648
8649 // XXX Deal with absolute and fixed frames...
8650 childFrames.AddChild(headerFooterFrame);
8651 }
8652 }
8653
8654 // Set the table frame's initial child list
8655 newFrame->SetInitialChildList(kPrincipalList, childFrames);
8656
8657 return newFrame;
8658 }
8659
CreateContinuingFrame(nsPresContext * aPresContext,nsIFrame * aFrame,nsContainerFrame * aParentFrame,bool aIsFluid)8660 nsIFrame* nsCSSFrameConstructor::CreateContinuingFrame(
8661 nsPresContext* aPresContext, nsIFrame* aFrame,
8662 nsContainerFrame* aParentFrame, bool aIsFluid) {
8663 nsIPresShell* shell = aPresContext->PresShell();
8664 nsStyleContext* styleContext = aFrame->StyleContext();
8665 nsIFrame* newFrame = nullptr;
8666 nsIFrame* nextContinuation = aFrame->GetNextContinuation();
8667 nsIFrame* nextInFlow = aFrame->GetNextInFlow();
8668
8669 // Use the frame type to determine what type of frame to create
8670 LayoutFrameType frameType = aFrame->Type();
8671 nsIContent* content = aFrame->GetContent();
8672
8673 NS_ASSERTION(aFrame->GetSplittableType() != NS_FRAME_NOT_SPLITTABLE,
8674 "why CreateContinuingFrame for a non-splittable frame?");
8675
8676 if (LayoutFrameType::Text == frameType) {
8677 newFrame = NS_NewContinuingTextFrame(shell, styleContext);
8678 newFrame->Init(content, aParentFrame, aFrame);
8679 } else if (LayoutFrameType::Inline == frameType) {
8680 newFrame = NS_NewInlineFrame(shell, styleContext);
8681 newFrame->Init(content, aParentFrame, aFrame);
8682 } else if (LayoutFrameType::Block == frameType) {
8683 MOZ_ASSERT(!aFrame->IsTableCaption(),
8684 "no support for fragmenting table captions yet");
8685 newFrame = NS_NewBlockFrame(shell, styleContext);
8686 newFrame->Init(content, aParentFrame, aFrame);
8687 #ifdef MOZ_XUL
8688 } else if (LayoutFrameType::XULLabel == frameType) {
8689 newFrame = NS_NewXULLabelFrame(shell, styleContext);
8690 newFrame->Init(content, aParentFrame, aFrame);
8691 #endif
8692 } else if (LayoutFrameType::ColumnSet == frameType) {
8693 MOZ_ASSERT(!aFrame->IsTableCaption(),
8694 "no support for fragmenting table captions yet");
8695 newFrame = NS_NewColumnSetFrame(shell, styleContext, nsFrameState(0));
8696 newFrame->Init(content, aParentFrame, aFrame);
8697 } else if (LayoutFrameType::Page == frameType) {
8698 nsContainerFrame* canvasFrame;
8699 newFrame = ConstructPageFrame(shell, aParentFrame, aFrame, canvasFrame);
8700 } else if (LayoutFrameType::TableWrapper == frameType) {
8701 newFrame = CreateContinuingOuterTableFrame(
8702 shell, aPresContext, aFrame, aParentFrame, content, styleContext);
8703
8704 } else if (LayoutFrameType::Table == frameType) {
8705 newFrame = CreateContinuingTableFrame(shell, aFrame, aParentFrame, content,
8706 styleContext);
8707
8708 } else if (LayoutFrameType::TableRowGroup == frameType) {
8709 newFrame = NS_NewTableRowGroupFrame(shell, styleContext);
8710 newFrame->Init(content, aParentFrame, aFrame);
8711 if (newFrame->GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN) {
8712 nsTableFrame::RegisterPositionedTablePart(newFrame);
8713 }
8714 } else if (LayoutFrameType::TableRow == frameType) {
8715 nsTableRowFrame* rowFrame = NS_NewTableRowFrame(shell, styleContext);
8716
8717 rowFrame->Init(content, aParentFrame, aFrame);
8718 if (rowFrame->GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN) {
8719 nsTableFrame::RegisterPositionedTablePart(rowFrame);
8720 }
8721
8722 // Create a continuing frame for each table cell frame
8723 nsFrameItems newChildList;
8724 nsIFrame* cellFrame = aFrame->PrincipalChildList().FirstChild();
8725 while (cellFrame) {
8726 // See if it's a table cell frame
8727 if (IS_TABLE_CELL(cellFrame->Type())) {
8728 nsIFrame* continuingCellFrame =
8729 CreateContinuingFrame(aPresContext, cellFrame, rowFrame);
8730 newChildList.AddChild(continuingCellFrame);
8731 }
8732 cellFrame = cellFrame->GetNextSibling();
8733 }
8734
8735 rowFrame->SetInitialChildList(kPrincipalList, newChildList);
8736 newFrame = rowFrame;
8737
8738 } else if (IS_TABLE_CELL(frameType)) {
8739 // Warning: If you change this and add a wrapper frame around table cell
8740 // frames, make sure Bug 368554 doesn't regress!
8741 // See IsInAutoWidthTableCellForQuirk() in nsImageFrame.cpp.
8742 nsTableFrame* tableFrame =
8743 static_cast<nsTableRowFrame*>(aParentFrame)->GetTableFrame();
8744 nsTableCellFrame* cellFrame =
8745 NS_NewTableCellFrame(shell, styleContext, tableFrame);
8746
8747 cellFrame->Init(content, aParentFrame, aFrame);
8748 if (cellFrame->GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN) {
8749 nsTableFrame::RegisterPositionedTablePart(cellFrame);
8750 }
8751
8752 // Create a continuing area frame
8753 nsIFrame* blockFrame = aFrame->PrincipalChildList().FirstChild();
8754 nsIFrame* continuingBlockFrame = CreateContinuingFrame(
8755 aPresContext, blockFrame, static_cast<nsContainerFrame*>(cellFrame));
8756
8757 SetInitialSingleChild(cellFrame, continuingBlockFrame);
8758 newFrame = cellFrame;
8759 } else if (LayoutFrameType::Line == frameType) {
8760 newFrame = NS_NewFirstLineFrame(shell, styleContext);
8761 newFrame->Init(content, aParentFrame, aFrame);
8762 } else if (LayoutFrameType::Letter == frameType) {
8763 newFrame = NS_NewFirstLetterFrame(shell, styleContext);
8764 newFrame->Init(content, aParentFrame, aFrame);
8765 } else if (LayoutFrameType::Image == frameType) {
8766 newFrame = NS_NewImageFrame(shell, styleContext);
8767 newFrame->Init(content, aParentFrame, aFrame);
8768 } else if (LayoutFrameType::ImageControl == frameType) {
8769 newFrame = NS_NewImageControlFrame(shell, styleContext);
8770 newFrame->Init(content, aParentFrame, aFrame);
8771 } else if (LayoutFrameType::Placeholder == frameType) {
8772 // create a continuing out of flow frame
8773 nsIFrame* oofFrame = nsPlaceholderFrame::GetRealFrameForPlaceholder(aFrame);
8774 nsIFrame* oofContFrame =
8775 CreateContinuingFrame(aPresContext, oofFrame, aParentFrame);
8776 newFrame = CreatePlaceholderFrameFor(
8777 shell, content, oofContFrame, aParentFrame, aFrame,
8778 aFrame->GetStateBits() & PLACEHOLDER_TYPE_MASK);
8779 } else if (LayoutFrameType::FieldSet == frameType) {
8780 nsContainerFrame* fieldset = NS_NewFieldSetFrame(shell, styleContext);
8781
8782 fieldset->Init(content, aParentFrame, aFrame);
8783
8784 // Create a continuing area frame
8785 // XXXbz we really shouldn't have to do this by hand!
8786 nsContainerFrame* blockFrame = GetFieldSetBlockFrame(aFrame);
8787 if (blockFrame) {
8788 nsIFrame* continuingBlockFrame =
8789 CreateContinuingFrame(aPresContext, blockFrame, fieldset);
8790 // Set the fieldset's initial child list
8791 SetInitialSingleChild(fieldset, continuingBlockFrame);
8792 } else {
8793 MOZ_ASSERT(aFrame->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER,
8794 "FieldSet block may only be null for overflow containers");
8795 }
8796 newFrame = fieldset;
8797 } else if (LayoutFrameType::Legend == frameType) {
8798 newFrame = NS_NewLegendFrame(shell, styleContext);
8799 newFrame->Init(content, aParentFrame, aFrame);
8800 } else if (LayoutFrameType::FlexContainer == frameType) {
8801 newFrame = NS_NewFlexContainerFrame(shell, styleContext);
8802 newFrame->Init(content, aParentFrame, aFrame);
8803 } else if (LayoutFrameType::GridContainer == frameType) {
8804 newFrame = NS_NewGridContainerFrame(shell, styleContext);
8805 newFrame->Init(content, aParentFrame, aFrame);
8806 } else if (LayoutFrameType::Ruby == frameType) {
8807 newFrame = NS_NewRubyFrame(shell, styleContext);
8808 newFrame->Init(content, aParentFrame, aFrame);
8809 } else if (LayoutFrameType::RubyBaseContainer == frameType) {
8810 newFrame = NS_NewRubyBaseContainerFrame(shell, styleContext);
8811 newFrame->Init(content, aParentFrame, aFrame);
8812 } else if (LayoutFrameType::RubyTextContainer == frameType) {
8813 newFrame = NS_NewRubyTextContainerFrame(shell, styleContext);
8814 newFrame->Init(content, aParentFrame, aFrame);
8815 } else if (LayoutFrameType::Details == frameType) {
8816 newFrame = NS_NewDetailsFrame(shell, styleContext);
8817 newFrame->Init(content, aParentFrame, aFrame);
8818 } else {
8819 MOZ_CRASH("unexpected frame type");
8820 }
8821
8822 // Init() set newFrame to be a fluid continuation of aFrame.
8823 // If we want a non-fluid continuation, we need to call SetPrevContinuation()
8824 // to reset NS_FRAME_IS_FLUID_CONTINUATION.
8825 if (!aIsFluid) {
8826 newFrame->SetPrevContinuation(aFrame);
8827 }
8828
8829 // A continuation of generated content is also generated content
8830 if (aFrame->GetStateBits() & NS_FRAME_GENERATED_CONTENT) {
8831 newFrame->AddStateBits(NS_FRAME_GENERATED_CONTENT);
8832 }
8833
8834 // A continuation of nsIAnonymousContentCreator content is also
8835 // nsIAnonymousContentCreator created content
8836 if (aFrame->GetStateBits() & NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT) {
8837 newFrame->AddStateBits(NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT);
8838 }
8839
8840 // A continuation of an out-of-flow is also an out-of-flow
8841 if (aFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
8842 newFrame->AddStateBits(NS_FRAME_OUT_OF_FLOW);
8843 }
8844
8845 if (nextInFlow) {
8846 nextInFlow->SetPrevInFlow(newFrame);
8847 newFrame->SetNextInFlow(nextInFlow);
8848 } else if (nextContinuation) {
8849 nextContinuation->SetPrevContinuation(newFrame);
8850 newFrame->SetNextContinuation(nextContinuation);
8851 }
8852
8853 MOZ_ASSERT(!newFrame->GetNextSibling(), "unexpected sibling");
8854 return newFrame;
8855 }
8856
ReplicateFixedFrames(nsPageContentFrame * aParentFrame)8857 nsresult nsCSSFrameConstructor::ReplicateFixedFrames(
8858 nsPageContentFrame* aParentFrame) {
8859 // Now deal with fixed-pos things.... They should appear on all pages,
8860 // so we want to move over the placeholders when processing the child
8861 // of the pageContentFrame.
8862
8863 nsIFrame* prevPageContentFrame = aParentFrame->GetPrevInFlow();
8864 if (!prevPageContentFrame) {
8865 return NS_OK;
8866 }
8867 nsContainerFrame* canvasFrame =
8868 do_QueryFrame(aParentFrame->PrincipalChildList().FirstChild());
8869 nsIFrame* prevCanvasFrame =
8870 prevPageContentFrame->PrincipalChildList().FirstChild();
8871 if (!canvasFrame || !prevCanvasFrame) {
8872 // document's root element frame missing
8873 return NS_ERROR_UNEXPECTED;
8874 }
8875
8876 nsFrameItems fixedPlaceholders;
8877 nsIFrame* firstFixed =
8878 prevPageContentFrame->GetChildList(nsIFrame::kFixedList).FirstChild();
8879 if (!firstFixed) {
8880 return NS_OK;
8881 }
8882
8883 // Don't allow abs-pos descendants of the fixed content to escape the content.
8884 // This should not normally be possible (because fixed-pos elements should
8885 // be absolute containers) but fixed-pos tables currently aren't abs-pos
8886 // containers.
8887 TreeMatchContextHolder matchContext(mDocument);
8888 nsFrameConstructorState state(mPresShell, matchContext, aParentFrame, nullptr,
8889 mRootElementFrame);
8890 state.mCreatingExtraFrames = true;
8891
8892 // We can't use an ancestor filter here, because we're not going to
8893 // be usefully recurring down the tree. This means that other
8894 // places in frame construction can't assume a filter is
8895 // initialized!
8896
8897 // Iterate across fixed frames and replicate each whose placeholder is a
8898 // descendant of aFrame. (We don't want to explicitly copy placeholders that
8899 // are within fixed frames, because that would cause duplicates on the new
8900 // page - bug 389619)
8901 for (nsIFrame* fixed = firstFixed; fixed; fixed = fixed->GetNextSibling()) {
8902 nsIFrame* prevPlaceholder = fixed->GetPlaceholderFrame();
8903 if (prevPlaceholder && nsLayoutUtils::IsProperAncestorFrame(
8904 prevCanvasFrame, prevPlaceholder)) {
8905 // We want to use the same style as the primary style frame for
8906 // our content
8907 nsIContent* content = fixed->GetContent();
8908 nsStyleContext* styleContext =
8909 nsLayoutUtils::GetStyleFrame(content)->StyleContext();
8910 AutoFrameConstructionItemList items(this);
8911 AddFrameConstructionItemsInternal(
8912 state, content, canvasFrame, content->NodeInfo()->NameAtom(),
8913 content->GetNameSpaceID(), true, styleContext,
8914 ITEM_ALLOW_XBL_BASE | ITEM_ALLOW_PAGE_BREAK, nullptr, items);
8915 ConstructFramesFromItemList(state, items, canvasFrame,
8916 /* aParentIsWrapperAnonBox = */ false,
8917 fixedPlaceholders);
8918 }
8919 }
8920
8921 // Add the placeholders to our primary child list.
8922 // XXXbz this is a little screwed up, since the fixed frames will have
8923 // broken auto-positioning. Oh, well.
8924 NS_ASSERTION(!canvasFrame->PrincipalChildList().FirstChild(),
8925 "leaking frames; doc root continuation must be empty");
8926 canvasFrame->SetInitialChildList(kPrincipalList, fixedPlaceholders);
8927 return NS_OK;
8928 }
8929
GetInsertionPoint(nsIContent * aChild)8930 nsCSSFrameConstructor::InsertionPoint nsCSSFrameConstructor::GetInsertionPoint(
8931 nsIContent* aChild) {
8932 MOZ_ASSERT(aChild);
8933 nsIContent* insertionElement = aChild->GetFlattenedTreeParent();
8934 if (!insertionElement) {
8935 // The element doesn't belong in the flattened tree, and thus we don't want
8936 // to render it.
8937 return {};
8938 }
8939
8940 return {GetContentInsertionFrameFor(insertionElement), insertionElement};
8941 }
8942
8943 // Capture state for the frame tree rooted at the frame associated with the
8944 // content object, aContent
CaptureStateForFramesOf(nsIContent * aContent,nsILayoutHistoryState * aHistoryState)8945 void nsCSSFrameConstructor::CaptureStateForFramesOf(
8946 nsIContent* aContent, nsILayoutHistoryState* aHistoryState) {
8947 if (!aHistoryState) {
8948 return;
8949 }
8950 nsIFrame* frame = aContent->GetPrimaryFrame();
8951 if (frame == mRootElementFrame) {
8952 frame = mRootElementFrame
8953 ? GetAbsoluteContainingBlock(mRootElementFrame, FIXED_POS)
8954 : GetRootFrame();
8955 }
8956 for (; frame;
8957 frame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(frame)) {
8958 CaptureFrameState(frame, aHistoryState);
8959 }
8960 }
8961
8962 #ifdef MOZ_OLD_STYLE
DefinitelyEqualURIsAndPrincipal(mozilla::css::URLValue * aURI1,mozilla::css::URLValue * aURI2)8963 static bool DefinitelyEqualURIsAndPrincipal(mozilla::css::URLValue* aURI1,
8964 mozilla::css::URLValue* aURI2) {
8965 return aURI1 == aURI2 ||
8966 (aURI1 && aURI2 && aURI1->DefinitelyEqualURIsAndPrincipal(*aURI2));
8967 }
8968
MaybeRecreateFramesForElement(Element * aElement)8969 nsStyleContext* nsCSSFrameConstructor::MaybeRecreateFramesForElement(
8970 Element* aElement) {
8971 RefPtr<nsStyleContext> oldContext = GetDisplayNoneStyleFor(aElement);
8972 StyleDisplay oldDisplay = StyleDisplay::None;
8973 if (!oldContext) {
8974 oldContext = GetDisplayContentsStyleFor(aElement);
8975 if (!oldContext) {
8976 return nullptr;
8977 }
8978 oldDisplay = StyleDisplay::Contents;
8979 }
8980
8981 // The parent has a frame, so try resolving a new context.
8982 RefPtr<nsStyleContext> newContext = mPresShell->StyleSet()->ResolveStyleFor(
8983 aElement, oldContext->AsGecko()->GetParent(),
8984 LazyComputeBehavior::Assert);
8985
8986 if (oldDisplay == StyleDisplay::None) {
8987 ChangeRegisteredDisplayNoneStyleFor(aElement, newContext);
8988 } else {
8989 ChangeRegisteredDisplayContentsStyleFor(aElement, newContext);
8990 }
8991
8992 const nsStyleDisplay* disp = newContext->StyleDisplay();
8993 if (oldDisplay == disp->mDisplay) {
8994 // We can skip trying to recreate frames here, but only if our style
8995 // context does not have a binding URI that differs from our old one.
8996 // Otherwise, we should try to recreate, because we may want to apply the
8997 // new binding
8998 if (!disp->mBinding) {
8999 return newContext;
9000 }
9001 const nsStyleDisplay* oldDisp = oldContext->PeekStyleDisplay();
9002 if (oldDisp &&
9003 DefinitelyEqualURIsAndPrincipal(disp->mBinding, oldDisp->mBinding)) {
9004 return newContext;
9005 }
9006 }
9007
9008 RecreateFramesForContent(aElement, InsertionKind::Sync);
9009 return nullptr;
9010 }
9011 #endif
9012
IsWhitespaceFrame(nsIFrame * aFrame)9013 static bool IsWhitespaceFrame(nsIFrame* aFrame) {
9014 MOZ_ASSERT(aFrame, "invalid argument");
9015 return aFrame->IsTextFrame() && aFrame->GetContent()->TextIsOnlyWhitespace();
9016 }
9017
FindFirstNonWhitespaceChild(nsIFrame * aParentFrame)9018 static nsIFrame* FindFirstNonWhitespaceChild(nsIFrame* aParentFrame) {
9019 nsIFrame* f = aParentFrame->PrincipalChildList().FirstChild();
9020 while (f && IsWhitespaceFrame(f)) {
9021 f = f->GetNextSibling();
9022 }
9023 return f;
9024 }
9025
FindNextNonWhitespaceSibling(nsIFrame * aFrame)9026 static nsIFrame* FindNextNonWhitespaceSibling(nsIFrame* aFrame) {
9027 nsIFrame* f = aFrame;
9028 do {
9029 f = f->GetNextSibling();
9030 } while (f && IsWhitespaceFrame(f));
9031 return f;
9032 }
9033
FindPreviousNonWhitespaceSibling(nsIFrame * aFrame)9034 static nsIFrame* FindPreviousNonWhitespaceSibling(nsIFrame* aFrame) {
9035 nsIFrame* f = aFrame;
9036 do {
9037 f = f->GetPrevSibling();
9038 } while (f && IsWhitespaceFrame(f));
9039 return f;
9040 }
9041
MaybeRecreateContainerForFrameRemoval(nsIFrame * aFrame)9042 bool nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(
9043 nsIFrame* aFrame) {
9044 NS_PRECONDITION(aFrame, "Must have a frame");
9045 NS_PRECONDITION(aFrame->GetParent(), "Frame shouldn't be root");
9046 NS_PRECONDITION(aFrame == aFrame->FirstContinuation(),
9047 "aFrame not the result of GetPrimaryFrame()?");
9048
9049 if (IsFramePartOfIBSplit(aFrame)) {
9050 // The removal functions can't handle removal of an {ib} split directly; we
9051 // need to rebuild the containing block.
9052 #ifdef DEBUG
9053 if (gNoisyContentUpdates) {
9054 printf(
9055 "nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval: "
9056 "frame=");
9057 nsFrame::ListTag(stdout, aFrame);
9058 printf(" is ib-split\n");
9059 }
9060 #endif
9061
9062 ReframeContainingBlock(aFrame);
9063 return true;
9064 }
9065
9066 nsContainerFrame* insertionFrame = aFrame->GetContentInsertionFrame();
9067 if (insertionFrame && insertionFrame->IsLegendFrame() &&
9068 aFrame->GetParent()->IsFieldSetFrame()) {
9069 RecreateFramesForContent(aFrame->GetParent()->GetContent(),
9070 InsertionKind::Async);
9071 return true;
9072 }
9073
9074 nsIFrame* inFlowFrame = (aFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW)
9075 ? aFrame->GetPlaceholderFrame()
9076 : aFrame;
9077 MOZ_ASSERT(inFlowFrame, "How did that happen?");
9078 MOZ_ASSERT(inFlowFrame == inFlowFrame->FirstContinuation(),
9079 "placeholder for primary frame has previous continuations?");
9080 nsIFrame* parent = inFlowFrame->GetParent();
9081
9082 if (parent && parent->IsDetailsFrame()) {
9083 HTMLSummaryElement* summary =
9084 HTMLSummaryElement::FromContent(aFrame->GetContent());
9085 DetailsFrame* detailsFrame = static_cast<DetailsFrame*>(parent);
9086
9087 // Unlike adding summary element cases, we need to check children of the
9088 // parent details frame since at this moment the summary element has been
9089 // already removed from the parent details element's child list.
9090 if (summary && detailsFrame->HasMainSummaryFrame(aFrame)) {
9091 // When removing a summary, we should reframe the parent details frame to
9092 // ensure that another summary is used or the default summary is
9093 // generated.
9094 RecreateFramesForContent(parent->GetContent(), InsertionKind::Async);
9095 return true;
9096 }
9097 }
9098
9099 // Now check for possibly needing to reconstruct due to a pseudo parent
9100 // For the case of ruby pseudo parent, effectively, only pseudo rb/rt frame
9101 // need to be checked here, since all other types of parent will be catched
9102 // by "Check ruby containers" section below.
9103 if (IsTableOrRubyPseudo(parent)) {
9104 if (FindFirstNonWhitespaceChild(parent) == inFlowFrame ||
9105 !FindNextNonWhitespaceSibling(inFlowFrame->LastContinuation()) ||
9106 // If it is a whitespace, and is the only child of the parent, the
9107 // pseudo parent was created for the space, and should now be removed.
9108 (IsWhitespaceFrame(aFrame) &&
9109 parent->PrincipalChildList().OnlyChild()) ||
9110 // If we're a table-column-group, then the OnlyChild check above is
9111 // not going to catch cases when we're the first child.
9112 (inFlowFrame->IsTableColGroupFrame() &&
9113 parent->GetChildList(nsIFrame::kColGroupList).FirstChild() ==
9114 inFlowFrame) ||
9115 // Similar if we're a table-caption.
9116 (inFlowFrame->IsTableCaption() &&
9117 parent->GetChildList(nsIFrame::kCaptionList).FirstChild() ==
9118 inFlowFrame)) {
9119 RecreateFramesForContent(parent->GetContent(), InsertionKind::Async);
9120 return true;
9121 }
9122 }
9123
9124 // Might need to reconstruct things if this frame's nextSibling is a table
9125 // or ruby pseudo, since removal of this frame might mean that this pseudo
9126 // needs to get merged with the frame's prevSibling if that's also a table
9127 // or ruby pseudo.
9128 nsIFrame* nextSibling =
9129 FindNextNonWhitespaceSibling(inFlowFrame->LastContinuation());
9130 NS_ASSERTION(!IsTableOrRubyPseudo(inFlowFrame), "Shouldn't happen here");
9131 // Effectively, for the ruby pseudo sibling case, only pseudo <ruby> frame
9132 // need to be checked here, since all other types of such frames will have
9133 // a ruby container parent, and be catched by "Check ruby containers" below.
9134 if (nextSibling && IsTableOrRubyPseudo(nextSibling)) {
9135 nsIFrame* prevSibling = FindPreviousNonWhitespaceSibling(inFlowFrame);
9136 if (prevSibling && IsTableOrRubyPseudo(prevSibling)) {
9137 #ifdef DEBUG
9138 if (gNoisyContentUpdates) {
9139 printf(
9140 "nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval: "
9141 "frame=");
9142 nsFrame::ListTag(stdout, aFrame);
9143 printf(
9144 " has a table pseudo next sibling of different type and a "
9145 "table pseudo prevsibling\n");
9146 }
9147 #endif
9148 // Good enough to recreate frames for aFrame's parent's content; even if
9149 // aFrame's parent is a pseudo, that'll be the right content node.
9150 RecreateFramesForContent(parent->GetContent(), InsertionKind::Async);
9151 return true;
9152 }
9153 }
9154
9155 // Check ruby containers
9156 LayoutFrameType parentType = parent->Type();
9157 if (parentType == LayoutFrameType::Ruby ||
9158 RubyUtils::IsRubyContainerBox(parentType)) {
9159 // In ruby containers, pseudo frames may be created from
9160 // whitespaces or even nothing. There are two cases we actually
9161 // need to handle here, but hard to check exactly:
9162 // 1. Status of spaces beside the frame may vary, and related
9163 // frames may be constructed or destroyed accordingly.
9164 // 2. The type of the first child of a ruby frame determines
9165 // whether a pseudo ruby base container should exist.
9166 RecreateFramesForContent(parent->GetContent(), InsertionKind::Async);
9167 return true;
9168 }
9169
9170 // Might need to reconstruct things if the removed frame's nextSibling is an
9171 // anonymous flex item. The removed frame might've been what divided two
9172 // runs of inline content into two anonymous flex items, which would now
9173 // need to be merged.
9174 // NOTE: It's fine that we've advanced nextSibling past whitespace (up above);
9175 // we're only interested in anonymous flex items here, and those can never
9176 // be adjacent to whitespace, since they absorb contiguous runs of inline
9177 // non-replaced content (including whitespace).
9178 if (nextSibling && IsAnonymousFlexOrGridItem(nextSibling)) {
9179 AssertAnonymousFlexOrGridItemParent(nextSibling, parent);
9180 #ifdef DEBUG
9181 if (gNoisyContentUpdates) {
9182 printf(
9183 "nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval: "
9184 "frame=");
9185 nsFrame::ListTag(stdout, aFrame);
9186 printf(" has an anonymous flex item as its next sibling\n");
9187 }
9188 #endif // DEBUG
9189 // Recreate frames for the flex container (the removed frame's parent)
9190 RecreateFramesForContent(parent->GetContent(), InsertionKind::Async);
9191 return true;
9192 }
9193
9194 // Might need to reconstruct things if the removed frame's nextSibling is
9195 // null and its parent is an anonymous flex item. (This might be the last
9196 // remaining child of that anonymous flex item, which can then go away.)
9197 if (!nextSibling && IsAnonymousFlexOrGridItem(parent)) {
9198 AssertAnonymousFlexOrGridItemParent(parent, parent->GetParent());
9199 #ifdef DEBUG
9200 if (gNoisyContentUpdates) {
9201 printf(
9202 "nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval: "
9203 "frame=");
9204 nsFrame::ListTag(stdout, aFrame);
9205 printf(" has an anonymous flex item as its parent\n");
9206 }
9207 #endif // DEBUG
9208 // Recreate frames for the flex container (the removed frame's grandparent)
9209 RecreateFramesForContent(parent->GetParent()->GetContent(),
9210 InsertionKind::Async);
9211 return true;
9212 }
9213
9214 #ifdef MOZ_XUL
9215 if (aFrame->IsPopupSetFrame()) {
9216 nsIRootBox* rootBox = nsIRootBox::GetRootBox(mPresShell);
9217 if (rootBox && rootBox->GetPopupSetFrame() == aFrame) {
9218 ReconstructDocElementHierarchy(InsertionKind::Async);
9219 return true;
9220 }
9221 }
9222 #endif
9223
9224 // Reconstruct if inflowFrame is parent's only child, and parent is, or has,
9225 // a non-fluid continuation, i.e. it was split by bidi resolution
9226 if (!inFlowFrame->GetPrevSibling() && !inFlowFrame->GetNextSibling() &&
9227 ((parent->GetPrevContinuation() && !parent->GetPrevInFlow()) ||
9228 (parent->GetNextContinuation() && !parent->GetNextInFlow()))) {
9229 RecreateFramesForContent(parent->GetContent(), InsertionKind::Async);
9230 return true;
9231 }
9232
9233 // We might still need to reconstruct things if the parent of inFlowFrame is
9234 // ib-split, since in that case the removal of aFrame might affect the
9235 // splitting of its parent.
9236 if (!IsFramePartOfIBSplit(parent)) {
9237 return false;
9238 }
9239
9240 // If inFlowFrame is not the only in-flow child of |parent|, then removing
9241 // it will change nothing about the {ib} split.
9242 if (inFlowFrame != parent->PrincipalChildList().FirstChild() ||
9243 inFlowFrame->LastContinuation()->GetNextSibling()) {
9244 return false;
9245 }
9246
9247 // If the parent is the first or last part of the {ib} split, then
9248 // removing one of its kids will have no effect on the splitting.
9249 // Get the first continuation up front so we don't have to do it twice.
9250 nsIFrame* parentFirstContinuation = parent->FirstContinuation();
9251 if (!GetIBSplitSibling(parentFirstContinuation) ||
9252 !GetIBSplitPrevSibling(parentFirstContinuation)) {
9253 return false;
9254 }
9255
9256 #ifdef DEBUG
9257 if (gNoisyContentUpdates) {
9258 printf(
9259 "nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval: "
9260 "frame=");
9261 nsFrame::ListTag(stdout, parent);
9262 printf(" is ib-split\n");
9263 }
9264 #endif
9265
9266 ReframeContainingBlock(parent);
9267 return true;
9268 }
9269
UpdateTableCellSpans(nsIContent * aContent)9270 void nsCSSFrameConstructor::UpdateTableCellSpans(nsIContent* aContent) {
9271 nsTableCellFrame* cellFrame = do_QueryFrame(aContent->GetPrimaryFrame());
9272
9273 // It's possible that this warning could fire if some other style change
9274 // simultaneously changes the 'display' of the element and makes it no
9275 // longer be a table cell.
9276 NS_WARNING_ASSERTION(cellFrame, "Hint should only be posted on table cells!");
9277
9278 if (cellFrame) {
9279 cellFrame->GetTableFrame()->RowOrColSpanChanged(cellFrame);
9280 }
9281 }
9282
RecreateFramesForContent(nsIContent * aContent,InsertionKind aInsertionKind)9283 void nsCSSFrameConstructor::RecreateFramesForContent(
9284 nsIContent* aContent, InsertionKind aInsertionKind) {
9285 MOZ_ASSERT(aContent);
9286
9287 // If there is no document, we don't want to recreate frames for it. (You
9288 // shouldn't generally be giving this method content without a document
9289 // anyway).
9290 // Rebuilding the frame tree can have bad effects, especially if it's the
9291 // frame tree for chrome (see bug 157322).
9292 if (NS_WARN_IF(!aContent->GetComposedDoc())) {
9293 return;
9294 }
9295
9296 // Is the frame ib-split? If so, we need to reframe the containing
9297 // block *here*, rather than trying to remove and re-insert the
9298 // content (which would otherwise result in *two* nested reframe
9299 // containing block from ContentRemoved() and ContentInserted(),
9300 // below!). We'd really like to optimize away one of those
9301 // containing block reframes, hence the code here.
9302
9303 nsIFrame* frame = aContent->GetPrimaryFrame();
9304 if (frame && frame->IsFrameOfType(nsIFrame::eMathML)) {
9305 // Reframe the topmost MathML element to prevent exponential blowup
9306 // (see bug 397518)
9307 while (true) {
9308 nsIContent* parentContent = aContent->GetParent();
9309 nsIFrame* parentContentFrame = parentContent->GetPrimaryFrame();
9310 if (!parentContentFrame ||
9311 !parentContentFrame->IsFrameOfType(nsIFrame::eMathML))
9312 break;
9313 aContent = parentContent;
9314 frame = parentContentFrame;
9315 }
9316 }
9317
9318 if (frame) {
9319 nsIFrame* nonGeneratedAncestor =
9320 nsLayoutUtils::GetNonGeneratedAncestor(frame);
9321 if (nonGeneratedAncestor->GetContent() != aContent) {
9322 return RecreateFramesForContent(nonGeneratedAncestor->GetContent(),
9323 InsertionKind::Async);
9324 }
9325
9326 if (frame->GetStateBits() & NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT) {
9327 // Recreate the frames for the entire nsIAnonymousContentCreator tree
9328 // since |frame| or one of its descendants may need an nsStyleContext
9329 // that associates it to a CSS pseudo-element, and only the
9330 // nsIAnonymousContentCreator that created this content knows how to make
9331 // that happen.
9332 nsIAnonymousContentCreator* acc = nullptr;
9333 nsIFrame* ancestor = nsLayoutUtils::GetParentOrPlaceholderFor(frame);
9334 while (!(acc = do_QueryFrame(ancestor))) {
9335 ancestor = nsLayoutUtils::GetParentOrPlaceholderFor(ancestor);
9336 }
9337 NS_ASSERTION(acc,
9338 "Where is the nsIAnonymousContentCreator? We may fail "
9339 "to recreate its content correctly");
9340 // nsSVGUseFrame is special, and we know this is unnecessary for it.
9341 if (!ancestor->IsSVGUseFrame()) {
9342 NS_ASSERTION(aContent->IsInNativeAnonymousSubtree(),
9343 "Why is NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT set?");
9344 return RecreateFramesForContent(ancestor->GetContent(),
9345 InsertionKind::Async);
9346 }
9347 }
9348
9349 nsIFrame* parent = frame->GetParent();
9350 nsIContent* parentContent = parent ? parent->GetContent() : nullptr;
9351 // If the parent frame is a leaf then the subsequent insert will fail to
9352 // create a frame, so we need to recreate the parent content. This happens
9353 // with native anonymous content from the editor.
9354 if (parent && parent->IsLeaf() && parentContent &&
9355 parentContent != aContent) {
9356 return RecreateFramesForContent(parentContent, InsertionKind::Async);
9357 }
9358 }
9359
9360 if (frame && MaybeRecreateContainerForFrameRemoval(frame)) {
9361 return;
9362 }
9363
9364 nsINode* containerNode = aContent->GetParentNode();
9365 // XXXbz how can containerNode be null here?
9366 if (containerNode) {
9367 // Before removing the frames associated with the content object,
9368 // ask them to save their state onto a temporary state object.
9369 CaptureStateForFramesOf(aContent, mTempFrameTreeState);
9370
9371 // Need the nsIContent parent, which might be null here, since we need to
9372 // pass it to ContentInserted and ContentRemoved.
9373 nsIContent* container = aContent->GetParent();
9374
9375 // Remove the frames associated with the content object.
9376 nsIContent* nextSibling = aContent->IsRootOfAnonymousSubtree()
9377 ? nullptr
9378 : aContent->GetNextSibling();
9379 bool didReconstruct = ContentRemoved(container, aContent, nextSibling,
9380 REMOVE_FOR_RECONSTRUCTION);
9381
9382 if (!didReconstruct) {
9383 if (aInsertionKind == InsertionKind::Async && aContent->IsElement()) {
9384 // FIXME(emilio, bug 1397239): There's nothing removing the frame state
9385 // for elements that go away before we come back to the frame
9386 // constructor.
9387 //
9388 // Also, it'd be nice to just use the `ContentRangeInserted` path for
9389 // both elements and non-elements, but we need to make lazy frame
9390 // construction to apply to all elements first.
9391 RestyleManager()->PostRestyleEvent(aContent->AsElement(),
9392 nsRestyleHint(0),
9393 nsChangeHint_ReconstructFrame);
9394 } else {
9395 // Now, recreate the frames associated with this content object. If
9396 // ContentRemoved triggered reconstruction, then we don't need to do
9397 // this because the frames will already have been built.
9398 ContentRangeInserted(container, aContent, aContent->GetNextSibling(),
9399 mTempFrameTreeState, aInsertionKind, nullptr);
9400 }
9401 }
9402 }
9403 }
9404
DestroyFramesFor(Element * aElement)9405 bool nsCSSFrameConstructor::DestroyFramesFor(Element* aElement) {
9406 MOZ_ASSERT(aElement && aElement->GetParentNode());
9407
9408 nsIContent* nextSibling = aElement->IsRootOfAnonymousSubtree()
9409 ? nullptr
9410 : aElement->GetNextSibling();
9411
9412 CaptureStateForFramesOf(aElement, mTempFrameTreeState);
9413 return ContentRemoved(aElement->GetParent(), aElement, nextSibling,
9414 REMOVE_FOR_RECONSTRUCTION);
9415 }
9416
9417 //////////////////////////////////////////////////////////////////////
9418
9419 // Block frame construction code
9420
GetFirstLetterStyle(nsIContent * aContent,nsStyleContext * aStyleContext)9421 already_AddRefed<nsStyleContext> nsCSSFrameConstructor::GetFirstLetterStyle(
9422 nsIContent* aContent, nsStyleContext* aStyleContext) {
9423 if (aContent) {
9424 return mPresShell->StyleSet()->ResolvePseudoElementStyle(
9425 aContent->AsElement(), CSSPseudoElementType::firstLetter, aStyleContext,
9426 nullptr);
9427 }
9428 return nullptr;
9429 }
9430
GetFirstLineStyle(nsIContent * aContent,nsStyleContext * aStyleContext)9431 already_AddRefed<nsStyleContext> nsCSSFrameConstructor::GetFirstLineStyle(
9432 nsIContent* aContent, nsStyleContext* aStyleContext) {
9433 if (aContent) {
9434 return mPresShell->StyleSet()->ResolvePseudoElementStyle(
9435 aContent->AsElement(), CSSPseudoElementType::firstLine, aStyleContext,
9436 nullptr);
9437 }
9438 return nullptr;
9439 }
9440
9441 // Predicate to see if a given content (block element) has
9442 // first-letter style applied to it.
ShouldHaveFirstLetterStyle(nsIContent * aContent,nsStyleContext * aStyleContext)9443 bool nsCSSFrameConstructor::ShouldHaveFirstLetterStyle(
9444 nsIContent* aContent, nsStyleContext* aStyleContext) {
9445 return nsLayoutUtils::HasPseudoStyle(aContent, aStyleContext,
9446 CSSPseudoElementType::firstLetter,
9447 mPresShell->GetPresContext());
9448 }
9449
HasFirstLetterStyle(nsIFrame * aBlockFrame)9450 bool nsCSSFrameConstructor::HasFirstLetterStyle(nsIFrame* aBlockFrame) {
9451 NS_PRECONDITION(aBlockFrame, "Need a frame");
9452 NS_ASSERTION(nsLayoutUtils::GetAsBlock(aBlockFrame), "Not a block frame?");
9453
9454 return (aBlockFrame->GetStateBits() & NS_BLOCK_HAS_FIRST_LETTER_STYLE) != 0;
9455 }
9456
ShouldHaveFirstLineStyle(nsIContent * aContent,nsStyleContext * aStyleContext)9457 bool nsCSSFrameConstructor::ShouldHaveFirstLineStyle(
9458 nsIContent* aContent, nsStyleContext* aStyleContext) {
9459 bool hasFirstLine = nsLayoutUtils::HasPseudoStyle(
9460 aContent, aStyleContext, CSSPseudoElementType::firstLine,
9461 mPresShell->GetPresContext());
9462 if (hasFirstLine) {
9463 // But disable for fieldsets
9464 int32_t namespaceID;
9465 nsAtom* tag =
9466 mDocument->BindingManager()->ResolveTag(aContent, &namespaceID);
9467 // This check must match the one in FindHTMLData.
9468 hasFirstLine =
9469 tag != nsGkAtoms::fieldset || namespaceID != kNameSpaceID_XHTML;
9470 }
9471
9472 return hasFirstLine;
9473 }
9474
ShouldHaveSpecialBlockStyle(nsIContent * aContent,nsStyleContext * aStyleContext,bool * aHaveFirstLetterStyle,bool * aHaveFirstLineStyle)9475 void nsCSSFrameConstructor::ShouldHaveSpecialBlockStyle(
9476 nsIContent* aContent, nsStyleContext* aStyleContext,
9477 bool* aHaveFirstLetterStyle, bool* aHaveFirstLineStyle) {
9478 *aHaveFirstLetterStyle = ShouldHaveFirstLetterStyle(aContent, aStyleContext);
9479 *aHaveFirstLineStyle = ShouldHaveFirstLineStyle(aContent, aStyleContext);
9480 }
9481
9482 /* static */
9483 const nsCSSFrameConstructor::PseudoParentData
9484 nsCSSFrameConstructor::sPseudoParentData[eParentTypeCount] = {
9485 {// Cell
9486 FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET |
9487 FCDATA_USE_CHILD_ITEMS |
9488 FCDATA_IS_WRAPPER_ANON_BOX |
9489 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRow),
9490 &nsCSSFrameConstructor::ConstructTableCell),
9491 &nsCSSAnonBoxes::tableCell},
9492 {// Row
9493 FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET |
9494 FCDATA_USE_CHILD_ITEMS |
9495 FCDATA_IS_WRAPPER_ANON_BOX |
9496 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRowGroup),
9497 &nsCSSFrameConstructor::ConstructTableRowOrRowGroup),
9498 &nsCSSAnonBoxes::tableRow},
9499 {// Row group
9500 FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET |
9501 FCDATA_USE_CHILD_ITEMS |
9502 FCDATA_IS_WRAPPER_ANON_BOX |
9503 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
9504 &nsCSSFrameConstructor::ConstructTableRowOrRowGroup),
9505 &nsCSSAnonBoxes::tableRowGroup},
9506 {// Column group
9507 FCDATA_DECL(
9508 FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET |
9509 FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_USE_CHILD_ITEMS |
9510 FCDATA_SKIP_ABSPOS_PUSH |
9511 // Not FCDATA_IS_WRAPPER_ANON_BOX, because we don't need to
9512 // restyle these: they have non-inheriting style contexts.
9513 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
9514 NS_NewTableColGroupFrame),
9515 &nsCSSAnonBoxes::tableColGroup},
9516 {// Table
9517 FULL_CTOR_FCDATA(FCDATA_SKIP_FRAMESET | FCDATA_USE_CHILD_ITEMS |
9518 FCDATA_IS_WRAPPER_ANON_BOX,
9519 &nsCSSFrameConstructor::ConstructTable),
9520 &nsCSSAnonBoxes::table},
9521 {// Ruby
9522 FCDATA_DECL(FCDATA_IS_LINE_PARTICIPANT | FCDATA_USE_CHILD_ITEMS |
9523 FCDATA_IS_WRAPPER_ANON_BOX | FCDATA_SKIP_FRAMESET,
9524 NS_NewRubyFrame),
9525 &nsCSSAnonBoxes::ruby},
9526 {// Ruby Base
9527 FCDATA_DECL(
9528 FCDATA_USE_CHILD_ITEMS | FCDATA_IS_LINE_PARTICIPANT |
9529 FCDATA_IS_WRAPPER_ANON_BOX |
9530 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRubyBaseContainer) |
9531 FCDATA_SKIP_FRAMESET,
9532 NS_NewRubyBaseFrame),
9533 &nsCSSAnonBoxes::rubyBase},
9534 {// Ruby Base Container
9535 FCDATA_DECL(FCDATA_USE_CHILD_ITEMS | FCDATA_IS_LINE_PARTICIPANT |
9536 FCDATA_IS_WRAPPER_ANON_BOX |
9537 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRuby) |
9538 FCDATA_SKIP_FRAMESET,
9539 NS_NewRubyBaseContainerFrame),
9540 &nsCSSAnonBoxes::rubyBaseContainer},
9541 {// Ruby Text
9542 FCDATA_DECL(
9543 FCDATA_USE_CHILD_ITEMS | FCDATA_IS_LINE_PARTICIPANT |
9544 FCDATA_IS_WRAPPER_ANON_BOX |
9545 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRubyTextContainer) |
9546 FCDATA_SKIP_FRAMESET,
9547 NS_NewRubyTextFrame),
9548 &nsCSSAnonBoxes::rubyText},
9549 {// Ruby Text Container
9550 FCDATA_DECL(FCDATA_USE_CHILD_ITEMS | FCDATA_IS_WRAPPER_ANON_BOX |
9551 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRuby) |
9552 FCDATA_SKIP_FRAMESET,
9553 NS_NewRubyTextContainerFrame),
9554 &nsCSSAnonBoxes::rubyTextContainer}};
9555
CreateNeededAnonFlexOrGridItems(nsFrameConstructorState & aState,FrameConstructionItemList & aItems,nsIFrame * aParentFrame)9556 void nsCSSFrameConstructor::CreateNeededAnonFlexOrGridItems(
9557 nsFrameConstructorState& aState, FrameConstructionItemList& aItems,
9558 nsIFrame* aParentFrame) {
9559 if (aItems.IsEmpty() || !::IsFlexOrGridContainer(aParentFrame)) {
9560 return;
9561 }
9562
9563 const bool isLegacyBox = IsFlexContainerForLegacyBox(aParentFrame);
9564 FCItemIterator iter(aItems);
9565 do {
9566 // Advance iter past children that don't want to be wrapped
9567 if (iter.SkipItemsThatDontNeedAnonFlexOrGridItem(aState, isLegacyBox)) {
9568 // Hit the end of the items without finding any remaining children that
9569 // need to be wrapped. We're finished!
9570 return;
9571 }
9572
9573 // If our next potentially-wrappable child is whitespace, then see if
9574 // there's anything wrappable immediately after it. If not, we just drop
9575 // the whitespace and move on. (We're not supposed to create any anonymous
9576 // flex/grid items that _only_ contain whitespace).
9577 // (BUT if this is generated content, then we don't give whitespace nodes
9578 // any special treatment, because they're probably not really whitespace --
9579 // they're just temporarily empty, waiting for their generated text.)
9580 // XXXdholbert If this node's generated text will *actually end up being
9581 // entirely whitespace*, then we technically should still skip over it, per
9582 // the CSS grid & flexbox specs. I'm not bothering with that at this point,
9583 // since it's a pretty extreme edge case.
9584 if (!aParentFrame->IsGeneratedContentFrame() &&
9585 iter.item().IsWhitespace(aState)) {
9586 FCItemIterator afterWhitespaceIter(iter);
9587 bool hitEnd = afterWhitespaceIter.SkipWhitespace(aState);
9588 bool nextChildNeedsAnonItem =
9589 !hitEnd && afterWhitespaceIter.item().NeedsAnonFlexOrGridItem(
9590 aState, isLegacyBox);
9591
9592 if (!nextChildNeedsAnonItem) {
9593 // There's nothing after the whitespace that we need to wrap, so we
9594 // just drop this run of whitespace.
9595 iter.DeleteItemsTo(this, afterWhitespaceIter);
9596 if (hitEnd) {
9597 // Nothing left to do -- we're finished!
9598 return;
9599 }
9600 // else, we have a next child and it does not want to be wrapped. So,
9601 // we jump back to the beginning of the loop to skip over that child
9602 // (and anything else non-wrappable after it)
9603 MOZ_ASSERT(!iter.IsDone() && !iter.item().NeedsAnonFlexOrGridItem(
9604 aState, isLegacyBox),
9605 "hitEnd and/or nextChildNeedsAnonItem lied");
9606 continue;
9607 }
9608 }
9609
9610 // Now |iter| points to the first child that needs to be wrapped in an
9611 // anonymous flex/grid item. Now we see how many children after it also want
9612 // to be wrapped in an anonymous flex/grid item.
9613 FCItemIterator endIter(iter); // iterator to find the end of the group
9614 endIter.SkipItemsThatNeedAnonFlexOrGridItem(aState, isLegacyBox);
9615
9616 NS_ASSERTION(iter != endIter,
9617 "Should've had at least one wrappable child to seek past");
9618
9619 // Now, we create the anonymous flex or grid item to contain the children
9620 // between |iter| and |endIter|.
9621 nsAtom* pseudoType = (aParentFrame->IsFlexContainerFrame())
9622 ? nsCSSAnonBoxes::anonymousFlexItem
9623 : nsCSSAnonBoxes::anonymousGridItem;
9624 nsStyleContext* parentStyle = aParentFrame->StyleContext();
9625 nsIContent* parentContent = aParentFrame->GetContent();
9626 already_AddRefed<nsStyleContext> wrapperStyle =
9627 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(pseudoType,
9628 parentStyle);
9629
9630 static const FrameConstructionData sBlockFormattingContextFCData =
9631 FCDATA_DECL(FCDATA_SKIP_FRAMESET | FCDATA_USE_CHILD_ITEMS |
9632 FCDATA_IS_WRAPPER_ANON_BOX,
9633 NS_NewBlockFormattingContext);
9634
9635 FrameConstructionItem* newItem = new (this)
9636 FrameConstructionItem(&sBlockFormattingContextFCData,
9637 // Use the content of our parent frame
9638 parentContent,
9639 // Lie about the tag; it doesn't matter anyway
9640 pseudoType, iter.item().mNameSpaceID,
9641 // no pending binding
9642 nullptr, wrapperStyle, true, nullptr);
9643
9644 newItem->mIsAllInline = newItem->mHasInlineEnds =
9645 newItem->mStyleContext->StyleDisplay()->IsInlineOutsideStyle();
9646 newItem->mIsBlock = !newItem->mIsAllInline;
9647
9648 MOZ_ASSERT(!newItem->mIsAllInline && newItem->mIsBlock,
9649 "expecting anonymous flex/grid items to be block-level "
9650 "(this will make a difference when we encounter "
9651 "'align-items: baseline')");
9652
9653 // Anonymous flex and grid items induce line boundaries around their
9654 // contents.
9655 newItem->mChildItems.SetLineBoundaryAtStart(true);
9656 newItem->mChildItems.SetLineBoundaryAtEnd(true);
9657 // The parent of the items in aItems is also the parent of the items
9658 // in mChildItems
9659 newItem->mChildItems.SetParentHasNoXBLChildren(
9660 aItems.ParentHasNoXBLChildren());
9661
9662 // Eat up all items between |iter| and |endIter| and put them in our
9663 // wrapper. This advances |iter| to point to |endIter|.
9664 iter.AppendItemsToList(this, endIter, newItem->mChildItems);
9665
9666 iter.InsertItem(newItem);
9667 } while (!iter.IsDone());
9668 }
9669
9670 /* static */ nsCSSFrameConstructor::RubyWhitespaceType
ComputeRubyWhitespaceType(StyleDisplay aPrevDisplay,StyleDisplay aNextDisplay)9671 nsCSSFrameConstructor::ComputeRubyWhitespaceType(StyleDisplay aPrevDisplay,
9672 StyleDisplay aNextDisplay) {
9673 MOZ_ASSERT(nsStyleDisplay::IsRubyDisplayType(aPrevDisplay) &&
9674 nsStyleDisplay::IsRubyDisplayType(aNextDisplay));
9675 if (aPrevDisplay == aNextDisplay &&
9676 (aPrevDisplay == StyleDisplay::RubyBase ||
9677 aPrevDisplay == StyleDisplay::RubyText)) {
9678 return eRubyInterLeafWhitespace;
9679 }
9680 if (aNextDisplay == StyleDisplay::RubyText ||
9681 aNextDisplay == StyleDisplay::RubyTextContainer) {
9682 return eRubyInterLevelWhitespace;
9683 }
9684 return eRubyInterSegmentWhitespace;
9685 }
9686
9687 /**
9688 * This function checks the content from |aStartIter| to |aEndIter|,
9689 * determines whether it contains only whitespace, and if yes,
9690 * interprets the type of whitespace. This method does not change
9691 * any of the iters.
9692 */
9693 /* static */ nsCSSFrameConstructor::RubyWhitespaceType
InterpretRubyWhitespace(nsFrameConstructorState & aState,const FCItemIterator & aStartIter,const FCItemIterator & aEndIter)9694 nsCSSFrameConstructor::InterpretRubyWhitespace(nsFrameConstructorState& aState,
9695 const FCItemIterator& aStartIter,
9696 const FCItemIterator& aEndIter) {
9697 if (!aStartIter.item().IsWhitespace(aState)) {
9698 return eRubyNotWhitespace;
9699 }
9700
9701 FCItemIterator spaceEndIter(aStartIter);
9702 spaceEndIter.SkipWhitespace(aState);
9703 if (spaceEndIter != aEndIter) {
9704 return eRubyNotWhitespace;
9705 }
9706
9707 // Any leading or trailing whitespace in non-pseudo ruby box
9708 // should have been trimmed, hence there should not be any
9709 // whitespace at the start or the end.
9710 MOZ_ASSERT(!aStartIter.AtStart() && !aEndIter.IsDone());
9711 FCItemIterator prevIter(aStartIter);
9712 prevIter.Prev();
9713 return ComputeRubyWhitespaceType(
9714 prevIter.item().mStyleContext->StyleDisplay()->mDisplay,
9715 aEndIter.item().mStyleContext->StyleDisplay()->mDisplay);
9716 }
9717
9718 /**
9719 * This function eats up consecutive items which do not want the current
9720 * parent into either a ruby base box or a ruby text box. When it
9721 * returns, |aIter| points to the first item it doesn't wrap.
9722 */
WrapItemsInPseudoRubyLeafBox(FCItemIterator & aIter,nsStyleContext * aParentStyle,nsIContent * aParentContent)9723 void nsCSSFrameConstructor::WrapItemsInPseudoRubyLeafBox(
9724 FCItemIterator& aIter, nsStyleContext* aParentStyle,
9725 nsIContent* aParentContent) {
9726 StyleDisplay parentDisplay = aParentStyle->StyleDisplay()->mDisplay;
9727 ParentType parentType, wrapperType;
9728 if (parentDisplay == StyleDisplay::RubyTextContainer) {
9729 parentType = eTypeRubyTextContainer;
9730 wrapperType = eTypeRubyText;
9731 } else {
9732 MOZ_ASSERT(parentDisplay == StyleDisplay::RubyBaseContainer);
9733 parentType = eTypeRubyBaseContainer;
9734 wrapperType = eTypeRubyBase;
9735 }
9736
9737 MOZ_ASSERT(aIter.item().DesiredParentType() != parentType,
9738 "Should point to something needs to be wrapped.");
9739
9740 FCItemIterator endIter(aIter);
9741 endIter.SkipItemsNotWantingParentType(parentType);
9742
9743 WrapItemsInPseudoParent(aParentContent, aParentStyle, wrapperType, aIter,
9744 endIter);
9745 }
9746
9747 /**
9748 * This function eats up consecutive items into a ruby level container.
9749 * It may create zero or one level container. When it returns, |aIter|
9750 * points to the first item it doesn't wrap.
9751 */
WrapItemsInPseudoRubyLevelContainer(nsFrameConstructorState & aState,FCItemIterator & aIter,nsStyleContext * aParentStyle,nsIContent * aParentContent)9752 void nsCSSFrameConstructor::WrapItemsInPseudoRubyLevelContainer(
9753 nsFrameConstructorState& aState, FCItemIterator& aIter,
9754 nsStyleContext* aParentStyle, nsIContent* aParentContent) {
9755 MOZ_ASSERT(aIter.item().DesiredParentType() != eTypeRuby,
9756 "Pointing to a level container?");
9757
9758 FrameConstructionItem& firstItem = aIter.item();
9759 ParentType wrapperType = firstItem.DesiredParentType();
9760 if (wrapperType != eTypeRubyTextContainer) {
9761 // If the first item is not ruby text,
9762 // it should be in a base container.
9763 wrapperType = eTypeRubyBaseContainer;
9764 }
9765
9766 FCItemIterator endIter(aIter);
9767 do {
9768 if (endIter.SkipItemsWantingParentType(wrapperType) ||
9769 // If the skipping above stops at some item which wants a
9770 // different ruby parent, then we have finished.
9771 IsRubyParentType(endIter.item().DesiredParentType())) {
9772 // No more items need to be wrapped in this level container.
9773 break;
9774 }
9775
9776 FCItemIterator contentEndIter(endIter);
9777 contentEndIter.SkipItemsNotWantingRubyParent();
9778 // endIter must be on something doesn't want a ruby parent.
9779 MOZ_ASSERT(contentEndIter != endIter);
9780
9781 // InterpretRubyWhitespace depends on the fact that any leading or
9782 // trailing whitespace described in the spec have been trimmed at
9783 // this point. With this precondition, it is safe not to check
9784 // whether contentEndIter has been done.
9785 RubyWhitespaceType whitespaceType =
9786 InterpretRubyWhitespace(aState, endIter, contentEndIter);
9787 if (whitespaceType == eRubyInterLevelWhitespace) {
9788 // Remove inter-level whitespace.
9789 bool atStart = (aIter == endIter);
9790 endIter.DeleteItemsTo(this, contentEndIter);
9791 if (atStart) {
9792 aIter = endIter;
9793 }
9794 } else if (whitespaceType == eRubyInterSegmentWhitespace) {
9795 // If this level container starts with inter-segment whitespaces,
9796 // wrap them. Break at contentEndIter. Otherwise, leave it here.
9797 // Break at endIter. They will be wrapped when we are here again.
9798 if (aIter == endIter) {
9799 MOZ_ASSERT(wrapperType == eTypeRubyBaseContainer,
9800 "Inter-segment whitespace should be wrapped in rbc");
9801 endIter = contentEndIter;
9802 }
9803 break;
9804 } else if (wrapperType == eTypeRubyTextContainer &&
9805 whitespaceType != eRubyInterLeafWhitespace) {
9806 // Misparented inline content that's not inter-annotation
9807 // whitespace doesn't belong in a pseudo ruby text container.
9808 // Break at endIter.
9809 break;
9810 } else {
9811 endIter = contentEndIter;
9812 }
9813 } while (!endIter.IsDone());
9814
9815 // It is possible that everything our parent wants us to wrap is
9816 // simply an inter-level whitespace, which has been trimmed, or
9817 // an inter-segment whitespace, which will be wrapped later.
9818 // In those cases, don't create anything.
9819 if (aIter != endIter) {
9820 WrapItemsInPseudoParent(aParentContent, aParentStyle, wrapperType, aIter,
9821 endIter);
9822 }
9823 }
9824
9825 /**
9826 * This function trims leading and trailing whitespaces
9827 * in the given item list.
9828 */
TrimLeadingAndTrailingWhitespaces(nsFrameConstructorState & aState,FrameConstructionItemList & aItems)9829 void nsCSSFrameConstructor::TrimLeadingAndTrailingWhitespaces(
9830 nsFrameConstructorState& aState, FrameConstructionItemList& aItems) {
9831 FCItemIterator iter(aItems);
9832 if (!iter.IsDone() && iter.item().IsWhitespace(aState)) {
9833 FCItemIterator spaceEndIter(iter);
9834 spaceEndIter.SkipWhitespace(aState);
9835 iter.DeleteItemsTo(this, spaceEndIter);
9836 }
9837
9838 iter.SetToEnd();
9839 if (!iter.AtStart()) {
9840 FCItemIterator spaceEndIter(iter);
9841 do {
9842 iter.Prev();
9843 if (iter.AtStart()) {
9844 // It's fine to not check the first item, because we
9845 // should have trimmed leading whitespaces above.
9846 break;
9847 }
9848 } while (iter.item().IsWhitespace(aState));
9849 iter.Next();
9850 if (iter != spaceEndIter) {
9851 iter.DeleteItemsTo(this, spaceEndIter);
9852 }
9853 }
9854 }
9855
9856 /**
9857 * This function walks through the child list (aItems) and creates
9858 * needed pseudo ruby boxes to wrap misparented children.
9859 */
CreateNeededPseudoInternalRubyBoxes(nsFrameConstructorState & aState,FrameConstructionItemList & aItems,nsIFrame * aParentFrame)9860 void nsCSSFrameConstructor::CreateNeededPseudoInternalRubyBoxes(
9861 nsFrameConstructorState& aState, FrameConstructionItemList& aItems,
9862 nsIFrame* aParentFrame) {
9863 const ParentType ourParentType = GetParentType(aParentFrame);
9864 if (!IsRubyParentType(ourParentType) ||
9865 aItems.AllWantParentType(ourParentType)) {
9866 return;
9867 }
9868
9869 if (!IsRubyPseudo(aParentFrame)) {
9870 // Normally, ruby pseudo frames start from and end at some elements,
9871 // which means they don't have leading and trailing whitespaces at
9872 // all. But there are two cases where they do actually have leading
9873 // or trailing whitespaces:
9874 // 1. It is an inter-segment whitespace which in an individual ruby
9875 // base container.
9876 // 2. The pseudo frame starts from or ends at consecutive inline
9877 // content, which is not pure whitespace, but includes some.
9878 // In either case, the whitespaces are not the leading or trailing
9879 // whitespaces defined in the spec, and thus should not be trimmed.
9880 TrimLeadingAndTrailingWhitespaces(aState, aItems);
9881 }
9882
9883 FCItemIterator iter(aItems);
9884 nsIContent* parentContent = aParentFrame->GetContent();
9885 nsStyleContext* parentStyle = aParentFrame->StyleContext();
9886 while (!iter.IsDone()) {
9887 if (!iter.SkipItemsWantingParentType(ourParentType)) {
9888 if (ourParentType == eTypeRuby) {
9889 WrapItemsInPseudoRubyLevelContainer(aState, iter, parentStyle,
9890 parentContent);
9891 } else {
9892 WrapItemsInPseudoRubyLeafBox(iter, parentStyle, parentContent);
9893 }
9894 }
9895 }
9896 }
9897
9898 /*
9899 * This function works as follows: we walk through the child list (aItems) and
9900 * find items that cannot have aParentFrame as their parent. We wrap
9901 * continuous runs of such items into a FrameConstructionItem for a frame that
9902 * gets them closer to their desired parents. For example, a run of non-row
9903 * children of a row-group will get wrapped in a row. When we later construct
9904 * the frame for this wrapper (in this case for the row), it'll be the correct
9905 * parent for the cells in the set of items we wrapped or we'll wrap cells
9906 * around everything else. At the end of this method, aItems is guaranteed to
9907 * contain only items for frames that can be direct kids of aParentFrame.
9908 */
CreateNeededPseudoContainers(nsFrameConstructorState & aState,FrameConstructionItemList & aItems,nsIFrame * aParentFrame)9909 void nsCSSFrameConstructor::CreateNeededPseudoContainers(
9910 nsFrameConstructorState& aState, FrameConstructionItemList& aItems,
9911 nsIFrame* aParentFrame) {
9912 ParentType ourParentType = GetParentType(aParentFrame);
9913 if (IsRubyParentType(ourParentType) ||
9914 aItems.AllWantParentType(ourParentType)) {
9915 // Nothing to do here
9916 return;
9917 }
9918
9919 FCItemIterator iter(aItems);
9920 do {
9921 if (iter.SkipItemsWantingParentType(ourParentType)) {
9922 // Nothing else to do here; we're finished
9923 return;
9924 }
9925
9926 // Now we're pointing to the first child that wants a different parent
9927 // type.
9928
9929 // Now try to figure out what kids we can group together. We can generally
9930 // group everything that has a different desired parent type from us. Two
9931 // exceptions to this:
9932 // 1) If our parent type is table, we can't group columns with anything
9933 // else other than whitespace.
9934 // 2) Whitespace that lies between two things we can group which both want
9935 // a non-block parent should be dropped, even if we can't group them
9936 // with each other and even if the whitespace wants a parent of
9937 // ourParentType. Ends of the list count as things that don't want a
9938 // block parent (so that for example we'll drop a whitespace-only list).
9939
9940 FCItemIterator endIter(iter); /* iterator to find the end of the group */
9941 ParentType groupingParentType = endIter.item().DesiredParentType();
9942 if (aItems.AllWantParentType(groupingParentType) &&
9943 groupingParentType != eTypeBlock) {
9944 // Just group them all and be done with it. We need the check for
9945 // eTypeBlock here to catch the "all the items are whitespace" case
9946 // described above.
9947 endIter.SetToEnd();
9948 } else {
9949 // Locate the end of the group.
9950
9951 // Keep track of the type the previous item wanted, in case we have to
9952 // deal with whitespace. Start it off with ourParentType, since that's
9953 // the last thing |iter| would have skipped over.
9954 ParentType prevParentType = ourParentType;
9955 do {
9956 // Walk an iterator past any whitespace that we might be able to drop
9957 // from the list
9958 FCItemIterator spaceEndIter(endIter);
9959 if (prevParentType != eTypeBlock &&
9960 !aParentFrame->IsGeneratedContentFrame() &&
9961 spaceEndIter.item().IsWhitespace(aState)) {
9962 bool trailingSpaces = spaceEndIter.SkipWhitespace(aState);
9963
9964 // We drop the whitespace in the following cases:
9965 // 1) If these are not trailing spaces and the next item wants a table
9966 // or table-part parent
9967 // 2) If these are trailing spaces and aParentFrame is a
9968 // tabular container according to rule 1.3 of CSS 2.1 Sec 17.2.1.
9969 // (Being a tabular container pretty much means ourParentType is
9970 // not eTypeBlock besides the eTypeColGroup case, which won't
9971 // reach here.)
9972 if ((!trailingSpaces &&
9973 IsTableParentType(spaceEndIter.item().DesiredParentType())) ||
9974 (trailingSpaces && ourParentType != eTypeBlock)) {
9975 bool updateStart = (iter == endIter);
9976 endIter.DeleteItemsTo(this, spaceEndIter);
9977 NS_ASSERTION(trailingSpaces == endIter.IsDone(),
9978 "These should match");
9979
9980 if (updateStart) {
9981 iter = endIter;
9982 }
9983
9984 if (trailingSpaces) {
9985 break; /* Found group end */
9986 }
9987
9988 if (updateStart) {
9989 // Update groupingParentType, since it might have been eTypeBlock
9990 // just because of the whitespace.
9991 groupingParentType = iter.item().DesiredParentType();
9992 }
9993 }
9994 }
9995
9996 // Now endIter points to a non-whitespace item or a non-droppable
9997 // whitespace item. In the latter case, if this is the end of the group
9998 // we'll traverse this whitespace again. But it'll all just be quick
9999 // DesiredParentType() checks which will match ourParentType (that's
10000 // what it means that this is the group end), so it's OK.
10001 // However, when we are grouping a ruby parent, and endIter points to
10002 // a non-droppable whitespace, if the next non-whitespace item also
10003 // wants a ruby parent, the whitespace should also be included into
10004 // the current ruby container.
10005 prevParentType = endIter.item().DesiredParentType();
10006 if (prevParentType == ourParentType &&
10007 (endIter == spaceEndIter || spaceEndIter.IsDone() ||
10008 !IsRubyParentType(groupingParentType) ||
10009 !IsRubyParentType(spaceEndIter.item().DesiredParentType()))) {
10010 // End the group at endIter.
10011 break;
10012 }
10013
10014 if (ourParentType == eTypeTable &&
10015 (prevParentType == eTypeColGroup) !=
10016 (groupingParentType == eTypeColGroup)) {
10017 // Either we started with columns and now found something else, or
10018 // vice versa. In any case, end the grouping.
10019 break;
10020 }
10021
10022 // If we have some whitespace that we were not able to drop and there is
10023 // an item after the whitespace that is already properly parented, then
10024 // make sure to include the spaces in our group but stop the group after
10025 // that.
10026 if (spaceEndIter != endIter && !spaceEndIter.IsDone() &&
10027 ourParentType == spaceEndIter.item().DesiredParentType()) {
10028 endIter = spaceEndIter;
10029 break;
10030 }
10031
10032 // Include the whitespace we didn't drop (if any) in the group.
10033 endIter = spaceEndIter;
10034 prevParentType = endIter.item().DesiredParentType();
10035
10036 endIter.Next();
10037 } while (!endIter.IsDone());
10038 }
10039
10040 if (iter == endIter) {
10041 // Nothing to wrap here; just skipped some whitespace
10042 continue;
10043 }
10044
10045 // Now group together all the items between iter and endIter. The right
10046 // parent type to use depends on ourParentType.
10047 ParentType wrapperType;
10048 switch (ourParentType) {
10049 case eTypeRow:
10050 // The parent type for a cell is eTypeBlock, since that's what a cell
10051 // looks like to its kids.
10052 wrapperType = eTypeBlock;
10053 break;
10054 case eTypeRowGroup:
10055 wrapperType = eTypeRow;
10056 break;
10057 case eTypeTable:
10058 // Either colgroup or rowgroup, depending on what we're grouping.
10059 wrapperType =
10060 groupingParentType == eTypeColGroup ? eTypeColGroup : eTypeRowGroup;
10061 break;
10062 case eTypeColGroup:
10063 MOZ_CRASH("Colgroups should be suppresing non-col child items");
10064 default:
10065 NS_ASSERTION(ourParentType == eTypeBlock, "Unrecognized parent type");
10066 if (IsRubyParentType(groupingParentType)) {
10067 wrapperType = eTypeRuby;
10068 } else {
10069 NS_ASSERTION(IsTableParentType(groupingParentType),
10070 "groupingParentType should be either Ruby or table");
10071 wrapperType = eTypeTable;
10072 }
10073 }
10074
10075 nsStyleContext* parentStyle = aParentFrame->StyleContext();
10076 WrapItemsInPseudoParent(aParentFrame->GetContent(), parentStyle,
10077 wrapperType, iter, endIter);
10078
10079 // Now |iter| points to the item that was the first one we didn't wrap;
10080 // loop and see whether we need to skip it or wrap it in something
10081 // different.
10082 } while (!iter.IsDone());
10083 }
10084
10085 /**
10086 * This method wraps frame construction item from |aIter| to
10087 * |aEndIter|. After it returns, aIter points to the first item
10088 * after the wrapper.
10089 */
WrapItemsInPseudoParent(nsIContent * aParentContent,nsStyleContext * aParentStyle,ParentType aWrapperType,FCItemIterator & aIter,const FCItemIterator & aEndIter)10090 void nsCSSFrameConstructor::WrapItemsInPseudoParent(
10091 nsIContent* aParentContent, nsStyleContext* aParentStyle,
10092 ParentType aWrapperType, FCItemIterator& aIter,
10093 const FCItemIterator& aEndIter) {
10094 const PseudoParentData& pseudoData = sPseudoParentData[aWrapperType];
10095 nsAtom* pseudoType = *pseudoData.mPseudoType;
10096 StyleDisplay parentDisplay = aParentStyle->StyleDisplay()->mDisplay;
10097
10098 if (pseudoType == nsCSSAnonBoxes::table &&
10099 (parentDisplay == StyleDisplay::Inline ||
10100 parentDisplay == StyleDisplay::RubyBase ||
10101 parentDisplay == StyleDisplay::RubyText)) {
10102 pseudoType = nsCSSAnonBoxes::inlineTable;
10103 }
10104
10105 already_AddRefed<nsStyleContext> wrapperStyle;
10106 if (pseudoData.mFCData.mBits & FCDATA_IS_WRAPPER_ANON_BOX) {
10107 wrapperStyle = mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
10108 pseudoType, aParentStyle);
10109 } else {
10110 wrapperStyle =
10111 mPresShell->StyleSet()->ResolveNonInheritingAnonymousBoxStyle(
10112 pseudoType);
10113 }
10114
10115 FrameConstructionItem* newItem = new (this)
10116 FrameConstructionItem(&pseudoData.mFCData,
10117 // Use the content of our parent frame
10118 aParentContent,
10119 // Lie about the tag; it doesn't matter anyway
10120 pseudoType,
10121 // The namespace does matter, however; it needs
10122 // to match that of our first child item to
10123 // match the old behavior
10124 aIter.item().mNameSpaceID,
10125 // no pending binding
10126 nullptr, wrapperStyle, true, nullptr);
10127
10128 const nsStyleDisplay* disp = newItem->mStyleContext->StyleDisplay();
10129 // Here we're cheating a tad... technically, table-internal items should be
10130 // inline if aParentFrame is inline, but they'll get wrapped in an
10131 // inline-table in the end, so it'll all work out. In any case, arguably
10132 // we don't need to maintain this state at this point... but it's better
10133 // to, I guess.
10134 newItem->mIsAllInline = newItem->mHasInlineEnds =
10135 disp->IsInlineOutsideStyle();
10136
10137 bool isRuby = disp->IsRubyDisplayType();
10138 // All types of ruby frames need a block frame to provide line layout,
10139 // hence they are always line participant.
10140 newItem->mIsLineParticipant = isRuby;
10141
10142 if (!isRuby) {
10143 // Table pseudo frames always induce line boundaries around their
10144 // contents.
10145 newItem->mChildItems.SetLineBoundaryAtStart(true);
10146 newItem->mChildItems.SetLineBoundaryAtEnd(true);
10147 }
10148 // The parent of the items in aItems is also the parent of the items
10149 // in mChildItems
10150 newItem->mChildItems.SetParentHasNoXBLChildren(
10151 aIter.List()->ParentHasNoXBLChildren());
10152
10153 // Eat up all items between |aIter| and |aEndIter| and put them in our
10154 // wrapper Advances |aIter| to point to |aEndIter|.
10155 aIter.AppendItemsToList(this, aEndIter, newItem->mChildItems);
10156
10157 aIter.InsertItem(newItem);
10158 }
10159
CreateNeededPseudoSiblings(nsFrameConstructorState & aState,FrameConstructionItemList & aItems,nsIFrame * aParentFrame)10160 void nsCSSFrameConstructor::CreateNeededPseudoSiblings(
10161 nsFrameConstructorState& aState, FrameConstructionItemList& aItems,
10162 nsIFrame* aParentFrame) {
10163 if (aItems.IsEmpty() || GetParentType(aParentFrame) != eTypeRuby) {
10164 return;
10165 }
10166
10167 FCItemIterator iter(aItems);
10168 StyleDisplay firstDisplay =
10169 iter.item().mStyleContext->StyleDisplay()->mDisplay;
10170 if (firstDisplay == StyleDisplay::RubyBaseContainer) {
10171 return;
10172 }
10173 NS_ASSERTION(firstDisplay == StyleDisplay::RubyTextContainer,
10174 "Child of ruby frame should either a rbc or a rtc");
10175
10176 const PseudoParentData& pseudoData =
10177 sPseudoParentData[eTypeRubyBaseContainer];
10178 already_AddRefed<nsStyleContext> pseudoStyle =
10179 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
10180 *pseudoData.mPseudoType, aParentFrame->StyleContext());
10181 FrameConstructionItem* newItem =
10182 new (this) FrameConstructionItem(&pseudoData.mFCData,
10183 // Use the content of the parent frame
10184 aParentFrame->GetContent(),
10185 // Tag type
10186 *pseudoData.mPseudoType,
10187 // Use the namespace of the rtc frame
10188 iter.item().mNameSpaceID,
10189 // no pending binding
10190 nullptr, pseudoStyle, true, nullptr);
10191 newItem->mIsAllInline = true;
10192 newItem->mChildItems.SetParentHasNoXBLChildren(true);
10193 iter.InsertItem(newItem);
10194 }
10195
10196 #ifdef DEBUG
10197 /**
10198 * Returns true iff aFrame should be wrapped in an anonymous flex/grid item,
10199 * rather than being a direct child of aContainerFrame.
10200 *
10201 * NOTE: aContainerFrame must be a flex or grid container - this function is
10202 * purely for sanity-checking the children of these container types.
10203 * NOTE: See also NeedsAnonFlexOrGridItem(), for the non-debug version of this
10204 * logic (which operates a bit earlier, on FCData instead of frames).
10205 */
FrameWantsToBeInAnonymousItem(const nsIFrame * aContainerFrame,const nsIFrame * aFrame)10206 static bool FrameWantsToBeInAnonymousItem(const nsIFrame* aContainerFrame,
10207 const nsIFrame* aFrame) {
10208 MOZ_ASSERT(::IsFlexOrGridContainer(aContainerFrame));
10209
10210 // Any line-participant frames (e.g. text) definitely want to be wrapped in
10211 // an anonymous flex/grid item.
10212 if (aFrame->IsFrameOfType(nsIFrame::eLineParticipant)) {
10213 return true;
10214 }
10215
10216 // If the container is a -webkit-{inline-}box or -moz-{inline-}box container,
10217 // then placeholders also need to be wrapped, for compatibility.
10218 if (IsFlexContainerForLegacyBox(aContainerFrame) &&
10219 aFrame->IsPlaceholderFrame()) {
10220 return true;
10221 }
10222
10223 return false;
10224 }
10225 #endif
10226
VerifyGridFlexContainerChildren(nsIFrame * aParentFrame,const nsFrameList & aChildren)10227 static void VerifyGridFlexContainerChildren(nsIFrame* aParentFrame,
10228 const nsFrameList& aChildren) {
10229 #ifdef DEBUG
10230 if (!::IsFlexOrGridContainer(aParentFrame)) {
10231 return;
10232 }
10233
10234 bool prevChildWasAnonItem = false;
10235 for (const nsIFrame* child : aChildren) {
10236 MOZ_ASSERT(!FrameWantsToBeInAnonymousItem(aParentFrame, child),
10237 "frame wants to be inside an anonymous item, but it isn't");
10238 if (IsAnonymousFlexOrGridItem(child)) {
10239 AssertAnonymousFlexOrGridItemParent(child, aParentFrame);
10240 MOZ_ASSERT(!prevChildWasAnonItem, "two anon items in a row");
10241 nsIFrame* firstWrappedChild = child->PrincipalChildList().FirstChild();
10242 MOZ_ASSERT(firstWrappedChild, "anonymous item shouldn't be empty");
10243 prevChildWasAnonItem = true;
10244 } else {
10245 prevChildWasAnonItem = false;
10246 }
10247 }
10248 #endif
10249 }
10250
ConstructFramesFromItemList(nsFrameConstructorState & aState,FrameConstructionItemList & aItems,nsContainerFrame * aParentFrame,bool aParentIsWrapperAnonBox,nsFrameItems & aFrameItems)10251 inline void nsCSSFrameConstructor::ConstructFramesFromItemList(
10252 nsFrameConstructorState& aState, FrameConstructionItemList& aItems,
10253 nsContainerFrame* aParentFrame, bool aParentIsWrapperAnonBox,
10254 nsFrameItems& aFrameItems) {
10255 // Ensure aParentIsWrapperAnonBox is correct. We _could_ compute it directly,
10256 // but it would be a bit slow, which is why we pass it from callers, who have
10257 // that information offhand in many cases.
10258 MOZ_ASSERT(ParentIsWrapperAnonBox(aParentFrame) == aParentIsWrapperAnonBox);
10259
10260 CreateNeededPseudoContainers(aState, aItems, aParentFrame);
10261 CreateNeededAnonFlexOrGridItems(aState, aItems, aParentFrame);
10262 CreateNeededPseudoInternalRubyBoxes(aState, aItems, aParentFrame);
10263 CreateNeededPseudoSiblings(aState, aItems, aParentFrame);
10264
10265 aItems.SetTriedConstructingFrames();
10266 for (FCItemIterator iter(aItems); !iter.IsDone(); iter.Next()) {
10267 NS_ASSERTION(iter.item().DesiredParentType() == GetParentType(aParentFrame),
10268 "Needed pseudos didn't get created; expect bad things");
10269 ConstructFramesFromItem(aState, iter, aParentFrame, aFrameItems);
10270 }
10271
10272 VerifyGridFlexContainerChildren(aParentFrame, aFrameItems);
10273 NS_ASSERTION(!aState.mHavePendingPopupgroup,
10274 "Should have proccessed it by now");
10275
10276 if (aParentIsWrapperAnonBox) {
10277 for (nsIFrame* f : aFrameItems) {
10278 f->SetParentIsWrapperAnonBox();
10279 }
10280 }
10281 }
10282
AddFCItemsForAnonymousContent(nsFrameConstructorState & aState,nsContainerFrame * aFrame,nsTArray<nsIAnonymousContentCreator::ContentInfo> & aAnonymousItems,FrameConstructionItemList & aItemsToConstruct,uint32_t aExtraFlags)10283 void nsCSSFrameConstructor::AddFCItemsForAnonymousContent(
10284 nsFrameConstructorState& aState, nsContainerFrame* aFrame,
10285 nsTArray<nsIAnonymousContentCreator::ContentInfo>& aAnonymousItems,
10286 FrameConstructionItemList& aItemsToConstruct, uint32_t aExtraFlags) {
10287 for (uint32_t i = 0; i < aAnonymousItems.Length(); ++i) {
10288 nsIContent* content = aAnonymousItems[i].mContent;
10289 // Gecko-styled nodes should have no pending restyle flags.
10290 MOZ_ASSERT(content->IsStyledByServo() || !content->IsElement() ||
10291 !(content->GetFlags() & ELEMENT_ALL_RESTYLE_FLAGS));
10292 // Assert some things about this content
10293 MOZ_ASSERT(!(content->GetFlags() &
10294 (NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME)),
10295 "Should not be marked as needing frames");
10296 MOZ_ASSERT(!content->GetPrimaryFrame(), "Should have no existing frame");
10297 MOZ_ASSERT(!content->IsNodeOfType(nsINode::eCOMMENT) &&
10298 !content->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION),
10299 "Why is someone creating garbage anonymous content");
10300
10301 RefPtr<nsStyleContext> styleContext;
10302 Maybe<TreeMatchContext::AutoParentDisplayBasedStyleFixupSkipper>
10303 parentDisplayBasedStyleFixupSkipper;
10304 MOZ_ASSERT(aState.mTreeMatchContext || content->IsStyledByServo());
10305 if (aState.mTreeMatchContext) {
10306 parentDisplayBasedStyleFixupSkipper.emplace(*aState.mTreeMatchContext);
10307 }
10308
10309 // Make sure we eagerly performed the servo cascade when the anonymous
10310 // nodes were created.
10311 MOZ_ASSERT(!content->IsStyledByServo() || !content->IsElement() ||
10312 content->AsElement()->HasServoData());
10313
10314 // Determine whether this NAC is pseudo-implementing.
10315 nsAtom* pseudo = nullptr;
10316 if (content->IsElement()) {
10317 auto pseudoType = content->AsElement()->GetPseudoElementType();
10318 if (pseudoType != CSSPseudoElementType::NotPseudo) {
10319 pseudo = nsCSSPseudoElements::GetPseudoAtom(pseudoType);
10320 }
10321 }
10322
10323 // Determine the appropriate parent style for this NAC, and if the NAC
10324 // implements a pseudo-element, the appropriate originating element
10325 // (that is to say, the element to the left of the ::pseudo-element in
10326 // the selector). This is all rather tricky, and merits some discussion.
10327 //
10328 // First, it's important to note that author stylesheets generally do not
10329 // apply to elements in native-anonymous subtrees. The exceptions to
10330 // this are web-exposed pseudo-elements, where authors can style the
10331 // pseudo-implementing NAC if the originating element is not itself in a NAC
10332 // subtree.
10333 //
10334 // For this reason, it's very important that we avoid using a style parent
10335 // that is inside a NAC subtree together with an originating element that
10336 // is not inside a NAC subtree, since that would allow authors to
10337 // explicitly inherit styles from internal elements, potentially making
10338 // the NAC hierarchy observable. To ensure this, and generally simplify
10339 // things, we always set the originating element to the style parent.
10340 //
10341 // As a consequence of the above, all web-exposed pseudo-elements (which,
10342 // by definition, must have a content-accessible originating element) must
10343 // also inherit style from that same content-accessible element. To avoid
10344 // unintuitive behavior differences between NAC elements that do and don't
10345 // correspond to web-exposed pseudo-elements, we follow this protocol for
10346 // all NAC, pseudo-implementing or not.
10347 //
10348 // However, things get tricky with the <video> element, where we have a
10349 // bunch of XBL-generated anonymous content descending from a native-
10350 // anonymous XULElement. The XBL elements inherit style from their
10351 // flattened tree parent, because that's how XBL works. But then we need
10352 // to figure out what to do when one of those anonymous XBL elements
10353 // (like an <input> element) generates its own (possibly pseudo-element-
10354 // implementing) NAC.
10355 //
10356 // In this case, we inherit style from the XBL-generated NAC-creating
10357 // element, rather than the <video> element. There are a number of good
10358 // reasons for this. First, inheriting from the great-grandparent while
10359 // the parent inherits from the grandparent would be bizarre at best.
10360 // Second, exposing pseudo-elements from elements within our particular
10361 // XBL implementation would allow content styles to (un)intentionally
10362 // alter the video controls, which would be very bad. Third, our UA
10363 // stylesheets have selectors like:
10364 //
10365 // input[type=range][orient=horizontal]::-moz-range-track
10366 //
10367 // and we need to make sure that the originating element is the <input>,
10368 // not the <video>, because that's where the |orient| attribute lives.
10369 //
10370 // The upshot of all of this is that, to find the style parent (and
10371 // originating element, if applicable), we walk up our parent chain to the
10372 // first element that is not itself NAC (distinct from whether it happens
10373 // to be in a NAC subtree).
10374 //
10375 // The one exception to all of this is scrollbar content, which we parent
10376 // directly to the scrollframe. This is because the special-snowflake
10377 // construction of scroll frames doesn't result in the placeholder frame
10378 // being constructed until later, which means that GetInFlowParent() doesn't
10379 // work right in the case of out-of-flow scrollframes.
10380 //
10381 // To implement all this, we need to pass the correct parent style context
10382 // here because SetPrimaryFrame() may not have been called on the content
10383 // yet and thus ResolveStyleContext can't find it otherwise.
10384 //
10385 // We don't need to worry about display:contents here, because such
10386 // elements don't get a frame and thus can't generate NAC. But we do need
10387 // to worry about anonymous boxes, which CorrectStyleParentFrame handles
10388 // for us.
10389 nsIFrame* inheritFrame = aFrame;
10390 if (!content->IsNativeScrollbarContent()) {
10391 while (inheritFrame->GetContent()->IsNativeAnonymous()) {
10392 inheritFrame = inheritFrame->GetInFlowParent();
10393 }
10394 }
10395
10396 nsIFrame* styleParentFrame =
10397 nsFrame::CorrectStyleParentFrame(inheritFrame, pseudo);
10398 // The only way we can not have a style parent now is if inheritFrame is the
10399 // canvas frame and we're the NAC parent for all the things added via
10400 // nsIDocument::InsertAnonymousContent.
10401 MOZ_ASSERT(styleParentFrame || inheritFrame->IsCanvasFrame());
10402 // And that anonymous div has no pseudo.
10403 MOZ_ASSERT(styleParentFrame || !pseudo);
10404
10405 Element* originating =
10406 pseudo ? styleParentFrame->GetContent()->AsElement() : nullptr;
10407 nsStyleContext* parentStyle =
10408 styleParentFrame ? styleParentFrame->StyleContext() : nullptr;
10409 styleContext =
10410 ResolveStyleContext(parentStyle, content, &aState, originating);
10411
10412 nsTArray<nsIAnonymousContentCreator::ContentInfo>* anonChildren = nullptr;
10413 if (!aAnonymousItems[i].mChildren.IsEmpty()) {
10414 anonChildren = &aAnonymousItems[i].mChildren;
10415 }
10416
10417 uint32_t flags = ITEM_ALLOW_XBL_BASE | ITEM_ALLOW_PAGE_BREAK |
10418 ITEM_IS_ANONYMOUSCONTENTCREATOR_CONTENT | aExtraFlags;
10419
10420 AddFrameConstructionItemsInternal(
10421 aState, content, aFrame, content->NodeInfo()->NameAtom(),
10422 content->GetNameSpaceID(), true, styleContext, flags, anonChildren,
10423 aItemsToConstruct);
10424 }
10425 }
10426
ProcessChildren(nsFrameConstructorState & aState,nsIContent * aContent,nsStyleContext * aStyleContext,nsContainerFrame * aFrame,const bool aCanHaveGeneratedContent,nsFrameItems & aFrameItems,const bool aAllowBlockStyles,PendingBinding * aPendingBinding,nsIFrame * aPossiblyLeafFrame)10427 void nsCSSFrameConstructor::ProcessChildren(
10428 nsFrameConstructorState& aState, nsIContent* aContent,
10429 nsStyleContext* aStyleContext, nsContainerFrame* aFrame,
10430 const bool aCanHaveGeneratedContent, nsFrameItems& aFrameItems,
10431 const bool aAllowBlockStyles, PendingBinding* aPendingBinding,
10432 nsIFrame* aPossiblyLeafFrame) {
10433 NS_PRECONDITION(aFrame, "Must have parent frame here");
10434 NS_PRECONDITION(aFrame->GetContentInsertionFrame() == aFrame,
10435 "Parent frame in ProcessChildren should be its own "
10436 "content insertion frame");
10437 const uint32_t kMaxDepth = 2 * MAX_REFLOW_DEPTH;
10438 static_assert(kMaxDepth <= UINT16_MAX, "mCurrentDepth type is too narrow");
10439 AutoRestore<uint16_t> savedDepth(mCurrentDepth);
10440 if (mCurrentDepth != UINT16_MAX) {
10441 ++mCurrentDepth;
10442 }
10443
10444 if (!aPossiblyLeafFrame) {
10445 aPossiblyLeafFrame = aFrame;
10446 }
10447
10448 // XXXbz ideally, this would do all the pushing of various
10449 // containing blocks as needed, so callers don't have to do it...
10450
10451 // Check that our parent frame is a block before allowing ::first-letter/line.
10452 // E.g. <button style="display:grid"> should not allow it.
10453 const bool allowFirstPseudos =
10454 aAllowBlockStyles && nsLayoutUtils::GetAsBlock(aFrame);
10455 bool haveFirstLetterStyle = false, haveFirstLineStyle = false;
10456 if (allowFirstPseudos) {
10457 ShouldHaveSpecialBlockStyle(aContent, aStyleContext, &haveFirstLetterStyle,
10458 &haveFirstLineStyle);
10459 }
10460
10461 const bool isFlexOrGridContainer = ::IsFlexOrGridContainer(aFrame);
10462 // The logic here needs to match the logic in GetFloatContainingBlock()
10463 // (Since we already have isFlexOrGridContainer, we check that eagerly instead
10464 // of letting ShouldSuppressFloatingOfDescendants look it up redundantly.)
10465 nsFrameConstructorSaveState floatSaveState;
10466 if (isFlexOrGridContainer || ShouldSuppressFloatingOfDescendants(aFrame)) {
10467 aState.PushFloatContainingBlock(nullptr, floatSaveState);
10468 } else if (aFrame->IsFloatContainingBlock()) {
10469 aState.PushFloatContainingBlock(aFrame, floatSaveState);
10470 }
10471
10472 nsFrameConstructorState::PendingBindingAutoPusher pusher(aState,
10473 aPendingBinding);
10474
10475 AutoFrameConstructionItemList itemsToConstruct(this);
10476
10477 // If we have first-letter or first-line style then frames can get
10478 // moved around so don't set these flags.
10479 if (allowFirstPseudos && !haveFirstLetterStyle && !haveFirstLineStyle) {
10480 itemsToConstruct.SetLineBoundaryAtStart(true);
10481 itemsToConstruct.SetLineBoundaryAtEnd(true);
10482 }
10483
10484 // Create any anonymous frames we need here. This must happen before the
10485 // non-anonymous children are processed to ensure that popups are never
10486 // constructed before the popupset.
10487 AutoTArray<nsIAnonymousContentCreator::ContentInfo, 4> anonymousItems;
10488 GetAnonymousContent(aContent, aPossiblyLeafFrame, anonymousItems);
10489 #ifdef DEBUG
10490 for (uint32_t i = 0; i < anonymousItems.Length(); ++i) {
10491 MOZ_ASSERT(anonymousItems[i].mContent->IsRootOfAnonymousSubtree(),
10492 "Content should know it's an anonymous subtree");
10493 }
10494 #endif
10495 AddFCItemsForAnonymousContent(aState, aFrame, anonymousItems,
10496 itemsToConstruct);
10497
10498 if (!aPossiblyLeafFrame->IsLeaf()) {
10499 // :before/:after content should have the same style context parent
10500 // as normal kids.
10501 // Note that we don't use this style context for looking up things like
10502 // special block styles because in some cases involving table pseudo-frames
10503 // it has nothing to do with the parent frame's desired behavior.
10504 nsStyleContext* styleContext;
10505
10506 if (aCanHaveGeneratedContent) {
10507 aFrame->AddStateBits(NS_FRAME_MAY_HAVE_GENERATED_CONTENT);
10508 styleContext =
10509 nsFrame::CorrectStyleParentFrame(aFrame, nullptr)->StyleContext();
10510 // Probe for generated content before
10511 CreateGeneratedContentItem(aState, aFrame, aContent->AsElement(),
10512 styleContext, CSSPseudoElementType::before,
10513 itemsToConstruct);
10514 }
10515
10516 const bool addChildItems = MOZ_LIKELY(mCurrentDepth < kMaxDepth);
10517 if (!addChildItems) {
10518 NS_WARNING("ProcessChildren max depth exceeded");
10519 }
10520
10521 // Don't blockify 'display' in ApplyStyleFixups unless aFrame really is
10522 // a flex/grid container frame, not just has display:flex/grid.
10523 Maybe<TreeMatchContext::AutoParentDisplayBasedStyleFixupSkipper>
10524 parentDisplayBasedStyleFixupSkipper;
10525 MOZ_ASSERT(aState.mTreeMatchContext || aContent->IsStyledByServo());
10526 if (!isFlexOrGridContainer && aState.mTreeMatchContext) {
10527 parentDisplayBasedStyleFixupSkipper.emplace(*aState.mTreeMatchContext);
10528 }
10529
10530 InsertionPoint insertion(aFrame, nullptr);
10531 FlattenedChildIterator iter(aContent);
10532 for (nsIContent* child = iter.GetNextChild(); child;
10533 child = iter.GetNextChild()) {
10534 // Get the parent of the content and check if it is a XBL children element
10535 // (if the content is a children element then parent != aContent because
10536 // the FlattenedChildIterator will transitively iterate through
10537 // <xbl:children> for default content). Push the children element as an
10538 // ancestor here because it does not have a frame and would not otherwise
10539 // be pushed as an ancestor.
10540 insertion.mContainer = aContent;
10541 nsIContent* parent = child->GetParent();
10542 MOZ_ASSERT(parent,
10543 "Parent must be non-null because we are iterating children.");
10544 TreeMatchContext::AutoAncestorPusher ancestorPusher(
10545 aState.mTreeMatchContext);
10546 if (parent != aContent && parent->IsElement()) {
10547 insertion.mContainer = child->GetFlattenedTreeParent();
10548 MOZ_ASSERT(insertion.mContainer == GetInsertionPoint(child).mContainer);
10549 if (aState.HasAncestorFilter()) {
10550 ancestorPusher.PushAncestorAndStyleScope(parent->AsElement());
10551 } else {
10552 ancestorPusher.PushStyleScope(parent->AsElement());
10553 }
10554 }
10555
10556 // Frame construction item construction should not post
10557 // restyles, so removing restyle flags here is safe.
10558 child->UnsetRestyleFlagsIfGecko();
10559 if (addChildItems) {
10560 AddFrameConstructionItems(aState, child, iter.XBLInvolved(), insertion,
10561 itemsToConstruct);
10562 } else {
10563 ClearLazyBits(child, child->GetNextSibling());
10564 }
10565 }
10566 itemsToConstruct.SetParentHasNoXBLChildren(!iter.XBLInvolved());
10567
10568 if (aCanHaveGeneratedContent) {
10569 // Probe for generated content after
10570 CreateGeneratedContentItem(aState, aFrame, aContent->AsElement(),
10571 styleContext, CSSPseudoElementType::after,
10572 itemsToConstruct);
10573 }
10574 } else {
10575 ClearLazyBits(aContent->GetFirstChild(), nullptr);
10576 }
10577
10578 ConstructFramesFromItemList(aState, itemsToConstruct, aFrame,
10579 /* aParentIsWrapperAnonBox = */ false,
10580 aFrameItems);
10581
10582 NS_ASSERTION(!allowFirstPseudos || !aFrame->IsXULBoxFrame(),
10583 "can't be both block and box");
10584
10585 if (haveFirstLetterStyle) {
10586 WrapFramesInFirstLetterFrame(aFrame, aFrameItems);
10587 }
10588 if (haveFirstLineStyle) {
10589 WrapFramesInFirstLineFrame(aState, aContent, aFrame, nullptr, aFrameItems);
10590 }
10591
10592 // We might end up with first-line frames that change
10593 // AnyKidsNeedBlockParent() without changing itemsToConstruct, but that
10594 // should never happen for cases whan aFrame->IsXULBoxFrame().
10595 NS_ASSERTION(!haveFirstLineStyle || !aFrame->IsXULBoxFrame(),
10596 "Shouldn't have first-line style if we're a box");
10597 NS_ASSERTION(
10598 !aFrame->IsXULBoxFrame() ||
10599 itemsToConstruct.AnyItemsNeedBlockParent() ==
10600 (AnyKidsNeedBlockParent(aFrameItems.FirstChild()) != nullptr),
10601 "Something went awry in our block parent calculations");
10602
10603 if (aFrame->IsXULBoxFrame() && itemsToConstruct.AnyItemsNeedBlockParent()) {
10604 // XXXbz we could do this on the FrameConstructionItemList level,
10605 // no? And if we cared we could look through the item list
10606 // instead of groveling through the framelist here..
10607 nsStyleContext* frameStyleContext = aFrame->StyleContext();
10608 // Report a warning for non-GC frames, for chrome:
10609 if (!aFrame->IsGeneratedContentFrame() &&
10610 mPresShell->GetPresContext()->IsChrome()) {
10611 nsIContent* badKid = AnyKidsNeedBlockParent(aFrameItems.FirstChild());
10612 nsDependentAtomString parentTag(aContent->NodeInfo()->NameAtom()),
10613 kidTag(badKid->NodeInfo()->NameAtom());
10614 const char16_t* params[] = {parentTag.get(), kidTag.get()};
10615 const nsStyleDisplay* display = frameStyleContext->StyleDisplay();
10616 const char* message = (display->mDisplay == StyleDisplay::MozInlineBox)
10617 ? "NeededToWrapXULInlineBox"
10618 : "NeededToWrapXUL";
10619 nsContentUtils::ReportToConsole(
10620 nsIScriptError::warningFlag,
10621 NS_LITERAL_CSTRING("Layout: FrameConstructor"), mDocument,
10622 nsContentUtils::eXUL_PROPERTIES, message, params,
10623 ArrayLength(params));
10624 }
10625
10626 RefPtr<nsStyleContext> blockSC =
10627 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
10628 nsCSSAnonBoxes::mozXULAnonymousBlock, frameStyleContext);
10629 nsBlockFrame* blockFrame = NS_NewBlockFrame(mPresShell, blockSC);
10630 // We might, in theory, want to set NS_BLOCK_FLOAT_MGR and
10631 // NS_BLOCK_MARGIN_ROOT, but I think it's a bad idea given that
10632 // a real block placed here wouldn't get those set on it.
10633
10634 InitAndRestoreFrame(aState, aContent, aFrame, blockFrame, false);
10635
10636 NS_ASSERTION(!blockFrame->HasView(), "need to do view reparenting");
10637 ReparentFrames(this, blockFrame, aFrameItems, false);
10638
10639 blockFrame->SetInitialChildList(kPrincipalList, aFrameItems);
10640 NS_ASSERTION(aFrameItems.IsEmpty(), "How did that happen?");
10641 aFrameItems.Clear();
10642 aFrameItems.AddChild(blockFrame);
10643
10644 aFrame->AddStateBits(NS_STATE_BOX_WRAPS_KIDS_IN_BLOCK);
10645 MOZ_ASSERT(!aFrame->IsLeaf(), "Why do we have an nsLeafBoxFrame here?");
10646 aFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
10647 }
10648 }
10649
10650 //----------------------------------------------------------------------
10651
10652 // Support for :first-line style
10653
10654 // Special routine to handle placing a list of frames into a block
10655 // frame that has first-line style. The routine ensures that the first
10656 // collection of inline frames end up in a first-line frame.
10657 // NOTE: aState may have containing block information related to a
10658 // different part of the frame tree than where the first line occurs.
10659 // In particular aState may be set up for where ContentInserted or
10660 // ContentAppended is inserting content, which may be some
10661 // non-first-in-flow continuation of the block to which the first-line
10662 // belongs. So this function needs to be careful about how it uses
10663 // aState.
WrapFramesInFirstLineFrame(nsFrameConstructorState & aState,nsIContent * aBlockContent,nsContainerFrame * aBlockFrame,nsFirstLineFrame * aLineFrame,nsFrameItems & aFrameItems)10664 void nsCSSFrameConstructor::WrapFramesInFirstLineFrame(
10665 nsFrameConstructorState& aState, nsIContent* aBlockContent,
10666 nsContainerFrame* aBlockFrame, nsFirstLineFrame* aLineFrame,
10667 nsFrameItems& aFrameItems) {
10668 // Find the part of aFrameItems that we want to put in the first-line
10669 nsFrameList::FrameLinkEnumerator link(aFrameItems);
10670 while (!link.AtEnd() && link.NextFrame()->IsInlineOutside()) {
10671 link.Next();
10672 }
10673
10674 nsFrameList firstLineChildren = aFrameItems.ExtractHead(link);
10675
10676 if (firstLineChildren.IsEmpty()) {
10677 // Nothing is supposed to go into the first-line; nothing to do
10678 return;
10679 }
10680
10681 if (!aLineFrame) {
10682 // Create line frame
10683 nsStyleContext* parentStyle =
10684 nsFrame::CorrectStyleParentFrame(aBlockFrame,
10685 nsCSSPseudoElements::firstLine)
10686 ->StyleContext();
10687 RefPtr<nsStyleContext> firstLineStyle =
10688 GetFirstLineStyle(aBlockContent, parentStyle);
10689
10690 aLineFrame = NS_NewFirstLineFrame(mPresShell, firstLineStyle);
10691
10692 // Initialize the line frame
10693 InitAndRestoreFrame(aState, aBlockContent, aBlockFrame, aLineFrame);
10694
10695 // The lineFrame will be the block's first child; the rest of the
10696 // frame list (after lastInlineFrame) will be the second and
10697 // subsequent children; insert lineFrame into aFrameItems.
10698 aFrameItems.InsertFrame(nullptr, nullptr, aLineFrame);
10699
10700 NS_ASSERTION(aLineFrame->StyleContext() == firstLineStyle,
10701 "Bogus style context on line frame");
10702 }
10703
10704 // Give the inline frames to the lineFrame <b>after</b> reparenting them
10705 ReparentFrames(this, aLineFrame, firstLineChildren, true);
10706 if (aLineFrame->PrincipalChildList().IsEmpty() &&
10707 (aLineFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
10708 aLineFrame->SetInitialChildList(kPrincipalList, firstLineChildren);
10709 } else {
10710 AppendFrames(aLineFrame, kPrincipalList, firstLineChildren);
10711 }
10712 }
10713
10714 // Special routine to handle appending a new frame to a block frame's
10715 // child list. Takes care of placing the new frame into the right
10716 // place when first-line style is present.
AppendFirstLineFrames(nsFrameConstructorState & aState,nsIContent * aBlockContent,nsContainerFrame * aBlockFrame,nsFrameItems & aFrameItems)10717 void nsCSSFrameConstructor::AppendFirstLineFrames(
10718 nsFrameConstructorState& aState, nsIContent* aBlockContent,
10719 nsContainerFrame* aBlockFrame, nsFrameItems& aFrameItems) {
10720 // It's possible that aBlockFrame needs to have a first-line frame
10721 // created because it doesn't currently have any children.
10722 const nsFrameList& blockKids = aBlockFrame->PrincipalChildList();
10723 if (blockKids.IsEmpty()) {
10724 WrapFramesInFirstLineFrame(aState, aBlockContent, aBlockFrame, nullptr,
10725 aFrameItems);
10726 return;
10727 }
10728
10729 // Examine the last block child - if it's a first-line frame then
10730 // appended frames need special treatment.
10731 nsIFrame* lastBlockKid = blockKids.LastChild();
10732 if (!lastBlockKid->IsLineFrame()) {
10733 // No first-line frame at the end of the list, therefore there is
10734 // an intervening block between any first-line frame the frames
10735 // we are appending. Therefore, we don't need any special
10736 // treatment of the appended frames.
10737 return;
10738 }
10739
10740 nsFirstLineFrame* lineFrame = static_cast<nsFirstLineFrame*>(lastBlockKid);
10741 WrapFramesInFirstLineFrame(aState, aBlockContent, aBlockFrame, lineFrame,
10742 aFrameItems);
10743 }
10744
CheckForFirstLineInsertion(nsIFrame * aParentFrame,nsFrameItems & aFrameItems)10745 void nsCSSFrameConstructor::CheckForFirstLineInsertion(
10746 nsIFrame* aParentFrame, nsFrameItems& aFrameItems) {
10747 MOZ_ASSERT(aParentFrame->StyleContext()->HasPseudoElementData(),
10748 "Why were we called?");
10749
10750 if (aFrameItems.IsEmpty()) {
10751 // Happens often enough, with the caption stuff. No need to do the ancestor
10752 // walk here.
10753 return;
10754 }
10755
10756 class RestyleManager* restyleManager = RestyleManager();
10757 if (!restyleManager->IsServo()) {
10758 // Gecko's style resolution is frame-based, so already has the right styles
10759 // even in the ::first-line case.
10760 return;
10761 }
10762
10763 // Check whether there's a ::first-line on the path up from aParentFrame.
10764 // Note that we can't stop until we've run out of ancestors with
10765 // pseudo-element data, because the first-letter might be somewhere way up the
10766 // tree; in particular it might be past our containing block.
10767 nsIFrame* ancestor = aParentFrame;
10768 while (ancestor) {
10769 if (!ancestor->StyleContext()->HasPseudoElementData()) {
10770 // We know we won't find a ::first-line now.
10771 return;
10772 }
10773
10774 if (!ancestor->IsLineFrame()) {
10775 ancestor = ancestor->GetParent();
10776 continue;
10777 }
10778
10779 if (!ancestor->StyleContext()->IsPseudoElement()) {
10780 // This is a continuation lineframe, not the first line; no need to do
10781 // anything to the styles.
10782 return;
10783 }
10784
10785 // Fix up the styles of aFrameItems for ::first-line.
10786 for (nsIFrame* f : aFrameItems) {
10787 restyleManager->ReparentStyleContext(f);
10788 }
10789 return;
10790 }
10791 }
10792
10793 //----------------------------------------------------------------------
10794
10795 // First-letter support
10796
10797 // Determine how many characters in the text fragment apply to the
10798 // first letter
FirstLetterCount(const nsTextFragment * aFragment)10799 static int32_t FirstLetterCount(const nsTextFragment* aFragment) {
10800 int32_t count = 0;
10801 int32_t firstLetterLength = 0;
10802
10803 int32_t i, n = aFragment->GetLength();
10804 for (i = 0; i < n; i++) {
10805 char16_t ch = aFragment->CharAt(i);
10806 // FIXME: take content language into account when deciding whitespace.
10807 if (dom::IsSpaceCharacter(ch)) {
10808 if (firstLetterLength) {
10809 break;
10810 }
10811 count++;
10812 continue;
10813 }
10814 // XXX I18n
10815 if ((ch == '\'') || (ch == '\"')) {
10816 if (firstLetterLength) {
10817 break;
10818 }
10819 // keep looping
10820 firstLetterLength = 1;
10821 } else {
10822 count++;
10823 break;
10824 }
10825 }
10826
10827 return count;
10828 }
10829
NeedFirstLetterContinuation(nsIContent * aContent)10830 static bool NeedFirstLetterContinuation(nsIContent* aContent) {
10831 NS_PRECONDITION(aContent, "null ptr");
10832
10833 bool result = false;
10834 if (aContent) {
10835 const nsTextFragment* frag = aContent->GetText();
10836 if (frag) {
10837 int32_t flc = FirstLetterCount(frag);
10838 int32_t tl = frag->GetLength();
10839 if (flc < tl) {
10840 result = true;
10841 }
10842 }
10843 }
10844 return result;
10845 }
10846
IsFirstLetterContent(nsIContent * aContent)10847 static bool IsFirstLetterContent(nsIContent* aContent) {
10848 return aContent->TextLength() && !aContent->TextIsOnlyWhitespace();
10849 }
10850
10851 /**
10852 * Create a letter frame, only make it a floating frame.
10853 */
CreateFloatingLetterFrame(nsFrameConstructorState & aState,nsIContent * aTextContent,nsIFrame * aTextFrame,nsContainerFrame * aParentFrame,nsStyleContext * aParentStyleContext,nsStyleContext * aStyleContext,nsFrameItems & aResult)10854 nsFirstLetterFrame* nsCSSFrameConstructor::CreateFloatingLetterFrame(
10855 nsFrameConstructorState& aState, nsIContent* aTextContent,
10856 nsIFrame* aTextFrame, nsContainerFrame* aParentFrame,
10857 nsStyleContext* aParentStyleContext, nsStyleContext* aStyleContext,
10858 nsFrameItems& aResult) {
10859 MOZ_ASSERT(aParentStyleContext);
10860
10861 nsFirstLetterFrame* letterFrame =
10862 NS_NewFirstLetterFrame(mPresShell, aStyleContext);
10863 // We don't want to use a text content for a non-text frame (because we want
10864 // its primary frame to be a text frame).
10865 nsIContent* letterContent = aParentFrame->GetContent();
10866 nsContainerFrame* containingBlock =
10867 aState.GetGeometricParent(aStyleContext->StyleDisplay(), aParentFrame);
10868 InitAndRestoreFrame(aState, letterContent, containingBlock, letterFrame);
10869
10870 // Init the text frame to refer to the letter frame. Make sure we
10871 // get a proper style context for it (the one passed in is for the
10872 // letter frame and will have the float property set on it; the text
10873 // frame shouldn't have that set).
10874 StyleSetHandle styleSet = mPresShell->StyleSet();
10875 RefPtr<nsStyleContext> textSC =
10876 styleSet->ResolveStyleForText(aTextContent, aStyleContext);
10877 aTextFrame->SetStyleContextWithoutNotification(textSC);
10878 InitAndRestoreFrame(aState, aTextContent, letterFrame, aTextFrame);
10879
10880 // And then give the text frame to the letter frame
10881 SetInitialSingleChild(letterFrame, aTextFrame);
10882
10883 // See if we will need to continue the text frame (does it contain
10884 // more than just the first-letter text or not?) If it does, then we
10885 // create (in advance) a continuation frame for it.
10886 nsIFrame* nextTextFrame = nullptr;
10887 if (NeedFirstLetterContinuation(aTextContent)) {
10888 // Create continuation
10889 nextTextFrame =
10890 CreateContinuingFrame(aState.mPresContext, aTextFrame, aParentFrame);
10891 RefPtr<nsStyleContext> newSC =
10892 styleSet->ResolveStyleForText(aTextContent, aParentStyleContext);
10893 nextTextFrame->SetStyleContext(newSC);
10894 }
10895
10896 NS_ASSERTION(aResult.IsEmpty(), "aResult should be an empty nsFrameItems!");
10897 // Put the new float before any of the floats in the block we're doing
10898 // first-letter for, that is, before any floats whose parent is
10899 // containingBlock.
10900 nsFrameList::FrameLinkEnumerator link(aState.mFloatedItems);
10901 while (!link.AtEnd() && link.NextFrame()->GetParent() != containingBlock) {
10902 link.Next();
10903 }
10904
10905 aState.AddChild(letterFrame, aResult, letterContent, aParentFrame, false,
10906 true, false, true, link.PrevFrame());
10907
10908 if (nextTextFrame) {
10909 aResult.AddChild(nextTextFrame);
10910 }
10911
10912 return letterFrame;
10913 }
10914
10915 /**
10916 * Create a new letter frame for aTextFrame. The letter frame will be
10917 * a child of aParentFrame.
10918 */
CreateLetterFrame(nsContainerFrame * aBlockFrame,nsContainerFrame * aBlockContinuation,nsIContent * aTextContent,nsContainerFrame * aParentFrame,nsFrameItems & aResult)10919 void nsCSSFrameConstructor::CreateLetterFrame(
10920 nsContainerFrame* aBlockFrame, nsContainerFrame* aBlockContinuation,
10921 nsIContent* aTextContent, nsContainerFrame* aParentFrame,
10922 nsFrameItems& aResult) {
10923 NS_PRECONDITION(aTextContent->IsNodeOfType(nsINode::eTEXT),
10924 "aTextContent isn't text");
10925 NS_ASSERTION(nsLayoutUtils::GetAsBlock(aBlockFrame), "Not a block frame?");
10926
10927 // Get style context for the first-letter-frame. Keep this in sync with
10928 // nsBlockFrame::UpdatePseudoElementStyles.
10929 nsIFrame* parentFrame = nsFrame::CorrectStyleParentFrame(
10930 aParentFrame, nsCSSPseudoElements::firstLetter);
10931
10932 nsStyleContext* parentStyleContext = parentFrame->StyleContext();
10933
10934 // Use content from containing block so that we can actually
10935 // find a matching style rule.
10936 nsIContent* blockContent = aBlockFrame->GetContent();
10937
10938 // Create first-letter style rule
10939 RefPtr<nsStyleContext> sc =
10940 GetFirstLetterStyle(blockContent, parentStyleContext);
10941
10942 if (sc) {
10943 if (sc->IsServo() && parentFrame->IsLineFrame()) {
10944 nsIFrame* parentIgnoringFirstLine = nsFrame::CorrectStyleParentFrame(
10945 aBlockFrame, nsCSSPseudoElements::firstLetter);
10946
10947 sc = mPresShell->StyleSet()->AsServo()->ReparentStyleContext(
10948 sc->AsServo(), parentStyleContext->AsServo(),
10949 parentIgnoringFirstLine->StyleContext()->AsServo(),
10950 parentStyleContext->AsServo(), blockContent->AsElement());
10951 }
10952
10953 RefPtr<nsStyleContext> textSC =
10954 mPresShell->StyleSet()->ResolveStyleForText(aTextContent, sc);
10955
10956 // Create a new text frame (the original one will be discarded)
10957 // pass a temporary stylecontext, the correct one will be set
10958 // later. Start off by unsetting the primary frame for
10959 // aTextContent, so it's no longer pointing to the to-be-destroyed
10960 // frame.
10961 // XXXbz it would be really nice to destroy the old frame _first_,
10962 // then create the new one, so we could avoid this hack.
10963 aTextContent->SetPrimaryFrame(nullptr);
10964 nsIFrame* textFrame = NS_NewTextFrame(mPresShell, textSC);
10965
10966 NS_ASSERTION(aBlockContinuation == GetFloatContainingBlock(aParentFrame),
10967 "Containing block is confused");
10968 TreeMatchContextHolder matchContext(mDocument);
10969 nsFrameConstructorState state(
10970 mPresShell, matchContext,
10971 GetAbsoluteContainingBlock(aParentFrame, FIXED_POS),
10972 GetAbsoluteContainingBlock(aParentFrame, ABS_POS), aBlockContinuation);
10973
10974 // Create the right type of first-letter frame
10975 const nsStyleDisplay* display = sc->StyleDisplay();
10976 nsFirstLetterFrame* letterFrame;
10977 if (display->IsFloatingStyle() &&
10978 !nsSVGUtils::IsInSVGTextSubtree(aParentFrame)) {
10979 // Make a floating first-letter frame
10980 letterFrame = CreateFloatingLetterFrame(state, aTextContent, textFrame,
10981 aParentFrame, parentStyleContext,
10982 sc, aResult);
10983 } else {
10984 // Make an inflow first-letter frame
10985 letterFrame = NS_NewFirstLetterFrame(mPresShell, sc);
10986
10987 // Initialize the first-letter-frame. We don't want to use a text
10988 // content for a non-text frame (because we want its primary frame to
10989 // be a text frame).
10990 nsIContent* letterContent = aParentFrame->GetContent();
10991 letterFrame->Init(letterContent, aParentFrame, nullptr);
10992
10993 InitAndRestoreFrame(state, aTextContent, letterFrame, textFrame);
10994
10995 SetInitialSingleChild(letterFrame, textFrame);
10996 aResult.Clear();
10997 aResult.AddChild(letterFrame);
10998 NS_ASSERTION(!aBlockFrame->GetPrevContinuation(),
10999 "should have the first continuation here");
11000 aBlockFrame->AddStateBits(NS_BLOCK_HAS_FIRST_LETTER_CHILD);
11001 }
11002 MOZ_ASSERT(
11003 !aBlockFrame->GetPrevContinuation(),
11004 "Setting up a first-letter frame on a non-first block continuation?");
11005 auto parent =
11006 static_cast<nsContainerFrame*>(aParentFrame->FirstContinuation());
11007 parent->SetHasFirstLetterChild();
11008 aBlockFrame->SetProperty(nsContainerFrame::FirstLetterProperty(),
11009 letterFrame);
11010 aTextContent->SetPrimaryFrame(textFrame);
11011 }
11012 }
11013
WrapFramesInFirstLetterFrame(nsContainerFrame * aBlockFrame,nsFrameItems & aBlockFrames)11014 void nsCSSFrameConstructor::WrapFramesInFirstLetterFrame(
11015 nsContainerFrame* aBlockFrame, nsFrameItems& aBlockFrames) {
11016 aBlockFrame->AddStateBits(NS_BLOCK_HAS_FIRST_LETTER_STYLE);
11017
11018 nsContainerFrame* parentFrame = nullptr;
11019 nsIFrame* textFrame = nullptr;
11020 nsIFrame* prevFrame = nullptr;
11021 nsFrameItems letterFrames;
11022 bool stopLooking = false;
11023 WrapFramesInFirstLetterFrame(
11024 aBlockFrame, aBlockFrame, aBlockFrame, aBlockFrames.FirstChild(),
11025 &parentFrame, &textFrame, &prevFrame, letterFrames, &stopLooking);
11026 if (parentFrame) {
11027 if (parentFrame == aBlockFrame) {
11028 // Take textFrame out of the block's frame list and substitute the
11029 // letter frame(s) instead.
11030 aBlockFrames.DestroyFrame(textFrame);
11031 aBlockFrames.InsertFrames(nullptr, prevFrame, letterFrames);
11032 } else {
11033 // Take the old textFrame out of the inline parent's child list
11034 RemoveFrame(kPrincipalList, textFrame);
11035
11036 // Insert in the letter frame(s)
11037 parentFrame->InsertFrames(kPrincipalList, prevFrame, letterFrames);
11038 }
11039 }
11040 }
11041
WrapFramesInFirstLetterFrame(nsContainerFrame * aBlockFrame,nsContainerFrame * aBlockContinuation,nsContainerFrame * aParentFrame,nsIFrame * aParentFrameList,nsContainerFrame ** aModifiedParent,nsIFrame ** aTextFrame,nsIFrame ** aPrevFrame,nsFrameItems & aLetterFrames,bool * aStopLooking)11042 void nsCSSFrameConstructor::WrapFramesInFirstLetterFrame(
11043 nsContainerFrame* aBlockFrame, nsContainerFrame* aBlockContinuation,
11044 nsContainerFrame* aParentFrame, nsIFrame* aParentFrameList,
11045 nsContainerFrame** aModifiedParent, nsIFrame** aTextFrame,
11046 nsIFrame** aPrevFrame, nsFrameItems& aLetterFrames, bool* aStopLooking) {
11047 nsIFrame* prevFrame = nullptr;
11048 nsIFrame* frame = aParentFrameList;
11049
11050 while (frame) {
11051 nsIFrame* nextFrame = frame->GetNextSibling();
11052
11053 LayoutFrameType frameType = frame->Type();
11054 if (LayoutFrameType::Text == frameType) {
11055 // Wrap up first-letter content in a letter frame
11056 nsIContent* textContent = frame->GetContent();
11057 if (IsFirstLetterContent(textContent)) {
11058 // Create letter frame to wrap up the text
11059 CreateLetterFrame(aBlockFrame, aBlockContinuation, textContent,
11060 aParentFrame, aLetterFrames);
11061
11062 // Provide adjustment information for parent
11063 *aModifiedParent = aParentFrame;
11064 *aTextFrame = frame;
11065 *aPrevFrame = prevFrame;
11066 *aStopLooking = true;
11067 return;
11068 }
11069 } else if (IsInlineFrame(frame) && frameType != LayoutFrameType::Br) {
11070 nsIFrame* kids = frame->PrincipalChildList().FirstChild();
11071 WrapFramesInFirstLetterFrame(aBlockFrame, aBlockContinuation,
11072 static_cast<nsContainerFrame*>(frame), kids,
11073 aModifiedParent, aTextFrame, aPrevFrame,
11074 aLetterFrames, aStopLooking);
11075 if (*aStopLooking) {
11076 return;
11077 }
11078 } else {
11079 // This will stop us looking to create more letter frames. For
11080 // example, maybe the frame-type is "letterFrame" or
11081 // "placeholderFrame". This keeps us from creating extra letter
11082 // frames, and also prevents us from creating letter frames when
11083 // the first real content child of a block is not text (e.g. an
11084 // image, hr, etc.)
11085 *aStopLooking = true;
11086 break;
11087 }
11088
11089 prevFrame = frame;
11090 frame = nextFrame;
11091 }
11092 }
11093
FindFirstLetterFrame(nsIFrame * aFrame,nsIFrame::ChildListID aListID)11094 static nsIFrame* FindFirstLetterFrame(nsIFrame* aFrame,
11095 nsIFrame::ChildListID aListID) {
11096 nsFrameList list = aFrame->GetChildList(aListID);
11097 for (nsFrameList::Enumerator e(list); !e.AtEnd(); e.Next()) {
11098 if (e.get()->IsLetterFrame()) {
11099 return e.get();
11100 }
11101 }
11102 return nullptr;
11103 }
11104
RemoveFloatingFirstLetterFrames(nsIPresShell * aPresShell,nsIFrame * aBlockFrame)11105 void nsCSSFrameConstructor::RemoveFloatingFirstLetterFrames(
11106 nsIPresShell* aPresShell, nsIFrame* aBlockFrame) {
11107 // Look for the first letter frame on the kFloatList, then kPushedFloatsList.
11108 nsIFrame* floatFrame =
11109 ::FindFirstLetterFrame(aBlockFrame, nsIFrame::kFloatList);
11110 if (!floatFrame) {
11111 floatFrame =
11112 ::FindFirstLetterFrame(aBlockFrame, nsIFrame::kPushedFloatsList);
11113 if (!floatFrame) {
11114 return;
11115 }
11116 }
11117
11118 // Take the text frame away from the letter frame (so it isn't
11119 // destroyed when we destroy the letter frame).
11120 nsIFrame* textFrame = floatFrame->PrincipalChildList().FirstChild();
11121 if (!textFrame) {
11122 return;
11123 }
11124
11125 // Discover the placeholder frame for the letter frame
11126 nsPlaceholderFrame* placeholderFrame = floatFrame->GetPlaceholderFrame();
11127 if (!placeholderFrame) {
11128 // Somethings really wrong
11129 return;
11130 }
11131 nsContainerFrame* parentFrame = placeholderFrame->GetParent();
11132 if (!parentFrame) {
11133 // Somethings really wrong
11134 return;
11135 }
11136 static_cast<nsContainerFrame*>(parentFrame->FirstContinuation())
11137 ->ClearHasFirstLetterChild();
11138
11139 // Create a new text frame with the right style context that maps
11140 // all of the content that was previously part of the letter frame
11141 // (and probably continued elsewhere).
11142 nsStyleContext* parentSC = parentFrame->StyleContext();
11143 nsIContent* textContent = textFrame->GetContent();
11144 if (!textContent) {
11145 return;
11146 }
11147 RefPtr<nsStyleContext> newSC =
11148 aPresShell->StyleSet()->ResolveStyleForText(textContent, parentSC);
11149 nsIFrame* newTextFrame = NS_NewTextFrame(aPresShell, newSC);
11150 newTextFrame->Init(textContent, parentFrame, nullptr);
11151
11152 // Destroy the old text frame's continuations (the old text frame
11153 // will be destroyed when its letter frame is destroyed).
11154 nsIFrame* frameToDelete = textFrame->LastContinuation();
11155 while (frameToDelete != textFrame) {
11156 nsIFrame* nextFrameToDelete = frameToDelete->GetPrevContinuation();
11157 RemoveFrame(kPrincipalList, frameToDelete);
11158 frameToDelete = nextFrameToDelete;
11159 }
11160
11161 nsIFrame* prevSibling = placeholderFrame->GetPrevSibling();
11162
11163 // Now that everything is set...
11164 #ifdef NOISY_FIRST_LETTER
11165 printf(
11166 "RemoveFloatingFirstLetterFrames: textContent=%p oldTextFrame=%p "
11167 "newTextFrame=%p\n",
11168 textContent.get(), textFrame, newTextFrame);
11169 #endif
11170
11171 // Remove placeholder frame and the float
11172 RemoveFrame(kPrincipalList, placeholderFrame);
11173
11174 // Now that the old frames are gone, we can start pointing to our
11175 // new primary frame.
11176 textContent->SetPrimaryFrame(newTextFrame);
11177
11178 // Wallpaper bug 822910.
11179 bool offsetsNeedFixing = prevSibling && prevSibling->IsTextFrame();
11180 if (offsetsNeedFixing) {
11181 prevSibling->AddStateBits(TEXT_OFFSETS_NEED_FIXING);
11182 }
11183
11184 // Insert text frame in its place
11185 nsFrameList textList(newTextFrame, newTextFrame);
11186 InsertFrames(parentFrame, kPrincipalList, prevSibling, textList);
11187
11188 if (offsetsNeedFixing) {
11189 prevSibling->RemoveStateBits(TEXT_OFFSETS_NEED_FIXING);
11190 }
11191 }
11192
RemoveFirstLetterFrames(nsIPresShell * aPresShell,nsContainerFrame * aFrame,nsContainerFrame * aBlockFrame,bool * aStopLooking)11193 void nsCSSFrameConstructor::RemoveFirstLetterFrames(
11194 nsIPresShell* aPresShell, nsContainerFrame* aFrame,
11195 nsContainerFrame* aBlockFrame, bool* aStopLooking) {
11196 nsIFrame* prevSibling = nullptr;
11197 nsIFrame* kid = aFrame->PrincipalChildList().FirstChild();
11198
11199 while (kid) {
11200 if (kid->IsLetterFrame()) {
11201 // Bingo. Found it. First steal away the text frame.
11202 static_cast<nsContainerFrame*>(aFrame->FirstContinuation())
11203 ->ClearHasFirstLetterChild();
11204 nsIFrame* textFrame = kid->PrincipalChildList().FirstChild();
11205 if (!textFrame) {
11206 break;
11207 }
11208
11209 // Create a new textframe
11210 nsStyleContext* parentSC = aFrame->StyleContext();
11211 if (!parentSC) {
11212 break;
11213 }
11214 nsIContent* textContent = textFrame->GetContent();
11215 if (!textContent) {
11216 break;
11217 }
11218 RefPtr<nsStyleContext> newSC =
11219 aPresShell->StyleSet()->ResolveStyleForText(textContent, parentSC);
11220 textFrame = NS_NewTextFrame(aPresShell, newSC);
11221 textFrame->Init(textContent, aFrame, nullptr);
11222
11223 // Next rip out the kid and replace it with the text frame
11224 RemoveFrame(kPrincipalList, kid);
11225
11226 // Now that the old frames are gone, we can start pointing to our
11227 // new primary frame.
11228 textContent->SetPrimaryFrame(textFrame);
11229
11230 // Wallpaper bug 822910.
11231 bool offsetsNeedFixing = prevSibling && prevSibling->IsTextFrame();
11232 if (offsetsNeedFixing) {
11233 prevSibling->AddStateBits(TEXT_OFFSETS_NEED_FIXING);
11234 }
11235
11236 // Insert text frame in its place
11237 nsFrameList textList(textFrame, textFrame);
11238 InsertFrames(aFrame, kPrincipalList, prevSibling, textList);
11239
11240 if (offsetsNeedFixing) {
11241 prevSibling->RemoveStateBits(TEXT_OFFSETS_NEED_FIXING);
11242 }
11243
11244 *aStopLooking = true;
11245 NS_ASSERTION(!aBlockFrame->GetPrevContinuation(),
11246 "should have the first continuation here");
11247 aBlockFrame->RemoveStateBits(NS_BLOCK_HAS_FIRST_LETTER_CHILD);
11248 break;
11249 } else if (IsInlineFrame(kid)) {
11250 nsContainerFrame* kidAsContainerFrame = do_QueryFrame(kid);
11251 if (kidAsContainerFrame) {
11252 // Look inside child inline frame for the letter frame.
11253 RemoveFirstLetterFrames(aPresShell, kidAsContainerFrame, aBlockFrame,
11254 aStopLooking);
11255 if (*aStopLooking) {
11256 break;
11257 }
11258 }
11259 }
11260 prevSibling = kid;
11261 kid = kid->GetNextSibling();
11262 }
11263 }
11264
RemoveLetterFrames(nsIPresShell * aPresShell,nsContainerFrame * aBlockFrame)11265 void nsCSSFrameConstructor::RemoveLetterFrames(nsIPresShell* aPresShell,
11266 nsContainerFrame* aBlockFrame) {
11267 aBlockFrame =
11268 static_cast<nsContainerFrame*>(aBlockFrame->FirstContinuation());
11269 aBlockFrame->RemoveProperty(nsContainerFrame::FirstLetterProperty());
11270 nsContainerFrame* continuation = aBlockFrame;
11271
11272 bool stopLooking = false;
11273 do {
11274 RemoveFloatingFirstLetterFrames(aPresShell, continuation);
11275 RemoveFirstLetterFrames(aPresShell, continuation, aBlockFrame,
11276 &stopLooking);
11277 if (stopLooking) {
11278 break;
11279 }
11280 continuation =
11281 static_cast<nsContainerFrame*>(continuation->GetNextContinuation());
11282 } while (continuation);
11283 }
11284
11285 // Fixup the letter frame situation for the given block
RecoverLetterFrames(nsContainerFrame * aBlockFrame)11286 void nsCSSFrameConstructor::RecoverLetterFrames(nsContainerFrame* aBlockFrame) {
11287 aBlockFrame =
11288 static_cast<nsContainerFrame*>(aBlockFrame->FirstContinuation());
11289 nsContainerFrame* continuation = aBlockFrame;
11290
11291 nsContainerFrame* parentFrame = nullptr;
11292 nsIFrame* textFrame = nullptr;
11293 nsIFrame* prevFrame = nullptr;
11294 nsFrameItems letterFrames;
11295 bool stopLooking = false;
11296 do {
11297 // XXX shouldn't this bit be set already (bug 408493), assert instead?
11298 continuation->AddStateBits(NS_BLOCK_HAS_FIRST_LETTER_STYLE);
11299 WrapFramesInFirstLetterFrame(
11300 aBlockFrame, continuation, continuation,
11301 continuation->PrincipalChildList().FirstChild(), &parentFrame,
11302 &textFrame, &prevFrame, letterFrames, &stopLooking);
11303 if (stopLooking) {
11304 break;
11305 }
11306 continuation =
11307 static_cast<nsContainerFrame*>(continuation->GetNextContinuation());
11308 } while (continuation);
11309
11310 if (parentFrame) {
11311 // Take the old textFrame out of the parent's child list
11312 RemoveFrame(kPrincipalList, textFrame);
11313
11314 // Insert in the letter frame(s)
11315 parentFrame->InsertFrames(kPrincipalList, prevFrame, letterFrames);
11316 }
11317 }
11318
11319 //----------------------------------------------------------------------
11320
11321 // listbox Widget Routines
11322
CreateListBoxContent(nsContainerFrame * aParentFrame,nsIFrame * aPrevFrame,nsIContent * aChild,nsIFrame ** aNewFrame,bool aIsAppend)11323 void nsCSSFrameConstructor::CreateListBoxContent(nsContainerFrame* aParentFrame,
11324 nsIFrame* aPrevFrame,
11325 nsIContent* aChild,
11326 nsIFrame** aNewFrame,
11327 bool aIsAppend) {
11328 #ifdef MOZ_XUL
11329 // Construct a new frame
11330 if (nullptr != aParentFrame) {
11331 nsFrameItems frameItems;
11332 TreeMatchContextHolder matchContext(mDocument);
11333 nsFrameConstructorState state(
11334 mPresShell, matchContext,
11335 GetAbsoluteContainingBlock(aParentFrame, FIXED_POS),
11336 GetAbsoluteContainingBlock(aParentFrame, ABS_POS),
11337 GetFloatContainingBlock(aParentFrame), do_AddRef(mTempFrameTreeState));
11338
11339 // If we ever initialize the ancestor filter on |state|, make sure
11340 // to push the right parent!
11341
11342 RefPtr<nsStyleContext> styleContext;
11343 styleContext = ResolveStyleContext(aParentFrame, aChild, &state);
11344
11345 // Pre-check for display "none" - only if we find that, do we create
11346 // any frame at all
11347 const nsStyleDisplay* display = styleContext->StyleDisplay();
11348
11349 if (StyleDisplay::None == display->mDisplay) {
11350 *aNewFrame = nullptr;
11351 return;
11352 }
11353
11354 AutoFrameConstructionItemList items(this);
11355 AddFrameConstructionItemsInternal(
11356 state, aChild, aParentFrame, aChild->NodeInfo()->NameAtom(),
11357 aChild->GetNameSpaceID(), true, styleContext, ITEM_ALLOW_XBL_BASE,
11358 nullptr, items);
11359 ConstructFramesFromItemList(state, items, aParentFrame,
11360 /* aParentIsWrapperAnonBox = */ false,
11361 frameItems);
11362
11363 nsIFrame* newFrame = frameItems.FirstChild();
11364 *aNewFrame = newFrame;
11365
11366 if (newFrame) {
11367 // Notify the parent frame
11368 if (aIsAppend)
11369 ((nsListBoxBodyFrame*)aParentFrame)->ListBoxAppendFrames(frameItems);
11370 else
11371 ((nsListBoxBodyFrame*)aParentFrame)
11372 ->ListBoxInsertFrames(aPrevFrame, frameItems);
11373 }
11374
11375 #ifdef ACCESSIBILITY
11376 if (newFrame) {
11377 nsAccessibilityService* accService = nsIPresShell::AccService();
11378 if (accService) {
11379 accService->ContentRangeInserted(mPresShell, aChild->GetParent(),
11380 aChild, aChild->GetNextSibling());
11381 }
11382 }
11383 #endif
11384 }
11385 #endif
11386 }
11387
11388 //----------------------------------------
11389
ConstructBlock(nsFrameConstructorState & aState,nsIContent * aContent,nsContainerFrame * aParentFrame,nsContainerFrame * aContentParentFrame,nsStyleContext * aStyleContext,nsContainerFrame ** aNewFrame,nsFrameItems & aFrameItems,nsIFrame * aPositionedFrameForAbsPosContainer,PendingBinding * aPendingBinding)11390 void nsCSSFrameConstructor::ConstructBlock(
11391 nsFrameConstructorState& aState, nsIContent* aContent,
11392 nsContainerFrame* aParentFrame, nsContainerFrame* aContentParentFrame,
11393 nsStyleContext* aStyleContext, nsContainerFrame** aNewFrame,
11394 nsFrameItems& aFrameItems, nsIFrame* aPositionedFrameForAbsPosContainer,
11395 PendingBinding* aPendingBinding) {
11396 // Create column wrapper if necessary
11397 nsContainerFrame* blockFrame = *aNewFrame;
11398 NS_ASSERTION((blockFrame->IsBlockFrame() || blockFrame->IsDetailsFrame()),
11399 "not a block frame nor a details frame?");
11400 nsContainerFrame* parent = aParentFrame;
11401 RefPtr<nsStyleContext> blockStyle = aStyleContext;
11402 const nsStyleColumn* columns = aStyleContext->StyleColumn();
11403
11404 if (columns->mColumnCount != NS_STYLE_COLUMN_COUNT_AUTO ||
11405 columns->mColumnWidth.GetUnit() != eStyleUnit_Auto) {
11406 nsContainerFrame* columnSetFrame = NS_NewColumnSetFrame(
11407 mPresShell, aStyleContext, nsFrameState(NS_FRAME_OWNS_ANON_BOXES));
11408
11409 InitAndRestoreFrame(aState, aContent, aParentFrame, columnSetFrame);
11410 blockStyle = mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
11411 nsCSSAnonBoxes::columnContent, aStyleContext);
11412 parent = columnSetFrame;
11413 *aNewFrame = columnSetFrame;
11414 if (aPositionedFrameForAbsPosContainer == blockFrame) {
11415 aPositionedFrameForAbsPosContainer = columnSetFrame;
11416 }
11417
11418 SetInitialSingleChild(columnSetFrame, blockFrame);
11419 }
11420
11421 blockFrame->SetStyleContextWithoutNotification(blockStyle);
11422 InitAndRestoreFrame(aState, aContent, parent, blockFrame);
11423
11424 aState.AddChild(*aNewFrame, aFrameItems, aContent,
11425 aContentParentFrame ? aContentParentFrame : aParentFrame);
11426 if (!mRootElementFrame) {
11427 // The frame we're constructing will be the root element frame.
11428 // Set mRootElementFrame before processing children.
11429 mRootElementFrame = *aNewFrame;
11430 }
11431
11432 // We should make the outer frame be the absolute containing block,
11433 // if one is required. We have to do this because absolute
11434 // positioning must be computed with respect to the CSS dimensions
11435 // of the element, which are the dimensions of the outer block. But
11436 // we can't really do that because only blocks can have absolute
11437 // children. So use the block and try to compensate with hacks
11438 // in nsBlockFrame::CalculateContainingBlockSizeForAbsolutes.
11439 nsFrameConstructorSaveState absoluteSaveState;
11440 (*aNewFrame)->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
11441 if (aPositionedFrameForAbsPosContainer) {
11442 // NS_ASSERTION(aRelPos, "should have made area frame for this");
11443 aState.PushAbsoluteContainingBlock(
11444 *aNewFrame, aPositionedFrameForAbsPosContainer, absoluteSaveState);
11445 }
11446
11447 // Process the child content
11448 nsFrameItems childItems;
11449 ProcessChildren(aState, aContent, aStyleContext, blockFrame, true, childItems,
11450 true, aPendingBinding);
11451
11452 // Set the frame's initial child list
11453 blockFrame->SetInitialChildList(kPrincipalList, childItems);
11454 }
11455
ConstructInline(nsFrameConstructorState & aState,FrameConstructionItem & aItem,nsContainerFrame * aParentFrame,const nsStyleDisplay * aDisplay,nsFrameItems & aFrameItems)11456 nsIFrame* nsCSSFrameConstructor::ConstructInline(
11457 nsFrameConstructorState& aState, FrameConstructionItem& aItem,
11458 nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay,
11459 nsFrameItems& aFrameItems) {
11460 // If an inline frame has non-inline kids, then we chop up the child list
11461 // into runs of blocks and runs of inlines, create anonymous block frames to
11462 // contain the runs of blocks, inline frames with our style context for the
11463 // runs of inlines, and put all these frames, in order, into aFrameItems. We
11464 // return the the first one. The whole setup is called an {ib}
11465 // split; in what follows "frames in the split" refers to the anonymous blocks
11466 // and inlines that contain our children.
11467 //
11468 // {ib} splits maintain the following invariants:
11469 // 1) All frames in the split have the NS_FRAME_PART_OF_IBSPLIT bit
11470 // set.
11471 // 2) Each frame in the split has the nsIFrame::IBSplitSibling
11472 // property pointing to the next frame in the split, except for the last
11473 // one, which does not have it set.
11474 // 3) Each frame in the split has the nsIFrame::IBSplitPrevSibling
11475 // property pointing to the previous frame in the split, except for the
11476 // first one, which does not have it set.
11477 // 4) The first and last frame in the split are always inlines.
11478 //
11479 // An invariant that is NOT maintained is that the wrappers are actually
11480 // linked via GetNextSibling linkage. A simple example is an inline
11481 // containing an inline that contains a block. The three parts of the inner
11482 // inline end up with three different parents.
11483 //
11484 // For example, this HTML:
11485 // <span>
11486 // <div>a</div>
11487 // <span>
11488 // b
11489 // <div>c</div>
11490 // </span>
11491 // d
11492 // <div>e</div>
11493 // f
11494 // </span>
11495 // Gives the following frame tree:
11496 //
11497 // Inline (outer span)
11498 // Block (anonymous, outer span)
11499 // Block (div)
11500 // Text("a")
11501 // Inline (outer span)
11502 // Inline (inner span)
11503 // Text("b")
11504 // Block (anonymous, outer span)
11505 // Block (anonymous, inner span)
11506 // Block (div)
11507 // Text("c")
11508 // Inline (outer span)
11509 // Inline (inner span)
11510 // Text("d")
11511 // Block (anonymous, outer span)
11512 // Block (div)
11513 // Text("e")
11514 // Inline (outer span)
11515 // Text("f")
11516
11517 nsIContent* const content = aItem.mContent;
11518 nsStyleContext* const styleContext = aItem.mStyleContext;
11519
11520 bool positioned = StyleDisplay::Inline == aDisplay->mDisplay &&
11521 aDisplay->IsRelativelyPositionedStyle() &&
11522 !nsSVGUtils::IsInSVGTextSubtree(aParentFrame);
11523
11524 nsInlineFrame* newFrame = NS_NewInlineFrame(mPresShell, styleContext);
11525
11526 // Initialize the frame
11527 InitAndRestoreFrame(aState, content, aParentFrame, newFrame);
11528
11529 // Inline frames can always have generated content
11530 newFrame->AddStateBits(NS_FRAME_MAY_HAVE_GENERATED_CONTENT);
11531
11532 nsFrameConstructorSaveState
11533 absoluteSaveState; // definition cannot be inside next block
11534 // because the object's destructor is significant
11535 // this is part of the fix for bug 42372
11536
11537 newFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
11538 if (positioned) {
11539 // Relatively positioned frames becomes a container for child
11540 // frames that are positioned
11541 aState.PushAbsoluteContainingBlock(newFrame, newFrame, absoluteSaveState);
11542 }
11543
11544 // Process the child content
11545 nsFrameItems childItems;
11546 ConstructFramesFromItemList(aState, aItem.mChildItems, newFrame,
11547 /* aParentIsWrapperAnonBox = */ false,
11548 childItems);
11549
11550 nsFrameList::FrameLinkEnumerator firstBlockEnumerator(childItems);
11551 if (!aItem.mIsAllInline) {
11552 FindFirstBlock(firstBlockEnumerator);
11553 }
11554
11555 if (aItem.mIsAllInline || firstBlockEnumerator.AtEnd()) {
11556 // This part is easy. We either already know we have no non-inline kids,
11557 // or haven't found any when constructing actual frames (the latter can
11558 // happen only if out-of-flows that we thought had no containing block
11559 // acquired one when ancestor inline frames and {ib} splits got
11560 // constructed). Just put all the kids into the single inline frame and
11561 // bail.
11562 newFrame->SetInitialChildList(kPrincipalList, childItems);
11563 aState.AddChild(newFrame, aFrameItems, content, aParentFrame);
11564 return newFrame;
11565 }
11566
11567 // This inline frame contains several types of children. Therefore this frame
11568 // has to be chopped into several pieces, as described above.
11569
11570 // Grab the first inline's kids
11571 nsFrameList firstInlineKids = childItems.ExtractHead(firstBlockEnumerator);
11572 newFrame->SetInitialChildList(kPrincipalList, firstInlineKids);
11573
11574 aFrameItems.AddChild(newFrame);
11575
11576 newFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
11577 CreateIBSiblings(aState, newFrame, positioned, childItems, aFrameItems);
11578
11579 return newFrame;
11580 }
11581
CreateIBSiblings(nsFrameConstructorState & aState,nsContainerFrame * aInitialInline,bool aIsPositioned,nsFrameItems & aChildItems,nsFrameItems & aSiblings)11582 void nsCSSFrameConstructor::CreateIBSiblings(nsFrameConstructorState& aState,
11583 nsContainerFrame* aInitialInline,
11584 bool aIsPositioned,
11585 nsFrameItems& aChildItems,
11586 nsFrameItems& aSiblings) {
11587 nsIContent* content = aInitialInline->GetContent();
11588 nsStyleContext* styleContext = aInitialInline->StyleContext();
11589 nsContainerFrame* parentFrame = aInitialInline->GetParent();
11590
11591 // Resolve the right style context for our anonymous blocks.
11592 // The distinction in styles is needed because of CSS 2.1, section
11593 // 9.2.1.1, which says:
11594 // When such an inline box is affected by relative positioning, any
11595 // resulting translation also affects the block-level box contained
11596 // in the inline box.
11597 RefPtr<nsStyleContext> blockSC =
11598 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
11599 nsCSSAnonBoxes::mozBlockInsideInlineWrapper, styleContext);
11600
11601 nsContainerFrame* lastNewInline =
11602 static_cast<nsContainerFrame*>(aInitialInline->FirstContinuation());
11603 do {
11604 // On entry to this loop aChildItems is not empty and the first frame in it
11605 // is block-level.
11606 NS_PRECONDITION(aChildItems.NotEmpty(), "Should have child items");
11607 NS_PRECONDITION(!aChildItems.FirstChild()->IsInlineOutside(),
11608 "Must have list starting with block");
11609
11610 // The initial run of blocks belongs to an anonymous block that we create
11611 // right now. The anonymous block will be the parent of these block
11612 // children of the inline.
11613 nsBlockFrame* blockFrame = NS_NewBlockFrame(mPresShell, blockSC);
11614 InitAndRestoreFrame(aState, content, parentFrame, blockFrame, false);
11615
11616 // Find the first non-block child which defines the end of our block kids
11617 // and the start of our next inline's kids
11618 nsFrameList::FrameLinkEnumerator firstNonBlock =
11619 FindFirstNonBlock(aChildItems);
11620 nsFrameList blockKids = aChildItems.ExtractHead(firstNonBlock);
11621
11622 MoveChildrenTo(aInitialInline, blockFrame, blockKids);
11623
11624 SetFrameIsIBSplit(lastNewInline, blockFrame);
11625 aSiblings.AddChild(blockFrame);
11626
11627 // Now grab the initial inlines in aChildItems and put them into an inline
11628 // frame.
11629 nsInlineFrame* inlineFrame = NS_NewInlineFrame(mPresShell, styleContext);
11630 InitAndRestoreFrame(aState, content, parentFrame, inlineFrame, false);
11631 inlineFrame->AddStateBits(NS_FRAME_MAY_HAVE_GENERATED_CONTENT |
11632 NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
11633 if (aIsPositioned) {
11634 inlineFrame->MarkAsAbsoluteContainingBlock();
11635 }
11636
11637 if (aChildItems.NotEmpty()) {
11638 nsFrameList::FrameLinkEnumerator firstBlock(aChildItems);
11639 FindFirstBlock(firstBlock);
11640 nsFrameList inlineKids = aChildItems.ExtractHead(firstBlock);
11641
11642 MoveChildrenTo(aInitialInline, inlineFrame, inlineKids);
11643 }
11644
11645 SetFrameIsIBSplit(blockFrame, inlineFrame);
11646 aSiblings.AddChild(inlineFrame);
11647 lastNewInline = inlineFrame;
11648 } while (aChildItems.NotEmpty());
11649
11650 SetFrameIsIBSplit(lastNewInline, nullptr);
11651 }
11652
BuildInlineChildItems(nsFrameConstructorState & aState,FrameConstructionItem & aParentItem,bool aItemIsWithinSVGText,bool aItemAllowsTextPathChild)11653 void nsCSSFrameConstructor::BuildInlineChildItems(
11654 nsFrameConstructorState& aState, FrameConstructionItem& aParentItem,
11655 bool aItemIsWithinSVGText, bool aItemAllowsTextPathChild) {
11656 // XXXbz should we preallocate aParentItem.mChildItems to some sane
11657 // length? Maybe even to parentContent->GetChildCount()?
11658 nsFrameConstructorState::PendingBindingAutoPusher pusher(
11659 aState, aParentItem.mPendingBinding);
11660
11661 nsStyleContext* const parentStyleContext = aParentItem.mStyleContext;
11662 nsIContent* const parentContent = aParentItem.mContent;
11663
11664 TreeMatchContext::AutoAncestorPusher ancestorPusher(aState.mTreeMatchContext);
11665 if (aState.HasAncestorFilter()) {
11666 ancestorPusher.PushAncestorAndStyleScope(parentContent->AsElement());
11667 } else {
11668 ancestorPusher.PushStyleScope(parentContent->AsElement());
11669 }
11670
11671 if (!aItemIsWithinSVGText) {
11672 // Probe for generated content before
11673 CreateGeneratedContentItem(aState, nullptr, parentContent->AsElement(),
11674 parentStyleContext, CSSPseudoElementType::before,
11675 aParentItem.mChildItems);
11676 }
11677
11678 uint32_t flags = ITEM_ALLOW_XBL_BASE | ITEM_ALLOW_PAGE_BREAK;
11679 if (aItemIsWithinSVGText) {
11680 flags |= ITEM_IS_WITHIN_SVG_TEXT;
11681 }
11682 if (aItemAllowsTextPathChild && aParentItem.mIsForSVGAElement) {
11683 flags |= ITEM_ALLOWS_TEXT_PATH_CHILD;
11684 }
11685
11686 if (!aParentItem.mAnonChildren.IsEmpty()) {
11687 // Use the anon-children list instead of the content tree child list so
11688 // that we use any special style context that should be associated with
11689 // the children, and so that we won't try to construct grandchildren frame
11690 // constructor items before the frame is available for their parent.
11691 AddFCItemsForAnonymousContent(aState, nullptr, aParentItem.mAnonChildren,
11692 aParentItem.mChildItems, flags);
11693 } else {
11694 // Use the content tree child list:
11695 FlattenedChildIterator iter(parentContent);
11696 for (nsIContent* content = iter.GetNextChild(); content;
11697 content = iter.GetNextChild()) {
11698 // Get the parent of the content and check if it is a XBL children element
11699 // (if the content is a children element then contentParent !=
11700 // parentContent because the FlattenedChildIterator will transitively
11701 // iterate through <xbl:children> for default content). Push the children
11702 // element as an ancestor here because it does not have a frame and would
11703 // not otherwise be pushed as an ancestor.
11704 nsIContent* contentParent = content->GetParent();
11705 MOZ_ASSERT(contentParent,
11706 "Parent must be non-null because we are iterating children.");
11707 TreeMatchContext::AutoAncestorPusher insertionPointPusher(
11708 aState.mTreeMatchContext);
11709 if (contentParent != parentContent && contentParent->IsElement()) {
11710 if (aState.HasAncestorFilter()) {
11711 insertionPointPusher.PushAncestorAndStyleScope(
11712 contentParent->AsElement());
11713 } else {
11714 insertionPointPusher.PushStyleScope(contentParent->AsElement());
11715 }
11716 }
11717
11718 // Manually check for comments/PIs, since we don't have a frame to pass to
11719 // AddFrameConstructionItems. We know our parent is a non-replaced
11720 // inline, so there is no need to do the NeedFrameFor check.
11721 content->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
11722 if (content->IsNodeOfType(nsINode::eCOMMENT) ||
11723 content->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION)) {
11724 continue;
11725 }
11726
11727 // See comment explaining why we need to remove the "is possible
11728 // restyle root" flags in AddFrameConstructionItems. But note
11729 // that we can remove all restyle flags, just like in
11730 // ProcessChildren and for the same reason.
11731 content->UnsetRestyleFlagsIfGecko();
11732
11733 RefPtr<nsStyleContext> childContext =
11734 ResolveStyleContext(parentStyleContext, content, &aState);
11735
11736 AddFrameConstructionItemsInternal(
11737 aState, content, nullptr, content->NodeInfo()->NameAtom(),
11738 content->GetNameSpaceID(), iter.XBLInvolved(), childContext, flags,
11739 nullptr, aParentItem.mChildItems);
11740 }
11741 }
11742
11743 if (!aItemIsWithinSVGText) {
11744 // Probe for generated content after
11745 CreateGeneratedContentItem(aState, nullptr, parentContent->AsElement(),
11746 parentStyleContext, CSSPseudoElementType::after,
11747 aParentItem.mChildItems);
11748 }
11749
11750 aParentItem.mIsAllInline = aParentItem.mChildItems.AreAllItemsInline();
11751 }
11752
11753 // return whether it's ok to append (in the AppendFrames sense) to
11754 // aParentFrame if our nextSibling is aNextSibling. aParentFrame must
11755 // be an ib-split inline.
IsSafeToAppendToIBSplitInline(nsIFrame * aParentFrame,nsIFrame * aNextSibling)11756 static bool IsSafeToAppendToIBSplitInline(nsIFrame* aParentFrame,
11757 nsIFrame* aNextSibling) {
11758 NS_PRECONDITION(IsInlineFrame(aParentFrame),
11759 "Must have an inline parent here");
11760 do {
11761 NS_ASSERTION(IsFramePartOfIBSplit(aParentFrame),
11762 "How is this not part of an ib-split?");
11763 if (aNextSibling || aParentFrame->GetNextContinuation() ||
11764 GetIBSplitSibling(aParentFrame)) {
11765 return false;
11766 }
11767
11768 aNextSibling = aParentFrame->GetNextSibling();
11769 aParentFrame = aParentFrame->GetParent();
11770 } while (IsInlineFrame(aParentFrame));
11771
11772 return true;
11773 }
11774
WipeContainingBlock(nsFrameConstructorState & aState,nsIFrame * aContainingBlock,nsIFrame * aFrame,FrameConstructionItemList & aItems,bool aIsAppend,nsIFrame * aPrevSibling)11775 bool nsCSSFrameConstructor::WipeContainingBlock(
11776 nsFrameConstructorState& aState, nsIFrame* aContainingBlock,
11777 nsIFrame* aFrame, FrameConstructionItemList& aItems, bool aIsAppend,
11778 nsIFrame* aPrevSibling) {
11779 if (aItems.IsEmpty()) {
11780 return false;
11781 }
11782
11783 // Before we go and append the frames, we must check for several
11784 // special situations.
11785
11786 // Situation #1 is a XUL frame that contains frames that are required
11787 // to be wrapped in blocks.
11788 if (aFrame->IsXULBoxFrame() &&
11789 !(aFrame->GetStateBits() & NS_STATE_BOX_WRAPS_KIDS_IN_BLOCK) &&
11790 aItems.AnyItemsNeedBlockParent()) {
11791 RecreateFramesForContent(aFrame->GetContent(), InsertionKind::Async);
11792 return true;
11793 }
11794
11795 nsIFrame* nextSibling = ::GetInsertNextSibling(aFrame, aPrevSibling);
11796
11797 // Situation #2 is a flex or grid container frame into which we're inserting
11798 // new inline non-replaced children, adjacent to an existing anonymous
11799 // flex or grid item.
11800 LayoutFrameType frameType = aFrame->Type();
11801 if (frameType == LayoutFrameType::FlexContainer ||
11802 frameType == LayoutFrameType::GridContainer) {
11803 FCItemIterator iter(aItems);
11804
11805 // Check if we're adding to-be-wrapped content right *after* an existing
11806 // anonymous flex or grid item (which would need to absorb this content).
11807 const bool isLegacyBox = IsFlexContainerForLegacyBox(aFrame);
11808 if (aPrevSibling && IsAnonymousFlexOrGridItem(aPrevSibling) &&
11809 iter.item().NeedsAnonFlexOrGridItem(aState, isLegacyBox)) {
11810 RecreateFramesForContent(aFrame->GetContent(), InsertionKind::Async);
11811 return true;
11812 }
11813
11814 // Check if we're adding to-be-wrapped content right *before* an existing
11815 // anonymous flex or grid item (which would need to absorb this content).
11816 if (nextSibling && IsAnonymousFlexOrGridItem(nextSibling)) {
11817 // Jump to the last entry in the list
11818 iter.SetToEnd();
11819 iter.Prev();
11820 if (iter.item().NeedsAnonFlexOrGridItem(aState, isLegacyBox)) {
11821 RecreateFramesForContent(aFrame->GetContent(), InsertionKind::Async);
11822 return true;
11823 }
11824 }
11825 }
11826
11827 // Situation #3 is an anonymous flex or grid item that's getting new children
11828 // who don't want to be wrapped.
11829 if (IsAnonymousFlexOrGridItem(aFrame)) {
11830 AssertAnonymousFlexOrGridItemParent(aFrame, aFrame->GetParent());
11831
11832 // We need to push a null float containing block to be sure that
11833 // "NeedsAnonFlexOrGridItem" will know we're not honoring floats for this
11834 // inserted content. (In particular, this is necessary in order for
11835 // its "GetGeometricParent" call to return the correct result.)
11836 // We're not honoring floats on this content because it has the
11837 // _flex/grid container_ as its parent in the content tree.
11838 nsFrameConstructorSaveState floatSaveState;
11839 aState.PushFloatContainingBlock(nullptr, floatSaveState);
11840
11841 FCItemIterator iter(aItems);
11842 // Skip over things that _do_ need an anonymous flex item, because
11843 // they're perfectly happy to go here -- they won't cause a reframe.
11844 nsIFrame* containerFrame = aFrame->GetParent();
11845 const bool isLegacyBox = IsFlexContainerForLegacyBox(containerFrame);
11846 if (!iter.SkipItemsThatNeedAnonFlexOrGridItem(aState, isLegacyBox)) {
11847 // We hit something that _doesn't_ need an anonymous flex item!
11848 // Rebuild the flex container to bust it out.
11849 RecreateFramesForContent(containerFrame->GetContent(),
11850 InsertionKind::Async);
11851 return true;
11852 }
11853
11854 // If we get here, then everything in |aItems| needs to be wrapped in
11855 // an anonymous flex or grid item. That's where it's already going - good!
11856 }
11857
11858 // Situation #4 is a ruby-related frame that's getting new children.
11859 // The situation for ruby is complex, especially when interacting with
11860 // spaces. It containes these two special cases apart from tables:
11861 // 1) There are effectively three types of white spaces in ruby frames
11862 // we handle differently: leading/tailing/inter-level space,
11863 // inter-base/inter-annotation space, and inter-segment space.
11864 // These three types of spaces can be converted to each other when
11865 // their sibling changes.
11866 // 2) The first effective child of a ruby frame must always be a ruby
11867 // base container. It should be created or destroyed accordingly.
11868 if (IsRubyPseudo(aFrame) || frameType == LayoutFrameType::Ruby ||
11869 RubyUtils::IsRubyContainerBox(frameType)) {
11870 // We want to optimize it better, and avoid reframing as much as
11871 // possible. But given the cases above, and the fact that a ruby
11872 // usually won't be very large, it should be fine to reframe it.
11873 RecreateFramesForContent(aFrame->GetContent(), InsertionKind::Async);
11874 return true;
11875 }
11876
11877 // Situation #5 is a case when table pseudo-frames don't work out right
11878 ParentType parentType = GetParentType(aFrame);
11879 // If all the kids want a parent of the type that aFrame is, then we're all
11880 // set to go. Indeed, there won't be any table pseudo-frames created between
11881 // aFrame and the kids, so those won't need to be merged with any table
11882 // pseudo-frames that might already be kids of aFrame. If aFrame itself is a
11883 // table pseudo-frame, then all the kids in this list would have wanted a
11884 // frame of that type wrapping them anyway, so putting them inside it is ok.
11885 if (!aItems.AllWantParentType(parentType)) {
11886 // Don't give up yet. If parentType is not eTypeBlock and the parent is
11887 // not a generated content frame, then try filtering whitespace out of the
11888 // list.
11889 if (parentType != eTypeBlock && !aFrame->IsGeneratedContentFrame()) {
11890 // For leading whitespace followed by a kid that wants our parent type,
11891 // there are four cases:
11892 // 1) We have a previous sibling which is not a table pseudo. That means
11893 // that previous sibling wanted a (non-block) parent of the type we're
11894 // looking at. Then the whitespace comes between two table-internal
11895 // elements, so should be collapsed out.
11896 // 2) We have a previous sibling which is a table pseudo. It might have
11897 // kids who want this whitespace, so we need to reframe.
11898 // 3) We have no previous sibling and our parent frame is not a table
11899 // pseudo. That means that we'll be at the beginning of our actual
11900 // non-block-type parent, and the whitespace is OK to collapse out.
11901 // If something is ever inserted before us, it'll find our own parent
11902 // as its parent and if it's something that would care about the
11903 // whitespace it'll want a block parent, so it'll trigger a reframe at
11904 // that point.
11905 // 4) We have no previous sibling and our parent frame is a table pseudo.
11906 // Need to reframe.
11907 // All that is predicated on finding the correct previous sibling. We
11908 // might have to walk backwards along continuations from aFrame to do so.
11909 //
11910 // It's always OK to drop whitespace between any two items that want a
11911 // parent of type parentType.
11912 //
11913 // For trailing whitespace preceded by a kid that wants our parent type,
11914 // there are four cases:
11915 // 1) We have a next sibling which is not a table pseudo. That means
11916 // that next sibling wanted a (non-block) parent of the type we're
11917 // looking at. Then the whitespace comes between two table-internal
11918 // elements, so should be collapsed out.
11919 // 2) We have a next sibling which is a table pseudo. It might have
11920 // kids who want this whitespace, so we need to reframe.
11921 // 3) We have no next sibling and our parent frame is not a table
11922 // pseudo. That means that we'll be at the end of our actual
11923 // non-block-type parent, and the whitespace is OK to collapse out.
11924 // If something is ever inserted after us, it'll find our own parent
11925 // as its parent and if it's something that would care about the
11926 // whitespace it'll want a block parent, so it'll trigger a reframe at
11927 // that point.
11928 // 4) We have no next sibling and our parent frame is a table pseudo.
11929 // Need to reframe.
11930 // All that is predicated on finding the correct next sibling. We might
11931 // have to walk forward along continuations from aFrame to do so. That
11932 // said, in the case when nextSibling is null at this point and aIsAppend
11933 // is true, we know we're in case 3. Furthermore, in that case we don't
11934 // even have to worry about the table pseudo situation; we know our
11935 // parent is not a table pseudo there.
11936 FCItemIterator iter(aItems);
11937 FCItemIterator start(iter);
11938 do {
11939 if (iter.SkipItemsWantingParentType(parentType)) {
11940 break;
11941 }
11942
11943 // iter points to an item that wants a different parent. If it's not
11944 // whitespace, we're done; no more point scanning the list.
11945 if (!iter.item().IsWhitespace(aState)) {
11946 break;
11947 }
11948
11949 if (iter == start) {
11950 // Leading whitespace. How to handle this depends on our
11951 // previous sibling and aFrame. See the long comment above.
11952 nsIFrame* prevSibling = aPrevSibling;
11953 if (!prevSibling) {
11954 // Try to find one after all
11955 nsIFrame* parentPrevCont = aFrame->GetPrevContinuation();
11956 while (parentPrevCont) {
11957 prevSibling =
11958 parentPrevCont->GetChildList(kPrincipalList).LastChild();
11959 if (prevSibling) {
11960 break;
11961 }
11962 parentPrevCont = parentPrevCont->GetPrevContinuation();
11963 }
11964 };
11965 if (prevSibling) {
11966 if (IsTablePseudo(prevSibling)) {
11967 // need to reframe
11968 break;
11969 }
11970 } else if (IsTablePseudo(aFrame)) {
11971 // need to reframe
11972 break;
11973 }
11974 }
11975
11976 FCItemIterator spaceEndIter(iter);
11977 // Advance spaceEndIter past any whitespace
11978 bool trailingSpaces = spaceEndIter.SkipWhitespace(aState);
11979
11980 bool okToDrop;
11981 if (trailingSpaces) {
11982 // Trailing whitespace. How to handle this depeds on aIsAppend, our
11983 // next sibling and aFrame. See the long comment above.
11984 okToDrop = aIsAppend && !nextSibling;
11985 if (!okToDrop) {
11986 if (!nextSibling) {
11987 // Try to find one after all
11988 nsIFrame* parentNextCont = aFrame->GetNextContinuation();
11989 while (parentNextCont) {
11990 nextSibling = parentNextCont->PrincipalChildList().FirstChild();
11991 if (nextSibling) {
11992 break;
11993 }
11994 parentNextCont = parentNextCont->GetNextContinuation();
11995 }
11996 }
11997
11998 okToDrop = (nextSibling && !IsTablePseudo(nextSibling)) ||
11999 (!nextSibling && !IsTablePseudo(aFrame));
12000 }
12001 #ifdef DEBUG
12002 else {
12003 NS_ASSERTION(!IsTablePseudo(aFrame), "How did that happen?");
12004 }
12005 #endif
12006 } else {
12007 okToDrop = (spaceEndIter.item().DesiredParentType() == parentType);
12008 }
12009
12010 if (okToDrop) {
12011 iter.DeleteItemsTo(this, spaceEndIter);
12012 } else {
12013 // We're done: we don't want to drop the whitespace, and it has the
12014 // wrong parent type.
12015 break;
12016 }
12017
12018 // Now loop, since |iter| points to item right after the whitespace we
12019 // removed.
12020 } while (!iter.IsDone());
12021 }
12022
12023 // We might be able to figure out some sort of optimizations here, but they
12024 // would have to depend on having a correct aPrevSibling and a correct next
12025 // sibling. For example, we can probably avoid reframing if none of
12026 // aFrame, aPrevSibling, and next sibling are table pseudo-frames. But it
12027 // doesn't seem worth it to worry about that for now, especially since we
12028 // in fact do not have a reliable aPrevSibling, nor any next sibling, in
12029 // this method.
12030
12031 // aItems might have changed, so recheck the parent type thing. In fact,
12032 // it might be empty, so recheck that too.
12033 if (aItems.IsEmpty()) {
12034 return false;
12035 }
12036
12037 if (!aItems.AllWantParentType(parentType)) {
12038 // Reframing aFrame->GetContent() is good enough, since the content of
12039 // table pseudo-frames is the ancestor content.
12040 RecreateFramesForContent(aFrame->GetContent(), InsertionKind::Async);
12041 return true;
12042 }
12043 }
12044
12045 // Now we have several cases involving {ib} splits. Put them all in a
12046 // do/while with breaks to take us to the "go and reconstruct" code.
12047 do {
12048 if (IsInlineFrame(aFrame)) {
12049 if (aItems.AreAllItemsInline()) {
12050 // We can just put the kids in.
12051 return false;
12052 }
12053
12054 if (!IsFramePartOfIBSplit(aFrame)) {
12055 // Need to go ahead and reconstruct.
12056 break;
12057 }
12058
12059 // Now we're adding kids including some blocks to an inline part of an
12060 // {ib} split. If we plan to call AppendFrames, and don't have a next
12061 // sibling for the new frames, and our parent is the last continuation of
12062 // the last part of the {ib} split, and the same is true of all our
12063 // ancestor inlines (they have no following continuations and they're the
12064 // last part of their {ib} splits and we'd be adding to the end for all
12065 // of them), then AppendFrames will handle things for us. Bail out in
12066 // that case.
12067 if (aIsAppend && IsSafeToAppendToIBSplitInline(aFrame, nextSibling)) {
12068 return false;
12069 }
12070
12071 // Need to reconstruct.
12072 break;
12073 }
12074
12075 // Now we know we have a block parent. If it's not part of an
12076 // ib-split, we're all set.
12077 if (!IsFramePartOfIBSplit(aFrame)) {
12078 return false;
12079 }
12080
12081 // We're adding some kids to a block part of an {ib} split. If all the
12082 // kids are blocks, we don't need to reconstruct.
12083 if (aItems.AreAllItemsBlock()) {
12084 return false;
12085 }
12086
12087 // We might have some inline kids for this block. Just fall out of the
12088 // loop and reconstruct.
12089 } while (0);
12090
12091 // If we don't have a containing block, start with aFrame and look for one.
12092 if (!aContainingBlock) {
12093 aContainingBlock = aFrame;
12094 }
12095
12096 // To find the right block to reframe, just walk up the tree until we find a
12097 // frame that is:
12098 // 1) Not part of an IB split
12099 // 2) Not a pseudo-frame
12100 // 3) Not an inline frame
12101 // We're guaranteed to find one, since nsStyleContext::ApplyStyleFixups
12102 // enforces that the root is display:none, display:table, or display:block.
12103 // Note that walking up "too far" is OK in terms of correctness, even if it
12104 // might be a little inefficient. This is why we walk out of all
12105 // pseudo-frames -- telling which ones are or are not OK to walk out of is
12106 // too hard (and I suspect that we do in fact need to walk out of all of
12107 // them).
12108 while (IsFramePartOfIBSplit(aContainingBlock) ||
12109 aContainingBlock->IsInlineOutside() ||
12110 aContainingBlock->StyleContext()->GetPseudo()) {
12111 aContainingBlock = aContainingBlock->GetParent();
12112 NS_ASSERTION(aContainingBlock,
12113 "Must have non-inline, non-ib-split, non-pseudo frame as "
12114 "root (or child of root, for a table root)!");
12115 }
12116
12117 // Tell parent of the containing block to reformulate the
12118 // entire block. This is painful and definitely not optimal
12119 // but it will *always* get the right answer.
12120
12121 nsIContent* blockContent = aContainingBlock->GetContent();
12122 #ifdef DEBUG
12123 if (gNoisyContentUpdates) {
12124 printf("nsCSSFrameConstructor::WipeContainingBlock: blockContent=%p\n",
12125 static_cast<void*>(blockContent));
12126 }
12127 #endif
12128 RecreateFramesForContent(blockContent, InsertionKind::Async);
12129 return true;
12130 }
12131
ReframeContainingBlock(nsIFrame * aFrame)12132 void nsCSSFrameConstructor::ReframeContainingBlock(nsIFrame* aFrame) {
12133 #ifdef DEBUG
12134 // ReframeContainingBlock is a NASTY routine, it causes terrible performance
12135 // problems so I want to see when it is happening! Unfortunately, it is
12136 // happening way to often because so much content on the web causes
12137 // block-in-inline frame situations and we handle them very poorly
12138 if (gNoisyContentUpdates) {
12139 printf("nsCSSFrameConstructor::ReframeContainingBlock frame=%p\n",
12140 static_cast<void*>(aFrame));
12141 }
12142 #endif
12143
12144 // XXXbz how exactly would we get here while isReflowing anyway? Should this
12145 // whole test be ifdef DEBUG?
12146 if (mPresShell->IsReflowLocked()) {
12147 // don't ReframeContainingBlock, this will result in a crash
12148 // if we remove a tree that's in reflow - see bug 121368 for testcase
12149 NS_ERROR(
12150 "Atemptted to nsCSSFrameConstructor::ReframeContainingBlock during a "
12151 "Reflow!!!");
12152 return;
12153 }
12154
12155 // Get the first "normal" ancestor of the target frame.
12156 nsIFrame* containingBlock = GetIBContainingBlockFor(aFrame);
12157 if (containingBlock) {
12158 // From here we look for the containing block in case the target
12159 // frame is already a block (which can happen when an inline frame
12160 // wraps some of its content in an anonymous block; see
12161 // ConstructInline)
12162
12163 // NOTE: We used to get the FloatContainingBlock here, but it was often
12164 // wrong. GetIBContainingBlock works much better and provides the correct
12165 // container in all cases so GetFloatContainingBlock(aFrame) has been
12166 // removed
12167
12168 // And get the containingBlock's content
12169 if (nsIContent* blockContent = containingBlock->GetContent()) {
12170 #ifdef DEBUG
12171 if (gNoisyContentUpdates) {
12172 printf(" ==> blockContent=%p\n", static_cast<void*>(blockContent));
12173 }
12174 #endif
12175 RecreateFramesForContent(blockContent->AsElement(), InsertionKind::Async);
12176 return;
12177 }
12178 }
12179
12180 // If we get here, we're screwed!
12181 RecreateFramesForContent(mPresShell->GetDocument()->GetRootElement(),
12182 InsertionKind::Async);
12183 }
12184
GenerateChildFrames(nsContainerFrame * aFrame)12185 void nsCSSFrameConstructor::GenerateChildFrames(nsContainerFrame* aFrame) {
12186 {
12187 nsAutoScriptBlocker scriptBlocker;
12188 nsFrameItems childItems;
12189 TreeMatchContextHolder matchContext(mDocument);
12190 nsFrameConstructorState state(mPresShell, matchContext, nullptr, nullptr,
12191 nullptr);
12192 // We don't have a parent frame with a pending binding constructor here,
12193 // so no need to worry about ordering of the kids' constructors with it.
12194 // Pass null for the PendingBinding.
12195 ProcessChildren(state, aFrame->GetContent(), aFrame->StyleContext(), aFrame,
12196 false, childItems, false, nullptr);
12197
12198 aFrame->SetInitialChildList(kPrincipalList, childItems);
12199 }
12200
12201 #ifdef ACCESSIBILITY
12202 nsAccessibilityService* accService = nsIPresShell::AccService();
12203 if (accService) {
12204 nsIContent* container = aFrame->GetContent();
12205 nsIContent* child = container->GetFirstChild();
12206 if (child) {
12207 accService->ContentRangeInserted(mPresShell, container, child, nullptr);
12208 }
12209 }
12210 #endif
12211
12212 // call XBL constructors after the frames are created
12213 mPresShell->GetDocument()->BindingManager()->ProcessAttachedQueue();
12214 }
12215
12216 //////////////////////////////////////////////////////////
12217 // nsCSSFrameConstructor::FrameConstructionItem methods //
12218 //////////////////////////////////////////////////////////
IsWhitespace(nsFrameConstructorState & aState) const12219 bool nsCSSFrameConstructor::FrameConstructionItem::IsWhitespace(
12220 nsFrameConstructorState& aState) const {
12221 NS_PRECONDITION(aState.mCreatingExtraFrames || !mContent->GetPrimaryFrame(),
12222 "How did that happen?");
12223 if (!mIsText) {
12224 return false;
12225 }
12226 mContent->SetFlags(NS_CREATE_FRAME_IF_NON_WHITESPACE |
12227 NS_REFRAME_IF_WHITESPACE);
12228 return mContent->TextIsOnlyWhitespace();
12229 }
12230
12231 //////////////////////////////////////////////////////////////
12232 // nsCSSFrameConstructor::FrameConstructionItemList methods //
12233 //////////////////////////////////////////////////////////////
AdjustCountsForItem(FrameConstructionItem * aItem,int32_t aDelta)12234 void nsCSSFrameConstructor::FrameConstructionItemList::AdjustCountsForItem(
12235 FrameConstructionItem* aItem, int32_t aDelta) {
12236 NS_PRECONDITION(aDelta == 1 || aDelta == -1, "Unexpected delta");
12237 mItemCount += aDelta;
12238 if (aItem->mIsAllInline) {
12239 mInlineCount += aDelta;
12240 }
12241 if (aItem->mIsBlock) {
12242 mBlockCount += aDelta;
12243 }
12244 if (aItem->mIsLineParticipant) {
12245 mLineParticipantCount += aDelta;
12246 }
12247 mDesiredParentCounts[aItem->DesiredParentType()] += aDelta;
12248 }
12249
12250 ////////////////////////////////////////////////////////////////////////
12251 // nsCSSFrameConstructor::FrameConstructionItemList::Iterator methods //
12252 ////////////////////////////////////////////////////////////////////////
12253 inline bool nsCSSFrameConstructor::FrameConstructionItemList::Iterator::
SkipItemsWantingParentType(ParentType aParentType)12254 SkipItemsWantingParentType(ParentType aParentType) {
12255 NS_PRECONDITION(!IsDone(), "Shouldn't be done yet");
12256 while (item().DesiredParentType() == aParentType) {
12257 Next();
12258 if (IsDone()) {
12259 return true;
12260 }
12261 }
12262 return false;
12263 }
12264
12265 inline bool nsCSSFrameConstructor::FrameConstructionItemList::Iterator::
SkipItemsNotWantingParentType(ParentType aParentType)12266 SkipItemsNotWantingParentType(ParentType aParentType) {
12267 NS_PRECONDITION(!IsDone(), "Shouldn't be done yet");
12268 while (item().DesiredParentType() != aParentType) {
12269 Next();
12270 if (IsDone()) {
12271 return true;
12272 }
12273 }
12274 return false;
12275 }
12276
12277 // Note: we implement -webkit-{inline-}box (and optionally -moz-{inline-}box)
12278 // using nsFlexContainerFrame, but we use different rules for what gets wrapped
12279 // in an anonymous flex item.
NeedsAnonFlexOrGridItem(const nsFrameConstructorState & aState,bool aIsLegacyBox)12280 bool nsCSSFrameConstructor::FrameConstructionItem::NeedsAnonFlexOrGridItem(
12281 const nsFrameConstructorState& aState, bool aIsLegacyBox) {
12282 if (mFCData->mBits & FCDATA_IS_LINE_PARTICIPANT) {
12283 // This will be an inline non-replaced box.
12284 return true;
12285 }
12286
12287 if (aIsLegacyBox) {
12288 if (mStyleContext->StyleDisplay()->IsInlineOutsideStyle()) {
12289 // In an emulated legacy box, all inline-level content gets wrapped in an
12290 // anonymous flex item.
12291 return true;
12292 }
12293 if (mIsPopup ||
12294 (!(mFCData->mBits & FCDATA_DISALLOW_OUT_OF_FLOW) &&
12295 aState.GetGeometricParent(mStyleContext->StyleDisplay(), nullptr))) {
12296 // We're abspos or fixedpos (or a XUL popup), which means we'll spawn a
12297 // placeholder which (because our container is an emulated legacy box)
12298 // we'll need to wrap in an anonymous flex item. So, we just treat
12299 // _this_ frame as if _it_ needs to be wrapped in an anonymous flex item,
12300 // and then when we spawn the placeholder, it'll end up in the right
12301 // spot.
12302 return true;
12303 }
12304 }
12305
12306 return false;
12307 }
12308
12309 inline bool nsCSSFrameConstructor::FrameConstructionItemList::Iterator::
SkipItemsThatNeedAnonFlexOrGridItem(const nsFrameConstructorState & aState,bool aIsLegacyBox)12310 SkipItemsThatNeedAnonFlexOrGridItem(const nsFrameConstructorState& aState,
12311 bool aIsLegacyBox) {
12312 NS_PRECONDITION(!IsDone(), "Shouldn't be done yet");
12313 while (item().NeedsAnonFlexOrGridItem(aState, aIsLegacyBox)) {
12314 Next();
12315 if (IsDone()) {
12316 return true;
12317 }
12318 }
12319 return false;
12320 }
12321
12322 inline bool nsCSSFrameConstructor::FrameConstructionItemList::Iterator::
SkipItemsThatDontNeedAnonFlexOrGridItem(const nsFrameConstructorState & aState,bool aIsLegacyBox)12323 SkipItemsThatDontNeedAnonFlexOrGridItem(
12324 const nsFrameConstructorState& aState, bool aIsLegacyBox) {
12325 NS_PRECONDITION(!IsDone(), "Shouldn't be done yet");
12326 while (!(item().NeedsAnonFlexOrGridItem(aState, aIsLegacyBox))) {
12327 Next();
12328 if (IsDone()) {
12329 return true;
12330 }
12331 }
12332 return false;
12333 }
12334
12335 inline bool nsCSSFrameConstructor::FrameConstructionItemList::Iterator::
SkipItemsNotWantingRubyParent()12336 SkipItemsNotWantingRubyParent() {
12337 NS_PRECONDITION(!IsDone(), "Shouldn't be done yet");
12338 while (!IsRubyParentType(item().DesiredParentType())) {
12339 Next();
12340 if (IsDone()) {
12341 return true;
12342 }
12343 }
12344 return false;
12345 }
12346
12347 inline bool
SkipWhitespace(nsFrameConstructorState & aState)12348 nsCSSFrameConstructor::FrameConstructionItemList::Iterator::SkipWhitespace(
12349 nsFrameConstructorState& aState) {
12350 NS_PRECONDITION(!IsDone(), "Shouldn't be done yet");
12351 NS_PRECONDITION(item().IsWhitespace(aState), "Not pointing to whitespace?");
12352 do {
12353 Next();
12354 if (IsDone()) {
12355 return true;
12356 }
12357 } while (item().IsWhitespace(aState));
12358
12359 return false;
12360 }
12361
12362 void nsCSSFrameConstructor::FrameConstructionItemList::Iterator::
AppendItemToList(FrameConstructionItemList & aTargetList)12363 AppendItemToList(FrameConstructionItemList& aTargetList) {
12364 NS_ASSERTION(&aTargetList != &mList, "Unexpected call");
12365 NS_PRECONDITION(!IsDone(), "should not be done");
12366
12367 FrameConstructionItem* item = mCurrent;
12368 Next();
12369 item->remove();
12370 aTargetList.mItems.insertBack(item);
12371
12372 mList.AdjustCountsForItem(item, -1);
12373 aTargetList.AdjustCountsForItem(item, 1);
12374 }
12375
12376 void nsCSSFrameConstructor::FrameConstructionItemList::Iterator::
AppendItemsToList(nsCSSFrameConstructor * aFCtor,const Iterator & aEnd,FrameConstructionItemList & aTargetList)12377 AppendItemsToList(nsCSSFrameConstructor* aFCtor, const Iterator& aEnd,
12378 FrameConstructionItemList& aTargetList) {
12379 NS_ASSERTION(&aTargetList != &mList, "Unexpected call");
12380 NS_PRECONDITION(&mList == &aEnd.mList, "End iterator for some other list?");
12381
12382 // We can't just move our guts to the other list if it already has
12383 // some information or if we're not moving our entire list.
12384 if (!AtStart() || !aEnd.IsDone() || !aTargetList.IsEmpty() ||
12385 !aTargetList.mUndisplayedItems.IsEmpty()) {
12386 do {
12387 AppendItemToList(aTargetList);
12388 } while (*this != aEnd);
12389 return;
12390 }
12391
12392 // Move our entire list of items into the empty target list.
12393 aTargetList.mItems = Move(mList.mItems);
12394
12395 // Copy over the various counters
12396 aTargetList.mInlineCount = mList.mInlineCount;
12397 aTargetList.mBlockCount = mList.mBlockCount;
12398 aTargetList.mLineParticipantCount = mList.mLineParticipantCount;
12399 aTargetList.mItemCount = mList.mItemCount;
12400 memcpy(aTargetList.mDesiredParentCounts, mList.mDesiredParentCounts,
12401 sizeof(aTargetList.mDesiredParentCounts));
12402
12403 // Swap out undisplayed item arrays, before we nuke the array on our end
12404 aTargetList.mUndisplayedItems.SwapElements(mList.mUndisplayedItems);
12405
12406 // reset mList
12407 mList.Reset(aFCtor);
12408
12409 // Point ourselves to aEnd, as advertised
12410 SetToEnd();
12411 MOZ_ASSERT(*this == aEnd, "How did that happen?");
12412 }
12413
InsertItem(FrameConstructionItem * aItem)12414 void nsCSSFrameConstructor::FrameConstructionItemList::Iterator::InsertItem(
12415 FrameConstructionItem* aItem) {
12416 if (IsDone()) {
12417 mList.mItems.insertBack(aItem);
12418 } else {
12419 // Just insert the item before us. There's no magic here.
12420 mCurrent->setPrevious(aItem);
12421 }
12422 mList.AdjustCountsForItem(aItem, 1);
12423
12424 MOZ_ASSERT(aItem->getNext() == mCurrent, "How did that happen?");
12425 }
12426
DeleteItemsTo(nsCSSFrameConstructor * aFCtor,const Iterator & aEnd)12427 void nsCSSFrameConstructor::FrameConstructionItemList::Iterator::DeleteItemsTo(
12428 nsCSSFrameConstructor* aFCtor, const Iterator& aEnd) {
12429 NS_PRECONDITION(&mList == &aEnd.mList, "End iterator for some other list?");
12430 NS_PRECONDITION(*this != aEnd, "Shouldn't be at aEnd yet");
12431
12432 do {
12433 NS_ASSERTION(!IsDone(), "Ran off end of list?");
12434 FrameConstructionItem* item = mCurrent;
12435 Next();
12436 item->remove();
12437 mList.AdjustCountsForItem(item, -1);
12438 item->Delete(aFCtor);
12439 } while (*this != aEnd);
12440 }
12441
QuotesDirty()12442 void nsCSSFrameConstructor::QuotesDirty() {
12443 mQuotesDirty = true;
12444 mPresShell->SetNeedLayoutFlush();
12445 }
12446
CountersDirty()12447 void nsCSSFrameConstructor::CountersDirty() {
12448 mCountersDirty = true;
12449 mPresShell->SetNeedLayoutFlush();
12450 }
12451
AllocateFCItem()12452 void* nsCSSFrameConstructor::AllocateFCItem() {
12453 void* item;
12454 if (mFirstFreeFCItem) {
12455 item = mFirstFreeFCItem;
12456 mFirstFreeFCItem = mFirstFreeFCItem->mNext;
12457 } else {
12458 item = mFCItemPool.Allocate(sizeof(FrameConstructionItem));
12459 }
12460 ++mFCItemsInUse;
12461 return item;
12462 }
12463
FreeFCItem(FrameConstructionItem * aItem)12464 void nsCSSFrameConstructor::FreeFCItem(FrameConstructionItem* aItem) {
12465 MOZ_ASSERT(mFCItemsInUse != 0);
12466 if (--mFCItemsInUse == 0) {
12467 // The arena is now unused - clear it but retain one chunk.
12468 mFirstFreeFCItem = nullptr;
12469 mFCItemPool.Clear();
12470 } else {
12471 // Prepend it to the list of free items.
12472 FreeFCItemLink* item = reinterpret_cast<FreeFCItemLink*>(aItem);
12473 item->mNext = mFirstFreeFCItem;
12474 mFirstFreeFCItem = item;
12475 }
12476 }
12477
AddSizeOfIncludingThis(nsWindowSizes & aSizes) const12478 void nsCSSFrameConstructor::AddSizeOfIncludingThis(
12479 nsWindowSizes& aSizes) const {
12480 if (nsIFrame* rootFrame = GetRootFrame()) {
12481 rootFrame->AddSizeOfExcludingThisForTree(aSizes);
12482 }
12483
12484 // This must be done after measuring from the frame tree, since frame
12485 // manager will measure sizes of staled computed values and style
12486 // structs, which only make sense after we know what are being used.
12487 nsFrameManager::AddSizeOfIncludingThis(aSizes);
12488
12489 // Measurement of the following members may be added later if DMD finds it
12490 // is worthwhile:
12491 // - mFCItemPool
12492 // - mQuoteList
12493 // - mCounterManager
12494 }
12495