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