Bumping manifests a=b2g-bump
[gecko.git] / layout / generic / nsContainerFrame.cpp
blob12291a380ae1aff3ee6645155f4c613e23b7ae60
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 /* base class #1 for rendering objects that have child lists */
8 #include "nsContainerFrame.h"
10 #include "nsAbsoluteContainingBlock.h"
11 #include "nsIDocument.h"
12 #include "nsPresContext.h"
13 #include "nsStyleContext.h"
14 #include "nsRect.h"
15 #include "nsPoint.h"
16 #include "nsStyleConsts.h"
17 #include "nsView.h"
18 #include "nsIPresShell.h"
19 #include "nsCOMPtr.h"
20 #include "nsGkAtoms.h"
21 #include "nsViewManager.h"
22 #include "nsIWidget.h"
23 #include "nsCSSRendering.h"
24 #include "nsError.h"
25 #include "nsDisplayList.h"
26 #include "nsIBaseWindow.h"
27 #include "nsBoxLayoutState.h"
28 #include "nsCSSFrameConstructor.h"
29 #include "nsBlockFrame.h"
30 #include "mozilla/AutoRestore.h"
31 #include "nsIFrameInlines.h"
32 #include "nsPrintfCString.h"
33 #include <algorithm>
35 #ifdef DEBUG
36 #undef NOISY
37 #else
38 #undef NOISY
39 #endif
41 using namespace mozilla;
42 using namespace mozilla::dom;
43 using namespace mozilla::layout;
45 NS_IMPL_FRAMEARENA_HELPERS(nsContainerFrame)
47 nsContainerFrame::~nsContainerFrame()
51 NS_QUERYFRAME_HEAD(nsContainerFrame)
52 NS_QUERYFRAME_ENTRY(nsContainerFrame)
53 NS_QUERYFRAME_TAIL_INHERITING(nsSplittableFrame)
55 void
56 nsContainerFrame::Init(nsIContent* aContent,
57 nsContainerFrame* aParent,
58 nsIFrame* aPrevInFlow)
60 nsSplittableFrame::Init(aContent, aParent, aPrevInFlow);
61 if (aPrevInFlow) {
62 // Make sure we copy bits from our prev-in-flow that will affect
63 // us. A continuation for a container frame needs to know if it
64 // has a child with a view so that we'll properly reposition it.
65 if (aPrevInFlow->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW)
66 AddStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW);
70 void
71 nsContainerFrame::SetInitialChildList(ChildListID aListID,
72 nsFrameList& aChildList)
74 MOZ_ASSERT(mFrames.IsEmpty(),
75 "unexpected second call to SetInitialChildList");
76 MOZ_ASSERT(aListID == kPrincipalList, "unexpected child list");
77 #ifdef DEBUG
78 nsFrame::VerifyDirtyBitSet(aChildList);
79 #endif
80 mFrames.SetFrames(aChildList);
83 void
84 nsContainerFrame::AppendFrames(ChildListID aListID,
85 nsFrameList& aFrameList)
87 MOZ_ASSERT(aListID == kPrincipalList || aListID == kNoReflowPrincipalList,
88 "unexpected child list");
89 if (aFrameList.NotEmpty()) {
90 mFrames.AppendFrames(this, aFrameList);
92 // Ask the parent frame to reflow me.
93 if (aListID == kPrincipalList)
95 PresContext()->PresShell()->
96 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
97 NS_FRAME_HAS_DIRTY_CHILDREN);
102 void
103 nsContainerFrame::InsertFrames(ChildListID aListID,
104 nsIFrame* aPrevFrame,
105 nsFrameList& aFrameList)
107 MOZ_ASSERT(aListID == kPrincipalList || aListID == kNoReflowPrincipalList,
108 "unexpected child list");
109 NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
110 "inserting after sibling frame with different parent");
112 if (aFrameList.NotEmpty()) {
113 // Insert frames after aPrevFrame
114 mFrames.InsertFrames(this, aPrevFrame, aFrameList);
116 if (aListID == kPrincipalList)
118 PresContext()->PresShell()->
119 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
120 NS_FRAME_HAS_DIRTY_CHILDREN);
125 void
126 nsContainerFrame::RemoveFrame(ChildListID aListID,
127 nsIFrame* aOldFrame)
129 MOZ_ASSERT(aListID == kPrincipalList || aListID == kNoReflowPrincipalList,
130 "unexpected child list");
132 // Loop and destroy aOldFrame and all of its continuations.
133 // Request a reflow on the parent frames involved unless we were explicitly
134 // told not to (kNoReflowPrincipalList).
135 bool generateReflowCommand = true;
136 if (kNoReflowPrincipalList == aListID) {
137 generateReflowCommand = false;
139 nsIPresShell* shell = PresContext()->PresShell();
140 nsContainerFrame* lastParent = nullptr;
141 while (aOldFrame) {
142 //XXXfr probably should use StealFrame here. I'm not sure if we need to
143 // check the overflow lists atm, but we'll need a prescontext lookup
144 // for overflow containers once we can split abspos elements with
145 // inline containing blocks.
146 nsIFrame* oldFrameNextContinuation = aOldFrame->GetNextContinuation();
147 nsContainerFrame* parent = aOldFrame->GetParent();
148 parent->StealFrame(aOldFrame, true);
149 aOldFrame->Destroy();
150 aOldFrame = oldFrameNextContinuation;
151 if (parent != lastParent && generateReflowCommand) {
152 shell->FrameNeedsReflow(parent, nsIPresShell::eTreeChange,
153 NS_FRAME_HAS_DIRTY_CHILDREN);
154 lastParent = parent;
159 void
160 nsContainerFrame::DestroyAbsoluteFrames(nsIFrame* aDestructRoot)
162 if (IsAbsoluteContainer()) {
163 GetAbsoluteContainingBlock()->DestroyFrames(this, aDestructRoot);
164 MarkAsNotAbsoluteContainingBlock();
168 void
169 nsContainerFrame::SafelyDestroyFrameListProp(nsIFrame* aDestructRoot,
170 nsIPresShell* aPresShell,
171 FramePropertyTable* aPropTable,
172 const FramePropertyDescriptor* aProp)
174 // Note that the last frame can be removed through another route and thus
175 // delete the property -- that's why we fetch the property again before
176 // removing each frame rather than fetching it once and iterating the list.
177 while (nsFrameList* frameList =
178 static_cast<nsFrameList*>(aPropTable->Get(this, aProp))) {
179 nsIFrame* frame = frameList->RemoveFirstChild();
180 if (MOZ_LIKELY(frame)) {
181 frame->DestroyFrom(aDestructRoot);
182 } else {
183 aPropTable->Remove(this, aProp);
184 frameList->Delete(aPresShell);
185 return;
190 void
191 nsContainerFrame::DestroyFrom(nsIFrame* aDestructRoot)
193 // Prevent event dispatch during destruction.
194 if (HasView()) {
195 GetView()->SetFrame(nullptr);
198 DestroyAbsoluteFrames(aDestructRoot);
200 // Destroy frames on the principal child list.
201 mFrames.DestroyFramesFrom(aDestructRoot);
203 // Destroy frames on the auxiliary frame lists and delete the lists.
204 nsPresContext* pc = PresContext();
205 nsIPresShell* shell = pc->PresShell();
206 FramePropertyTable* props = pc->PropertyTable();
207 SafelyDestroyFrameListProp(aDestructRoot, shell, props, OverflowProperty());
209 MOZ_ASSERT(IsFrameOfType(nsIFrame::eCanContainOverflowContainers) ||
210 !(props->Get(this, nsContainerFrame::OverflowContainersProperty()) ||
211 props->Get(this, nsContainerFrame::ExcessOverflowContainersProperty())),
212 "this type of frame should't have overflow containers");
214 SafelyDestroyFrameListProp(aDestructRoot, shell, props,
215 OverflowContainersProperty());
216 SafelyDestroyFrameListProp(aDestructRoot, shell, props,
217 ExcessOverflowContainersProperty());
219 nsSplittableFrame::DestroyFrom(aDestructRoot);
222 /////////////////////////////////////////////////////////////////////////////
223 // Child frame enumeration
225 const nsFrameList&
226 nsContainerFrame::GetChildList(ChildListID aListID) const
228 // We only know about the principal child list and the overflow lists.
229 switch (aListID) {
230 case kPrincipalList:
231 return mFrames;
232 case kOverflowList: {
233 nsFrameList* list = GetOverflowFrames();
234 return list ? *list : nsFrameList::EmptyList();
236 case kOverflowContainersList: {
237 nsFrameList* list = GetPropTableFrames(OverflowContainersProperty());
238 return list ? *list : nsFrameList::EmptyList();
240 case kExcessOverflowContainersList: {
241 nsFrameList* list =
242 GetPropTableFrames(ExcessOverflowContainersProperty());
243 return list ? *list : nsFrameList::EmptyList();
245 default:
246 return nsSplittableFrame::GetChildList(aListID);
250 static void AppendIfNonempty(const nsIFrame* aFrame,
251 FramePropertyTable* aPropTable,
252 const FramePropertyDescriptor* aProperty,
253 nsTArray<nsIFrame::ChildList>* aLists,
254 nsIFrame::ChildListID aListID)
256 nsFrameList* list = static_cast<nsFrameList*>(
257 aPropTable->Get(aFrame, aProperty));
258 if (list) {
259 list->AppendIfNonempty(aLists, aListID);
263 void
264 nsContainerFrame::GetChildLists(nsTArray<ChildList>* aLists) const
266 mFrames.AppendIfNonempty(aLists, kPrincipalList);
267 FramePropertyTable* propTable = PresContext()->PropertyTable();
268 ::AppendIfNonempty(this, propTable, OverflowProperty(),
269 aLists, kOverflowList);
270 if (IsFrameOfType(nsIFrame::eCanContainOverflowContainers)) {
271 ::AppendIfNonempty(this, propTable, OverflowContainersProperty(),
272 aLists, kOverflowContainersList);
273 ::AppendIfNonempty(this, propTable, ExcessOverflowContainersProperty(),
274 aLists, kExcessOverflowContainersList);
276 nsSplittableFrame::GetChildLists(aLists);
279 /////////////////////////////////////////////////////////////////////////////
280 // Painting/Events
282 void
283 nsContainerFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
284 const nsRect& aDirtyRect,
285 const nsDisplayListSet& aLists)
287 DisplayBorderBackgroundOutline(aBuilder, aLists);
289 BuildDisplayListForNonBlockChildren(aBuilder, aDirtyRect, aLists);
292 void
293 nsContainerFrame::BuildDisplayListForNonBlockChildren(nsDisplayListBuilder* aBuilder,
294 const nsRect& aDirtyRect,
295 const nsDisplayListSet& aLists,
296 uint32_t aFlags)
298 nsIFrame* kid = mFrames.FirstChild();
299 // Put each child's background directly onto the content list
300 nsDisplayListSet set(aLists, aLists.Content());
301 // The children should be in content order
302 while (kid) {
303 BuildDisplayListForChild(aBuilder, kid, aDirtyRect, set, aFlags);
304 kid = kid->GetNextSibling();
308 /* virtual */ void
309 nsContainerFrame::ChildIsDirty(nsIFrame* aChild)
311 NS_ASSERTION(NS_SUBTREE_DIRTY(aChild), "child isn't actually dirty");
313 AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
316 bool
317 nsContainerFrame::IsLeaf() const
319 return false;
322 nsIFrame::FrameSearchResult
323 nsContainerFrame::PeekOffsetNoAmount(bool aForward, int32_t* aOffset)
325 NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range");
326 // Don't allow the caret to stay in an empty (leaf) container frame.
327 return CONTINUE_EMPTY;
330 nsIFrame::FrameSearchResult
331 nsContainerFrame::PeekOffsetCharacter(bool aForward, int32_t* aOffset,
332 bool aRespectClusters)
334 NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range");
335 // Don't allow the caret to stay in an empty (leaf) container frame.
336 return CONTINUE_EMPTY;
339 /////////////////////////////////////////////////////////////////////////////
340 // Helper member functions
342 static nsresult
343 ReparentFrameViewTo(nsIFrame* aFrame,
344 nsViewManager* aViewManager,
345 nsView* aNewParentView,
346 nsView* aOldParentView)
349 // XXX What to do about placeholder views for "position: fixed" elements?
350 // They should be reparented too.
352 // Does aFrame have a view?
353 if (aFrame->HasView()) {
354 #ifdef MOZ_XUL
355 if (aFrame->GetType() == nsGkAtoms::menuPopupFrame) {
356 // This view must be parented by the root view, don't reparent it.
357 return NS_OK;
359 #endif
360 nsView* view = aFrame->GetView();
361 // Verify that the current parent view is what we think it is
362 //nsView* parentView;
363 //NS_ASSERTION(parentView == aOldParentView, "unexpected parent view");
365 aViewManager->RemoveChild(view);
367 // The view will remember the Z-order and other attributes that have been set on it.
368 nsView* insertBefore = nsLayoutUtils::FindSiblingViewFor(aNewParentView, aFrame);
369 aViewManager->InsertChild(aNewParentView, view, insertBefore, insertBefore != nullptr);
370 } else {
371 nsIFrame::ChildListIterator lists(aFrame);
372 for (; !lists.IsDone(); lists.Next()) {
373 // Iterate the child frames, and check each child frame to see if it has
374 // a view
375 nsFrameList::Enumerator childFrames(lists.CurrentList());
376 for (; !childFrames.AtEnd(); childFrames.Next()) {
377 ReparentFrameViewTo(childFrames.get(), aViewManager,
378 aNewParentView, aOldParentView);
383 return NS_OK;
386 void
387 nsContainerFrame::CreateViewForFrame(nsIFrame* aFrame,
388 bool aForce)
390 if (aFrame->HasView()) {
391 return;
394 // If we don't yet have a view, see if we need a view
395 if (!aForce && !aFrame->NeedsView()) {
396 // don't need a view
397 return;
400 nsView* parentView = aFrame->GetParent()->GetClosestView();
401 NS_ASSERTION(parentView, "no parent with view");
403 nsViewManager* viewManager = parentView->GetViewManager();
404 NS_ASSERTION(viewManager, "null view manager");
406 // Create a view
407 nsView* view = viewManager->CreateView(aFrame->GetRect(), parentView);
409 SyncFrameViewProperties(aFrame->PresContext(), aFrame, nullptr, view);
411 nsView* insertBefore = nsLayoutUtils::FindSiblingViewFor(parentView, aFrame);
412 // we insert this view 'above' the insertBefore view, unless insertBefore is null,
413 // in which case we want to call with aAbove == false to insert at the beginning
414 // in document order
415 viewManager->InsertChild(parentView, view, insertBefore, insertBefore != nullptr);
417 // REVIEW: Don't create a widget for fixed-pos elements anymore.
418 // ComputeRepaintRegionForCopy will calculate the right area to repaint
419 // when we scroll.
420 // Reparent views on any child frames (or their descendants) to this
421 // view. We can just call ReparentFrameViewTo on this frame because
422 // we know this frame has no view, so it will crawl the children. Also,
423 // we know that any descendants with views must have 'parentView' as their
424 // parent view.
425 ReparentFrameViewTo(aFrame, viewManager, view, parentView);
427 // Remember our view
428 aFrame->SetView(view);
430 NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
431 ("nsContainerFrame::CreateViewForFrame: frame=%p view=%p",
432 aFrame));
436 * Position the view associated with |aKidFrame|, if there is one. A
437 * container frame should call this method after positioning a frame,
438 * but before |Reflow|.
440 void
441 nsContainerFrame::PositionFrameView(nsIFrame* aKidFrame)
443 nsIFrame* parentFrame = aKidFrame->GetParent();
444 if (!aKidFrame->HasView() || !parentFrame)
445 return;
447 nsView* view = aKidFrame->GetView();
448 nsViewManager* vm = view->GetViewManager();
449 nsPoint pt;
450 nsView* ancestorView = parentFrame->GetClosestView(&pt);
452 if (ancestorView != view->GetParent()) {
453 NS_ASSERTION(ancestorView == view->GetParent()->GetParent(),
454 "Allowed only one anonymous view between frames");
455 // parentFrame is responsible for positioning aKidFrame's view
456 // explicitly
457 return;
460 pt += aKidFrame->GetPosition();
461 vm->MoveViewTo(view, pt.x, pt.y);
464 nsresult
465 nsContainerFrame::ReparentFrameView(nsIFrame* aChildFrame,
466 nsIFrame* aOldParentFrame,
467 nsIFrame* aNewParentFrame)
469 NS_PRECONDITION(aChildFrame, "null child frame pointer");
470 NS_PRECONDITION(aOldParentFrame, "null old parent frame pointer");
471 NS_PRECONDITION(aNewParentFrame, "null new parent frame pointer");
472 NS_PRECONDITION(aOldParentFrame != aNewParentFrame, "same old and new parent frame");
474 // See if either the old parent frame or the new parent frame have a view
475 while (!aOldParentFrame->HasView() && !aNewParentFrame->HasView()) {
476 // Walk up both the old parent frame and the new parent frame nodes
477 // stopping when we either find a common parent or views for one
478 // or both of the frames.
480 // This works well in the common case where we push/pull and the old parent
481 // frame and the new parent frame are part of the same flow. They will
482 // typically be the same distance (height wise) from the
483 aOldParentFrame = aOldParentFrame->GetParent();
484 aNewParentFrame = aNewParentFrame->GetParent();
486 // We should never walk all the way to the root frame without finding
487 // a view
488 NS_ASSERTION(aOldParentFrame && aNewParentFrame, "didn't find view");
490 // See if we reached a common ancestor
491 if (aOldParentFrame == aNewParentFrame) {
492 break;
496 // See if we found a common parent frame
497 if (aOldParentFrame == aNewParentFrame) {
498 // We found a common parent and there are no views between the old parent
499 // and the common parent or the new parent frame and the common parent.
500 // Because neither the old parent frame nor the new parent frame have views,
501 // then any child views don't need reparenting
502 return NS_OK;
505 // We found views for one or both of the ancestor frames before we
506 // found a common ancestor.
507 nsView* oldParentView = aOldParentFrame->GetClosestView();
508 nsView* newParentView = aNewParentFrame->GetClosestView();
510 // See if the old parent frame and the new parent frame are in the
511 // same view sub-hierarchy. If they are then we don't have to do
512 // anything
513 if (oldParentView != newParentView) {
514 // They're not so we need to reparent any child views
515 return ReparentFrameViewTo(aChildFrame, oldParentView->GetViewManager(), newParentView,
516 oldParentView);
519 return NS_OK;
522 nsresult
523 nsContainerFrame::ReparentFrameViewList(const nsFrameList& aChildFrameList,
524 nsIFrame* aOldParentFrame,
525 nsIFrame* aNewParentFrame)
527 NS_PRECONDITION(aChildFrameList.NotEmpty(), "empty child frame list");
528 NS_PRECONDITION(aOldParentFrame, "null old parent frame pointer");
529 NS_PRECONDITION(aNewParentFrame, "null new parent frame pointer");
530 NS_PRECONDITION(aOldParentFrame != aNewParentFrame, "same old and new parent frame");
532 // See if either the old parent frame or the new parent frame have a view
533 while (!aOldParentFrame->HasView() && !aNewParentFrame->HasView()) {
534 // Walk up both the old parent frame and the new parent frame nodes
535 // stopping when we either find a common parent or views for one
536 // or both of the frames.
538 // This works well in the common case where we push/pull and the old parent
539 // frame and the new parent frame are part of the same flow. They will
540 // typically be the same distance (height wise) from the
541 aOldParentFrame = aOldParentFrame->GetParent();
542 aNewParentFrame = aNewParentFrame->GetParent();
544 // We should never walk all the way to the root frame without finding
545 // a view
546 NS_ASSERTION(aOldParentFrame && aNewParentFrame, "didn't find view");
548 // See if we reached a common ancestor
549 if (aOldParentFrame == aNewParentFrame) {
550 break;
555 // See if we found a common parent frame
556 if (aOldParentFrame == aNewParentFrame) {
557 // We found a common parent and there are no views between the old parent
558 // and the common parent or the new parent frame and the common parent.
559 // Because neither the old parent frame nor the new parent frame have views,
560 // then any child views don't need reparenting
561 return NS_OK;
564 // We found views for one or both of the ancestor frames before we
565 // found a common ancestor.
566 nsView* oldParentView = aOldParentFrame->GetClosestView();
567 nsView* newParentView = aNewParentFrame->GetClosestView();
569 // See if the old parent frame and the new parent frame are in the
570 // same view sub-hierarchy. If they are then we don't have to do
571 // anything
572 if (oldParentView != newParentView) {
573 nsViewManager* viewManager = oldParentView->GetViewManager();
575 // They're not so we need to reparent any child views
576 for (nsFrameList::Enumerator e(aChildFrameList); !e.AtEnd(); e.Next()) {
577 ReparentFrameViewTo(e.get(), viewManager, newParentView, oldParentView);
581 return NS_OK;
584 static nsIWidget*
585 GetPresContextContainerWidget(nsPresContext* aPresContext)
587 nsCOMPtr<nsISupports> container = aPresContext->Document()->GetContainer();
588 nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(container);
589 if (!baseWindow)
590 return nullptr;
592 nsCOMPtr<nsIWidget> mainWidget;
593 baseWindow->GetMainWidget(getter_AddRefs(mainWidget));
594 return mainWidget;
597 static bool
598 IsTopLevelWidget(nsIWidget* aWidget)
600 nsWindowType windowType = aWidget->WindowType();
601 return windowType == eWindowType_toplevel ||
602 windowType == eWindowType_dialog ||
603 windowType == eWindowType_sheet;
604 // popups aren't toplevel so they're not handled here
607 void
608 nsContainerFrame::SyncWindowProperties(nsPresContext* aPresContext,
609 nsIFrame* aFrame,
610 nsView* aView,
611 nsRenderingContext* aRC)
613 #ifdef MOZ_XUL
614 if (!aView || !nsCSSRendering::IsCanvasFrame(aFrame) || !aView->HasWidget())
615 return;
617 nsIWidget* windowWidget = GetPresContextContainerWidget(aPresContext);
618 if (!windowWidget || !IsTopLevelWidget(windowWidget))
619 return;
621 nsViewManager* vm = aView->GetViewManager();
622 nsView* rootView = vm->GetRootView();
624 if (aView != rootView)
625 return;
627 Element* rootElement = aPresContext->Document()->GetRootElement();
628 if (!rootElement || !rootElement->IsXUL()) {
629 // Scrollframes use native widgets which don't work well with
630 // translucent windows, at least in Windows XP. So if the document
631 // has a root scrollrame it's useless to try to make it transparent,
632 // we'll just get something broken.
633 // nsCSSFrameConstructor::ConstructRootFrame constructs root
634 // scrollframes whenever the root element is not a XUL element, so
635 // we test for that here. We can't just call
636 // presShell->GetRootScrollFrame() since that might not have
637 // been constructed yet.
638 // We can change this to allow translucent toplevel HTML documents
639 // (e.g. to do something like Dashboard widgets), once we
640 // have broad support for translucent scrolled documents, but be
641 // careful because apparently some Firefox extensions expect
642 // openDialog("something.html") to produce an opaque window
643 // even if the HTML doesn't have a background-color set.
644 return;
647 nsIFrame *rootFrame = aPresContext->PresShell()->FrameConstructor()->GetRootElementStyleFrame();
648 if (!rootFrame)
649 return;
651 nsTransparencyMode mode = nsLayoutUtils::GetFrameTransparency(aFrame, rootFrame);
652 nsIWidget* viewWidget = aView->GetWidget();
653 viewWidget->SetTransparencyMode(mode);
654 windowWidget->SetWindowShadowStyle(rootFrame->StyleUIReset()->mWindowShadow);
656 if (!aRC)
657 return;
659 nsBoxLayoutState aState(aPresContext, aRC);
660 nsSize minSize = rootFrame->GetMinSize(aState);
661 nsSize maxSize = rootFrame->GetMaxSize(aState);
663 SetSizeConstraints(aPresContext, windowWidget, minSize, maxSize);
664 #endif
667 void nsContainerFrame::SetSizeConstraints(nsPresContext* aPresContext,
668 nsIWidget* aWidget,
669 const nsSize& aMinSize,
670 const nsSize& aMaxSize)
672 nsIntSize devMinSize(aPresContext->AppUnitsToDevPixels(aMinSize.width),
673 aPresContext->AppUnitsToDevPixels(aMinSize.height));
674 nsIntSize devMaxSize(aMaxSize.width == NS_INTRINSICSIZE ? NS_MAXSIZE :
675 aPresContext->AppUnitsToDevPixels(aMaxSize.width),
676 aMaxSize.height == NS_INTRINSICSIZE ? NS_MAXSIZE :
677 aPresContext->AppUnitsToDevPixels(aMaxSize.height));
679 // MinSize has a priority over MaxSize
680 if (devMinSize.width > devMaxSize.width)
681 devMaxSize.width = devMinSize.width;
682 if (devMinSize.height > devMaxSize.height)
683 devMaxSize.height = devMinSize.height;
685 widget::SizeConstraints constraints(devMinSize, devMaxSize);
687 // The sizes are in inner window sizes, so convert them into outer window sizes.
688 // Use a size of (200, 200) as only the difference between the inner and outer
689 // size is needed.
690 nsIntSize windowSize = aWidget->ClientToWindowSize(nsIntSize(200, 200));
691 if (constraints.mMinSize.width)
692 constraints.mMinSize.width += windowSize.width - 200;
693 if (constraints.mMinSize.height)
694 constraints.mMinSize.height += windowSize.height - 200;
695 if (constraints.mMaxSize.width != NS_MAXSIZE)
696 constraints.mMaxSize.width += windowSize.width - 200;
697 if (constraints.mMaxSize.height != NS_MAXSIZE)
698 constraints.mMaxSize.height += windowSize.height - 200;
700 aWidget->SetSizeConstraints(constraints);
703 void
704 nsContainerFrame::SyncFrameViewAfterReflow(nsPresContext* aPresContext,
705 nsIFrame* aFrame,
706 nsView* aView,
707 const nsRect& aVisualOverflowArea,
708 uint32_t aFlags)
710 if (!aView) {
711 return;
714 // Make sure the view is sized and positioned correctly
715 if (0 == (aFlags & NS_FRAME_NO_MOVE_VIEW)) {
716 PositionFrameView(aFrame);
719 if (0 == (aFlags & NS_FRAME_NO_SIZE_VIEW)) {
720 nsViewManager* vm = aView->GetViewManager();
722 vm->ResizeView(aView, aVisualOverflowArea, true);
726 void
727 nsContainerFrame::SyncFrameViewProperties(nsPresContext* aPresContext,
728 nsIFrame* aFrame,
729 nsStyleContext* aStyleContext,
730 nsView* aView,
731 uint32_t aFlags)
733 NS_ASSERTION(!aStyleContext || aFrame->StyleContext() == aStyleContext,
734 "Wrong style context for frame?");
736 if (!aView) {
737 return;
740 nsViewManager* vm = aView->GetViewManager();
742 if (nullptr == aStyleContext) {
743 aStyleContext = aFrame->StyleContext();
746 // Make sure visibility is correct. This only affects nsSubdocumentFrame.
747 if (0 == (aFlags & NS_FRAME_NO_VISIBILITY) &&
748 !aFrame->SupportsVisibilityHidden()) {
749 // See if the view should be hidden or visible
750 vm->SetViewVisibility(aView,
751 aStyleContext->StyleVisibility()->IsVisible()
752 ? nsViewVisibility_kShow : nsViewVisibility_kHide);
755 // See if the frame is being relatively positioned or absolutely
756 // positioned
757 bool isPositioned = aFrame->IsPositioned();
759 int32_t zIndex = 0;
760 bool autoZIndex = false;
762 if (!isPositioned) {
763 autoZIndex = true;
764 } else {
765 // Make sure z-index is correct
766 const nsStylePosition* position = aStyleContext->StylePosition();
768 if (position->mZIndex.GetUnit() == eStyleUnit_Integer) {
769 zIndex = position->mZIndex.GetIntValue();
770 } else if (position->mZIndex.GetUnit() == eStyleUnit_Auto) {
771 autoZIndex = true;
775 vm->SetViewZIndex(aView, autoZIndex, zIndex);
778 static nscoord GetCoord(const nsStyleCoord& aCoord, nscoord aIfNotCoord)
780 if (aCoord.ConvertsToLength()) {
781 return nsRuleNode::ComputeCoordPercentCalc(aCoord, 0);
783 return aIfNotCoord;
786 void
787 nsContainerFrame::DoInlineIntrinsicISize(nsRenderingContext *aRenderingContext,
788 InlineIntrinsicISizeData *aData,
789 nsLayoutUtils::IntrinsicISizeType aType)
791 if (GetPrevInFlow())
792 return; // Already added.
794 NS_PRECONDITION(aType == nsLayoutUtils::MIN_ISIZE ||
795 aType == nsLayoutUtils::PREF_ISIZE, "bad type");
797 mozilla::css::Side startSide, endSide;
798 if (StyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR) {
799 startSide = NS_SIDE_LEFT;
800 endSide = NS_SIDE_RIGHT;
801 } else {
802 startSide = NS_SIDE_RIGHT;
803 endSide = NS_SIDE_LEFT;
806 const nsStylePadding *stylePadding = StylePadding();
807 const nsStyleBorder *styleBorder = StyleBorder();
808 const nsStyleMargin *styleMargin = StyleMargin();
810 // This goes at the beginning no matter how things are broken and how
811 // messy the bidi situations are, since per CSS2.1 section 8.6
812 // (implemented in bug 328168), the startSide border is always on the
813 // first line.
814 // This frame is a first-in-flow, but it might have a previous bidi
815 // continuation, in which case that continuation should handle the startSide
816 // border.
817 // For box-decoration-break:clone we setup clonePBM = startPBM + endPBM and
818 // add that to each line. For box-decoration-break:slice clonePBM is zero.
819 nscoord clonePBM = 0; // PBM = PaddingBorderMargin
820 const bool sliceBreak =
821 styleBorder->mBoxDecorationBreak == NS_STYLE_BOX_DECORATION_BREAK_SLICE;
822 if (!GetPrevContinuation()) {
823 nscoord startPBM =
824 // clamp negative calc() to 0
825 std::max(GetCoord(stylePadding->mPadding.Get(startSide), 0), 0) +
826 styleBorder->GetComputedBorderWidth(startSide) +
827 GetCoord(styleMargin->mMargin.Get(startSide), 0);
828 if (MOZ_LIKELY(sliceBreak)) {
829 aData->currentLine += startPBM;
830 } else {
831 clonePBM = startPBM;
835 nscoord endPBM =
836 // clamp negative calc() to 0
837 std::max(GetCoord(stylePadding->mPadding.Get(endSide), 0), 0) +
838 styleBorder->GetComputedBorderWidth(endSide) +
839 GetCoord(styleMargin->mMargin.Get(endSide), 0);
840 if (MOZ_UNLIKELY(!sliceBreak)) {
841 clonePBM += endPBM;
844 const nsLineList_iterator* savedLine = aData->line;
845 nsIFrame* const savedLineContainer = aData->lineContainer;
847 nsContainerFrame *lastInFlow;
848 for (nsContainerFrame *nif = this; nif;
849 nif = static_cast<nsContainerFrame*>(nif->GetNextInFlow())) {
850 if (aData->currentLine == 0) {
851 aData->currentLine = clonePBM;
853 for (nsIFrame *kid = nif->mFrames.FirstChild(); kid;
854 kid = kid->GetNextSibling()) {
855 if (aType == nsLayoutUtils::MIN_ISIZE)
856 kid->AddInlineMinISize(aRenderingContext,
857 static_cast<InlineMinISizeData*>(aData));
858 else
859 kid->AddInlinePrefISize(aRenderingContext,
860 static_cast<InlinePrefISizeData*>(aData));
863 // After we advance to our next-in-flow, the stored line and line container
864 // may no longer be correct. Just forget them.
865 aData->line = nullptr;
866 aData->lineContainer = nullptr;
868 lastInFlow = nif;
871 aData->line = savedLine;
872 aData->lineContainer = savedLineContainer;
874 // This goes at the end no matter how things are broken and how
875 // messy the bidi situations are, since per CSS2.1 section 8.6
876 // (implemented in bug 328168), the endSide border is always on the
877 // last line.
878 // We reached the last-in-flow, but it might have a next bidi
879 // continuation, in which case that continuation should handle
880 // the endSide border.
881 if (MOZ_LIKELY(!lastInFlow->GetNextContinuation() && sliceBreak)) {
882 aData->currentLine += endPBM;
886 /* virtual */
887 LogicalSize
888 nsContainerFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext,
889 WritingMode aWM,
890 const LogicalSize& aCBSize,
891 nscoord aAvailableISize,
892 const LogicalSize& aMargin,
893 const LogicalSize& aBorder,
894 const LogicalSize& aPadding,
895 bool aShrinkWrap)
897 LogicalSize result(aWM, 0xdeadbeef, NS_UNCONSTRAINEDSIZE);
898 nscoord availBased = aAvailableISize - aMargin.ISize(aWM) -
899 aBorder.ISize(aWM) - aPadding.ISize(aWM);
900 // replaced elements always shrink-wrap
901 if (aShrinkWrap || IsFrameOfType(eReplaced)) {
902 // don't bother setting it if the result won't be used
903 const nsStyleCoord& inlineStyleCoord =
904 aWM.IsVertical() ? StylePosition()->mHeight : StylePosition()->mWidth;
905 if (inlineStyleCoord.GetUnit() == eStyleUnit_Auto) {
906 result.ISize(aWM) = ShrinkWidthToFit(aRenderingContext, availBased);
908 } else {
909 result.ISize(aWM) = availBased;
911 return result;
915 * Invokes the WillReflow() function, positions the frame and its view (if
916 * requested), and then calls Reflow(). If the reflow succeeds and the child
917 * frame is complete, deletes any next-in-flows using DeleteNextInFlowChild()
919 void
920 nsContainerFrame::ReflowChild(nsIFrame* aKidFrame,
921 nsPresContext* aPresContext,
922 nsHTMLReflowMetrics& aDesiredSize,
923 const nsHTMLReflowState& aReflowState,
924 nscoord aX,
925 nscoord aY,
926 uint32_t aFlags,
927 nsReflowStatus& aStatus,
928 nsOverflowContinuationTracker* aTracker)
930 NS_PRECONDITION(aReflowState.frame == aKidFrame, "bad reflow state");
932 // Send the WillReflow() notification, and position the child frame
933 // and its view if requested
934 aKidFrame->WillReflow(aPresContext);
936 if (NS_FRAME_NO_MOVE_FRAME != (aFlags & NS_FRAME_NO_MOVE_FRAME)) {
937 aKidFrame->SetPosition(nsPoint(aX, aY));
940 if (0 == (aFlags & NS_FRAME_NO_MOVE_VIEW)) {
941 PositionFrameView(aKidFrame);
944 // Reflow the child frame
945 aKidFrame->Reflow(aPresContext, aDesiredSize, aReflowState, aStatus);
947 // If the child frame is complete, delete any next-in-flows,
948 // but only if the NO_DELETE_NEXT_IN_FLOW flag isn't set.
949 if (NS_FRAME_IS_FULLY_COMPLETE(aStatus) &&
950 !(aFlags & NS_FRAME_NO_DELETE_NEXT_IN_FLOW_CHILD)) {
951 nsIFrame* kidNextInFlow = aKidFrame->GetNextInFlow();
952 if (kidNextInFlow) {
953 // Remove all of the childs next-in-flows. Make sure that we ask
954 // the right parent to do the removal (it's possible that the
955 // parent is not this because we are executing pullup code)
956 nsOverflowContinuationTracker::AutoFinish fini(aTracker, aKidFrame);
957 kidNextInFlow->GetParent()->DeleteNextInFlowChild(kidNextInFlow, true);
964 * Position the views of |aFrame|'s descendants. A container frame
965 * should call this method if it moves a frame after |Reflow|.
967 void
968 nsContainerFrame::PositionChildViews(nsIFrame* aFrame)
970 if (!(aFrame->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW)) {
971 return;
974 // Recursively walk aFrame's child frames.
975 // Process the additional child lists, but skip the popup list as the
976 // view for popups is managed by the parent. Currently only nsMenuFrame
977 // and nsPopupSetFrame have a popupList and during layout will adjust the
978 // view manually to position the popup.
979 ChildListIterator lists(aFrame);
980 for (; !lists.IsDone(); lists.Next()) {
981 if (lists.CurrentID() == kPopupList) {
982 continue;
984 nsFrameList::Enumerator childFrames(lists.CurrentList());
985 for (; !childFrames.AtEnd(); childFrames.Next()) {
986 // Position the frame's view (if it has one) otherwise recursively
987 // process its children
988 nsIFrame* childFrame = childFrames.get();
989 if (childFrame->HasView()) {
990 PositionFrameView(childFrame);
991 } else {
992 PositionChildViews(childFrame);
999 * The second half of frame reflow. Does the following:
1000 * - sets the frame's bounds
1001 * - sizes and positions (if requested) the frame's view. If the frame's final
1002 * position differs from the current position and the frame itself does not
1003 * have a view, then any child frames with views are positioned so they stay
1004 * in sync
1005 * - sets the view's visibility, opacity, content transparency, and clip
1006 * - invoked the DidReflow() function
1008 * Flags:
1009 * NS_FRAME_NO_MOVE_FRAME - don't move the frame. aX and aY are ignored in this
1010 * case. Also implies NS_FRAME_NO_MOVE_VIEW
1011 * NS_FRAME_NO_MOVE_VIEW - don't position the frame's view. Set this if you
1012 * don't want to automatically sync the frame and view
1013 * NS_FRAME_NO_SIZE_VIEW - don't size the frame's view
1015 void
1016 nsContainerFrame::FinishReflowChild(nsIFrame* aKidFrame,
1017 nsPresContext* aPresContext,
1018 const nsHTMLReflowMetrics& aDesiredSize,
1019 const nsHTMLReflowState* aReflowState,
1020 nscoord aX,
1021 nscoord aY,
1022 uint32_t aFlags)
1024 nsPoint curOrigin = aKidFrame->GetPosition();
1026 if (NS_FRAME_NO_MOVE_FRAME != (aFlags & NS_FRAME_NO_MOVE_FRAME)) {
1027 aKidFrame->SetRect(nsRect(aX, aY, aDesiredSize.Width(), aDesiredSize.Height()));
1028 } else {
1029 aKidFrame->SetSize(nsSize(aDesiredSize.Width(), aDesiredSize.Height()));
1032 if (aKidFrame->HasView()) {
1033 nsView* view = aKidFrame->GetView();
1034 // Make sure the frame's view is properly sized and positioned and has
1035 // things like opacity correct
1036 SyncFrameViewAfterReflow(aPresContext, aKidFrame, view,
1037 aDesiredSize.VisualOverflow(), aFlags);
1040 if (!(aFlags & NS_FRAME_NO_MOVE_VIEW) &&
1041 (curOrigin.x != aX || curOrigin.y != aY)) {
1042 if (!aKidFrame->HasView()) {
1043 // If the frame has moved, then we need to make sure any child views are
1044 // correctly positioned
1045 PositionChildViews(aKidFrame);
1049 aKidFrame->DidReflow(aPresContext, aReflowState, nsDidReflowStatus::FINISHED);
1052 void
1053 nsContainerFrame::ReflowOverflowContainerChildren(nsPresContext* aPresContext,
1054 const nsHTMLReflowState& aReflowState,
1055 nsOverflowAreas& aOverflowRects,
1056 uint32_t aFlags,
1057 nsReflowStatus& aStatus)
1059 NS_PRECONDITION(aPresContext, "null pointer");
1061 nsFrameList* overflowContainers =
1062 GetPropTableFrames(OverflowContainersProperty());
1064 NS_ASSERTION(!(overflowContainers && GetPrevInFlow()
1065 && static_cast<nsContainerFrame*>(GetPrevInFlow())
1066 ->GetPropTableFrames(ExcessOverflowContainersProperty())),
1067 "conflicting overflow containers lists");
1069 if (!overflowContainers) {
1070 // Drain excess from previnflow
1071 nsContainerFrame* prev = (nsContainerFrame*) GetPrevInFlow();
1072 if (prev) {
1073 nsFrameList* excessFrames =
1074 prev->RemovePropTableFrames(ExcessOverflowContainersProperty());
1075 if (excessFrames) {
1076 excessFrames->ApplySetParent(this);
1077 nsContainerFrame::ReparentFrameViewList(*excessFrames, prev, this);
1078 overflowContainers = excessFrames;
1079 SetPropTableFrames(overflowContainers, OverflowContainersProperty());
1084 // Our own excess overflow containers from a previous reflow can still be
1085 // present if our next-in-flow hasn't been reflown yet.
1086 nsFrameList* selfExcessOCFrames =
1087 RemovePropTableFrames(ExcessOverflowContainersProperty());
1088 if (selfExcessOCFrames) {
1089 if (overflowContainers) {
1090 overflowContainers->AppendFrames(nullptr, *selfExcessOCFrames);
1091 selfExcessOCFrames->Delete(aPresContext->PresShell());
1092 } else {
1093 overflowContainers = selfExcessOCFrames;
1094 SetPropTableFrames(overflowContainers, OverflowContainersProperty());
1097 if (!overflowContainers) {
1098 return; // nothing to reflow
1101 nsOverflowContinuationTracker tracker(this, false, false);
1102 bool shouldReflowAllKids = aReflowState.ShouldReflowAllKids();
1104 for (nsIFrame* frame = overflowContainers->FirstChild(); frame;
1105 frame = frame->GetNextSibling()) {
1106 if (frame->GetPrevInFlow()->GetParent() != GetPrevInFlow()) {
1107 // frame's prevInFlow has moved, skip reflowing this frame;
1108 // it will get reflowed once it's been placed
1109 continue;
1111 // If the available vertical height has changed, we need to reflow
1112 // even if the frame isn't dirty.
1113 if (shouldReflowAllKids || NS_SUBTREE_DIRTY(frame)) {
1114 // Get prev-in-flow
1115 nsIFrame* prevInFlow = frame->GetPrevInFlow();
1116 NS_ASSERTION(prevInFlow,
1117 "overflow container frame must have a prev-in-flow");
1118 NS_ASSERTION(frame->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER,
1119 "overflow container frame must have overflow container bit set");
1120 nsRect prevRect = prevInFlow->GetRect();
1122 // Initialize reflow params
1123 WritingMode wm = frame->GetWritingMode();
1124 LogicalSize availSpace(wm, LogicalSize(wm, prevRect.Size()).ISize(wm),
1125 aReflowState.AvailableSize(wm).BSize(wm));
1126 nsHTMLReflowMetrics desiredSize(aReflowState);
1127 nsHTMLReflowState frameState(aPresContext, aReflowState,
1128 frame, availSpace);
1129 nsReflowStatus frameStatus;
1131 // Reflow
1132 ReflowChild(frame, aPresContext, desiredSize, frameState,
1133 prevRect.x, 0, aFlags, frameStatus, &tracker);
1134 //XXXfr Do we need to override any shrinkwrap effects here?
1135 // e.g. desiredSize.Width() = prevRect.width;
1136 FinishReflowChild(frame, aPresContext, desiredSize, &frameState,
1137 prevRect.x, 0, aFlags);
1139 // Handle continuations
1140 if (!NS_FRAME_IS_FULLY_COMPLETE(frameStatus)) {
1141 if (frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
1142 // Abspos frames can't cause their parent to be incomplete,
1143 // only overflow incomplete.
1144 NS_FRAME_SET_OVERFLOW_INCOMPLETE(frameStatus);
1146 else {
1147 NS_ASSERTION(NS_FRAME_IS_COMPLETE(frameStatus),
1148 "overflow container frames can't be incomplete, only overflow-incomplete");
1151 // Acquire a next-in-flow, creating it if necessary
1152 nsIFrame* nif = frame->GetNextInFlow();
1153 if (!nif) {
1154 NS_ASSERTION(frameStatus & NS_FRAME_REFLOW_NEXTINFLOW,
1155 "Someone forgot a REFLOW_NEXTINFLOW flag");
1156 nif = aPresContext->PresShell()->FrameConstructor()->
1157 CreateContinuingFrame(aPresContext, frame, this);
1159 else if (!(nif->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) {
1160 // used to be a normal next-in-flow; steal it from the child list
1161 nsresult rv = nif->GetParent()->StealFrame(nif);
1162 if (NS_FAILED(rv)) {
1163 return;
1167 tracker.Insert(nif, frameStatus);
1169 NS_MergeReflowStatusInto(&aStatus, frameStatus);
1170 // At this point it would be nice to assert !frame->GetOverflowRect().IsEmpty(),
1171 // but we have some unsplittable frames that, when taller than
1172 // availableHeight will push zero-height content into a next-in-flow.
1174 else {
1175 tracker.Skip(frame, aStatus);
1176 if (aReflowState.mFloatManager)
1177 nsBlockFrame::RecoverFloatsFor(frame, *aReflowState.mFloatManager);
1179 ConsiderChildOverflow(aOverflowRects, frame);
1183 void
1184 nsContainerFrame::DisplayOverflowContainers(nsDisplayListBuilder* aBuilder,
1185 const nsRect& aDirtyRect,
1186 const nsDisplayListSet& aLists)
1188 nsFrameList* overflowconts = GetPropTableFrames(OverflowContainersProperty());
1189 if (overflowconts) {
1190 for (nsIFrame* frame = overflowconts->FirstChild(); frame;
1191 frame = frame->GetNextSibling()) {
1192 BuildDisplayListForChild(aBuilder, frame, aDirtyRect, aLists);
1197 static bool
1198 TryRemoveFrame(nsIFrame* aFrame, FramePropertyTable* aPropTable,
1199 const FramePropertyDescriptor* aProp, nsIFrame* aChildToRemove)
1201 nsFrameList* list = static_cast<nsFrameList*>(aPropTable->Get(aFrame, aProp));
1202 if (list && list->StartRemoveFrame(aChildToRemove)) {
1203 // aChildToRemove *may* have been removed from this list.
1204 if (list->IsEmpty()) {
1205 aPropTable->Remove(aFrame, aProp);
1206 list->Delete(aFrame->PresContext()->PresShell());
1208 return true;
1210 return false;
1213 nsresult
1214 nsContainerFrame::StealFrame(nsIFrame* aChild,
1215 bool aForceNormal)
1217 #ifdef DEBUG
1218 if (!mFrames.ContainsFrame(aChild)) {
1219 nsFrameList* list = GetOverflowFrames();
1220 if (!list || !list->ContainsFrame(aChild)) {
1221 FramePropertyTable* propTable = PresContext()->PropertyTable();
1222 list = static_cast<nsFrameList*>(
1223 propTable->Get(this, OverflowContainersProperty()));
1224 if (!list || !list->ContainsFrame(aChild)) {
1225 list = static_cast<nsFrameList*>(
1226 propTable->Get(this, ExcessOverflowContainersProperty()));
1227 MOZ_ASSERT(list && list->ContainsFrame(aChild), "aChild isn't our child"
1228 " or on a frame list not supported by StealFrame");
1232 #endif
1234 bool removed;
1235 if ((aChild->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)
1236 && !aForceNormal) {
1237 FramePropertyTable* propTable = PresContext()->PropertyTable();
1238 // Try removing from the overflow container list.
1239 removed = ::TryRemoveFrame(this, propTable, OverflowContainersProperty(),
1240 aChild);
1241 if (!removed) {
1242 // It must be in the excess overflow container list.
1243 removed = ::TryRemoveFrame(this, propTable,
1244 ExcessOverflowContainersProperty(),
1245 aChild);
1247 } else {
1248 removed = mFrames.StartRemoveFrame(aChild);
1249 if (!removed) {
1250 // We didn't find the child in our principal child list.
1251 // Maybe it's on the overflow list?
1252 nsFrameList* frameList = GetOverflowFrames();
1253 if (frameList) {
1254 removed = frameList->ContinueRemoveFrame(aChild);
1255 if (frameList->IsEmpty()) {
1256 DestroyOverflowList();
1262 NS_POSTCONDITION(removed, "StealFrame: can't find aChild");
1263 return removed ? NS_OK : NS_ERROR_UNEXPECTED;
1266 nsFrameList
1267 nsContainerFrame::StealFramesAfter(nsIFrame* aChild)
1269 NS_ASSERTION(!aChild ||
1270 !(aChild->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER),
1271 "StealFramesAfter doesn't handle overflow containers");
1272 NS_ASSERTION(GetType() != nsGkAtoms::blockFrame, "unexpected call");
1274 if (!aChild) {
1275 nsFrameList copy(mFrames);
1276 mFrames.Clear();
1277 return copy;
1280 for (nsFrameList::FrameLinkEnumerator iter(mFrames); !iter.AtEnd();
1281 iter.Next()) {
1282 if (iter.PrevFrame() == aChild) {
1283 return mFrames.ExtractTail(iter);
1287 // We didn't find the child in the principal child list.
1288 // Maybe it's on the overflow list?
1289 nsFrameList* overflowFrames = GetOverflowFrames();
1290 if (overflowFrames) {
1291 for (nsFrameList::FrameLinkEnumerator iter(*overflowFrames); !iter.AtEnd();
1292 iter.Next()) {
1293 if (iter.PrevFrame() == aChild) {
1294 return overflowFrames->ExtractTail(iter);
1299 NS_ERROR("StealFramesAfter: can't find aChild");
1300 return nsFrameList::EmptyList();
1304 * Create a next-in-flow for aFrame. Will return the newly created
1305 * frame in aNextInFlowResult <b>if and only if</b> a new frame is
1306 * created; otherwise nullptr is returned in aNextInFlowResult.
1308 nsresult
1309 nsContainerFrame::CreateNextInFlow(nsIFrame* aFrame,
1310 nsIFrame*& aNextInFlowResult)
1312 NS_PRECONDITION(GetType() != nsGkAtoms::blockFrame,
1313 "you should have called nsBlockFrame::CreateContinuationFor instead");
1314 NS_PRECONDITION(mFrames.ContainsFrame(aFrame), "expected an in-flow child frame");
1316 nsPresContext* pc = PresContext();
1317 aNextInFlowResult = nullptr;
1319 nsIFrame* nextInFlow = aFrame->GetNextInFlow();
1320 if (nullptr == nextInFlow) {
1321 // Create a continuation frame for the child frame and insert it
1322 // into our child list.
1323 nextInFlow = pc->PresShell()->FrameConstructor()->
1324 CreateContinuingFrame(pc, aFrame, this);
1325 mFrames.InsertFrame(nullptr, aFrame, nextInFlow);
1327 NS_FRAME_LOG(NS_FRAME_TRACE_NEW_FRAMES,
1328 ("nsContainerFrame::CreateNextInFlow: frame=%p nextInFlow=%p",
1329 aFrame, nextInFlow));
1331 aNextInFlowResult = nextInFlow;
1333 return NS_OK;
1337 * Remove and delete aNextInFlow and its next-in-flows. Updates the sibling and flow
1338 * pointers
1340 void
1341 nsContainerFrame::DeleteNextInFlowChild(nsIFrame* aNextInFlow,
1342 bool aDeletingEmptyFrames)
1344 #ifdef DEBUG
1345 nsIFrame* prevInFlow = aNextInFlow->GetPrevInFlow();
1346 #endif
1347 NS_PRECONDITION(prevInFlow, "bad prev-in-flow");
1349 // If the next-in-flow has a next-in-flow then delete it, too (and
1350 // delete it first).
1351 // Do this in a loop so we don't overflow the stack for frames
1352 // with very many next-in-flows
1353 nsIFrame* nextNextInFlow = aNextInFlow->GetNextInFlow();
1354 if (nextNextInFlow) {
1355 nsAutoTArray<nsIFrame*, 8> frames;
1356 for (nsIFrame* f = nextNextInFlow; f; f = f->GetNextInFlow()) {
1357 frames.AppendElement(f);
1359 for (int32_t i = frames.Length() - 1; i >= 0; --i) {
1360 nsIFrame* delFrame = frames.ElementAt(i);
1361 delFrame->GetParent()->
1362 DeleteNextInFlowChild(delFrame, aDeletingEmptyFrames);
1366 // Take the next-in-flow out of the parent's child list
1367 DebugOnly<nsresult> rv = StealFrame(aNextInFlow);
1368 NS_ASSERTION(NS_SUCCEEDED(rv), "StealFrame failure");
1370 #ifdef DEBUG
1371 if (aDeletingEmptyFrames) {
1372 nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(aNextInFlow);
1374 #endif
1376 // Delete the next-in-flow frame and its descendants. This will also
1377 // remove it from its next-in-flow/prev-in-flow chain.
1378 aNextInFlow->Destroy();
1380 NS_POSTCONDITION(!prevInFlow->GetNextInFlow(), "non null next-in-flow");
1384 * Set the frames on the overflow list
1386 void
1387 nsContainerFrame::SetOverflowFrames(const nsFrameList& aOverflowFrames)
1389 NS_PRECONDITION(aOverflowFrames.NotEmpty(), "Shouldn't be called");
1391 nsPresContext* pc = PresContext();
1392 nsFrameList* newList = new (pc->PresShell()) nsFrameList(aOverflowFrames);
1394 pc->PropertyTable()->Set(this, OverflowProperty(), newList);
1397 nsFrameList*
1398 nsContainerFrame::GetPropTableFrames(const FramePropertyDescriptor* aProperty) const
1400 FramePropertyTable* propTable = PresContext()->PropertyTable();
1401 return static_cast<nsFrameList*>(propTable->Get(this, aProperty));
1404 nsFrameList*
1405 nsContainerFrame::RemovePropTableFrames(const FramePropertyDescriptor* aProperty)
1407 FramePropertyTable* propTable = PresContext()->PropertyTable();
1408 return static_cast<nsFrameList*>(propTable->Remove(this, aProperty));
1411 void
1412 nsContainerFrame::SetPropTableFrames(nsFrameList* aFrameList,
1413 const FramePropertyDescriptor* aProperty)
1415 NS_PRECONDITION(aProperty && aFrameList, "null ptr");
1416 NS_PRECONDITION(
1417 (aProperty != nsContainerFrame::OverflowContainersProperty() &&
1418 aProperty != nsContainerFrame::ExcessOverflowContainersProperty()) ||
1419 IsFrameOfType(nsIFrame::eCanContainOverflowContainers),
1420 "this type of frame can't have overflow containers");
1421 MOZ_ASSERT(!GetPropTableFrames(aProperty));
1422 PresContext()->PropertyTable()->Set(this, aProperty, aFrameList);
1426 * Push aFromChild and its next siblings to the next-in-flow. Change the
1427 * geometric parent of each frame that's pushed. If there is no next-in-flow
1428 * the frames are placed on the overflow list (and the geometric parent is
1429 * left unchanged).
1431 * Updates the next-in-flow's child count. Does <b>not</b> update the
1432 * pusher's child count.
1434 * @param aFromChild the first child frame to push. It is disconnected from
1435 * aPrevSibling
1436 * @param aPrevSibling aFromChild's previous sibling. Must not be null. It's
1437 * an error to push a parent's first child frame
1439 void
1440 nsContainerFrame::PushChildren(nsIFrame* aFromChild,
1441 nsIFrame* aPrevSibling)
1443 NS_PRECONDITION(aFromChild, "null pointer");
1444 NS_PRECONDITION(aPrevSibling, "pushing first child");
1445 NS_PRECONDITION(aPrevSibling->GetNextSibling() == aFromChild, "bad prev sibling");
1447 // Disconnect aFromChild from its previous sibling
1448 nsFrameList tail = mFrames.RemoveFramesAfter(aPrevSibling);
1450 nsContainerFrame* nextInFlow =
1451 static_cast<nsContainerFrame*>(GetNextInFlow());
1452 if (nextInFlow) {
1453 // XXX This is not a very good thing to do. If it gets removed
1454 // then remove the copy of this routine that doesn't do this from
1455 // nsInlineFrame.
1456 // When pushing and pulling frames we need to check for whether any
1457 // views need to be reparented.
1458 for (nsIFrame* f = aFromChild; f; f = f->GetNextSibling()) {
1459 nsContainerFrame::ReparentFrameView(f, this, nextInFlow);
1461 nextInFlow->mFrames.InsertFrames(nextInFlow, nullptr, tail);
1463 else {
1464 // Add the frames to our overflow list
1465 SetOverflowFrames(tail);
1470 * Moves any frames on the overflow lists (the prev-in-flow's overflow list and
1471 * the receiver's overflow list) to the child list.
1473 * Updates this frame's child count and content mapping.
1475 * @return true if any frames were moved and false otherwise
1477 bool
1478 nsContainerFrame::MoveOverflowToChildList()
1480 bool result = false;
1482 // Check for an overflow list with our prev-in-flow
1483 nsContainerFrame* prevInFlow = (nsContainerFrame*)GetPrevInFlow();
1484 if (nullptr != prevInFlow) {
1485 AutoFrameListPtr prevOverflowFrames(PresContext(),
1486 prevInFlow->StealOverflowFrames());
1487 if (prevOverflowFrames) {
1488 // Tables are special; they can have repeated header/footer
1489 // frames on mFrames at this point.
1490 NS_ASSERTION(mFrames.IsEmpty() || GetType() == nsGkAtoms::tableFrame,
1491 "bad overflow list");
1492 // When pushing and pulling frames we need to check for whether any
1493 // views need to be reparented.
1494 nsContainerFrame::ReparentFrameViewList(*prevOverflowFrames,
1495 prevInFlow, this);
1496 mFrames.AppendFrames(this, *prevOverflowFrames);
1497 result = true;
1501 // It's also possible that we have an overflow list for ourselves.
1502 return DrainSelfOverflowList() || result;
1505 bool
1506 nsContainerFrame::DrainSelfOverflowList()
1508 AutoFrameListPtr overflowFrames(PresContext(), StealOverflowFrames());
1509 if (overflowFrames) {
1510 NS_ASSERTION(mFrames.NotEmpty(), "overflow list w/o frames");
1511 mFrames.AppendFrames(nullptr, *overflowFrames);
1512 return true;
1514 return false;
1517 nsOverflowContinuationTracker::nsOverflowContinuationTracker(nsContainerFrame* aFrame,
1518 bool aWalkOOFFrames,
1519 bool aSkipOverflowContainerChildren)
1520 : mOverflowContList(nullptr),
1521 mPrevOverflowCont(nullptr),
1522 mSentry(nullptr),
1523 mParent(aFrame),
1524 mSkipOverflowContainerChildren(aSkipOverflowContainerChildren),
1525 mWalkOOFFrames(aWalkOOFFrames)
1527 NS_PRECONDITION(aFrame, "null frame pointer");
1528 SetupOverflowContList();
1531 void
1532 nsOverflowContinuationTracker::SetupOverflowContList()
1534 NS_PRECONDITION(mParent, "null frame pointer");
1535 NS_PRECONDITION(!mOverflowContList, "already have list");
1536 nsContainerFrame* nif =
1537 static_cast<nsContainerFrame*>(mParent->GetNextInFlow());
1538 if (nif) {
1539 mOverflowContList = nif->GetPropTableFrames(
1540 nsContainerFrame::OverflowContainersProperty());
1541 if (mOverflowContList) {
1542 mParent = nif;
1543 SetUpListWalker();
1546 if (!mOverflowContList) {
1547 mOverflowContList = mParent->GetPropTableFrames(
1548 nsContainerFrame::ExcessOverflowContainersProperty());
1549 if (mOverflowContList) {
1550 SetUpListWalker();
1556 * Helper function to walk past overflow continuations whose prev-in-flow
1557 * isn't a normal child and to set mSentry and mPrevOverflowCont correctly.
1559 void
1560 nsOverflowContinuationTracker::SetUpListWalker()
1562 NS_ASSERTION(!mSentry && !mPrevOverflowCont,
1563 "forgot to reset mSentry or mPrevOverflowCont");
1564 if (mOverflowContList) {
1565 nsIFrame* cur = mOverflowContList->FirstChild();
1566 if (mSkipOverflowContainerChildren) {
1567 while (cur && (cur->GetPrevInFlow()->GetStateBits()
1568 & NS_FRAME_IS_OVERFLOW_CONTAINER)) {
1569 mPrevOverflowCont = cur;
1570 cur = cur->GetNextSibling();
1572 while (cur && (!(cur->GetStateBits() & NS_FRAME_OUT_OF_FLOW)
1573 == mWalkOOFFrames)) {
1574 mPrevOverflowCont = cur;
1575 cur = cur->GetNextSibling();
1578 if (cur) {
1579 mSentry = cur->GetPrevInFlow();
1585 * Helper function to step forward through the overflow continuations list.
1586 * Sets mSentry and mPrevOverflowCont, skipping over OOF or non-OOF frames
1587 * as appropriate. May only be called when we have already set up an
1588 * mOverflowContList; mOverflowContList cannot be null.
1590 void
1591 nsOverflowContinuationTracker::StepForward()
1593 NS_PRECONDITION(mOverflowContList, "null list");
1595 // Step forward
1596 if (mPrevOverflowCont) {
1597 mPrevOverflowCont = mPrevOverflowCont->GetNextSibling();
1599 else {
1600 mPrevOverflowCont = mOverflowContList->FirstChild();
1603 // Skip over oof or non-oof frames as appropriate
1604 if (mSkipOverflowContainerChildren) {
1605 nsIFrame* cur = mPrevOverflowCont->GetNextSibling();
1606 while (cur && (!(cur->GetStateBits() & NS_FRAME_OUT_OF_FLOW)
1607 == mWalkOOFFrames)) {
1608 mPrevOverflowCont = cur;
1609 cur = cur->GetNextSibling();
1613 // Set up the sentry
1614 mSentry = (mPrevOverflowCont->GetNextSibling())
1615 ? mPrevOverflowCont->GetNextSibling()->GetPrevInFlow()
1616 : nullptr;
1619 nsresult
1620 nsOverflowContinuationTracker::Insert(nsIFrame* aOverflowCont,
1621 nsReflowStatus& aReflowStatus)
1623 NS_PRECONDITION(aOverflowCont, "null frame pointer");
1624 NS_PRECONDITION(!mSkipOverflowContainerChildren || mWalkOOFFrames ==
1625 !!(aOverflowCont->GetStateBits() & NS_FRAME_OUT_OF_FLOW),
1626 "shouldn't insert frame that doesn't match walker type");
1627 NS_PRECONDITION(aOverflowCont->GetPrevInFlow(),
1628 "overflow containers must have a prev-in-flow");
1629 nsresult rv = NS_OK;
1630 bool reparented = false;
1631 nsPresContext* presContext = aOverflowCont->PresContext();
1632 bool addToList = !mSentry || aOverflowCont != mSentry->GetNextInFlow();
1634 // If we have a list and aOverflowCont is already in it then don't try to
1635 // add it again.
1636 if (addToList && aOverflowCont->GetParent() == mParent &&
1637 (aOverflowCont->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) &&
1638 mOverflowContList && mOverflowContList->ContainsFrame(aOverflowCont)) {
1639 addToList = false;
1640 mPrevOverflowCont = aOverflowCont->GetPrevSibling();
1643 if (addToList) {
1644 if (aOverflowCont->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) {
1645 // aOverflowCont is in some other overflow container list,
1646 // steal it first
1647 NS_ASSERTION(!(mOverflowContList &&
1648 mOverflowContList->ContainsFrame(aOverflowCont)),
1649 "overflow containers out of order");
1650 rv = aOverflowCont->GetParent()->StealFrame(aOverflowCont);
1651 NS_ENSURE_SUCCESS(rv, rv);
1653 else {
1654 aOverflowCont->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
1656 if (!mOverflowContList) {
1657 mOverflowContList = new (presContext->PresShell()) nsFrameList();
1658 mParent->SetPropTableFrames(mOverflowContList,
1659 nsContainerFrame::ExcessOverflowContainersProperty());
1660 SetUpListWalker();
1662 if (aOverflowCont->GetParent() != mParent) {
1663 nsContainerFrame::ReparentFrameView(aOverflowCont,
1664 aOverflowCont->GetParent(),
1665 mParent);
1666 reparented = true;
1669 // If aOverflowCont has a prev/next-in-flow that might be in
1670 // mOverflowContList we need to find it and insert after/before it to
1671 // maintain the order amongst next-in-flows in this list.
1672 nsIFrame* pif = aOverflowCont->GetPrevInFlow();
1673 nsIFrame* nif = aOverflowCont->GetNextInFlow();
1674 if ((pif && pif->GetParent() == mParent && pif != mPrevOverflowCont) ||
1675 (nif && nif->GetParent() == mParent && mPrevOverflowCont)) {
1676 for (nsFrameList::Enumerator e(*mOverflowContList); !e.AtEnd(); e.Next()) {
1677 nsIFrame* f = e.get();
1678 if (f == pif) {
1679 mPrevOverflowCont = pif;
1680 break;
1682 if (f == nif) {
1683 mPrevOverflowCont = f->GetPrevSibling();
1684 break;
1689 mOverflowContList->InsertFrame(mParent, mPrevOverflowCont, aOverflowCont);
1690 aReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
1693 // If we need to reflow it, mark it dirty
1694 if (aReflowStatus & NS_FRAME_REFLOW_NEXTINFLOW)
1695 aOverflowCont->AddStateBits(NS_FRAME_IS_DIRTY);
1697 // It's in our list, just step forward
1698 StepForward();
1699 NS_ASSERTION(mPrevOverflowCont == aOverflowCont ||
1700 (mSkipOverflowContainerChildren &&
1701 (mPrevOverflowCont->GetStateBits() & NS_FRAME_OUT_OF_FLOW) !=
1702 (aOverflowCont->GetStateBits() & NS_FRAME_OUT_OF_FLOW)),
1703 "OverflowContTracker in unexpected state");
1705 if (addToList) {
1706 // Convert all non-overflow-container continuations of aOverflowCont
1707 // into overflow containers and move them to our overflow
1708 // tracker. This preserves the invariant that the next-continuations
1709 // of an overflow container are also overflow containers.
1710 nsIFrame* f = aOverflowCont->GetNextContinuation();
1711 if (f && (!(f->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) ||
1712 (!reparented && f->GetParent() == mParent) ||
1713 (reparented && f->GetParent() != mParent))) {
1714 if (!(f->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) {
1715 rv = f->GetParent()->StealFrame(f);
1716 NS_ENSURE_SUCCESS(rv, rv);
1718 Insert(f, aReflowStatus);
1721 return rv;
1724 void
1725 nsOverflowContinuationTracker::BeginFinish(nsIFrame* aChild)
1727 NS_PRECONDITION(aChild, "null ptr");
1728 NS_PRECONDITION(aChild->GetNextInFlow(),
1729 "supposed to call Finish *before* deleting next-in-flow!");
1730 for (nsIFrame* f = aChild; f; f = f->GetNextInFlow()) {
1731 // We'll update these in EndFinish after the next-in-flows are gone.
1732 if (f == mPrevOverflowCont) {
1733 mSentry = nullptr;
1734 mPrevOverflowCont = nullptr;
1735 break;
1737 if (f == mSentry) {
1738 mSentry = nullptr;
1739 break;
1744 void
1745 nsOverflowContinuationTracker::EndFinish(nsIFrame* aChild)
1747 if (!mOverflowContList) {
1748 return;
1750 // Forget mOverflowContList if it was deleted.
1751 nsPresContext* pc = aChild->PresContext();
1752 FramePropertyTable* propTable = pc->PropertyTable();
1753 nsFrameList* eoc = static_cast<nsFrameList*>(propTable->Get(mParent,
1754 nsContainerFrame::ExcessOverflowContainersProperty()));
1755 if (eoc != mOverflowContList) {
1756 nsFrameList* oc = static_cast<nsFrameList*>(propTable->Get(mParent,
1757 nsContainerFrame::OverflowContainersProperty()));
1758 if (oc != mOverflowContList) {
1759 // mOverflowContList was deleted
1760 mPrevOverflowCont = nullptr;
1761 mSentry = nullptr;
1762 mParent = aChild->GetParent();
1763 mOverflowContList = nullptr;
1764 SetupOverflowContList();
1765 return;
1768 // The list survived, update mSentry if needed.
1769 if (!mSentry) {
1770 if (!mPrevOverflowCont) {
1771 SetUpListWalker();
1772 } else {
1773 mozilla::AutoRestore<nsIFrame*> saved(mPrevOverflowCont);
1774 // step backward to make StepForward() use our current mPrevOverflowCont
1775 mPrevOverflowCont = mPrevOverflowCont->GetPrevSibling();
1776 StepForward();
1781 /////////////////////////////////////////////////////////////////////////////
1782 // Debugging
1784 #ifdef DEBUG_FRAME_DUMP
1785 void
1786 nsContainerFrame::List(FILE* out, const char* aPrefix, uint32_t aFlags) const
1788 nsCString str;
1789 ListGeneric(str, aPrefix, aFlags);
1791 // Output the children
1792 bool outputOneList = false;
1793 ChildListIterator lists(this);
1794 for (; !lists.IsDone(); lists.Next()) {
1795 if (outputOneList) {
1796 str += aPrefix;
1798 if (lists.CurrentID() != kPrincipalList) {
1799 if (!outputOneList) {
1800 str += "\n";
1801 str += aPrefix;
1803 str += nsPrintfCString("%s %p ", mozilla::layout::ChildListName(lists.CurrentID()),
1804 &GetChildList(lists.CurrentID()));
1806 fprintf_stderr(out, "%s<\n", str.get());
1807 str = "";
1808 nsFrameList::Enumerator childFrames(lists.CurrentList());
1809 for (; !childFrames.AtEnd(); childFrames.Next()) {
1810 nsIFrame* kid = childFrames.get();
1811 // Verify the child frame's parent frame pointer is correct
1812 NS_ASSERTION(kid->GetParent() == this, "bad parent frame pointer");
1814 // Have the child frame list
1815 nsCString pfx(aPrefix);
1816 pfx += " ";
1817 kid->List(out, pfx.get(), aFlags);
1819 fprintf_stderr(out, "%s>\n", aPrefix);
1820 outputOneList = true;
1823 if (!outputOneList) {
1824 fprintf_stderr(out, "%s<>\n", str.get());
1827 #endif