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 /* base class #1 for rendering objects that have child lists */
8
9 #include "nsContainerFrame.h"
10
11 #include "mozilla/ComputedStyle.h"
12 #include "mozilla/PresShell.h"
13 #include "mozilla/dom/HTMLSummaryElement.h"
14 #include "nsAbsoluteContainingBlock.h"
15 #include "nsAttrValue.h"
16 #include "nsAttrValueInlines.h"
17 #include "nsFlexContainerFrame.h"
18 #include "mozilla/dom/Document.h"
19 #include "nsPresContext.h"
20 #include "nsRect.h"
21 #include "nsPoint.h"
22 #include "nsStyleConsts.h"
23 #include "nsView.h"
24 #include "nsCOMPtr.h"
25 #include "nsGkAtoms.h"
26 #include "nsViewManager.h"
27 #include "nsIWidget.h"
28 #include "nsCSSRendering.h"
29 #include "nsError.h"
30 #include "nsDisplayList.h"
31 #include "nsIBaseWindow.h"
32 #include "nsBoxLayoutState.h"
33 #include "nsCSSFrameConstructor.h"
34 #include "nsBlockFrame.h"
35 #include "nsBulletFrame.h"
36 #include "nsPlaceholderFrame.h"
37 #include "mozilla/AutoRestore.h"
38 #include "nsIFrameInlines.h"
39 #include "nsPrintfCString.h"
40 #include <algorithm>
41
42 using namespace mozilla;
43 using namespace mozilla::dom;
44 using namespace mozilla::layout;
45
46 nsContainerFrame::~nsContainerFrame() = default;
47
48 NS_QUERYFRAME_HEAD(nsContainerFrame)
NS_QUERYFRAME_ENTRY(nsContainerFrame)49 NS_QUERYFRAME_ENTRY(nsContainerFrame)
50 NS_QUERYFRAME_TAIL_INHERITING(nsSplittableFrame)
51
52 void nsContainerFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
53 nsIFrame* aPrevInFlow) {
54 nsSplittableFrame::Init(aContent, aParent, aPrevInFlow);
55 if (aPrevInFlow) {
56 // Make sure we copy bits from our prev-in-flow that will affect
57 // us. A continuation for a container frame needs to know if it
58 // has a child with a view so that we'll properly reposition it.
59 if (aPrevInFlow->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW)
60 AddStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW);
61 }
62 }
63
SetInitialChildList(ChildListID aListID,nsFrameList & aChildList)64 void nsContainerFrame::SetInitialChildList(ChildListID aListID,
65 nsFrameList& aChildList) {
66 #ifdef DEBUG
67 nsFrame::VerifyDirtyBitSet(aChildList);
68 for (nsIFrame* f : aChildList) {
69 MOZ_ASSERT(f->GetParent() == this, "Unexpected parent");
70 }
71 #endif
72 if (aListID == kPrincipalList) {
73 MOZ_ASSERT(mFrames.IsEmpty(),
74 "unexpected second call to SetInitialChildList");
75 mFrames.SetFrames(aChildList);
76 } else if (aListID == kBackdropList) {
77 MOZ_ASSERT(StyleDisplay()->mTopLayer != StyleTopLayer::None,
78 "Only top layer frames should have backdrop");
79 MOZ_ASSERT(GetStateBits() & NS_FRAME_OUT_OF_FLOW,
80 "Top layer frames should be out-of-flow");
81 MOZ_ASSERT(!GetProperty(BackdropProperty()),
82 "We shouldn't have setup backdrop frame list before");
83 #ifdef DEBUG
84 {
85 nsIFrame* placeholder = aChildList.FirstChild();
86 MOZ_ASSERT(aChildList.OnlyChild(), "Should have only one backdrop");
87 MOZ_ASSERT(placeholder->IsPlaceholderFrame(),
88 "The frame to be stored should be a placeholder");
89 MOZ_ASSERT(static_cast<nsPlaceholderFrame*>(placeholder)
90 ->GetOutOfFlowFrame()
91 ->IsBackdropFrame(),
92 "The placeholder should points to a backdrop frame");
93 }
94 #endif
95 nsFrameList* list = new (PresShell()) nsFrameList(aChildList);
96 SetProperty(BackdropProperty(), list);
97 } else {
98 MOZ_ASSERT_UNREACHABLE("Unexpected child list");
99 }
100 }
101
AppendFrames(ChildListID aListID,nsFrameList & aFrameList)102 void nsContainerFrame::AppendFrames(ChildListID aListID,
103 nsFrameList& aFrameList) {
104 MOZ_ASSERT(aListID == kPrincipalList || aListID == kNoReflowPrincipalList,
105 "unexpected child list");
106
107 if (MOZ_UNLIKELY(aFrameList.IsEmpty())) {
108 return;
109 }
110
111 DrainSelfOverflowList(); // ensure the last frame is in mFrames
112 mFrames.AppendFrames(this, aFrameList);
113
114 if (aListID != kNoReflowPrincipalList) {
115 PresShell()->FrameNeedsReflow(this, IntrinsicDirty::TreeChange,
116 NS_FRAME_HAS_DIRTY_CHILDREN);
117 }
118 }
119
InsertFrames(ChildListID aListID,nsIFrame * aPrevFrame,const nsLineList::iterator * aPrevFrameLine,nsFrameList & aFrameList)120 void nsContainerFrame::InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame,
121 const nsLineList::iterator* aPrevFrameLine,
122 nsFrameList& aFrameList) {
123 MOZ_ASSERT(aListID == kPrincipalList || aListID == kNoReflowPrincipalList,
124 "unexpected child list");
125 NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
126 "inserting after sibling frame with different parent");
127
128 if (MOZ_UNLIKELY(aFrameList.IsEmpty())) {
129 return;
130 }
131
132 DrainSelfOverflowList(); // ensure aPrevFrame is in mFrames
133 mFrames.InsertFrames(this, aPrevFrame, aFrameList);
134
135 if (aListID != kNoReflowPrincipalList) {
136 PresShell()->FrameNeedsReflow(this, IntrinsicDirty::TreeChange,
137 NS_FRAME_HAS_DIRTY_CHILDREN);
138 }
139 }
140
RemoveFrame(ChildListID aListID,nsIFrame * aOldFrame)141 void nsContainerFrame::RemoveFrame(ChildListID aListID, nsIFrame* aOldFrame) {
142 MOZ_ASSERT(aListID == kPrincipalList || aListID == kNoReflowPrincipalList,
143 "unexpected child list");
144
145 AutoTArray<nsIFrame*, 10> continuations;
146 {
147 nsIFrame* continuation = aOldFrame;
148 while (continuation) {
149 continuations.AppendElement(continuation);
150 continuation = continuation->GetNextContinuation();
151 }
152 }
153
154 mozilla::PresShell* presShell = PresShell();
155 nsContainerFrame* lastParent = nullptr;
156
157 // Loop and destroy aOldFrame and all of its continuations.
158 //
159 // Request a reflow on the parent frames involved unless we were explicitly
160 // told not to (kNoReflowPrincipalList).
161 const bool generateReflowCommand = (kNoReflowPrincipalList != aListID);
162 for (nsIFrame* continuation : Reversed(continuations)) {
163 nsContainerFrame* parent = continuation->GetParent();
164
165 // Please note that 'parent' may not actually be where 'continuation' lives.
166 // We really MUST use StealFrame() and nothing else here.
167 // @see nsInlineFrame::StealFrame for details.
168 parent->StealFrame(continuation);
169 continuation->Destroy();
170 if (generateReflowCommand && parent != lastParent) {
171 presShell->FrameNeedsReflow(parent, IntrinsicDirty::TreeChange,
172 NS_FRAME_HAS_DIRTY_CHILDREN);
173 lastParent = parent;
174 }
175 }
176 }
177
DestroyAbsoluteFrames(nsIFrame * aDestructRoot,PostDestroyData & aPostDestroyData)178 void nsContainerFrame::DestroyAbsoluteFrames(
179 nsIFrame* aDestructRoot, PostDestroyData& aPostDestroyData) {
180 if (IsAbsoluteContainer()) {
181 GetAbsoluteContainingBlock()->DestroyFrames(this, aDestructRoot,
182 aPostDestroyData);
183 MarkAsNotAbsoluteContainingBlock();
184 }
185 }
186
SafelyDestroyFrameListProp(nsIFrame * aDestructRoot,PostDestroyData & aPostDestroyData,mozilla::PresShell * aPresShell,FrameListPropertyDescriptor aProp)187 void nsContainerFrame::SafelyDestroyFrameListProp(
188 nsIFrame* aDestructRoot, PostDestroyData& aPostDestroyData,
189 mozilla::PresShell* aPresShell, FrameListPropertyDescriptor aProp) {
190 // Note that the last frame can be removed through another route and thus
191 // delete the property -- that's why we fetch the property again before
192 // removing each frame rather than fetching it once and iterating the list.
193 while (nsFrameList* frameList = GetProperty(aProp)) {
194 nsIFrame* frame = frameList->RemoveFirstChild();
195 if (MOZ_LIKELY(frame)) {
196 frame->DestroyFrom(aDestructRoot, aPostDestroyData);
197 } else {
198 Unused << TakeProperty(aProp);
199 frameList->Delete(aPresShell);
200 return;
201 }
202 }
203 }
204
DestroyFrom(nsIFrame * aDestructRoot,PostDestroyData & aPostDestroyData)205 void nsContainerFrame::DestroyFrom(nsIFrame* aDestructRoot,
206 PostDestroyData& aPostDestroyData) {
207 // Prevent event dispatch during destruction.
208 if (HasView()) {
209 GetView()->SetFrame(nullptr);
210 }
211
212 DestroyAbsoluteFrames(aDestructRoot, aPostDestroyData);
213
214 // Destroy frames on the principal child list.
215 mFrames.DestroyFramesFrom(aDestructRoot, aPostDestroyData);
216
217 // If we have any IB split siblings, clear their references to us.
218 if (HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) {
219 // Delete previous sibling's reference to me.
220 if (nsIFrame* prevSib = GetProperty(nsIFrame::IBSplitPrevSibling())) {
221 NS_WARNING_ASSERTION(
222 this == prevSib->GetProperty(nsIFrame::IBSplitSibling()),
223 "IB sibling chain is inconsistent");
224 prevSib->RemoveProperty(nsIFrame::IBSplitSibling());
225 }
226
227 // Delete next sibling's reference to me.
228 if (nsIFrame* nextSib = GetProperty(nsIFrame::IBSplitSibling())) {
229 NS_WARNING_ASSERTION(
230 this == nextSib->GetProperty(nsIFrame::IBSplitPrevSibling()),
231 "IB sibling chain is inconsistent");
232 nextSib->RemoveProperty(nsIFrame::IBSplitPrevSibling());
233 }
234
235 #ifdef DEBUG
236 // This is just so we can assert it's not set in nsFrame::DestroyFrom.
237 RemoveStateBits(NS_FRAME_PART_OF_IBSPLIT);
238 #endif
239 }
240
241 if (MOZ_UNLIKELY(!mProperties.IsEmpty())) {
242 using T = mozilla::FrameProperties::UntypedDescriptor;
243 bool hasO = false, hasOC = false, hasEOC = false, hasBackdrop = false;
244 mProperties.ForEach([&](const T& aProp, void*) {
245 if (aProp == OverflowProperty()) {
246 hasO = true;
247 } else if (aProp == OverflowContainersProperty()) {
248 hasOC = true;
249 } else if (aProp == ExcessOverflowContainersProperty()) {
250 hasEOC = true;
251 } else if (aProp == BackdropProperty()) {
252 hasBackdrop = true;
253 }
254 return true;
255 });
256
257 // Destroy frames on the auxiliary frame lists and delete the lists.
258 nsPresContext* pc = PresContext();
259 mozilla::PresShell* presShell = pc->PresShell();
260 if (hasO) {
261 SafelyDestroyFrameListProp(aDestructRoot, aPostDestroyData, presShell,
262 OverflowProperty());
263 }
264
265 MOZ_ASSERT(
266 IsFrameOfType(eCanContainOverflowContainers) || !(hasOC || hasEOC),
267 "this type of frame shouldn't have overflow containers");
268 if (hasOC) {
269 SafelyDestroyFrameListProp(aDestructRoot, aPostDestroyData, presShell,
270 OverflowContainersProperty());
271 }
272 if (hasEOC) {
273 SafelyDestroyFrameListProp(aDestructRoot, aPostDestroyData, presShell,
274 ExcessOverflowContainersProperty());
275 }
276
277 MOZ_ASSERT(!GetProperty(BackdropProperty()) ||
278 StyleDisplay()->mTopLayer != StyleTopLayer::None,
279 "only top layer frame may have backdrop");
280 if (hasBackdrop) {
281 SafelyDestroyFrameListProp(aDestructRoot, aPostDestroyData, presShell,
282 BackdropProperty());
283 }
284 }
285
286 nsSplittableFrame::DestroyFrom(aDestructRoot, aPostDestroyData);
287 }
288
289 /////////////////////////////////////////////////////////////////////////////
290 // Child frame enumeration
291
GetChildList(ChildListID aListID) const292 const nsFrameList& nsContainerFrame::GetChildList(ChildListID aListID) const {
293 // We only know about the principal child list, the overflow lists,
294 // and the backdrop list.
295 switch (aListID) {
296 case kPrincipalList:
297 return mFrames;
298 case kOverflowList: {
299 nsFrameList* list = GetOverflowFrames();
300 return list ? *list : nsFrameList::EmptyList();
301 }
302 case kOverflowContainersList: {
303 nsFrameList* list = GetPropTableFrames(OverflowContainersProperty());
304 return list ? *list : nsFrameList::EmptyList();
305 }
306 case kExcessOverflowContainersList: {
307 nsFrameList* list =
308 GetPropTableFrames(ExcessOverflowContainersProperty());
309 return list ? *list : nsFrameList::EmptyList();
310 }
311 case kBackdropList: {
312 nsFrameList* list = GetPropTableFrames(BackdropProperty());
313 return list ? *list : nsFrameList::EmptyList();
314 }
315 default:
316 return nsSplittableFrame::GetChildList(aListID);
317 }
318 }
319
GetChildLists(nsTArray<ChildList> * aLists) const320 void nsContainerFrame::GetChildLists(nsTArray<ChildList>* aLists) const {
321 mFrames.AppendIfNonempty(aLists, kPrincipalList);
322
323 using T = mozilla::FrameProperties::UntypedDescriptor;
324 mProperties.ForEach([this, aLists](const T& aProp, void* aValue) {
325 typedef const nsFrameList* L;
326 if (aProp == OverflowProperty()) {
327 L(aValue)->AppendIfNonempty(aLists, kOverflowList);
328 } else if (aProp == OverflowContainersProperty()) {
329 MOZ_ASSERT(IsFrameOfType(nsIFrame::eCanContainOverflowContainers),
330 "found unexpected OverflowContainersProperty");
331 Unused << this; // silence clang -Wunused-lambda-capture in opt builds
332 L(aValue)->AppendIfNonempty(aLists, kOverflowContainersList);
333 } else if (aProp == ExcessOverflowContainersProperty()) {
334 MOZ_ASSERT(IsFrameOfType(nsIFrame::eCanContainOverflowContainers),
335 "found unexpected ExcessOverflowContainersProperty");
336 Unused << this; // silence clang -Wunused-lambda-capture in opt builds
337 L(aValue)->AppendIfNonempty(aLists, kExcessOverflowContainersList);
338 } else if (aProp == BackdropProperty()) {
339 L(aValue)->AppendIfNonempty(aLists, kBackdropList);
340 }
341 return true;
342 });
343
344 nsSplittableFrame::GetChildLists(aLists);
345 }
346
347 /////////////////////////////////////////////////////////////////////////////
348 // Painting/Events
349
BuildDisplayList(nsDisplayListBuilder * aBuilder,const nsDisplayListSet & aLists)350 void nsContainerFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
351 const nsDisplayListSet& aLists) {
352 DisplayBorderBackgroundOutline(aBuilder, aLists);
353 BuildDisplayListForNonBlockChildren(aBuilder, aLists);
354 }
355
BuildDisplayListForNonBlockChildren(nsDisplayListBuilder * aBuilder,const nsDisplayListSet & aLists,uint32_t aFlags)356 void nsContainerFrame::BuildDisplayListForNonBlockChildren(
357 nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists,
358 uint32_t aFlags) {
359 nsIFrame* kid = mFrames.FirstChild();
360 // Put each child's background directly onto the content list
361 nsDisplayListSet set(aLists, aLists.Content());
362 // The children should be in content order
363 while (kid) {
364 BuildDisplayListForChild(aBuilder, kid, set, aFlags);
365 kid = kid->GetNextSibling();
366 }
367 }
368
369 /* virtual */
ChildIsDirty(nsIFrame * aChild)370 void nsContainerFrame::ChildIsDirty(nsIFrame* aChild) {
371 NS_ASSERTION(NS_SUBTREE_DIRTY(aChild), "child isn't actually dirty");
372
373 AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
374 }
375
PeekOffsetNoAmount(bool aForward,int32_t * aOffset)376 nsIFrame::FrameSearchResult nsContainerFrame::PeekOffsetNoAmount(
377 bool aForward, int32_t* aOffset) {
378 NS_ASSERTION(aOffset && *aOffset <= 1, "aOffset out of range");
379 // Don't allow the caret to stay in an empty (leaf) container frame.
380 return CONTINUE_EMPTY;
381 }
382
PeekOffsetCharacter(bool aForward,int32_t * aOffset,PeekOffsetCharacterOptions aOptions)383 nsIFrame::FrameSearchResult nsContainerFrame::PeekOffsetCharacter(
384 bool aForward, int32_t* aOffset, PeekOffsetCharacterOptions aOptions) {
385 NS_ASSERTION(aOffset && *aOffset <= 1, "aOffset out of range");
386 // Don't allow the caret to stay in an empty (leaf) container frame.
387 return CONTINUE_EMPTY;
388 }
389
390 /////////////////////////////////////////////////////////////////////////////
391 // Helper member functions
392
393 /**
394 * Position the view associated with |aKidFrame|, if there is one. A
395 * container frame should call this method after positioning a frame,
396 * but before |Reflow|.
397 */
PositionFrameView(nsIFrame * aKidFrame)398 void nsContainerFrame::PositionFrameView(nsIFrame* aKidFrame) {
399 nsIFrame* parentFrame = aKidFrame->GetParent();
400 if (!aKidFrame->HasView() || !parentFrame) return;
401
402 nsView* view = aKidFrame->GetView();
403 nsViewManager* vm = view->GetViewManager();
404 nsPoint pt;
405 nsView* ancestorView = parentFrame->GetClosestView(&pt);
406
407 if (ancestorView != view->GetParent()) {
408 NS_ASSERTION(ancestorView == view->GetParent()->GetParent(),
409 "Allowed only one anonymous view between frames");
410 // parentFrame is responsible for positioning aKidFrame's view
411 // explicitly
412 return;
413 }
414
415 pt += aKidFrame->GetPosition();
416 vm->MoveViewTo(view, pt.x, pt.y);
417 }
418
ReparentFrameView(nsIFrame * aChildFrame,nsIFrame * aOldParentFrame,nsIFrame * aNewParentFrame)419 nsresult nsContainerFrame::ReparentFrameView(nsIFrame* aChildFrame,
420 nsIFrame* aOldParentFrame,
421 nsIFrame* aNewParentFrame) {
422 MOZ_ASSERT(aChildFrame, "null child frame pointer");
423 MOZ_ASSERT(aOldParentFrame, "null old parent frame pointer");
424 MOZ_ASSERT(aNewParentFrame, "null new parent frame pointer");
425 MOZ_ASSERT(aOldParentFrame != aNewParentFrame,
426 "same old and new parent frame");
427
428 // See if either the old parent frame or the new parent frame have a view
429 while (!aOldParentFrame->HasView() && !aNewParentFrame->HasView()) {
430 // Walk up both the old parent frame and the new parent frame nodes
431 // stopping when we either find a common parent or views for one
432 // or both of the frames.
433 //
434 // This works well in the common case where we push/pull and the old parent
435 // frame and the new parent frame are part of the same flow. They will
436 // typically be the same distance (height wise) from the
437 aOldParentFrame = aOldParentFrame->GetParent();
438 aNewParentFrame = aNewParentFrame->GetParent();
439
440 // We should never walk all the way to the root frame without finding
441 // a view
442 NS_ASSERTION(aOldParentFrame && aNewParentFrame, "didn't find view");
443
444 // See if we reached a common ancestor
445 if (aOldParentFrame == aNewParentFrame) {
446 break;
447 }
448 }
449
450 // See if we found a common parent frame
451 if (aOldParentFrame == aNewParentFrame) {
452 // We found a common parent and there are no views between the old parent
453 // and the common parent or the new parent frame and the common parent.
454 // Because neither the old parent frame nor the new parent frame have views,
455 // then any child views don't need reparenting
456 return NS_OK;
457 }
458
459 // We found views for one or both of the ancestor frames before we
460 // found a common ancestor.
461 nsView* oldParentView = aOldParentFrame->GetClosestView();
462 nsView* newParentView = aNewParentFrame->GetClosestView();
463
464 // See if the old parent frame and the new parent frame are in the
465 // same view sub-hierarchy. If they are then we don't have to do
466 // anything
467 if (oldParentView != newParentView) {
468 // They're not so we need to reparent any child views
469 aChildFrame->ReparentFrameViewTo(oldParentView->GetViewManager(),
470 newParentView, oldParentView);
471 }
472
473 return NS_OK;
474 }
475
ReparentFrameViewList(const nsFrameList & aChildFrameList,nsIFrame * aOldParentFrame,nsIFrame * aNewParentFrame)476 void nsContainerFrame::ReparentFrameViewList(const nsFrameList& aChildFrameList,
477 nsIFrame* aOldParentFrame,
478 nsIFrame* aNewParentFrame) {
479 MOZ_ASSERT(aChildFrameList.NotEmpty(), "empty child frame list");
480 MOZ_ASSERT(aOldParentFrame, "null old parent frame pointer");
481 MOZ_ASSERT(aNewParentFrame, "null new parent frame pointer");
482 MOZ_ASSERT(aOldParentFrame != aNewParentFrame,
483 "same old and new parent frame");
484
485 // See if either the old parent frame or the new parent frame have a view
486 while (!aOldParentFrame->HasView() && !aNewParentFrame->HasView()) {
487 // Walk up both the old parent frame and the new parent frame nodes
488 // stopping when we either find a common parent or views for one
489 // or both of the frames.
490 //
491 // This works well in the common case where we push/pull and the old parent
492 // frame and the new parent frame are part of the same flow. They will
493 // typically be the same distance (height wise) from the
494 aOldParentFrame = aOldParentFrame->GetParent();
495 aNewParentFrame = aNewParentFrame->GetParent();
496
497 // We should never walk all the way to the root frame without finding
498 // a view
499 NS_ASSERTION(aOldParentFrame && aNewParentFrame, "didn't find view");
500
501 // See if we reached a common ancestor
502 if (aOldParentFrame == aNewParentFrame) {
503 break;
504 }
505 }
506
507 // See if we found a common parent frame
508 if (aOldParentFrame == aNewParentFrame) {
509 // We found a common parent and there are no views between the old parent
510 // and the common parent or the new parent frame and the common parent.
511 // Because neither the old parent frame nor the new parent frame have views,
512 // then any child views don't need reparenting
513 return;
514 }
515
516 // We found views for one or both of the ancestor frames before we
517 // found a common ancestor.
518 nsView* oldParentView = aOldParentFrame->GetClosestView();
519 nsView* newParentView = aNewParentFrame->GetClosestView();
520
521 // See if the old parent frame and the new parent frame are in the
522 // same view sub-hierarchy. If they are then we don't have to do
523 // anything
524 if (oldParentView != newParentView) {
525 nsViewManager* viewManager = oldParentView->GetViewManager();
526
527 // They're not so we need to reparent any child views
528 for (nsFrameList::Enumerator e(aChildFrameList); !e.AtEnd(); e.Next()) {
529 e.get()->ReparentFrameViewTo(viewManager, newParentView, oldParentView);
530 }
531 }
532 }
533
ReparentFrame(nsIFrame * aFrame,nsContainerFrame * aOldParent,nsContainerFrame * aNewParent)534 void nsContainerFrame::ReparentFrame(nsIFrame* aFrame,
535 nsContainerFrame* aOldParent,
536 nsContainerFrame* aNewParent) {
537 NS_ASSERTION(aOldParent == aFrame->GetParent(),
538 "Parent not consistent with expectations");
539
540 aFrame->SetParent(aNewParent);
541
542 // When pushing and pulling frames we need to check for whether any
543 // views need to be reparented
544 ReparentFrameView(aFrame, aOldParent, aNewParent);
545 }
546
ReparentFrames(nsFrameList & aFrameList,nsContainerFrame * aOldParent,nsContainerFrame * aNewParent)547 void nsContainerFrame::ReparentFrames(nsFrameList& aFrameList,
548 nsContainerFrame* aOldParent,
549 nsContainerFrame* aNewParent) {
550 for (auto* f : aFrameList) {
551 ReparentFrame(f, aOldParent, aNewParent);
552 }
553 }
554
GetPresContextContainerWidget(nsPresContext * aPresContext)555 static nsIWidget* GetPresContextContainerWidget(nsPresContext* aPresContext) {
556 nsCOMPtr<nsISupports> container = aPresContext->Document()->GetContainer();
557 nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(container);
558 if (!baseWindow) return nullptr;
559
560 nsCOMPtr<nsIWidget> mainWidget;
561 baseWindow->GetMainWidget(getter_AddRefs(mainWidget));
562 return mainWidget;
563 }
564
IsTopLevelWidget(nsIWidget * aWidget)565 static bool IsTopLevelWidget(nsIWidget* aWidget) {
566 nsWindowType windowType = aWidget->WindowType();
567 return windowType == eWindowType_toplevel ||
568 windowType == eWindowType_dialog || windowType == eWindowType_popup ||
569 windowType == eWindowType_sheet;
570 }
571
SyncWindowProperties(nsPresContext * aPresContext,nsIFrame * aFrame,nsView * aView,gfxContext * aRC,uint32_t aFlags)572 void nsContainerFrame::SyncWindowProperties(nsPresContext* aPresContext,
573 nsIFrame* aFrame, nsView* aView,
574 gfxContext* aRC, uint32_t aFlags) {
575 #ifdef MOZ_XUL
576 if (!aView || !nsCSSRendering::IsCanvasFrame(aFrame) || !aView->HasWidget())
577 return;
578
579 nsCOMPtr<nsIWidget> windowWidget =
580 GetPresContextContainerWidget(aPresContext);
581 if (!windowWidget || !IsTopLevelWidget(windowWidget)) return;
582
583 nsViewManager* vm = aView->GetViewManager();
584 nsView* rootView = vm->GetRootView();
585
586 if (aView != rootView) return;
587
588 Element* rootElement = aPresContext->Document()->GetRootElement();
589 if (!rootElement) {
590 return;
591 }
592
593 nsIFrame* rootFrame =
594 aPresContext->PresShell()->FrameConstructor()->GetRootElementStyleFrame();
595 if (!rootFrame) return;
596
597 if (aFlags & SET_ASYNC) {
598 aView->SetNeedsWindowPropertiesSync();
599 return;
600 }
601
602 RefPtr<nsPresContext> kungFuDeathGrip(aPresContext);
603 AutoWeakFrame weak(rootFrame);
604
605 if (!aPresContext->PresShell()->GetRootScrollFrame()) {
606 // Scrollframes use native widgets which don't work well with
607 // translucent windows, at least in Windows XP. So if the document
608 // has a root scrollrame it's useless to try to make it transparent,
609 // we'll just get something broken.
610 // We can change this to allow translucent toplevel HTML documents
611 // (e.g. to do something like Dashboard widgets), once we
612 // have broad support for translucent scrolled documents, but be
613 // careful because apparently some Firefox extensions expect
614 // openDialog("something.html") to produce an opaque window
615 // even if the HTML doesn't have a background-color set.
616 nsTransparencyMode mode =
617 nsLayoutUtils::GetFrameTransparency(aFrame, rootFrame);
618 StyleWindowShadow shadow = rootFrame->StyleUIReset()->mWindowShadow;
619 nsCOMPtr<nsIWidget> viewWidget = aView->GetWidget();
620 viewWidget->SetTransparencyMode(mode);
621 windowWidget->SetWindowShadowStyle(shadow);
622 }
623
624 if (!aRC) return;
625
626 if (!weak.IsAlive()) {
627 return;
628 }
629
630 nsSize minSize(0, 0);
631 nsSize maxSize(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
632 if (rootElement->IsXULElement()) {
633 nsBoxLayoutState aState(aPresContext, aRC);
634 minSize = rootFrame->GetXULMinSize(aState);
635 maxSize = rootFrame->GetXULMaxSize(aState);
636 } else {
637 auto* pos = rootFrame->StylePosition();
638 if (pos->mMinWidth.ConvertsToLength()) {
639 minSize.width = pos->mMinWidth.ToLength();
640 }
641 if (pos->mMinHeight.ConvertsToLength()) {
642 minSize.height = pos->mMinHeight.ToLength();
643 }
644 if (pos->mMaxWidth.ConvertsToLength()) {
645 maxSize.width = pos->mMaxWidth.ToLength();
646 }
647 if (pos->mMaxHeight.ConvertsToLength()) {
648 maxSize.height = pos->mMaxHeight.ToLength();
649 }
650 }
651 SetSizeConstraints(aPresContext, windowWidget, minSize, maxSize);
652 #endif
653 }
654
SetSizeConstraints(nsPresContext * aPresContext,nsIWidget * aWidget,const nsSize & aMinSize,const nsSize & aMaxSize)655 void nsContainerFrame::SetSizeConstraints(nsPresContext* aPresContext,
656 nsIWidget* aWidget,
657 const nsSize& aMinSize,
658 const nsSize& aMaxSize) {
659 LayoutDeviceIntSize devMinSize(
660 aPresContext->AppUnitsToDevPixels(aMinSize.width),
661 aPresContext->AppUnitsToDevPixels(aMinSize.height));
662 LayoutDeviceIntSize devMaxSize(
663 aMaxSize.width == NS_UNCONSTRAINEDSIZE
664 ? NS_MAXSIZE
665 : aPresContext->AppUnitsToDevPixels(aMaxSize.width),
666 aMaxSize.height == NS_UNCONSTRAINEDSIZE
667 ? NS_MAXSIZE
668 : aPresContext->AppUnitsToDevPixels(aMaxSize.height));
669
670 // MinSize has a priority over MaxSize
671 if (devMinSize.width > devMaxSize.width) devMaxSize.width = devMinSize.width;
672 if (devMinSize.height > devMaxSize.height)
673 devMaxSize.height = devMinSize.height;
674
675 widget::SizeConstraints constraints(devMinSize, devMaxSize);
676
677 // The sizes are in inner window sizes, so convert them into outer window
678 // sizes. Use a size of (200, 200) as only the difference between the inner
679 // and outer size is needed.
680 LayoutDeviceIntSize windowSize =
681 aWidget->ClientToWindowSize(LayoutDeviceIntSize(200, 200));
682 if (constraints.mMinSize.width)
683 constraints.mMinSize.width += windowSize.width - 200;
684 if (constraints.mMinSize.height)
685 constraints.mMinSize.height += windowSize.height - 200;
686 if (constraints.mMaxSize.width != NS_MAXSIZE)
687 constraints.mMaxSize.width += windowSize.width - 200;
688 if (constraints.mMaxSize.height != NS_MAXSIZE)
689 constraints.mMaxSize.height += windowSize.height - 200;
690
691 aWidget->SetSizeConstraints(constraints);
692 }
693
SyncFrameViewAfterReflow(nsPresContext * aPresContext,nsIFrame * aFrame,nsView * aView,const nsRect & aVisualOverflowArea,ReflowChildFlags aFlags)694 void nsContainerFrame::SyncFrameViewAfterReflow(
695 nsPresContext* aPresContext, nsIFrame* aFrame, nsView* aView,
696 const nsRect& aVisualOverflowArea, ReflowChildFlags aFlags) {
697 if (!aView) {
698 return;
699 }
700
701 // Make sure the view is sized and positioned correctly
702 if (!(aFlags & ReflowChildFlags::NoMoveView)) {
703 PositionFrameView(aFrame);
704 }
705
706 if (!(aFlags & ReflowChildFlags::NoSizeView)) {
707 nsViewManager* vm = aView->GetViewManager();
708
709 vm->ResizeView(aView, aVisualOverflowArea, true);
710 }
711 }
712
GetCoord(const LengthPercentage & aCoord,nscoord aIfNotCoord)713 static nscoord GetCoord(const LengthPercentage& aCoord, nscoord aIfNotCoord) {
714 if (aCoord.ConvertsToLength()) {
715 return aCoord.ToLength();
716 }
717 return aIfNotCoord;
718 }
719
GetCoord(const LengthPercentageOrAuto & aCoord,nscoord aIfNotCoord)720 static nscoord GetCoord(const LengthPercentageOrAuto& aCoord,
721 nscoord aIfNotCoord) {
722 if (aCoord.IsAuto()) {
723 return aIfNotCoord;
724 }
725 return GetCoord(aCoord.AsLengthPercentage(), aIfNotCoord);
726 }
727
DoInlineIntrinsicISize(gfxContext * aRenderingContext,InlineIntrinsicISizeData * aData,nsLayoutUtils::IntrinsicISizeType aType)728 void nsContainerFrame::DoInlineIntrinsicISize(
729 gfxContext* aRenderingContext, InlineIntrinsicISizeData* aData,
730 nsLayoutUtils::IntrinsicISizeType aType) {
731 if (GetPrevInFlow()) return; // Already added.
732
733 MOZ_ASSERT(
734 aType == nsLayoutUtils::MIN_ISIZE || aType == nsLayoutUtils::PREF_ISIZE,
735 "bad type");
736
737 WritingMode wm = GetWritingMode();
738 mozilla::Side startSide = wm.PhysicalSideForInlineAxis(eLogicalEdgeStart);
739 mozilla::Side endSide = wm.PhysicalSideForInlineAxis(eLogicalEdgeEnd);
740
741 const nsStylePadding* stylePadding = StylePadding();
742 const nsStyleBorder* styleBorder = StyleBorder();
743 const nsStyleMargin* styleMargin = StyleMargin();
744
745 // This goes at the beginning no matter how things are broken and how
746 // messy the bidi situations are, since per CSS2.1 section 8.6
747 // (implemented in bug 328168), the startSide border is always on the
748 // first line.
749 // This frame is a first-in-flow, but it might have a previous bidi
750 // continuation, in which case that continuation should handle the startSide
751 // border.
752 // For box-decoration-break:clone we setup clonePBM = startPBM + endPBM and
753 // add that to each line. For box-decoration-break:slice clonePBM is zero.
754 nscoord clonePBM = 0; // PBM = PaddingBorderMargin
755 const bool sliceBreak =
756 styleBorder->mBoxDecorationBreak == StyleBoxDecorationBreak::Slice;
757 if (!GetPrevContinuation() || MOZ_UNLIKELY(!sliceBreak)) {
758 nscoord startPBM =
759 // clamp negative calc() to 0
760 std::max(GetCoord(stylePadding->mPadding.Get(startSide), 0), 0) +
761 styleBorder->GetComputedBorderWidth(startSide) +
762 GetCoord(styleMargin->mMargin.Get(startSide), 0);
763 if (MOZ_LIKELY(sliceBreak)) {
764 aData->mCurrentLine += startPBM;
765 } else {
766 clonePBM = startPBM;
767 }
768 }
769
770 nscoord endPBM =
771 // clamp negative calc() to 0
772 std::max(GetCoord(stylePadding->mPadding.Get(endSide), 0), 0) +
773 styleBorder->GetComputedBorderWidth(endSide) +
774 GetCoord(styleMargin->mMargin.Get(endSide), 0);
775 if (MOZ_UNLIKELY(!sliceBreak)) {
776 clonePBM += endPBM;
777 aData->mCurrentLine += clonePBM;
778 }
779
780 const nsLineList_iterator* savedLine = aData->mLine;
781 nsIFrame* const savedLineContainer = aData->LineContainer();
782
783 nsContainerFrame* lastInFlow;
784 for (nsContainerFrame* nif = this; nif;
785 nif = static_cast<nsContainerFrame*>(nif->GetNextInFlow())) {
786 if (aData->mCurrentLine == 0) {
787 aData->mCurrentLine = clonePBM;
788 }
789 for (nsIFrame* kid : nif->mFrames) {
790 if (aType == nsLayoutUtils::MIN_ISIZE)
791 kid->AddInlineMinISize(aRenderingContext,
792 static_cast<InlineMinISizeData*>(aData));
793 else
794 kid->AddInlinePrefISize(aRenderingContext,
795 static_cast<InlinePrefISizeData*>(aData));
796 }
797
798 // After we advance to our next-in-flow, the stored line and line container
799 // may no longer be correct. Just forget them.
800 aData->mLine = nullptr;
801 aData->SetLineContainer(nullptr);
802
803 lastInFlow = nif;
804 }
805
806 aData->mLine = savedLine;
807 aData->SetLineContainer(savedLineContainer);
808
809 // This goes at the end no matter how things are broken and how
810 // messy the bidi situations are, since per CSS2.1 section 8.6
811 // (implemented in bug 328168), the endSide border is always on the
812 // last line.
813 // We reached the last-in-flow, but it might have a next bidi
814 // continuation, in which case that continuation should handle
815 // the endSide border.
816 if (MOZ_LIKELY(!lastInFlow->GetNextContinuation() && sliceBreak)) {
817 aData->mCurrentLine += endPBM;
818 }
819 }
820
821 /* virtual */
ComputeAutoSize(gfxContext * aRenderingContext,WritingMode aWM,const LogicalSize & aCBSize,nscoord aAvailableISize,const LogicalSize & aMargin,const LogicalSize & aBorder,const LogicalSize & aPadding,ComputeSizeFlags aFlags)822 LogicalSize nsContainerFrame::ComputeAutoSize(
823 gfxContext* aRenderingContext, WritingMode aWM, const LogicalSize& aCBSize,
824 nscoord aAvailableISize, const LogicalSize& aMargin,
825 const LogicalSize& aBorder, const LogicalSize& aPadding,
826 ComputeSizeFlags aFlags) {
827 LogicalSize result(aWM, 0xdeadbeef, NS_UNCONSTRAINEDSIZE);
828 nscoord availBased = aAvailableISize - aMargin.ISize(aWM) -
829 aBorder.ISize(aWM) - aPadding.ISize(aWM);
830 // replaced elements always shrink-wrap
831 if ((aFlags & ComputeSizeFlags::eShrinkWrap) || IsFrameOfType(eReplaced)) {
832 // Only bother computing our 'auto' ISize if the result will be used.
833 // It'll be used under two scenarios:
834 // - If our ISize property is itself 'auto'.
835 // - If we're using flex-basis in place of our ISize property (i.e. we're a
836 // flex item with our inline axis being the main axis), AND we have
837 // flex-basis:content.
838 const nsStylePosition* pos = StylePosition();
839 if (pos->ISize(aWM).IsAuto() ||
840 (pos->mFlexBasis.IsContent() && IsFlexItem() &&
841 nsFlexContainerFrame::IsItemInlineAxisMainAxis(this))) {
842 result.ISize(aWM) =
843 ShrinkWidthToFit(aRenderingContext, availBased, aFlags);
844 }
845 } else {
846 result.ISize(aWM) = availBased;
847 }
848
849 if (IsTableCaption()) {
850 // If we're a container for font size inflation, then shrink
851 // wrapping inside of us should not apply font size inflation.
852 AutoMaybeDisableFontInflation an(this);
853
854 WritingMode tableWM = GetParent()->GetWritingMode();
855 uint8_t captionSide = StyleTableBorder()->mCaptionSide;
856
857 if (aWM.IsOrthogonalTo(tableWM)) {
858 if (captionSide == NS_STYLE_CAPTION_SIDE_TOP ||
859 captionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE ||
860 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM ||
861 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE) {
862 // For an orthogonal caption on a block-dir side of the table,
863 // shrink-wrap to min-isize.
864 result.ISize(aWM) = GetMinISize(aRenderingContext);
865 } else {
866 // An orthogonal caption on an inline-dir side of the table
867 // is constrained to the containing block.
868 nscoord pref = GetPrefISize(aRenderingContext);
869 if (pref > aCBSize.ISize(aWM)) {
870 pref = aCBSize.ISize(aWM);
871 }
872 if (pref < result.ISize(aWM)) {
873 result.ISize(aWM) = pref;
874 }
875 }
876 } else {
877 if (captionSide == NS_STYLE_CAPTION_SIDE_LEFT ||
878 captionSide == NS_STYLE_CAPTION_SIDE_RIGHT) {
879 result.ISize(aWM) = GetMinISize(aRenderingContext);
880 } else if (captionSide == NS_STYLE_CAPTION_SIDE_TOP ||
881 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) {
882 // The outer frame constrains our available isize to the isize of
883 // the table. Grow if our min-isize is bigger than that, but not
884 // larger than the containing block isize. (It would really be nice
885 // to transmit that information another way, so we could grow up to
886 // the table's available isize, but that's harder.)
887 nscoord min = GetMinISize(aRenderingContext);
888 if (min > aCBSize.ISize(aWM)) {
889 min = aCBSize.ISize(aWM);
890 }
891 if (min > result.ISize(aWM)) {
892 result.ISize(aWM) = min;
893 }
894 }
895 }
896 }
897 return result;
898 }
899
ReflowChild(nsIFrame * aKidFrame,nsPresContext * aPresContext,ReflowOutput & aDesiredSize,const ReflowInput & aReflowInput,const WritingMode & aWM,const LogicalPoint & aPos,const nsSize & aContainerSize,ReflowChildFlags aFlags,nsReflowStatus & aStatus,nsOverflowContinuationTracker * aTracker)900 void nsContainerFrame::ReflowChild(
901 nsIFrame* aKidFrame, nsPresContext* aPresContext,
902 ReflowOutput& aDesiredSize, const ReflowInput& aReflowInput,
903 const WritingMode& aWM, const LogicalPoint& aPos,
904 const nsSize& aContainerSize, ReflowChildFlags aFlags,
905 nsReflowStatus& aStatus, nsOverflowContinuationTracker* aTracker) {
906 MOZ_ASSERT(aReflowInput.mFrame == aKidFrame, "bad reflow input");
907 if (aWM.IsPhysicalRTL()) {
908 NS_ASSERTION(aContainerSize.width != NS_UNCONSTRAINEDSIZE,
909 "ReflowChild with unconstrained container width!");
910 }
911 MOZ_ASSERT(aDesiredSize.VisualOverflow() == nsRect(0, 0, 0, 0) &&
912 aDesiredSize.ScrollableOverflow() == nsRect(0, 0, 0, 0),
913 "please reset the overflow areas before calling ReflowChild");
914
915 // Position the child frame and its view if requested.
916 if (ReflowChildFlags::NoMoveFrame !=
917 (aFlags & ReflowChildFlags::NoMoveFrame)) {
918 aKidFrame->SetPosition(aWM, aPos, aContainerSize);
919 }
920
921 if (!(aFlags & ReflowChildFlags::NoMoveView)) {
922 PositionFrameView(aKidFrame);
923 PositionChildViews(aKidFrame);
924 }
925
926 // Reflow the child frame
927 aKidFrame->Reflow(aPresContext, aDesiredSize, aReflowInput, aStatus);
928
929 // If the child frame is complete, delete any next-in-flows,
930 // but only if the NoDeleteNextInFlowChild flag isn't set.
931 if (!aStatus.IsInlineBreakBefore() && aStatus.IsFullyComplete() &&
932 !(aFlags & ReflowChildFlags::NoDeleteNextInFlowChild)) {
933 nsIFrame* kidNextInFlow = aKidFrame->GetNextInFlow();
934 if (kidNextInFlow) {
935 // Remove all of the childs next-in-flows. Make sure that we ask
936 // the right parent to do the removal (it's possible that the
937 // parent is not this because we are executing pullup code)
938 nsOverflowContinuationTracker::AutoFinish fini(aTracker, aKidFrame);
939 kidNextInFlow->GetParent()->DeleteNextInFlowChild(kidNextInFlow, true);
940 }
941 }
942 }
943
944 // XXX temporary: hold on to a copy of the old physical version of
945 // ReflowChild so that we can convert callers incrementally.
ReflowChild(nsIFrame * aKidFrame,nsPresContext * aPresContext,ReflowOutput & aDesiredSize,const ReflowInput & aReflowInput,nscoord aX,nscoord aY,ReflowChildFlags aFlags,nsReflowStatus & aStatus,nsOverflowContinuationTracker * aTracker)946 void nsContainerFrame::ReflowChild(nsIFrame* aKidFrame,
947 nsPresContext* aPresContext,
948 ReflowOutput& aDesiredSize,
949 const ReflowInput& aReflowInput, nscoord aX,
950 nscoord aY, ReflowChildFlags aFlags,
951 nsReflowStatus& aStatus,
952 nsOverflowContinuationTracker* aTracker) {
953 MOZ_ASSERT(aReflowInput.mFrame == aKidFrame, "bad reflow input");
954
955 // Position the child frame and its view if requested.
956 if (ReflowChildFlags::NoMoveFrame !=
957 (aFlags & ReflowChildFlags::NoMoveFrame)) {
958 aKidFrame->SetPosition(nsPoint(aX, aY));
959 }
960
961 if (!(aFlags & ReflowChildFlags::NoMoveView)) {
962 PositionFrameView(aKidFrame);
963 PositionChildViews(aKidFrame);
964 }
965
966 // Reflow the child frame
967 aKidFrame->Reflow(aPresContext, aDesiredSize, aReflowInput, aStatus);
968
969 // If the child frame is complete, delete any next-in-flows,
970 // but only if the NoDeleteNextInFlowChild flag isn't set.
971 if (aStatus.IsFullyComplete() &&
972 !(aFlags & ReflowChildFlags::NoDeleteNextInFlowChild)) {
973 nsIFrame* kidNextInFlow = aKidFrame->GetNextInFlow();
974 if (kidNextInFlow) {
975 // Remove all of the childs next-in-flows. Make sure that we ask
976 // the right parent to do the removal (it's possible that the
977 // parent is not this because we are executing pullup code)
978 nsOverflowContinuationTracker::AutoFinish fini(aTracker, aKidFrame);
979 kidNextInFlow->GetParent()->DeleteNextInFlowChild(kidNextInFlow, true);
980 }
981 }
982 }
983
984 /**
985 * Position the views of |aFrame|'s descendants. A container frame
986 * should call this method if it moves a frame after |Reflow|.
987 */
PositionChildViews(nsIFrame * aFrame)988 void nsContainerFrame::PositionChildViews(nsIFrame* aFrame) {
989 if (!(aFrame->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW)) {
990 return;
991 }
992
993 // Recursively walk aFrame's child frames.
994 // Process the additional child lists, but skip the popup list as the
995 // view for popups is managed by the parent. Currently only nsMenuFrame
996 // and nsPopupSetFrame have a popupList and during layout will adjust the
997 // view manually to position the popup.
998 for (const auto& [list, listID] : aFrame->ChildLists()) {
999 if (listID == kPopupList) {
1000 continue;
1001 }
1002 for (nsIFrame* childFrame : list) {
1003 // Position the frame's view (if it has one) otherwise recursively
1004 // process its children
1005 if (childFrame->HasView()) {
1006 PositionFrameView(childFrame);
1007 } else {
1008 PositionChildViews(childFrame);
1009 }
1010 }
1011 }
1012 }
1013
1014 /**
1015 * De-optimize function to work around a VC2017 15.5+ compiler bug:
1016 * https://bugzil.la/1424281#c12
1017 */
1018 #if defined(_MSC_VER) && !defined(__clang__) && defined(_M_AMD64)
1019 # pragma optimize("g", off)
1020 #endif
FinishReflowChild(nsIFrame * aKidFrame,nsPresContext * aPresContext,const ReflowOutput & aDesiredSize,const ReflowInput * aReflowInput,const WritingMode & aWM,const LogicalPoint & aPos,const nsSize & aContainerSize,nsIFrame::ReflowChildFlags aFlags)1021 void nsContainerFrame::FinishReflowChild(
1022 nsIFrame* aKidFrame, nsPresContext* aPresContext,
1023 const ReflowOutput& aDesiredSize, const ReflowInput* aReflowInput,
1024 const WritingMode& aWM, const LogicalPoint& aPos,
1025 const nsSize& aContainerSize, nsIFrame::ReflowChildFlags aFlags) {
1026 MOZ_ASSERT(!aReflowInput || aReflowInput->mFrame == aKidFrame);
1027 MOZ_ASSERT(aReflowInput || aKidFrame->IsFrameOfType(eMathML) ||
1028 aKidFrame->IsTableCellFrame(),
1029 "aReflowInput should be passed in almost all cases");
1030
1031 if (aWM.IsPhysicalRTL()) {
1032 NS_ASSERTION(aContainerSize.width != NS_UNCONSTRAINEDSIZE,
1033 "FinishReflowChild with unconstrained container width!");
1034 }
1035
1036 nsPoint curOrigin = aKidFrame->GetPosition();
1037 WritingMode outerWM = aDesiredSize.GetWritingMode();
1038 LogicalSize convertedSize =
1039 aDesiredSize.Size(outerWM).ConvertTo(aWM, outerWM);
1040 LogicalPoint pos(aPos);
1041
1042 if (aFlags & ReflowChildFlags::ApplyRelativePositioning) {
1043 MOZ_ASSERT(aReflowInput, "caller must have passed reflow input");
1044 // ApplyRelativePositioning in right-to-left writing modes needs to know
1045 // the updated frame width to set the normal position correctly.
1046 aKidFrame->SetSize(aWM, convertedSize);
1047
1048 const LogicalMargin offsets =
1049 aReflowInput->ComputedLogicalOffsets().ConvertTo(
1050 aWM, aReflowInput->GetWritingMode());
1051 ReflowInput::ApplyRelativePositioning(aKidFrame, aWM, offsets, &pos,
1052 aContainerSize);
1053 }
1054
1055 if (ReflowChildFlags::NoMoveFrame !=
1056 (aFlags & ReflowChildFlags::NoMoveFrame)) {
1057 aKidFrame->SetRect(aWM, LogicalRect(aWM, pos, convertedSize),
1058 aContainerSize);
1059 } else {
1060 aKidFrame->SetSize(aWM, convertedSize);
1061 }
1062
1063 if (aKidFrame->HasView()) {
1064 nsView* view = aKidFrame->GetView();
1065 // Make sure the frame's view is properly sized and positioned and has
1066 // things like opacity correct
1067 SyncFrameViewAfterReflow(aPresContext, aKidFrame, view,
1068 aDesiredSize.VisualOverflow(), aFlags);
1069 }
1070
1071 nsPoint newOrigin = aKidFrame->GetPosition();
1072 if (!(aFlags & ReflowChildFlags::NoMoveView) && curOrigin != newOrigin) {
1073 if (!aKidFrame->HasView()) {
1074 // If the frame has moved, then we need to make sure any child views are
1075 // correctly positioned
1076 PositionChildViews(aKidFrame);
1077 }
1078 }
1079
1080 aKidFrame->DidReflow(aPresContext, aReflowInput);
1081 }
1082 #if defined(_MSC_VER) && !defined(__clang__) && defined(_M_AMD64)
1083 # pragma optimize("", on)
1084 #endif
1085
1086 // XXX temporary: hold on to a copy of the old physical version of
1087 // FinishReflowChild so that we can convert callers incrementally.
FinishReflowChild(nsIFrame * aKidFrame,nsPresContext * aPresContext,const ReflowOutput & aDesiredSize,const ReflowInput * aReflowInput,nscoord aX,nscoord aY,ReflowChildFlags aFlags)1088 void nsContainerFrame::FinishReflowChild(nsIFrame* aKidFrame,
1089 nsPresContext* aPresContext,
1090 const ReflowOutput& aDesiredSize,
1091 const ReflowInput* aReflowInput,
1092 nscoord aX, nscoord aY,
1093 ReflowChildFlags aFlags) {
1094 MOZ_ASSERT(!(aFlags & ReflowChildFlags::ApplyRelativePositioning),
1095 "only the logical version supports ApplyRelativePositioning "
1096 "since ApplyRelativePositioning requires the container size");
1097
1098 nsPoint curOrigin = aKidFrame->GetPosition();
1099 nsPoint pos(aX, aY);
1100 nsSize size(aDesiredSize.PhysicalSize());
1101
1102 if (ReflowChildFlags::NoMoveFrame !=
1103 (aFlags & ReflowChildFlags::NoMoveFrame)) {
1104 aKidFrame->SetRect(nsRect(pos, size));
1105 } else {
1106 aKidFrame->SetSize(size);
1107 }
1108
1109 if (aKidFrame->HasView()) {
1110 nsView* view = aKidFrame->GetView();
1111 // Make sure the frame's view is properly sized and positioned and has
1112 // things like opacity correct
1113 SyncFrameViewAfterReflow(aPresContext, aKidFrame, view,
1114 aDesiredSize.VisualOverflow(), aFlags);
1115 }
1116
1117 if (!(aFlags & ReflowChildFlags::NoMoveView) && curOrigin != pos) {
1118 if (!aKidFrame->HasView()) {
1119 // If the frame has moved, then we need to make sure any child views are
1120 // correctly positioned
1121 PositionChildViews(aKidFrame);
1122 }
1123 }
1124
1125 aKidFrame->DidReflow(aPresContext, aReflowInput);
1126 }
1127
ReflowOverflowContainerChildren(nsPresContext * aPresContext,const ReflowInput & aReflowInput,nsOverflowAreas & aOverflowRects,ReflowChildFlags aFlags,nsReflowStatus & aStatus,ChildFrameMerger aMergeFunc)1128 void nsContainerFrame::ReflowOverflowContainerChildren(
1129 nsPresContext* aPresContext, const ReflowInput& aReflowInput,
1130 nsOverflowAreas& aOverflowRects, ReflowChildFlags aFlags,
1131 nsReflowStatus& aStatus, ChildFrameMerger aMergeFunc) {
1132 MOZ_ASSERT(aPresContext, "null pointer");
1133
1134 nsFrameList* overflowContainers =
1135 DrainExcessOverflowContainersList(aMergeFunc);
1136 if (!overflowContainers) {
1137 return; // nothing to reflow
1138 }
1139
1140 nsOverflowContinuationTracker tracker(this, false, false);
1141 bool shouldReflowAllKids = aReflowInput.ShouldReflowAllKids();
1142
1143 for (nsIFrame* frame : *overflowContainers) {
1144 if (frame->GetPrevInFlow()->GetParent() != GetPrevInFlow()) {
1145 // frame's prevInFlow has moved, skip reflowing this frame;
1146 // it will get reflowed once it's been placed
1147 if (GetNextInFlow()) {
1148 // We report OverflowIncomplete status in this case to avoid our parent
1149 // deleting our next-in-flows which might destroy non-empty frames.
1150 nsReflowStatus status;
1151 status.SetOverflowIncomplete();
1152 aStatus.MergeCompletionStatusFrom(status);
1153 }
1154 continue;
1155 }
1156 // If the available vertical height has changed, we need to reflow
1157 // even if the frame isn't dirty.
1158 if (shouldReflowAllKids || NS_SUBTREE_DIRTY(frame)) {
1159 // Get prev-in-flow
1160 nsIFrame* prevInFlow = frame->GetPrevInFlow();
1161 NS_ASSERTION(prevInFlow,
1162 "overflow container frame must have a prev-in-flow");
1163 NS_ASSERTION(
1164 frame->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER,
1165 "overflow container frame must have overflow container bit set");
1166 WritingMode wm = frame->GetWritingMode();
1167 nsSize containerSize = aReflowInput.AvailableSize(wm).GetPhysicalSize(wm);
1168 LogicalRect prevRect = prevInFlow->GetLogicalRect(wm, containerSize);
1169
1170 // Initialize reflow params
1171 LogicalSize availSpace(wm, prevRect.ISize(wm),
1172 aReflowInput.AvailableSize(wm).BSize(wm));
1173 ReflowOutput desiredSize(aReflowInput);
1174 ReflowInput frameState(aPresContext, aReflowInput, frame, availSpace);
1175 nsReflowStatus frameStatus;
1176
1177 // Reflow
1178 LogicalPoint pos(wm, prevRect.IStart(wm), 0);
1179 ReflowChild(frame, aPresContext, desiredSize, frameState, wm, pos,
1180 containerSize, aFlags, frameStatus, &tracker);
1181 // XXXfr Do we need to override any shrinkwrap effects here?
1182 // e.g. desiredSize.Width() = prevRect.width;
1183 FinishReflowChild(frame, aPresContext, desiredSize, &frameState, wm, pos,
1184 containerSize, aFlags);
1185
1186 // Handle continuations
1187 if (!frameStatus.IsFullyComplete()) {
1188 if (frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
1189 // Abspos frames can't cause their parent to be incomplete,
1190 // only overflow incomplete.
1191 frameStatus.SetOverflowIncomplete();
1192 } else {
1193 NS_ASSERTION(frameStatus.IsComplete(),
1194 "overflow container frames can't be incomplete, only "
1195 "overflow-incomplete");
1196 }
1197
1198 // Acquire a next-in-flow, creating it if necessary
1199 nsIFrame* nif = frame->GetNextInFlow();
1200 if (!nif) {
1201 NS_ASSERTION(frameStatus.NextInFlowNeedsReflow(),
1202 "Someone forgot a NextInFlowNeedsReflow flag");
1203 nif = PresShell()->FrameConstructor()->CreateContinuingFrame(frame,
1204 this);
1205 } else if (!(nif->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) {
1206 // used to be a normal next-in-flow; steal it from the child list
1207 nsresult rv = nif->GetParent()->StealFrame(nif);
1208 if (NS_FAILED(rv)) {
1209 return;
1210 }
1211 }
1212
1213 tracker.Insert(nif, frameStatus);
1214 }
1215 aStatus.MergeCompletionStatusFrom(frameStatus);
1216 // At this point it would be nice to assert
1217 // !frame->GetOverflowRect().IsEmpty(), but we have some unsplittable
1218 // frames that, when taller than availableHeight will push zero-height
1219 // content into a next-in-flow.
1220 } else {
1221 tracker.Skip(frame, aStatus);
1222 if (aReflowInput.mFloatManager) {
1223 nsBlockFrame::RecoverFloatsFor(frame, *aReflowInput.mFloatManager,
1224 aReflowInput.GetWritingMode(),
1225 aReflowInput.ComputedPhysicalSize());
1226 }
1227 }
1228 ConsiderChildOverflow(aOverflowRects, frame);
1229 }
1230 }
1231
DisplayOverflowContainers(nsDisplayListBuilder * aBuilder,const nsDisplayListSet & aLists)1232 void nsContainerFrame::DisplayOverflowContainers(
1233 nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) {
1234 nsFrameList* overflowconts = GetPropTableFrames(OverflowContainersProperty());
1235 if (overflowconts) {
1236 for (nsIFrame* frame : *overflowconts) {
1237 BuildDisplayListForChild(aBuilder, frame, aLists);
1238 }
1239 }
1240 }
1241
TryRemoveFrame(nsIFrame * aFrame,nsContainerFrame::FrameListPropertyDescriptor aProp,nsIFrame * aChildToRemove)1242 static bool TryRemoveFrame(nsIFrame* aFrame,
1243 nsContainerFrame::FrameListPropertyDescriptor aProp,
1244 nsIFrame* aChildToRemove) {
1245 nsFrameList* list = aFrame->GetProperty(aProp);
1246 if (list && list->StartRemoveFrame(aChildToRemove)) {
1247 // aChildToRemove *may* have been removed from this list.
1248 if (list->IsEmpty()) {
1249 Unused << aFrame->TakeProperty(aProp);
1250 list->Delete(aFrame->PresShell());
1251 }
1252 return true;
1253 }
1254 return false;
1255 }
1256
MaybeStealOverflowContainerFrame(nsIFrame * aChild)1257 bool nsContainerFrame::MaybeStealOverflowContainerFrame(nsIFrame* aChild) {
1258 bool removed = false;
1259 if (MOZ_UNLIKELY(aChild->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) {
1260 // Try removing from the overflow container list.
1261 removed = ::TryRemoveFrame(this, OverflowContainersProperty(), aChild);
1262 if (!removed) {
1263 // It might be in the excess overflow container list.
1264 removed =
1265 ::TryRemoveFrame(this, ExcessOverflowContainersProperty(), aChild);
1266 }
1267 }
1268 return removed;
1269 }
1270
StealFrame(nsIFrame * aChild)1271 nsresult nsContainerFrame::StealFrame(nsIFrame* aChild) {
1272 #ifdef DEBUG
1273 if (!mFrames.ContainsFrame(aChild)) {
1274 nsFrameList* list = GetOverflowFrames();
1275 if (!list || !list->ContainsFrame(aChild)) {
1276 list = GetProperty(OverflowContainersProperty());
1277 if (!list || !list->ContainsFrame(aChild)) {
1278 list = GetProperty(ExcessOverflowContainersProperty());
1279 MOZ_ASSERT(list && list->ContainsFrame(aChild),
1280 "aChild isn't our child"
1281 " or on a frame list not supported by StealFrame");
1282 }
1283 }
1284 }
1285 #endif
1286
1287 bool removed = MaybeStealOverflowContainerFrame(aChild);
1288 if (!removed) {
1289 // NOTE nsColumnSetFrame and nsCanvasFrame have their overflow containers
1290 // on the normal lists so we might get here also if the frame bit
1291 // NS_FRAME_IS_OVERFLOW_CONTAINER is set.
1292 removed = mFrames.StartRemoveFrame(aChild);
1293 if (!removed) {
1294 // We didn't find the child in our principal child list.
1295 // Maybe it's on the overflow list?
1296 nsFrameList* frameList = GetOverflowFrames();
1297 if (frameList) {
1298 removed = frameList->ContinueRemoveFrame(aChild);
1299 if (frameList->IsEmpty()) {
1300 DestroyOverflowList();
1301 }
1302 }
1303 }
1304 }
1305
1306 MOZ_ASSERT(removed, "StealFrame: can't find aChild");
1307 return removed ? NS_OK : NS_ERROR_UNEXPECTED;
1308 }
1309
StealFramesAfter(nsIFrame * aChild)1310 nsFrameList nsContainerFrame::StealFramesAfter(nsIFrame* aChild) {
1311 NS_ASSERTION(
1312 !aChild || !(aChild->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER),
1313 "StealFramesAfter doesn't handle overflow containers");
1314 NS_ASSERTION(!IsBlockFrame(), "unexpected call");
1315
1316 if (!aChild) {
1317 nsFrameList copy(mFrames);
1318 mFrames.Clear();
1319 return copy;
1320 }
1321
1322 for (nsFrameList::FrameLinkEnumerator iter(mFrames); !iter.AtEnd();
1323 iter.Next()) {
1324 if (iter.PrevFrame() == aChild) {
1325 return mFrames.ExtractTail(iter);
1326 }
1327 }
1328
1329 // We didn't find the child in the principal child list.
1330 // Maybe it's on the overflow list?
1331 nsFrameList* overflowFrames = GetOverflowFrames();
1332 if (overflowFrames) {
1333 for (nsFrameList::FrameLinkEnumerator iter(*overflowFrames); !iter.AtEnd();
1334 iter.Next()) {
1335 if (iter.PrevFrame() == aChild) {
1336 return overflowFrames->ExtractTail(iter);
1337 }
1338 }
1339 }
1340
1341 NS_ERROR("StealFramesAfter: can't find aChild");
1342 return nsFrameList::EmptyList();
1343 }
1344
1345 /*
1346 * Create a next-in-flow for aFrame. Will return the newly created
1347 * frame <b>if and only if</b> a new frame is created; otherwise
1348 * nullptr is returned.
1349 */
CreateNextInFlow(nsIFrame * aFrame)1350 nsIFrame* nsContainerFrame::CreateNextInFlow(nsIFrame* aFrame) {
1351 MOZ_ASSERT(
1352 !IsBlockFrame(),
1353 "you should have called nsBlockFrame::CreateContinuationFor instead");
1354 MOZ_ASSERT(mFrames.ContainsFrame(aFrame), "expected an in-flow child frame");
1355
1356 nsIFrame* nextInFlow = aFrame->GetNextInFlow();
1357 if (nullptr == nextInFlow) {
1358 // Create a continuation frame for the child frame and insert it
1359 // into our child list.
1360 nextInFlow =
1361 PresShell()->FrameConstructor()->CreateContinuingFrame(aFrame, this);
1362 mFrames.InsertFrame(nullptr, aFrame, nextInFlow);
1363
1364 NS_FRAME_LOG(NS_FRAME_TRACE_NEW_FRAMES,
1365 ("nsContainerFrame::CreateNextInFlow: frame=%p nextInFlow=%p",
1366 aFrame, nextInFlow));
1367
1368 return nextInFlow;
1369 }
1370 return nullptr;
1371 }
1372
1373 /**
1374 * Remove and delete aNextInFlow and its next-in-flows. Updates the sibling and
1375 * flow pointers
1376 */
DeleteNextInFlowChild(nsIFrame * aNextInFlow,bool aDeletingEmptyFrames)1377 void nsContainerFrame::DeleteNextInFlowChild(nsIFrame* aNextInFlow,
1378 bool aDeletingEmptyFrames) {
1379 #ifdef DEBUG
1380 nsIFrame* prevInFlow = aNextInFlow->GetPrevInFlow();
1381 #endif
1382 MOZ_ASSERT(prevInFlow, "bad prev-in-flow");
1383
1384 // If the next-in-flow has a next-in-flow then delete it, too (and
1385 // delete it first).
1386 // Do this in a loop so we don't overflow the stack for frames
1387 // with very many next-in-flows
1388 nsIFrame* nextNextInFlow = aNextInFlow->GetNextInFlow();
1389 if (nextNextInFlow) {
1390 AutoTArray<nsIFrame*, 8> frames;
1391 for (nsIFrame* f = nextNextInFlow; f; f = f->GetNextInFlow()) {
1392 frames.AppendElement(f);
1393 }
1394 for (nsIFrame* delFrame : Reversed(frames)) {
1395 delFrame->GetParent()->DeleteNextInFlowChild(delFrame,
1396 aDeletingEmptyFrames);
1397 }
1398 }
1399
1400 // Take the next-in-flow out of the parent's child list
1401 DebugOnly<nsresult> rv = StealFrame(aNextInFlow);
1402 NS_ASSERTION(NS_SUCCEEDED(rv), "StealFrame failure");
1403
1404 #ifdef DEBUG
1405 if (aDeletingEmptyFrames) {
1406 nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(aNextInFlow);
1407 }
1408 #endif
1409
1410 // Delete the next-in-flow frame and its descendants. This will also
1411 // remove it from its next-in-flow/prev-in-flow chain.
1412 aNextInFlow->Destroy();
1413
1414 MOZ_ASSERT(!prevInFlow->GetNextInFlow(), "non null next-in-flow");
1415 }
1416
1417 /**
1418 * Set the frames on the overflow list
1419 */
SetOverflowFrames(const nsFrameList & aOverflowFrames)1420 void nsContainerFrame::SetOverflowFrames(const nsFrameList& aOverflowFrames) {
1421 MOZ_ASSERT(aOverflowFrames.NotEmpty(), "Shouldn't be called");
1422
1423 nsPresContext* pc = PresContext();
1424 nsFrameList* newList = new (pc->PresShell()) nsFrameList(aOverflowFrames);
1425
1426 SetProperty(OverflowProperty(), newList);
1427 }
1428
GetPropTableFrames(FrameListPropertyDescriptor aProperty) const1429 nsFrameList* nsContainerFrame::GetPropTableFrames(
1430 FrameListPropertyDescriptor aProperty) const {
1431 return GetProperty(aProperty);
1432 }
1433
RemovePropTableFrames(FrameListPropertyDescriptor aProperty)1434 nsFrameList* nsContainerFrame::RemovePropTableFrames(
1435 FrameListPropertyDescriptor aProperty) {
1436 return TakeProperty(aProperty);
1437 }
1438
SetPropTableFrames(nsFrameList * aFrameList,FrameListPropertyDescriptor aProperty)1439 void nsContainerFrame::SetPropTableFrames(
1440 nsFrameList* aFrameList, FrameListPropertyDescriptor aProperty) {
1441 MOZ_ASSERT(aProperty && aFrameList, "null ptr");
1442 MOZ_ASSERT(
1443 (aProperty != nsContainerFrame::OverflowContainersProperty() &&
1444 aProperty != nsContainerFrame::ExcessOverflowContainersProperty()) ||
1445 IsFrameOfType(nsIFrame::eCanContainOverflowContainers),
1446 "this type of frame can't have overflow containers");
1447 MOZ_ASSERT(!GetPropTableFrames(aProperty));
1448 SetProperty(aProperty, aFrameList);
1449 }
1450
PushChildrenToOverflow(nsIFrame * aFromChild,nsIFrame * aPrevSibling)1451 void nsContainerFrame::PushChildrenToOverflow(nsIFrame* aFromChild,
1452 nsIFrame* aPrevSibling) {
1453 MOZ_ASSERT(aFromChild, "null pointer");
1454 MOZ_ASSERT(aPrevSibling, "pushing first child");
1455 MOZ_ASSERT(aPrevSibling->GetNextSibling() == aFromChild, "bad prev sibling");
1456
1457 // Add the frames to our overflow list (let our next in flow drain
1458 // our overflow list when it is ready)
1459 SetOverflowFrames(mFrames.RemoveFramesAfter(aPrevSibling));
1460 }
1461
PushChildren(nsIFrame * aFromChild,nsIFrame * aPrevSibling)1462 void nsContainerFrame::PushChildren(nsIFrame* aFromChild,
1463 nsIFrame* aPrevSibling) {
1464 MOZ_ASSERT(aFromChild, "null pointer");
1465 MOZ_ASSERT(aPrevSibling, "pushing first child");
1466 MOZ_ASSERT(aPrevSibling->GetNextSibling() == aFromChild, "bad prev sibling");
1467
1468 // Disconnect aFromChild from its previous sibling
1469 nsFrameList tail = mFrames.RemoveFramesAfter(aPrevSibling);
1470
1471 nsContainerFrame* nextInFlow =
1472 static_cast<nsContainerFrame*>(GetNextInFlow());
1473 if (nextInFlow) {
1474 // XXX This is not a very good thing to do. If it gets removed
1475 // then remove the copy of this routine that doesn't do this from
1476 // nsInlineFrame.
1477 // When pushing and pulling frames we need to check for whether any
1478 // views need to be reparented.
1479 for (nsIFrame* f = aFromChild; f; f = f->GetNextSibling()) {
1480 nsContainerFrame::ReparentFrameView(f, this, nextInFlow);
1481 }
1482 nextInFlow->mFrames.InsertFrames(nextInFlow, nullptr, tail);
1483 } else {
1484 // Add the frames to our overflow list
1485 SetOverflowFrames(tail);
1486 }
1487 }
1488
PushIncompleteChildren(const FrameHashtable & aPushedItems,const FrameHashtable & aIncompleteItems,const FrameHashtable & aOverflowIncompleteItems)1489 bool nsContainerFrame::PushIncompleteChildren(
1490 const FrameHashtable& aPushedItems, const FrameHashtable& aIncompleteItems,
1491 const FrameHashtable& aOverflowIncompleteItems) {
1492 MOZ_ASSERT(IsFlexOrGridContainer(),
1493 "Only Grid / Flex containers can call this!");
1494
1495 if (aPushedItems.IsEmpty() && aIncompleteItems.IsEmpty() &&
1496 aOverflowIncompleteItems.IsEmpty()) {
1497 return false;
1498 }
1499
1500 // Iterate the children in normal document order and append them (or a NIF)
1501 // to one of the following frame lists according to their status.
1502 nsFrameList pushedList;
1503 nsFrameList incompleteList;
1504 nsFrameList overflowIncompleteList;
1505 auto* fc = PresShell()->FrameConstructor();
1506 for (nsIFrame* child = GetChildList(kPrincipalList).FirstChild(); child;) {
1507 MOZ_ASSERT((aPushedItems.Contains(child) ? 1 : 0) +
1508 (aIncompleteItems.Contains(child) ? 1 : 0) +
1509 (aOverflowIncompleteItems.Contains(child) ? 1 : 0) <=
1510 1,
1511 "child should only be in one of these sets");
1512 // Save the next-sibling so we can continue the loop if |child| is moved.
1513 nsIFrame* next = child->GetNextSibling();
1514 if (aPushedItems.Contains(child)) {
1515 MOZ_ASSERT(child->GetParent() == this);
1516 StealFrame(child);
1517 pushedList.AppendFrame(nullptr, child);
1518 } else if (aIncompleteItems.Contains(child)) {
1519 nsIFrame* childNIF = child->GetNextInFlow();
1520 if (!childNIF) {
1521 childNIF = fc->CreateContinuingFrame(child, this);
1522 incompleteList.AppendFrame(nullptr, childNIF);
1523 } else {
1524 auto* parent = childNIF->GetParent();
1525 MOZ_ASSERT(parent != this || !mFrames.ContainsFrame(childNIF),
1526 "child's NIF shouldn't be in the same principal list");
1527 // If child's existing NIF is an overflow container, convert it to an
1528 // actual NIF, since now |child| has non-overflow stuff to give it.
1529 // Or, if it's further away then our next-in-flow, then pull it up.
1530 if ((childNIF->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) ||
1531 (parent != this && parent != GetNextInFlow())) {
1532 parent->StealFrame(childNIF);
1533 childNIF->RemoveStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
1534 if (parent == this) {
1535 incompleteList.AppendFrame(nullptr, childNIF);
1536 } else {
1537 // If childNIF already lives on the next fragment, then we
1538 // don't need to reparent it, since we know it's destined to end
1539 // up there anyway. Just move it to its parent's overflow list.
1540 if (parent == GetNextInFlow()) {
1541 nsFrameList toMove(childNIF, childNIF);
1542 parent->MergeSortedOverflow(toMove);
1543 } else {
1544 ReparentFrame(childNIF, parent, this);
1545 incompleteList.AppendFrame(nullptr, childNIF);
1546 }
1547 }
1548 }
1549 }
1550 } else if (aOverflowIncompleteItems.Contains(child)) {
1551 nsIFrame* childNIF = child->GetNextInFlow();
1552 if (!childNIF) {
1553 childNIF = fc->CreateContinuingFrame(child, this);
1554 childNIF->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
1555 overflowIncompleteList.AppendFrame(nullptr, childNIF);
1556 } else {
1557 DebugOnly<nsContainerFrame*> lastParent = this;
1558 auto* nif = static_cast<nsContainerFrame*>(GetNextInFlow());
1559 // If child has any non-overflow-container NIFs, convert them to
1560 // overflow containers, since that's all |child| needs now.
1561 while (childNIF &&
1562 !childNIF->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER)) {
1563 auto* parent = childNIF->GetParent();
1564 parent->StealFrame(childNIF);
1565 childNIF->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
1566 if (parent == this) {
1567 overflowIncompleteList.AppendFrame(nullptr, childNIF);
1568 } else {
1569 if (!nif || parent == nif) {
1570 nsFrameList toMove(childNIF, childNIF);
1571 parent->MergeSortedExcessOverflowContainers(toMove);
1572 } else {
1573 ReparentFrame(childNIF, parent, nif);
1574 nsFrameList toMove(childNIF, childNIF);
1575 nif->MergeSortedExcessOverflowContainers(toMove);
1576 }
1577 // We only need to reparent the first childNIF (or not at all if
1578 // its parent is our NIF).
1579 nif = nullptr;
1580 }
1581 lastParent = parent;
1582 childNIF = childNIF->GetNextInFlow();
1583 }
1584 }
1585 }
1586 child = next;
1587 }
1588
1589 // Merge the results into our respective overflow child lists.
1590 if (!pushedList.IsEmpty()) {
1591 MergeSortedOverflow(pushedList);
1592 }
1593 if (!incompleteList.IsEmpty()) {
1594 MergeSortedOverflow(incompleteList);
1595 }
1596 if (!overflowIncompleteList.IsEmpty()) {
1597 MergeSortedExcessOverflowContainers(overflowIncompleteList);
1598 }
1599 return true;
1600 }
1601
NormalizeChildLists()1602 void nsContainerFrame::NormalizeChildLists() {
1603 MOZ_ASSERT(IsFlexOrGridContainer(),
1604 "Only Flex / Grid containers can call this!");
1605
1606 // Note: the following description uses grid container as an example. Flex
1607 // container is similar.
1608 //
1609 // First we gather child frames we should include in our reflow/placement,
1610 // i.e. overflowed children from our prev-in-flow, and pushed first-in-flow
1611 // children (that might now fit). It's important to note that these children
1612 // can be in arbitrary order vis-a-vis the current children in our lists.
1613 // E.g. grid items in the document order: A, B, C may be placed in the rows
1614 // 3, 2, 1. Assume each row goes in a separate grid container fragment,
1615 // and we reflow the second fragment. Now if C (in fragment 1) overflows,
1616 // we can't just prepend it to our mFrames like we usually do because that
1617 // would violate the document order invariant that other code depends on.
1618 // Similarly if we pull up child A (from fragment 3) we can't just append
1619 // that for the same reason. Instead, we must sort these children into
1620 // our child lists. (The sorting is trivial given that both lists are
1621 // already fully sorted individually - it's just a merge.)
1622 //
1623 // The invariants that we maintain are that each grid container child list
1624 // is sorted in the normal document order at all times, but that children
1625 // in different grid container continuations may be in arbitrary order.
1626
1627 const auto didPushItemsBit = IsFlexContainerFrame()
1628 ? NS_STATE_FLEX_DID_PUSH_ITEMS
1629 : NS_STATE_GRID_DID_PUSH_ITEMS;
1630 const auto hasChildNifBit = IsFlexContainerFrame()
1631 ? NS_STATE_FLEX_HAS_CHILD_NIFS
1632 : NS_STATE_GRID_HAS_CHILD_NIFS;
1633
1634 auto* prevInFlow = static_cast<nsContainerFrame*>(GetPrevInFlow());
1635 // Merge overflow frames from our prev-in-flow into our principal child list.
1636 if (prevInFlow) {
1637 AutoFrameListPtr overflow(PresContext(), prevInFlow->StealOverflowFrames());
1638 if (overflow) {
1639 ReparentFrames(*overflow, prevInFlow, this);
1640 MergeSortedFrameLists(mFrames, *overflow, GetContent());
1641
1642 // Move trailing next-in-flows into our overflow list.
1643 nsFrameList continuations;
1644 for (nsIFrame* f = mFrames.FirstChild(); f;) {
1645 nsIFrame* next = f->GetNextSibling();
1646 nsIFrame* pif = f->GetPrevInFlow();
1647 if (pif && pif->GetParent() == this) {
1648 mFrames.RemoveFrame(f);
1649 continuations.AppendFrame(nullptr, f);
1650 }
1651 f = next;
1652 }
1653 MergeSortedOverflow(continuations);
1654
1655 // Move trailing OC next-in-flows into our excess overflow containers
1656 // list.
1657 nsFrameList* overflowContainers =
1658 GetPropTableFrames(OverflowContainersProperty());
1659 if (overflowContainers) {
1660 nsFrameList moveToEOC;
1661 for (nsIFrame* f = overflowContainers->FirstChild(); f;) {
1662 nsIFrame* next = f->GetNextSibling();
1663 nsIFrame* pif = f->GetPrevInFlow();
1664 if (pif && pif->GetParent() == this) {
1665 overflowContainers->RemoveFrame(f);
1666 moveToEOC.AppendFrame(nullptr, f);
1667 }
1668 f = next;
1669 }
1670 if (overflowContainers->IsEmpty()) {
1671 (void)TakeProperty(OverflowContainersProperty());
1672 overflowContainers->Delete(PresShell());
1673 }
1674 MergeSortedExcessOverflowContainers(moveToEOC);
1675 }
1676 }
1677 }
1678
1679 // Merge our own overflow frames into our principal child list,
1680 // except those that are a next-in-flow for one of our items.
1681 DebugOnly<bool> foundOwnPushedChild = false;
1682 {
1683 nsFrameList* ourOverflow = GetOverflowFrames();
1684 if (ourOverflow) {
1685 nsFrameList items;
1686 for (nsIFrame* f = ourOverflow->FirstChild(); f;) {
1687 nsIFrame* next = f->GetNextSibling();
1688 nsIFrame* pif = f->GetPrevInFlow();
1689 if (!pif || pif->GetParent() != this) {
1690 MOZ_ASSERT(f->GetParent() == this);
1691 ourOverflow->RemoveFrame(f);
1692 items.AppendFrame(nullptr, f);
1693 if (!pif) {
1694 foundOwnPushedChild = true;
1695 }
1696 }
1697 f = next;
1698 }
1699 MergeSortedFrameLists(mFrames, items, GetContent());
1700 if (ourOverflow->IsEmpty()) {
1701 DestroyOverflowList();
1702 }
1703 }
1704 }
1705
1706 // Push any child next-in-flows in our principal list to OverflowList.
1707 if (HasAnyStateBits(hasChildNifBit)) {
1708 nsFrameList framesToPush;
1709 nsIFrame* firstChild = mFrames.FirstChild();
1710 // Note that we potentially modify our mFrames list as we go.
1711 for (auto* child = firstChild; child; child = child->GetNextSibling()) {
1712 if (auto* childNIF = child->GetNextInFlow()) {
1713 if (childNIF->GetParent() == this) {
1714 for (auto* c = child->GetNextSibling(); c; c = c->GetNextSibling()) {
1715 if (c == childNIF) {
1716 // child's next-in-flow is in our principal child list, push it.
1717 mFrames.RemoveFrame(childNIF);
1718 framesToPush.AppendFrame(nullptr, childNIF);
1719 break;
1720 }
1721 }
1722 }
1723 }
1724 }
1725 if (!framesToPush.IsEmpty()) {
1726 MergeSortedOverflow(framesToPush);
1727 }
1728 RemoveStateBits(hasChildNifBit);
1729 }
1730
1731 // Pull up any first-in-flow children we might have pushed.
1732 if (HasAnyStateBits(didPushItemsBit)) {
1733 RemoveStateBits(didPushItemsBit);
1734 nsFrameList items;
1735 auto* nif = static_cast<nsContainerFrame*>(GetNextInFlow());
1736 auto* firstNIF = nif;
1737 DebugOnly<bool> nifNeedPushedItem = false;
1738 while (nif) {
1739 nsFrameList nifItems;
1740 for (nsIFrame* nifChild = nif->GetChildList(kPrincipalList).FirstChild();
1741 nifChild;) {
1742 nsIFrame* next = nifChild->GetNextSibling();
1743 if (!nifChild->GetPrevInFlow()) {
1744 nif->StealFrame(nifChild);
1745 ReparentFrame(nifChild, nif, this);
1746 nifItems.AppendFrame(nullptr, nifChild);
1747 nifNeedPushedItem = false;
1748 }
1749 nifChild = next;
1750 }
1751 MergeSortedFrameLists(items, nifItems, GetContent());
1752
1753 if (!nif->HasAnyStateBits(didPushItemsBit)) {
1754 MOZ_ASSERT(!nifNeedPushedItem || mDidPushItemsBitMayLie,
1755 "The state bit stored in didPushItemsBit lied!");
1756 break;
1757 }
1758 nifNeedPushedItem = true;
1759
1760 for (nsIFrame* nifChild = nif->GetChildList(kOverflowList).FirstChild();
1761 nifChild;) {
1762 nsIFrame* next = nifChild->GetNextSibling();
1763 if (!nifChild->GetPrevInFlow()) {
1764 nif->StealFrame(nifChild);
1765 ReparentFrame(nifChild, nif, this);
1766 nifItems.AppendFrame(nullptr, nifChild);
1767 nifNeedPushedItem = false;
1768 }
1769 nifChild = next;
1770 }
1771 MergeSortedFrameLists(items, nifItems, GetContent());
1772
1773 nif->RemoveStateBits(didPushItemsBit);
1774 nif = static_cast<nsContainerFrame*>(nif->GetNextInFlow());
1775 MOZ_ASSERT(nif || !nifNeedPushedItem || mDidPushItemsBitMayLie,
1776 "The state bit stored in didPushItemsBit lied!");
1777 }
1778
1779 if (!items.IsEmpty()) {
1780 // Pull up the first next-in-flow of the pulled up items too, unless its
1781 // parent is our nif (to avoid leaving a hole there).
1782 nsFrameList childNIFs;
1783 nsFrameList childOCNIFs;
1784 for (auto* child : items) {
1785 auto* childNIF = child->GetNextInFlow();
1786 if (childNIF && childNIF->GetParent() != firstNIF) {
1787 auto* parent = childNIF->GetParent();
1788 parent->StealFrame(childNIF);
1789 ReparentFrame(childNIF, parent, firstNIF);
1790 if ((childNIF->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) {
1791 childOCNIFs.AppendFrame(nullptr, childNIF);
1792 } else {
1793 childNIFs.AppendFrame(nullptr, childNIF);
1794 }
1795 }
1796 }
1797 // Merge items' NIFs into our NIF's respective overflow child lists.
1798 firstNIF->MergeSortedOverflow(childNIFs);
1799 firstNIF->MergeSortedExcessOverflowContainers(childOCNIFs);
1800 }
1801
1802 MOZ_ASSERT(
1803 foundOwnPushedChild || !items.IsEmpty() || mDidPushItemsBitMayLie,
1804 "The state bit stored in didPushItemsBit lied!");
1805 MergeSortedFrameLists(mFrames, items, GetContent());
1806 }
1807 }
1808
NoteNewChildren(ChildListID aListID,const nsFrameList & aFrameList)1809 void nsContainerFrame::NoteNewChildren(ChildListID aListID,
1810 const nsFrameList& aFrameList) {
1811 #ifdef DEBUG
1812 ChildListIDs supportedLists = {kAbsoluteList, kFixedList, kPrincipalList};
1813 // We don't handle the kBackdropList frames in any way, but it only contains
1814 // a placeholder for ::backdrop which is OK to not reflow (for now anyway).
1815 supportedLists += kBackdropList;
1816 MOZ_ASSERT(supportedLists.contains(aListID), "unexpected child list");
1817 #endif
1818
1819 MOZ_ASSERT(IsFlexOrGridContainer(),
1820 "Only Flex / Grid containers can call this!");
1821
1822 mozilla::PresShell* presShell = PresShell();
1823 const auto didPushItemsBit = IsFlexContainerFrame()
1824 ? NS_STATE_FLEX_DID_PUSH_ITEMS
1825 : NS_STATE_GRID_DID_PUSH_ITEMS;
1826 for (auto* pif = GetPrevInFlow(); pif; pif = pif->GetPrevInFlow()) {
1827 if (aListID == kPrincipalList) {
1828 pif->AddStateBits(didPushItemsBit);
1829 }
1830 presShell->FrameNeedsReflow(pif, IntrinsicDirty::TreeChange,
1831 NS_FRAME_IS_DIRTY);
1832 }
1833 }
1834
MoveOverflowToChildList()1835 bool nsContainerFrame::MoveOverflowToChildList() {
1836 bool result = false;
1837
1838 // Check for an overflow list with our prev-in-flow
1839 nsContainerFrame* prevInFlow = (nsContainerFrame*)GetPrevInFlow();
1840 if (nullptr != prevInFlow) {
1841 AutoFrameListPtr prevOverflowFrames(PresContext(),
1842 prevInFlow->StealOverflowFrames());
1843 if (prevOverflowFrames) {
1844 // Tables are special; they can have repeated header/footer
1845 // frames on mFrames at this point.
1846 NS_ASSERTION(mFrames.IsEmpty() || IsTableFrame(), "bad overflow list");
1847 // When pushing and pulling frames we need to check for whether any
1848 // views need to be reparented.
1849 nsContainerFrame::ReparentFrameViewList(*prevOverflowFrames, prevInFlow,
1850 this);
1851 mFrames.AppendFrames(this, *prevOverflowFrames);
1852 result = true;
1853 }
1854 }
1855
1856 // It's also possible that we have an overflow list for ourselves.
1857 return DrainSelfOverflowList() || result;
1858 }
1859
MergeSortedOverflow(nsFrameList & aList)1860 void nsContainerFrame::MergeSortedOverflow(nsFrameList& aList) {
1861 if (aList.IsEmpty()) {
1862 return;
1863 }
1864 MOZ_ASSERT(
1865 !aList.FirstChild()->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER),
1866 "this is the wrong list to put this child frame");
1867 MOZ_ASSERT(aList.FirstChild()->GetParent() == this);
1868 nsFrameList* overflow = GetOverflowFrames();
1869 if (overflow) {
1870 MergeSortedFrameLists(*overflow, aList, GetContent());
1871 } else {
1872 SetOverflowFrames(aList);
1873 }
1874 }
1875
MergeSortedExcessOverflowContainers(nsFrameList & aList)1876 void nsContainerFrame::MergeSortedExcessOverflowContainers(nsFrameList& aList) {
1877 if (aList.IsEmpty()) {
1878 return;
1879 }
1880 MOZ_ASSERT(
1881 aList.FirstChild()->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER),
1882 "this is the wrong list to put this child frame");
1883 MOZ_ASSERT(aList.FirstChild()->GetParent() == this);
1884 nsFrameList* eoc = GetPropTableFrames(ExcessOverflowContainersProperty());
1885 if (eoc) {
1886 MergeSortedFrameLists(*eoc, aList, GetContent());
1887 } else {
1888 SetPropTableFrames(new (PresShell()) nsFrameList(aList),
1889 ExcessOverflowContainersProperty());
1890 }
1891 }
1892
1893 /**
1894 * Is aFrame1 a prev-continuation of aFrame2?
1895 */
IsPrevContinuationOf(nsIFrame * aFrame1,nsIFrame * aFrame2)1896 static bool IsPrevContinuationOf(nsIFrame* aFrame1, nsIFrame* aFrame2) {
1897 nsIFrame* prev = aFrame2;
1898 while ((prev = prev->GetPrevContinuation())) {
1899 if (prev == aFrame1) {
1900 return true;
1901 }
1902 }
1903 return false;
1904 }
1905
MergeSortedFrameLists(nsFrameList & aDest,nsFrameList & aSrc,nsIContent * aCommonAncestor)1906 void nsContainerFrame::MergeSortedFrameLists(nsFrameList& aDest,
1907 nsFrameList& aSrc,
1908 nsIContent* aCommonAncestor) {
1909 nsIFrame* dest = aDest.FirstChild();
1910 for (nsIFrame* src = aSrc.FirstChild(); src;) {
1911 if (!dest) {
1912 aDest.AppendFrames(nullptr, aSrc);
1913 break;
1914 }
1915 nsIContent* srcContent = src->GetContent();
1916 nsIContent* destContent = dest->GetContent();
1917 int32_t result = nsLayoutUtils::CompareTreePosition(srcContent, destContent,
1918 aCommonAncestor);
1919 if (MOZ_UNLIKELY(result == 0)) {
1920 // NOTE: we get here when comparing ::before/::after for the same element.
1921 if (MOZ_UNLIKELY(srcContent->IsGeneratedContentContainerForBefore())) {
1922 if (MOZ_LIKELY(!destContent->IsGeneratedContentContainerForBefore()) ||
1923 ::IsPrevContinuationOf(src, dest)) {
1924 result = -1;
1925 }
1926 } else if (MOZ_UNLIKELY(
1927 srcContent->IsGeneratedContentContainerForAfter())) {
1928 if (MOZ_UNLIKELY(destContent->IsGeneratedContentContainerForAfter()) &&
1929 ::IsPrevContinuationOf(src, dest)) {
1930 result = -1;
1931 }
1932 } else if (::IsPrevContinuationOf(src, dest)) {
1933 result = -1;
1934 }
1935 }
1936 if (result < 0) {
1937 // src should come before dest
1938 nsIFrame* next = src->GetNextSibling();
1939 aSrc.RemoveFrame(src);
1940 aDest.InsertFrame(nullptr, dest->GetPrevSibling(), src);
1941 src = next;
1942 } else {
1943 dest = dest->GetNextSibling();
1944 }
1945 }
1946 MOZ_ASSERT(aSrc.IsEmpty());
1947 }
1948
MoveInlineOverflowToChildList(nsIFrame * aLineContainer)1949 bool nsContainerFrame::MoveInlineOverflowToChildList(nsIFrame* aLineContainer) {
1950 MOZ_ASSERT(aLineContainer,
1951 "Must have line container for moving inline overflows");
1952
1953 bool result = false;
1954
1955 // Check for an overflow list with our prev-in-flow
1956 if (auto prevInFlow = static_cast<nsContainerFrame*>(GetPrevInFlow())) {
1957 AutoFrameListPtr prevOverflowFrames(PresContext(),
1958 prevInFlow->StealOverflowFrames());
1959 if (prevOverflowFrames) {
1960 // We may need to reparent floats from prev-in-flow to our line
1961 // container if the container has prev continuation.
1962 if (aLineContainer->GetPrevContinuation()) {
1963 ReparentFloatsForInlineChild(aLineContainer,
1964 prevOverflowFrames->FirstChild(), true);
1965 }
1966 // When pushing and pulling frames we need to check for whether
1967 // any views need to be reparented.
1968 nsContainerFrame::ReparentFrameViewList(*prevOverflowFrames, prevInFlow,
1969 this);
1970 // Prepend overflow frames to the list.
1971 mFrames.InsertFrames(this, nullptr, *prevOverflowFrames);
1972 result = true;
1973 }
1974 }
1975
1976 // It's also possible that we have overflow list for ourselves.
1977 return DrainSelfOverflowList() || result;
1978 }
1979
DrainSelfOverflowList()1980 bool nsContainerFrame::DrainSelfOverflowList() {
1981 AutoFrameListPtr overflowFrames(PresContext(), StealOverflowFrames());
1982 if (overflowFrames) {
1983 mFrames.AppendFrames(nullptr, *overflowFrames);
1984 return true;
1985 }
1986 return false;
1987 }
1988
DrainAndMergeSelfOverflowList()1989 bool nsContainerFrame::DrainAndMergeSelfOverflowList() {
1990 MOZ_ASSERT(IsFlexOrGridContainer(),
1991 "Only Flex / Grid containers can call this!");
1992
1993 // Unlike nsContainerFrame::DrainSelfOverflowList, flex or grid containers
1994 // need to merge these lists so that the resulting mFrames is in document
1995 // content order.
1996 // NOTE: nsContainerFrame::AppendFrames/InsertFrames calls this method and
1997 // there are also direct calls from the fctor (FindAppendPrevSibling).
1998 AutoFrameListPtr overflowFrames(PresContext(), StealOverflowFrames());
1999 if (overflowFrames) {
2000 MergeSortedFrameLists(mFrames, *overflowFrames, GetContent());
2001 // We set a frame bit to push them again in Reflow() to avoid creating
2002 // multiple flex / grid items per flex / grid container fragment for the
2003 // same content.
2004 AddStateBits(IsFlexContainerFrame() ? NS_STATE_FLEX_HAS_CHILD_NIFS
2005 : NS_STATE_GRID_HAS_CHILD_NIFS);
2006 return true;
2007 }
2008 return false;
2009 }
2010
DrainExcessOverflowContainersList(ChildFrameMerger aMergeFunc)2011 nsFrameList* nsContainerFrame::DrainExcessOverflowContainersList(
2012 ChildFrameMerger aMergeFunc) {
2013 nsFrameList* overflowContainers =
2014 GetPropTableFrames(OverflowContainersProperty());
2015
2016 NS_ASSERTION(!(overflowContainers && GetPrevInFlow() &&
2017 static_cast<nsContainerFrame*>(GetPrevInFlow())
2018 ->GetPropTableFrames(ExcessOverflowContainersProperty())),
2019 "conflicting overflow containers lists");
2020
2021 if (!overflowContainers) {
2022 // Drain excess from previnflow
2023 nsContainerFrame* prev = (nsContainerFrame*)GetPrevInFlow();
2024 if (prev) {
2025 nsFrameList* excessFrames =
2026 prev->RemovePropTableFrames(ExcessOverflowContainersProperty());
2027 if (excessFrames) {
2028 excessFrames->ApplySetParent(this);
2029 nsContainerFrame::ReparentFrameViewList(*excessFrames, prev, this);
2030 overflowContainers = excessFrames;
2031 SetPropTableFrames(overflowContainers, OverflowContainersProperty());
2032 }
2033 }
2034 }
2035
2036 // Our own excess overflow containers from a previous reflow can still be
2037 // present if our next-in-flow hasn't been reflown yet. Move any children
2038 // from it that don't have a continuation in this frame to the
2039 // OverflowContainers list.
2040 nsFrameList* selfExcessOCFrames =
2041 RemovePropTableFrames(ExcessOverflowContainersProperty());
2042 if (selfExcessOCFrames) {
2043 nsFrameList toMove;
2044 auto child = selfExcessOCFrames->FirstChild();
2045 while (child) {
2046 auto next = child->GetNextSibling();
2047 MOZ_ASSERT(child->GetPrevInFlow(),
2048 "ExcessOverflowContainers frames must be continuations");
2049 if (child->GetPrevInFlow()->GetParent() != this) {
2050 selfExcessOCFrames->RemoveFrame(child);
2051 toMove.AppendFrame(nullptr, child);
2052 }
2053 child = next;
2054 }
2055 if (toMove.IsEmpty()) {
2056 SetPropTableFrames(selfExcessOCFrames,
2057 ExcessOverflowContainersProperty());
2058 } else if (overflowContainers) {
2059 aMergeFunc(*overflowContainers, toMove, this);
2060 if (selfExcessOCFrames->IsEmpty()) {
2061 selfExcessOCFrames->Delete(PresShell());
2062 } else {
2063 SetPropTableFrames(selfExcessOCFrames,
2064 ExcessOverflowContainersProperty());
2065 }
2066 } else {
2067 if (selfExcessOCFrames->IsEmpty()) {
2068 *selfExcessOCFrames = toMove;
2069 overflowContainers = selfExcessOCFrames;
2070 } else {
2071 SetPropTableFrames(selfExcessOCFrames,
2072 ExcessOverflowContainersProperty());
2073 auto shell = PresShell();
2074 overflowContainers = new (shell) nsFrameList(toMove);
2075 }
2076 SetPropTableFrames(overflowContainers, OverflowContainersProperty());
2077 }
2078 }
2079
2080 return overflowContainers;
2081 }
2082
GetNextInFlowChild(ContinuationTraversingState & aState,bool * aIsInOverflow)2083 nsIFrame* nsContainerFrame::GetNextInFlowChild(
2084 ContinuationTraversingState& aState, bool* aIsInOverflow) {
2085 nsContainerFrame*& nextInFlow = aState.mNextInFlow;
2086 while (nextInFlow) {
2087 // See if there is any frame in the container
2088 nsIFrame* frame = nextInFlow->mFrames.FirstChild();
2089 if (frame) {
2090 if (aIsInOverflow) {
2091 *aIsInOverflow = false;
2092 }
2093 return frame;
2094 }
2095 // No frames in the principal list, try its overflow list
2096 nsFrameList* overflowFrames = nextInFlow->GetOverflowFrames();
2097 if (overflowFrames) {
2098 if (aIsInOverflow) {
2099 *aIsInOverflow = true;
2100 }
2101 return overflowFrames->FirstChild();
2102 }
2103 nextInFlow = static_cast<nsContainerFrame*>(nextInFlow->GetNextInFlow());
2104 }
2105 return nullptr;
2106 }
2107
PullNextInFlowChild(ContinuationTraversingState & aState)2108 nsIFrame* nsContainerFrame::PullNextInFlowChild(
2109 ContinuationTraversingState& aState) {
2110 bool isInOverflow;
2111 nsIFrame* frame = GetNextInFlowChild(aState, &isInOverflow);
2112 if (frame) {
2113 nsContainerFrame* nextInFlow = aState.mNextInFlow;
2114 if (isInOverflow) {
2115 nsFrameList* overflowFrames = nextInFlow->GetOverflowFrames();
2116 overflowFrames->RemoveFirstChild();
2117 if (overflowFrames->IsEmpty()) {
2118 nextInFlow->DestroyOverflowList();
2119 }
2120 } else {
2121 nextInFlow->mFrames.RemoveFirstChild();
2122 }
2123
2124 // Move the frame to the principal frame list of this container
2125 mFrames.AppendFrame(this, frame);
2126 // AppendFrame has reparented the frame, we need
2127 // to reparent the frame view then.
2128 nsContainerFrame::ReparentFrameView(frame, nextInFlow, this);
2129 }
2130 return frame;
2131 }
2132
2133 /* static */
ReparentFloatsForInlineChild(nsIFrame * aOurLineContainer,nsIFrame * aFrame,bool aReparentSiblings)2134 void nsContainerFrame::ReparentFloatsForInlineChild(nsIFrame* aOurLineContainer,
2135 nsIFrame* aFrame,
2136 bool aReparentSiblings) {
2137 // XXXbz this would be better if it took a nsFrameList or a frame
2138 // list slice....
2139 NS_ASSERTION(aOurLineContainer->GetNextContinuation() ||
2140 aOurLineContainer->GetPrevContinuation(),
2141 "Don't call this when we have no continuation, it's a waste");
2142 if (!aFrame) {
2143 NS_ASSERTION(aReparentSiblings, "Why did we get called?");
2144 return;
2145 }
2146
2147 nsBlockFrame* frameBlock = nsLayoutUtils::GetFloatContainingBlock(aFrame);
2148 if (!frameBlock || frameBlock == aOurLineContainer) {
2149 return;
2150 }
2151
2152 nsBlockFrame* ourBlock = do_QueryFrame(aOurLineContainer);
2153 NS_ASSERTION(ourBlock, "Not a block, but broke vertically?");
2154
2155 while (true) {
2156 ourBlock->ReparentFloats(aFrame, frameBlock, false);
2157
2158 if (!aReparentSiblings) return;
2159 nsIFrame* next = aFrame->GetNextSibling();
2160 if (!next) return;
2161 if (next->GetParent() == aFrame->GetParent()) {
2162 aFrame = next;
2163 continue;
2164 }
2165 // This is paranoid and will hardly ever get hit ... but we can't actually
2166 // trust that the frames in the sibling chain all have the same parent,
2167 // because lazy reparenting may be going on. If we find a different
2168 // parent we need to redo our analysis.
2169 ReparentFloatsForInlineChild(aOurLineContainer, next, aReparentSiblings);
2170 return;
2171 }
2172 }
2173
ResolvedOrientationIsVertical()2174 bool nsContainerFrame::ResolvedOrientationIsVertical() {
2175 StyleOrient orient = StyleDisplay()->mOrient;
2176 switch (orient) {
2177 case StyleOrient::Horizontal:
2178 return false;
2179 case StyleOrient::Vertical:
2180 return true;
2181 case StyleOrient::Inline:
2182 return GetWritingMode().IsVertical();
2183 case StyleOrient::Block:
2184 return !GetWritingMode().IsVertical();
2185 }
2186 MOZ_ASSERT_UNREACHABLE("unexpected -moz-orient value");
2187 return false;
2188 }
2189
CSSAlignmentForAbsPosChild(const ReflowInput & aChildRI,LogicalAxis aLogicalAxis) const2190 StyleAlignFlags nsContainerFrame::CSSAlignmentForAbsPosChild(
2191 const ReflowInput& aChildRI, LogicalAxis aLogicalAxis) const {
2192 MOZ_ASSERT(aChildRI.mFrame->IsAbsolutelyPositioned(),
2193 "This method should only be called for abspos children");
2194 NS_ERROR(
2195 "Child classes that use css box alignment for abspos children "
2196 "should provide their own implementation of this method!");
2197
2198 // In the unexpected/unlikely event that this implementation gets invoked,
2199 // just use "start" alignment.
2200 return StyleAlignFlags::START;
2201 }
2202
nsOverflowContinuationTracker(nsContainerFrame * aFrame,bool aWalkOOFFrames,bool aSkipOverflowContainerChildren)2203 nsOverflowContinuationTracker::nsOverflowContinuationTracker(
2204 nsContainerFrame* aFrame, bool aWalkOOFFrames,
2205 bool aSkipOverflowContainerChildren)
2206 : mOverflowContList(nullptr),
2207 mPrevOverflowCont(nullptr),
2208 mSentry(nullptr),
2209 mParent(aFrame),
2210 mSkipOverflowContainerChildren(aSkipOverflowContainerChildren),
2211 mWalkOOFFrames(aWalkOOFFrames) {
2212 MOZ_ASSERT(aFrame, "null frame pointer");
2213 SetupOverflowContList();
2214 }
2215
SetupOverflowContList()2216 void nsOverflowContinuationTracker::SetupOverflowContList() {
2217 MOZ_ASSERT(mParent, "null frame pointer");
2218 MOZ_ASSERT(!mOverflowContList, "already have list");
2219 nsContainerFrame* nif =
2220 static_cast<nsContainerFrame*>(mParent->GetNextInFlow());
2221 if (nif) {
2222 mOverflowContList =
2223 nif->GetPropTableFrames(nsContainerFrame::OverflowContainersProperty());
2224 if (mOverflowContList) {
2225 mParent = nif;
2226 SetUpListWalker();
2227 }
2228 }
2229 if (!mOverflowContList) {
2230 mOverflowContList = mParent->GetPropTableFrames(
2231 nsContainerFrame::ExcessOverflowContainersProperty());
2232 if (mOverflowContList) {
2233 SetUpListWalker();
2234 }
2235 }
2236 }
2237
2238 /**
2239 * Helper function to walk past overflow continuations whose prev-in-flow
2240 * isn't a normal child and to set mSentry and mPrevOverflowCont correctly.
2241 */
SetUpListWalker()2242 void nsOverflowContinuationTracker::SetUpListWalker() {
2243 NS_ASSERTION(!mSentry && !mPrevOverflowCont,
2244 "forgot to reset mSentry or mPrevOverflowCont");
2245 if (mOverflowContList) {
2246 nsIFrame* cur = mOverflowContList->FirstChild();
2247 if (mSkipOverflowContainerChildren) {
2248 while (cur && (cur->GetPrevInFlow()->GetStateBits() &
2249 NS_FRAME_IS_OVERFLOW_CONTAINER)) {
2250 mPrevOverflowCont = cur;
2251 cur = cur->GetNextSibling();
2252 }
2253 while (cur && (!(cur->GetStateBits() & NS_FRAME_OUT_OF_FLOW) ==
2254 mWalkOOFFrames)) {
2255 mPrevOverflowCont = cur;
2256 cur = cur->GetNextSibling();
2257 }
2258 }
2259 if (cur) {
2260 mSentry = cur->GetPrevInFlow();
2261 }
2262 }
2263 }
2264
2265 /**
2266 * Helper function to step forward through the overflow continuations list.
2267 * Sets mSentry and mPrevOverflowCont, skipping over OOF or non-OOF frames
2268 * as appropriate. May only be called when we have already set up an
2269 * mOverflowContList; mOverflowContList cannot be null.
2270 */
StepForward()2271 void nsOverflowContinuationTracker::StepForward() {
2272 MOZ_ASSERT(mOverflowContList, "null list");
2273
2274 // Step forward
2275 if (mPrevOverflowCont) {
2276 mPrevOverflowCont = mPrevOverflowCont->GetNextSibling();
2277 } else {
2278 mPrevOverflowCont = mOverflowContList->FirstChild();
2279 }
2280
2281 // Skip over oof or non-oof frames as appropriate
2282 if (mSkipOverflowContainerChildren) {
2283 nsIFrame* cur = mPrevOverflowCont->GetNextSibling();
2284 while (cur &&
2285 (!(cur->GetStateBits() & NS_FRAME_OUT_OF_FLOW) == mWalkOOFFrames)) {
2286 mPrevOverflowCont = cur;
2287 cur = cur->GetNextSibling();
2288 }
2289 }
2290
2291 // Set up the sentry
2292 mSentry = (mPrevOverflowCont->GetNextSibling())
2293 ? mPrevOverflowCont->GetNextSibling()->GetPrevInFlow()
2294 : nullptr;
2295 }
2296
Insert(nsIFrame * aOverflowCont,nsReflowStatus & aReflowStatus)2297 nsresult nsOverflowContinuationTracker::Insert(nsIFrame* aOverflowCont,
2298 nsReflowStatus& aReflowStatus) {
2299 MOZ_ASSERT(aOverflowCont, "null frame pointer");
2300 MOZ_ASSERT(!mSkipOverflowContainerChildren ||
2301 mWalkOOFFrames ==
2302 !!(aOverflowCont->GetStateBits() & NS_FRAME_OUT_OF_FLOW),
2303 "shouldn't insert frame that doesn't match walker type");
2304 MOZ_ASSERT(aOverflowCont->GetPrevInFlow(),
2305 "overflow containers must have a prev-in-flow");
2306
2307 nsresult rv = NS_OK;
2308 bool reparented = false;
2309 nsPresContext* presContext = aOverflowCont->PresContext();
2310 bool addToList = !mSentry || aOverflowCont != mSentry->GetNextInFlow();
2311
2312 // If we have a list and aOverflowCont is already in it then don't try to
2313 // add it again.
2314 if (addToList && aOverflowCont->GetParent() == mParent &&
2315 (aOverflowCont->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) &&
2316 mOverflowContList && mOverflowContList->ContainsFrame(aOverflowCont)) {
2317 addToList = false;
2318 mPrevOverflowCont = aOverflowCont->GetPrevSibling();
2319 }
2320
2321 if (addToList) {
2322 if (aOverflowCont->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) {
2323 // aOverflowCont is in some other overflow container list,
2324 // steal it first
2325 NS_ASSERTION(!(mOverflowContList &&
2326 mOverflowContList->ContainsFrame(aOverflowCont)),
2327 "overflow containers out of order");
2328 rv = aOverflowCont->GetParent()->StealFrame(aOverflowCont);
2329 NS_ENSURE_SUCCESS(rv, rv);
2330 } else {
2331 aOverflowCont->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
2332 }
2333 if (!mOverflowContList) {
2334 mOverflowContList = new (presContext->PresShell()) nsFrameList();
2335 mParent->SetPropTableFrames(
2336 mOverflowContList,
2337 nsContainerFrame::ExcessOverflowContainersProperty());
2338 SetUpListWalker();
2339 }
2340 if (aOverflowCont->GetParent() != mParent) {
2341 nsContainerFrame::ReparentFrameView(aOverflowCont,
2342 aOverflowCont->GetParent(), mParent);
2343 reparented = true;
2344 }
2345
2346 // If aOverflowCont has a prev/next-in-flow that might be in
2347 // mOverflowContList we need to find it and insert after/before it to
2348 // maintain the order amongst next-in-flows in this list.
2349 nsIFrame* pif = aOverflowCont->GetPrevInFlow();
2350 nsIFrame* nif = aOverflowCont->GetNextInFlow();
2351 if ((pif && pif->GetParent() == mParent && pif != mPrevOverflowCont) ||
2352 (nif && nif->GetParent() == mParent && mPrevOverflowCont)) {
2353 for (nsFrameList::Enumerator e(*mOverflowContList); !e.AtEnd();
2354 e.Next()) {
2355 nsIFrame* f = e.get();
2356 if (f == pif) {
2357 mPrevOverflowCont = pif;
2358 break;
2359 }
2360 if (f == nif) {
2361 mPrevOverflowCont = f->GetPrevSibling();
2362 break;
2363 }
2364 }
2365 }
2366
2367 mOverflowContList->InsertFrame(mParent, mPrevOverflowCont, aOverflowCont);
2368 aReflowStatus.SetNextInFlowNeedsReflow();
2369 }
2370
2371 // If we need to reflow it, mark it dirty
2372 if (aReflowStatus.NextInFlowNeedsReflow()) {
2373 aOverflowCont->MarkSubtreeDirty();
2374 }
2375
2376 // It's in our list, just step forward
2377 StepForward();
2378 NS_ASSERTION(
2379 mPrevOverflowCont == aOverflowCont ||
2380 (mSkipOverflowContainerChildren &&
2381 (mPrevOverflowCont->GetStateBits() & NS_FRAME_OUT_OF_FLOW) !=
2382 (aOverflowCont->GetStateBits() & NS_FRAME_OUT_OF_FLOW)),
2383 "OverflowContTracker in unexpected state");
2384
2385 if (addToList) {
2386 // Convert all non-overflow-container next-in-flows of aOverflowCont
2387 // into overflow containers and move them to our overflow
2388 // tracker. This preserves the invariant that the next-in-flows
2389 // of an overflow container are also overflow containers.
2390 nsIFrame* f = aOverflowCont->GetNextInFlow();
2391 if (f && (!(f->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) ||
2392 (!reparented && f->GetParent() == mParent) ||
2393 (reparented && f->GetParent() != mParent))) {
2394 if (!(f->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) {
2395 rv = f->GetParent()->StealFrame(f);
2396 NS_ENSURE_SUCCESS(rv, rv);
2397 }
2398 Insert(f, aReflowStatus);
2399 }
2400 }
2401 return rv;
2402 }
2403
BeginFinish(nsIFrame * aChild)2404 void nsOverflowContinuationTracker::BeginFinish(nsIFrame* aChild) {
2405 MOZ_ASSERT(aChild, "null ptr");
2406 MOZ_ASSERT(aChild->GetNextInFlow(),
2407 "supposed to call Finish *before* deleting next-in-flow!");
2408
2409 for (nsIFrame* f = aChild; f; f = f->GetNextInFlow()) {
2410 // We'll update these in EndFinish after the next-in-flows are gone.
2411 if (f == mPrevOverflowCont) {
2412 mSentry = nullptr;
2413 mPrevOverflowCont = nullptr;
2414 break;
2415 }
2416 if (f == mSentry) {
2417 mSentry = nullptr;
2418 break;
2419 }
2420 }
2421 }
2422
EndFinish(nsIFrame * aChild)2423 void nsOverflowContinuationTracker::EndFinish(nsIFrame* aChild) {
2424 if (!mOverflowContList) {
2425 return;
2426 }
2427 // Forget mOverflowContList if it was deleted.
2428 nsFrameList* eoc = mParent->GetProperty(
2429 nsContainerFrame::ExcessOverflowContainersProperty());
2430 if (eoc != mOverflowContList) {
2431 nsFrameList* oc = static_cast<nsFrameList*>(
2432 mParent->GetProperty(nsContainerFrame::OverflowContainersProperty()));
2433 if (oc != mOverflowContList) {
2434 // mOverflowContList was deleted
2435 mPrevOverflowCont = nullptr;
2436 mSentry = nullptr;
2437 mParent = aChild->GetParent();
2438 mOverflowContList = nullptr;
2439 SetupOverflowContList();
2440 return;
2441 }
2442 }
2443 // The list survived, update mSentry if needed.
2444 if (!mSentry) {
2445 if (!mPrevOverflowCont) {
2446 SetUpListWalker();
2447 } else {
2448 mozilla::AutoRestore<nsIFrame*> saved(mPrevOverflowCont);
2449 // step backward to make StepForward() use our current mPrevOverflowCont
2450 mPrevOverflowCont = mPrevOverflowCont->GetPrevSibling();
2451 StepForward();
2452 }
2453 }
2454 }
2455
2456 /////////////////////////////////////////////////////////////////////////////
2457 // Debugging
2458
2459 #ifdef DEBUG
SanityCheckChildListsBeforeReflow() const2460 void nsContainerFrame::SanityCheckChildListsBeforeReflow() const {
2461 MOZ_ASSERT(IsFlexOrGridContainer(),
2462 "Only Flex / Grid containers can call this!");
2463
2464 const auto didPushItemsBit = IsFlexContainerFrame()
2465 ? NS_STATE_FLEX_DID_PUSH_ITEMS
2466 : NS_STATE_GRID_DID_PUSH_ITEMS;
2467 ChildListIDs absLists = {kAbsoluteList, kFixedList, kOverflowContainersList,
2468 kExcessOverflowContainersList};
2469 ChildListIDs itemLists = {kPrincipalList, kOverflowList};
2470 for (const nsIFrame* f = this; f; f = f->GetNextInFlow()) {
2471 MOZ_ASSERT(!f->HasAnyStateBits(didPushItemsBit),
2472 "At start of reflow, we should've pulled items back from all "
2473 "NIFs and cleared the state bit stored in didPushItemsBit in "
2474 "the process.");
2475 for (const auto& [list, listID] : f->ChildLists()) {
2476 if (!itemLists.contains(listID)) {
2477 MOZ_ASSERT(absLists.contains(listID) || listID == kBackdropList,
2478 "unexpected non-empty child list");
2479 continue;
2480 }
2481 for (const auto* child : list) {
2482 MOZ_ASSERT(f == this || child->GetPrevInFlow(),
2483 "all pushed items must be pulled up before reflow");
2484 }
2485 }
2486 }
2487 // If we have a prev-in-flow, each of its children's next-in-flow
2488 // should be one of our children or be null.
2489 const auto* pif = static_cast<nsContainerFrame*>(GetPrevInFlow());
2490 if (pif) {
2491 const nsFrameList* oc = GetPropTableFrames(OverflowContainersProperty());
2492 const nsFrameList* eoc =
2493 GetPropTableFrames(ExcessOverflowContainersProperty());
2494 const nsFrameList* pifEOC =
2495 pif->GetPropTableFrames(ExcessOverflowContainersProperty());
2496 for (const nsIFrame* child : pif->GetChildList(kPrincipalList)) {
2497 const nsIFrame* childNIF = child->GetNextInFlow();
2498 MOZ_ASSERT(!childNIF || mFrames.ContainsFrame(childNIF) ||
2499 (pifEOC && pifEOC->ContainsFrame(childNIF)) ||
2500 (oc && oc->ContainsFrame(childNIF)) ||
2501 (eoc && eoc->ContainsFrame(childNIF)));
2502 }
2503 }
2504 }
2505
SetDidPushItemsBitIfNeeded(ChildListID aListID,nsIFrame * aOldFrame)2506 void nsContainerFrame::SetDidPushItemsBitIfNeeded(ChildListID aListID,
2507 nsIFrame* aOldFrame) {
2508 MOZ_ASSERT(IsFlexOrGridContainer(),
2509 "Only Flex / Grid containers can call this!");
2510
2511 // Note that kPrincipalList doesn't mean aOldFrame must be on that list.
2512 // It can also be on kOverflowList, in which case it might be a pushed
2513 // item, and if it's the only pushed item our DID_PUSH_ITEMS bit will lie.
2514 if (aListID == kPrincipalList && !aOldFrame->GetPrevInFlow()) {
2515 // Since the bit may lie, set the mDidPushItemsBitMayLie value to true for
2516 // ourself and for all our prev-in-flows.
2517 nsContainerFrame* frameThatMayLie = this;
2518 do {
2519 frameThatMayLie->mDidPushItemsBitMayLie = true;
2520 frameThatMayLie =
2521 static_cast<nsContainerFrame*>(frameThatMayLie->GetPrevInFlow());
2522 } while (frameThatMayLie);
2523 }
2524 }
2525 #endif
2526
2527 #ifdef DEBUG_FRAME_DUMP
List(FILE * out,const char * aPrefix,ListFlags aFlags) const2528 void nsContainerFrame::List(FILE* out, const char* aPrefix,
2529 ListFlags aFlags) const {
2530 nsCString str;
2531 ListGeneric(str, aPrefix, aFlags);
2532 ExtraContainerFrameInfo(str);
2533
2534 // Output the children
2535 bool outputOneList = false;
2536 for (const auto& [list, listID] : ChildLists()) {
2537 if (outputOneList) {
2538 str += aPrefix;
2539 }
2540 if (listID != kPrincipalList) {
2541 if (!outputOneList) {
2542 str += "\n";
2543 str += aPrefix;
2544 }
2545 str += nsPrintfCString("%s %p ", mozilla::layout::ChildListName(listID),
2546 &GetChildList(listID));
2547 }
2548 fprintf_stderr(out, "%s<\n", str.get());
2549 str = "";
2550 for (nsIFrame* kid : list) {
2551 // Verify the child frame's parent frame pointer is correct
2552 NS_ASSERTION(kid->GetParent() == this, "bad parent frame pointer");
2553
2554 // Have the child frame list
2555 nsCString pfx(aPrefix);
2556 pfx += " ";
2557 kid->List(out, pfx.get(), aFlags);
2558 }
2559 fprintf_stderr(out, "%s>\n", aPrefix);
2560 outputOneList = true;
2561 }
2562
2563 if (!outputOneList) {
2564 fprintf_stderr(out, "%s<>\n", str.get());
2565 }
2566 }
2567
ListWithMatchedRules(FILE * out,const char * aPrefix) const2568 void nsContainerFrame::ListWithMatchedRules(FILE* out,
2569 const char* aPrefix) const {
2570 fprintf_stderr(out, "%s%s\n", aPrefix, ListTag().get());
2571
2572 nsCString rulePrefix;
2573 rulePrefix += aPrefix;
2574 rulePrefix += " ";
2575 ListMatchedRules(out, rulePrefix.get());
2576
2577 nsCString childPrefix;
2578 childPrefix += aPrefix;
2579 childPrefix += " ";
2580
2581 for (const auto& childList : ChildLists()) {
2582 for (const nsIFrame* kid : childList.mList) {
2583 kid->ListWithMatchedRules(out, childPrefix.get());
2584 }
2585 }
2586 }
2587
ExtraContainerFrameInfo(nsACString & aTo) const2588 void nsContainerFrame::ExtraContainerFrameInfo(nsACString& aTo) const {
2589 (void)aTo;
2590 }
2591
2592 #endif
2593