Merge m-c to fx-team.
[gecko.git] / layout / generic / nsContainerFrame.cpp
blob1060064e1a79ca3322ce3127fd4cfd9fb9d7bb63
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 <algorithm>
34 #ifdef DEBUG
35 #undef NOISY
36 #else
37 #undef NOISY
38 #endif
40 using namespace mozilla;
41 using namespace mozilla::dom;
42 using namespace mozilla::layout;
44 NS_IMPL_FRAMEARENA_HELPERS(nsContainerFrame)
46 nsContainerFrame::~nsContainerFrame()
50 NS_QUERYFRAME_HEAD(nsContainerFrame)
51 NS_QUERYFRAME_ENTRY(nsContainerFrame)
52 NS_QUERYFRAME_TAIL_INHERITING(nsSplittableFrame)
54 void
55 nsContainerFrame::Init(nsIContent* aContent,
56 nsIFrame* aParent,
57 nsIFrame* aPrevInFlow)
59 nsSplittableFrame::Init(aContent, aParent, aPrevInFlow);
60 if (aPrevInFlow) {
61 // Make sure we copy bits from our prev-in-flow that will affect
62 // us. A continuation for a container frame needs to know if it
63 // has a child with a view so that we'll properly reposition it.
64 if (aPrevInFlow->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW)
65 AddStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW);
69 NS_IMETHODIMP
70 nsContainerFrame::SetInitialChildList(ChildListID aListID,
71 nsFrameList& aChildList)
73 nsresult result;
74 if (mFrames.NotEmpty()) {
75 // We already have child frames which means we've already been
76 // initialized
77 NS_NOTREACHED("unexpected second call to SetInitialChildList");
78 result = NS_ERROR_UNEXPECTED;
79 } else if (aListID != kPrincipalList) {
80 // All we know about is the principal child list.
81 NS_NOTREACHED("unknown frame list");
82 result = NS_ERROR_INVALID_ARG;
83 } else {
84 #ifdef DEBUG
85 nsFrame::VerifyDirtyBitSet(aChildList);
86 #endif
87 mFrames.SetFrames(aChildList);
88 result = NS_OK;
90 return result;
93 NS_IMETHODIMP
94 nsContainerFrame::AppendFrames(ChildListID aListID,
95 nsFrameList& aFrameList)
97 if (aListID != kPrincipalList) {
98 #ifdef IBMBIDI
99 if (aListID != kNoReflowPrincipalList)
100 #endif
102 NS_ERROR("unexpected child list");
103 return NS_ERROR_INVALID_ARG;
106 if (aFrameList.NotEmpty()) {
107 mFrames.AppendFrames(this, aFrameList);
109 // Ask the parent frame to reflow me.
110 #ifdef IBMBIDI
111 if (aListID == kPrincipalList)
112 #endif
114 PresContext()->PresShell()->
115 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
116 NS_FRAME_HAS_DIRTY_CHILDREN);
119 return NS_OK;
122 NS_IMETHODIMP
123 nsContainerFrame::InsertFrames(ChildListID aListID,
124 nsIFrame* aPrevFrame,
125 nsFrameList& aFrameList)
127 NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
128 "inserting after sibling frame with different parent");
130 if (aListID != kPrincipalList) {
131 #ifdef IBMBIDI
132 if (aListID != kNoReflowPrincipalList)
133 #endif
135 NS_ERROR("unexpected child list");
136 return NS_ERROR_INVALID_ARG;
139 if (aFrameList.NotEmpty()) {
140 // Insert frames after aPrevFrame
141 mFrames.InsertFrames(this, aPrevFrame, aFrameList);
143 #ifdef IBMBIDI
144 if (aListID == kPrincipalList)
145 #endif
147 PresContext()->PresShell()->
148 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
149 NS_FRAME_HAS_DIRTY_CHILDREN);
152 return NS_OK;
155 NS_IMETHODIMP
156 nsContainerFrame::RemoveFrame(ChildListID aListID,
157 nsIFrame* aOldFrame)
159 if (aListID != kPrincipalList) {
160 #ifdef IBMBIDI
161 if (kNoReflowPrincipalList != aListID)
162 #endif
164 NS_ERROR("unexpected child list");
165 return NS_ERROR_INVALID_ARG;
169 // Loop and destroy aOldFrame and all of its continuations.
170 // Request a reflow on the parent frames involved unless we were explicitly
171 // told not to (kNoReflowPrincipalList).
172 bool generateReflowCommand = true;
173 #ifdef IBMBIDI
174 if (kNoReflowPrincipalList == aListID) {
175 generateReflowCommand = false;
177 #endif
178 nsPresContext* pc = PresContext();
179 nsContainerFrame* lastParent = nullptr;
180 while (aOldFrame) {
181 //XXXfr probably should use StealFrame here. I'm not sure if we need to
182 // check the overflow lists atm, but we'll need a prescontext lookup
183 // for overflow containers once we can split abspos elements with
184 // inline containing blocks.
185 nsIFrame* oldFrameNextContinuation = aOldFrame->GetNextContinuation();
186 nsContainerFrame* parent =
187 static_cast<nsContainerFrame*>(aOldFrame->GetParent());
188 parent->StealFrame(pc, aOldFrame, true);
189 aOldFrame->Destroy();
190 aOldFrame = oldFrameNextContinuation;
191 if (parent != lastParent && generateReflowCommand) {
192 pc->PresShell()->
193 FrameNeedsReflow(parent, nsIPresShell::eTreeChange,
194 NS_FRAME_HAS_DIRTY_CHILDREN);
195 lastParent = parent;
198 return NS_OK;
201 void
202 nsContainerFrame::DestroyAbsoluteFrames(nsIFrame* aDestructRoot)
204 if (IsAbsoluteContainer()) {
205 GetAbsoluteContainingBlock()->DestroyFrames(this, aDestructRoot);
206 MarkAsNotAbsoluteContainingBlock();
210 void
211 nsContainerFrame::SafelyDestroyFrameListProp(nsIFrame* aDestructRoot,
212 nsIPresShell* aPresShell,
213 FramePropertyTable* aPropTable,
214 const FramePropertyDescriptor* aProp)
216 // Note that the last frame can be removed through another route and thus
217 // delete the property -- that's why we fetch the property again before
218 // removing each frame rather than fetching it once and iterating the list.
219 while (nsFrameList* frameList =
220 static_cast<nsFrameList*>(aPropTable->Get(this, aProp))) {
221 nsIFrame* frame = frameList->RemoveFirstChild();
222 if (MOZ_LIKELY(frame)) {
223 frame->DestroyFrom(aDestructRoot);
224 } else {
225 aPropTable->Remove(this, aProp);
226 frameList->Delete(aPresShell);
227 return;
232 void
233 nsContainerFrame::DestroyFrom(nsIFrame* aDestructRoot)
235 // Prevent event dispatch during destruction.
236 if (HasView()) {
237 GetView()->SetFrame(nullptr);
240 DestroyAbsoluteFrames(aDestructRoot);
242 // Destroy frames on the principal child list.
243 mFrames.DestroyFramesFrom(aDestructRoot);
245 // Destroy frames on the auxiliary frame lists and delete the lists.
246 nsPresContext* pc = PresContext();
247 nsIPresShell* shell = pc->PresShell();
248 FramePropertyTable* props = pc->PropertyTable();
249 SafelyDestroyFrameListProp(aDestructRoot, shell, props, OverflowProperty());
251 MOZ_ASSERT(IsFrameOfType(nsIFrame::eCanContainOverflowContainers) ||
252 !(props->Get(this, nsContainerFrame::OverflowContainersProperty()) ||
253 props->Get(this, nsContainerFrame::ExcessOverflowContainersProperty())),
254 "this type of frame should't have overflow containers");
256 SafelyDestroyFrameListProp(aDestructRoot, shell, props,
257 OverflowContainersProperty());
258 SafelyDestroyFrameListProp(aDestructRoot, shell, props,
259 ExcessOverflowContainersProperty());
261 nsSplittableFrame::DestroyFrom(aDestructRoot);
264 /////////////////////////////////////////////////////////////////////////////
265 // Child frame enumeration
267 const nsFrameList&
268 nsContainerFrame::GetChildList(ChildListID aListID) const
270 // We only know about the principal child list and the overflow lists.
271 switch (aListID) {
272 case kPrincipalList:
273 return mFrames;
274 case kOverflowList: {
275 nsFrameList* list = GetOverflowFrames();
276 return list ? *list : nsFrameList::EmptyList();
278 case kOverflowContainersList: {
279 nsFrameList* list =
280 GetPropTableFrames(PresContext(), OverflowContainersProperty());
281 return list ? *list : nsFrameList::EmptyList();
283 case kExcessOverflowContainersList: {
284 nsFrameList* list =
285 GetPropTableFrames(PresContext(), ExcessOverflowContainersProperty());
286 return list ? *list : nsFrameList::EmptyList();
288 default:
289 return nsSplittableFrame::GetChildList(aListID);
293 static void AppendIfNonempty(const nsIFrame* aFrame,
294 FramePropertyTable* aPropTable,
295 const FramePropertyDescriptor* aProperty,
296 nsTArray<nsIFrame::ChildList>* aLists,
297 nsIFrame::ChildListID aListID)
299 nsFrameList* list = static_cast<nsFrameList*>(
300 aPropTable->Get(aFrame, aProperty));
301 if (list) {
302 list->AppendIfNonempty(aLists, aListID);
306 void
307 nsContainerFrame::GetChildLists(nsTArray<ChildList>* aLists) const
309 mFrames.AppendIfNonempty(aLists, kPrincipalList);
310 FramePropertyTable* propTable = PresContext()->PropertyTable();
311 ::AppendIfNonempty(this, propTable, OverflowProperty(),
312 aLists, kOverflowList);
313 if (IsFrameOfType(nsIFrame::eCanContainOverflowContainers)) {
314 ::AppendIfNonempty(this, propTable, OverflowContainersProperty(),
315 aLists, kOverflowContainersList);
316 ::AppendIfNonempty(this, propTable, ExcessOverflowContainersProperty(),
317 aLists, kExcessOverflowContainersList);
319 nsSplittableFrame::GetChildLists(aLists);
322 /////////////////////////////////////////////////////////////////////////////
323 // Painting/Events
325 void
326 nsContainerFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
327 const nsRect& aDirtyRect,
328 const nsDisplayListSet& aLists)
330 DisplayBorderBackgroundOutline(aBuilder, aLists);
332 BuildDisplayListForNonBlockChildren(aBuilder, aDirtyRect, aLists);
335 void
336 nsContainerFrame::BuildDisplayListForNonBlockChildren(nsDisplayListBuilder* aBuilder,
337 const nsRect& aDirtyRect,
338 const nsDisplayListSet& aLists,
339 uint32_t aFlags)
341 nsIFrame* kid = mFrames.FirstChild();
342 // Put each child's background directly onto the content list
343 nsDisplayListSet set(aLists, aLists.Content());
344 // The children should be in content order
345 while (kid) {
346 BuildDisplayListForChild(aBuilder, kid, aDirtyRect, set, aFlags);
347 kid = kid->GetNextSibling();
351 /* virtual */ void
352 nsContainerFrame::ChildIsDirty(nsIFrame* aChild)
354 NS_ASSERTION(NS_SUBTREE_DIRTY(aChild), "child isn't actually dirty");
356 AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
359 bool
360 nsContainerFrame::IsLeaf() const
362 return false;
365 bool
366 nsContainerFrame::PeekOffsetNoAmount(bool aForward, int32_t* aOffset)
368 NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range");
369 // Don't allow the caret to stay in an empty (leaf) container frame.
370 return false;
373 bool
374 nsContainerFrame::PeekOffsetCharacter(bool aForward, int32_t* aOffset,
375 bool aRespectClusters)
377 NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range");
378 // Don't allow the caret to stay in an empty (leaf) container frame.
379 return false;
382 /////////////////////////////////////////////////////////////////////////////
383 // Helper member functions
385 static nsresult
386 ReparentFrameViewTo(nsIFrame* aFrame,
387 nsViewManager* aViewManager,
388 nsView* aNewParentView,
389 nsView* aOldParentView)
392 // XXX What to do about placeholder views for "position: fixed" elements?
393 // They should be reparented too.
395 // Does aFrame have a view?
396 if (aFrame->HasView()) {
397 #ifdef MOZ_XUL
398 if (aFrame->GetType() == nsGkAtoms::menuPopupFrame) {
399 // This view must be parented by the root view, don't reparent it.
400 return NS_OK;
402 #endif
403 nsView* view = aFrame->GetView();
404 // Verify that the current parent view is what we think it is
405 //nsView* parentView;
406 //NS_ASSERTION(parentView == aOldParentView, "unexpected parent view");
408 aViewManager->RemoveChild(view);
410 // The view will remember the Z-order and other attributes that have been set on it.
411 nsView* insertBefore = nsLayoutUtils::FindSiblingViewFor(aNewParentView, aFrame);
412 aViewManager->InsertChild(aNewParentView, view, insertBefore, insertBefore != nullptr);
413 } else {
414 nsIFrame::ChildListIterator lists(aFrame);
415 for (; !lists.IsDone(); lists.Next()) {
416 // Iterate the child frames, and check each child frame to see if it has
417 // a view
418 nsFrameList::Enumerator childFrames(lists.CurrentList());
419 for (; !childFrames.AtEnd(); childFrames.Next()) {
420 ReparentFrameViewTo(childFrames.get(), aViewManager,
421 aNewParentView, aOldParentView);
426 return NS_OK;
429 void
430 nsContainerFrame::CreateViewForFrame(nsIFrame* aFrame,
431 bool aForce)
433 if (aFrame->HasView()) {
434 return;
437 // If we don't yet have a view, see if we need a view
438 if (!aForce && !aFrame->NeedsView()) {
439 // don't need a view
440 return;
443 nsView* parentView = aFrame->GetParent()->GetClosestView();
444 NS_ASSERTION(parentView, "no parent with view");
446 nsViewManager* viewManager = parentView->GetViewManager();
447 NS_ASSERTION(viewManager, "null view manager");
449 // Create a view
450 nsView* view = viewManager->CreateView(aFrame->GetRect(), parentView);
452 SyncFrameViewProperties(aFrame->PresContext(), aFrame, nullptr, view);
454 nsView* insertBefore = nsLayoutUtils::FindSiblingViewFor(parentView, aFrame);
455 // we insert this view 'above' the insertBefore view, unless insertBefore is null,
456 // in which case we want to call with aAbove == false to insert at the beginning
457 // in document order
458 viewManager->InsertChild(parentView, view, insertBefore, insertBefore != nullptr);
460 // REVIEW: Don't create a widget for fixed-pos elements anymore.
461 // ComputeRepaintRegionForCopy will calculate the right area to repaint
462 // when we scroll.
463 // Reparent views on any child frames (or their descendants) to this
464 // view. We can just call ReparentFrameViewTo on this frame because
465 // we know this frame has no view, so it will crawl the children. Also,
466 // we know that any descendants with views must have 'parentView' as their
467 // parent view.
468 ReparentFrameViewTo(aFrame, viewManager, view, parentView);
470 // Remember our view
471 aFrame->SetView(view);
473 NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
474 ("nsContainerFrame::CreateViewForFrame: frame=%p view=%p",
475 aFrame));
479 * Position the view associated with |aKidFrame|, if there is one. A
480 * container frame should call this method after positioning a frame,
481 * but before |Reflow|.
483 void
484 nsContainerFrame::PositionFrameView(nsIFrame* aKidFrame)
486 nsIFrame* parentFrame = aKidFrame->GetParent();
487 if (!aKidFrame->HasView() || !parentFrame)
488 return;
490 nsView* view = aKidFrame->GetView();
491 nsViewManager* vm = view->GetViewManager();
492 nsPoint pt;
493 nsView* ancestorView = parentFrame->GetClosestView(&pt);
495 if (ancestorView != view->GetParent()) {
496 NS_ASSERTION(ancestorView == view->GetParent()->GetParent(),
497 "Allowed only one anonymous view between frames");
498 // parentFrame is responsible for positioning aKidFrame's view
499 // explicitly
500 return;
503 pt += aKidFrame->GetPosition();
504 vm->MoveViewTo(view, pt.x, pt.y);
507 nsresult
508 nsContainerFrame::ReparentFrameView(nsPresContext* aPresContext,
509 nsIFrame* aChildFrame,
510 nsIFrame* aOldParentFrame,
511 nsIFrame* aNewParentFrame)
513 NS_PRECONDITION(aChildFrame, "null child frame pointer");
514 NS_PRECONDITION(aOldParentFrame, "null old parent frame pointer");
515 NS_PRECONDITION(aNewParentFrame, "null new parent frame pointer");
516 NS_PRECONDITION(aOldParentFrame != aNewParentFrame, "same old and new parent frame");
518 // See if either the old parent frame or the new parent frame have a view
519 while (!aOldParentFrame->HasView() && !aNewParentFrame->HasView()) {
520 // Walk up both the old parent frame and the new parent frame nodes
521 // stopping when we either find a common parent or views for one
522 // or both of the frames.
524 // This works well in the common case where we push/pull and the old parent
525 // frame and the new parent frame are part of the same flow. They will
526 // typically be the same distance (height wise) from the
527 aOldParentFrame = aOldParentFrame->GetParent();
528 aNewParentFrame = aNewParentFrame->GetParent();
530 // We should never walk all the way to the root frame without finding
531 // a view
532 NS_ASSERTION(aOldParentFrame && aNewParentFrame, "didn't find view");
534 // See if we reached a common ancestor
535 if (aOldParentFrame == aNewParentFrame) {
536 break;
540 // See if we found a common parent frame
541 if (aOldParentFrame == aNewParentFrame) {
542 // We found a common parent and there are no views between the old parent
543 // and the common parent or the new parent frame and the common parent.
544 // Because neither the old parent frame nor the new parent frame have views,
545 // then any child views don't need reparenting
546 return NS_OK;
549 // We found views for one or both of the ancestor frames before we
550 // found a common ancestor.
551 nsView* oldParentView = aOldParentFrame->GetClosestView();
552 nsView* newParentView = aNewParentFrame->GetClosestView();
554 // See if the old parent frame and the new parent frame are in the
555 // same view sub-hierarchy. If they are then we don't have to do
556 // anything
557 if (oldParentView != newParentView) {
558 // They're not so we need to reparent any child views
559 return ReparentFrameViewTo(aChildFrame, oldParentView->GetViewManager(), newParentView,
560 oldParentView);
563 return NS_OK;
566 nsresult
567 nsContainerFrame::ReparentFrameViewList(nsPresContext* aPresContext,
568 const nsFrameList& aChildFrameList,
569 nsIFrame* aOldParentFrame,
570 nsIFrame* aNewParentFrame)
572 NS_PRECONDITION(aChildFrameList.NotEmpty(), "empty child frame list");
573 NS_PRECONDITION(aOldParentFrame, "null old parent frame pointer");
574 NS_PRECONDITION(aNewParentFrame, "null new parent frame pointer");
575 NS_PRECONDITION(aOldParentFrame != aNewParentFrame, "same old and new parent frame");
577 // See if either the old parent frame or the new parent frame have a view
578 while (!aOldParentFrame->HasView() && !aNewParentFrame->HasView()) {
579 // Walk up both the old parent frame and the new parent frame nodes
580 // stopping when we either find a common parent or views for one
581 // or both of the frames.
583 // This works well in the common case where we push/pull and the old parent
584 // frame and the new parent frame are part of the same flow. They will
585 // typically be the same distance (height wise) from the
586 aOldParentFrame = aOldParentFrame->GetParent();
587 aNewParentFrame = aNewParentFrame->GetParent();
589 // We should never walk all the way to the root frame without finding
590 // a view
591 NS_ASSERTION(aOldParentFrame && aNewParentFrame, "didn't find view");
593 // See if we reached a common ancestor
594 if (aOldParentFrame == aNewParentFrame) {
595 break;
600 // See if we found a common parent frame
601 if (aOldParentFrame == aNewParentFrame) {
602 // We found a common parent and there are no views between the old parent
603 // and the common parent or the new parent frame and the common parent.
604 // Because neither the old parent frame nor the new parent frame have views,
605 // then any child views don't need reparenting
606 return NS_OK;
609 // We found views for one or both of the ancestor frames before we
610 // found a common ancestor.
611 nsView* oldParentView = aOldParentFrame->GetClosestView();
612 nsView* newParentView = aNewParentFrame->GetClosestView();
614 // See if the old parent frame and the new parent frame are in the
615 // same view sub-hierarchy. If they are then we don't have to do
616 // anything
617 if (oldParentView != newParentView) {
618 nsViewManager* viewManager = oldParentView->GetViewManager();
620 // They're not so we need to reparent any child views
621 for (nsFrameList::Enumerator e(aChildFrameList); !e.AtEnd(); e.Next()) {
622 ReparentFrameViewTo(e.get(), viewManager, newParentView, oldParentView);
626 return NS_OK;
629 static nsIWidget*
630 GetPresContextContainerWidget(nsPresContext* aPresContext)
632 nsCOMPtr<nsISupports> container = aPresContext->Document()->GetContainer();
633 nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(container);
634 if (!baseWindow)
635 return nullptr;
637 nsCOMPtr<nsIWidget> mainWidget;
638 baseWindow->GetMainWidget(getter_AddRefs(mainWidget));
639 return mainWidget;
642 static bool
643 IsTopLevelWidget(nsIWidget* aWidget)
645 nsWindowType windowType;
646 aWidget->GetWindowType(windowType);
647 return windowType == eWindowType_toplevel ||
648 windowType == eWindowType_dialog ||
649 windowType == eWindowType_sheet;
650 // popups aren't toplevel so they're not handled here
653 void
654 nsContainerFrame::SyncWindowProperties(nsPresContext* aPresContext,
655 nsIFrame* aFrame,
656 nsView* aView,
657 nsRenderingContext* aRC)
659 #ifdef MOZ_XUL
660 if (!aView || !nsCSSRendering::IsCanvasFrame(aFrame) || !aView->HasWidget())
661 return;
663 nsIWidget* windowWidget = GetPresContextContainerWidget(aPresContext);
664 if (!windowWidget || !IsTopLevelWidget(windowWidget))
665 return;
667 nsViewManager* vm = aView->GetViewManager();
668 nsView* rootView = vm->GetRootView();
670 if (aView != rootView)
671 return;
673 Element* rootElement = aPresContext->Document()->GetRootElement();
674 if (!rootElement || !rootElement->IsXUL()) {
675 // Scrollframes use native widgets which don't work well with
676 // translucent windows, at least in Windows XP. So if the document
677 // has a root scrollrame it's useless to try to make it transparent,
678 // we'll just get something broken.
679 // nsCSSFrameConstructor::ConstructRootFrame constructs root
680 // scrollframes whenever the root element is not a XUL element, so
681 // we test for that here. We can't just call
682 // presShell->GetRootScrollFrame() since that might not have
683 // been constructed yet.
684 // We can change this to allow translucent toplevel HTML documents
685 // (e.g. to do something like Dashboard widgets), once we
686 // have broad support for translucent scrolled documents, but be
687 // careful because apparently some Firefox extensions expect
688 // openDialog("something.html") to produce an opaque window
689 // even if the HTML doesn't have a background-color set.
690 return;
693 nsIFrame *rootFrame = aPresContext->PresShell()->FrameConstructor()->GetRootElementStyleFrame();
694 if (!rootFrame)
695 return;
697 nsTransparencyMode mode = nsLayoutUtils::GetFrameTransparency(aFrame, rootFrame);
698 nsIWidget* viewWidget = aView->GetWidget();
699 viewWidget->SetTransparencyMode(mode);
700 windowWidget->SetWindowShadowStyle(rootFrame->StyleUIReset()->mWindowShadow);
702 if (!aRC)
703 return;
705 nsBoxLayoutState aState(aPresContext, aRC);
706 nsSize minSize = rootFrame->GetMinSize(aState);
707 nsSize maxSize = rootFrame->GetMaxSize(aState);
709 SetSizeConstraints(aPresContext, windowWidget, minSize, maxSize);
710 #endif
713 void nsContainerFrame::SetSizeConstraints(nsPresContext* aPresContext,
714 nsIWidget* aWidget,
715 const nsSize& aMinSize,
716 const nsSize& aMaxSize)
718 nsIntSize devMinSize(aPresContext->AppUnitsToDevPixels(aMinSize.width),
719 aPresContext->AppUnitsToDevPixels(aMinSize.height));
720 nsIntSize devMaxSize(aMaxSize.width == NS_INTRINSICSIZE ? NS_MAXSIZE :
721 aPresContext->AppUnitsToDevPixels(aMaxSize.width),
722 aMaxSize.height == NS_INTRINSICSIZE ? NS_MAXSIZE :
723 aPresContext->AppUnitsToDevPixels(aMaxSize.height));
724 widget::SizeConstraints constraints(devMinSize, devMaxSize);
726 // The sizes are in inner window sizes, so convert them into outer window sizes.
727 // Use a size of (200, 200) as only the difference between the inner and outer
728 // size is needed.
729 nsIntSize windowSize = aWidget->ClientToWindowSize(nsIntSize(200, 200));
730 if (constraints.mMinSize.width)
731 constraints.mMinSize.width += windowSize.width - 200;
732 if (constraints.mMinSize.height)
733 constraints.mMinSize.height += windowSize.height - 200;
734 if (constraints.mMaxSize.width != NS_MAXSIZE)
735 constraints.mMaxSize.width += windowSize.width - 200;
736 if (constraints.mMaxSize.height != NS_MAXSIZE)
737 constraints.mMaxSize.height += windowSize.height - 200;
739 aWidget->SetSizeConstraints(constraints);
742 void
743 nsContainerFrame::SyncFrameViewAfterReflow(nsPresContext* aPresContext,
744 nsIFrame* aFrame,
745 nsView* aView,
746 const nsRect& aVisualOverflowArea,
747 uint32_t aFlags)
749 if (!aView) {
750 return;
753 // Make sure the view is sized and positioned correctly
754 if (0 == (aFlags & NS_FRAME_NO_MOVE_VIEW)) {
755 PositionFrameView(aFrame);
758 if (0 == (aFlags & NS_FRAME_NO_SIZE_VIEW)) {
759 nsViewManager* vm = aView->GetViewManager();
761 vm->ResizeView(aView, aVisualOverflowArea, true);
765 void
766 nsContainerFrame::SyncFrameViewProperties(nsPresContext* aPresContext,
767 nsIFrame* aFrame,
768 nsStyleContext* aStyleContext,
769 nsView* aView,
770 uint32_t aFlags)
772 NS_ASSERTION(!aStyleContext || aFrame->StyleContext() == aStyleContext,
773 "Wrong style context for frame?");
775 if (!aView) {
776 return;
779 nsViewManager* vm = aView->GetViewManager();
781 if (nullptr == aStyleContext) {
782 aStyleContext = aFrame->StyleContext();
785 // Make sure visibility is correct. This only affects nsSubdocumentFrame.
786 if (0 == (aFlags & NS_FRAME_NO_VISIBILITY) &&
787 !aFrame->SupportsVisibilityHidden()) {
788 // See if the view should be hidden or visible
789 vm->SetViewVisibility(aView,
790 aStyleContext->StyleVisibility()->IsVisible()
791 ? nsViewVisibility_kShow : nsViewVisibility_kHide);
794 // See if the frame is being relatively positioned or absolutely
795 // positioned
796 bool isPositioned = aFrame->IsPositioned();
798 int32_t zIndex = 0;
799 bool autoZIndex = false;
801 if (!isPositioned) {
802 autoZIndex = true;
803 } else {
804 // Make sure z-index is correct
805 const nsStylePosition* position = aStyleContext->StylePosition();
807 if (position->mZIndex.GetUnit() == eStyleUnit_Integer) {
808 zIndex = position->mZIndex.GetIntValue();
809 } else if (position->mZIndex.GetUnit() == eStyleUnit_Auto) {
810 autoZIndex = true;
814 vm->SetViewZIndex(aView, autoZIndex, zIndex);
817 static nscoord GetCoord(const nsStyleCoord& aCoord, nscoord aIfNotCoord)
819 if (aCoord.ConvertsToLength()) {
820 return nsRuleNode::ComputeCoordPercentCalc(aCoord, 0);
822 return aIfNotCoord;
825 void
826 nsContainerFrame::DoInlineIntrinsicWidth(nsRenderingContext *aRenderingContext,
827 InlineIntrinsicWidthData *aData,
828 nsLayoutUtils::IntrinsicWidthType aType)
830 if (GetPrevInFlow())
831 return; // Already added.
833 NS_PRECONDITION(aType == nsLayoutUtils::MIN_WIDTH ||
834 aType == nsLayoutUtils::PREF_WIDTH, "bad type");
836 mozilla::css::Side startSide, endSide;
837 if (StyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR) {
838 startSide = NS_SIDE_LEFT;
839 endSide = NS_SIDE_RIGHT;
840 } else {
841 startSide = NS_SIDE_RIGHT;
842 endSide = NS_SIDE_LEFT;
845 const nsStylePadding *stylePadding = StylePadding();
846 const nsStyleBorder *styleBorder = StyleBorder();
847 const nsStyleMargin *styleMargin = StyleMargin();
849 // This goes at the beginning no matter how things are broken and how
850 // messy the bidi situations are, since per CSS2.1 section 8.6
851 // (implemented in bug 328168), the startSide border is always on the
852 // first line.
853 // This frame is a first-in-flow, but it might have a previous bidi
854 // continuation, in which case that continuation should handle the startSide
855 // border.
856 if (!GetPrevContinuation()) {
857 aData->currentLine +=
858 // clamp negative calc() to 0
859 std::max(GetCoord(stylePadding->mPadding.Get(startSide), 0), 0) +
860 styleBorder->GetComputedBorderWidth(startSide) +
861 GetCoord(styleMargin->mMargin.Get(startSide), 0);
864 const nsLineList_iterator* savedLine = aData->line;
865 nsIFrame* const savedLineContainer = aData->lineContainer;
867 nsContainerFrame *lastInFlow;
868 for (nsContainerFrame *nif = this; nif;
869 nif = static_cast<nsContainerFrame*>(nif->GetNextInFlow())) {
870 for (nsIFrame *kid = nif->mFrames.FirstChild(); kid;
871 kid = kid->GetNextSibling()) {
872 if (aType == nsLayoutUtils::MIN_WIDTH)
873 kid->AddInlineMinWidth(aRenderingContext,
874 static_cast<InlineMinWidthData*>(aData));
875 else
876 kid->AddInlinePrefWidth(aRenderingContext,
877 static_cast<InlinePrefWidthData*>(aData));
880 // After we advance to our next-in-flow, the stored line and line container
881 // may no longer be correct. Just forget them.
882 aData->line = nullptr;
883 aData->lineContainer = nullptr;
885 lastInFlow = nif;
888 aData->line = savedLine;
889 aData->lineContainer = savedLineContainer;
891 // This goes at the end no matter how things are broken and how
892 // messy the bidi situations are, since per CSS2.1 section 8.6
893 // (implemented in bug 328168), the endSide border is always on the
894 // last line.
895 // We reached the last-in-flow, but it might have a next bidi
896 // continuation, in which case that continuation should handle
897 // the endSide border.
898 if (!lastInFlow->GetNextContinuation()) {
899 aData->currentLine +=
900 // clamp negative calc() to 0
901 std::max(GetCoord(stylePadding->mPadding.Get(endSide), 0), 0) +
902 styleBorder->GetComputedBorderWidth(endSide) +
903 GetCoord(styleMargin->mMargin.Get(endSide), 0);
907 /* virtual */ nsSize
908 nsContainerFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext,
909 nsSize aCBSize, nscoord aAvailableWidth,
910 nsSize aMargin, nsSize aBorder,
911 nsSize aPadding, bool aShrinkWrap)
913 nsSize result(0xdeadbeef, NS_UNCONSTRAINEDSIZE);
914 nscoord availBased = aAvailableWidth - aMargin.width - aBorder.width -
915 aPadding.width;
916 // replaced elements always shrink-wrap
917 if (aShrinkWrap || IsFrameOfType(eReplaced)) {
918 // don't bother setting it if the result won't be used
919 if (StylePosition()->mWidth.GetUnit() == eStyleUnit_Auto) {
920 result.width = ShrinkWidthToFit(aRenderingContext, availBased);
922 } else {
923 result.width = availBased;
925 return result;
929 * Invokes the WillReflow() function, positions the frame and its view (if
930 * requested), and then calls Reflow(). If the reflow succeeds and the child
931 * frame is complete, deletes any next-in-flows using DeleteNextInFlowChild()
933 nsresult
934 nsContainerFrame::ReflowChild(nsIFrame* aKidFrame,
935 nsPresContext* aPresContext,
936 nsHTMLReflowMetrics& aDesiredSize,
937 const nsHTMLReflowState& aReflowState,
938 nscoord aX,
939 nscoord aY,
940 uint32_t aFlags,
941 nsReflowStatus& aStatus,
942 nsOverflowContinuationTracker* aTracker)
944 NS_PRECONDITION(aReflowState.frame == aKidFrame, "bad reflow state");
946 nsresult result;
948 // Send the WillReflow() notification, and position the child frame
949 // and its view if requested
950 aKidFrame->WillReflow(aPresContext);
952 if (NS_FRAME_NO_MOVE_FRAME != (aFlags & NS_FRAME_NO_MOVE_FRAME)) {
953 aKidFrame->SetPosition(nsPoint(aX, aY));
956 if (0 == (aFlags & NS_FRAME_NO_MOVE_VIEW)) {
957 PositionFrameView(aKidFrame);
960 // Reflow the child frame
961 result = aKidFrame->Reflow(aPresContext, aDesiredSize, aReflowState,
962 aStatus);
964 // If the reflow was successful and the child frame is complete, delete any
965 // next-in-flows, but only if the NO_DELETE_NEXT_IN_FLOW flag isn't set.
966 if (NS_SUCCEEDED(result) && NS_FRAME_IS_FULLY_COMPLETE(aStatus) &&
967 !(aFlags & NS_FRAME_NO_DELETE_NEXT_IN_FLOW_CHILD)) {
968 nsIFrame* kidNextInFlow = aKidFrame->GetNextInFlow();
969 if (kidNextInFlow) {
970 // Remove all of the childs next-in-flows. Make sure that we ask
971 // the right parent to do the removal (it's possible that the
972 // parent is not this because we are executing pullup code)
973 nsOverflowContinuationTracker::AutoFinish fini(aTracker, aKidFrame);
974 static_cast<nsContainerFrame*>(kidNextInFlow->GetParent())
975 ->DeleteNextInFlowChild(aPresContext, kidNextInFlow, true);
978 return result;
983 * Position the views of |aFrame|'s descendants. A container frame
984 * should call this method if it moves a frame after |Reflow|.
986 void
987 nsContainerFrame::PositionChildViews(nsIFrame* aFrame)
989 if (!(aFrame->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW)) {
990 return;
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 ChildListIterator lists(aFrame);
999 for (; !lists.IsDone(); lists.Next()) {
1000 if (lists.CurrentID() == kPopupList) {
1001 continue;
1003 nsFrameList::Enumerator childFrames(lists.CurrentList());
1004 for (; !childFrames.AtEnd(); childFrames.Next()) {
1005 // Position the frame's view (if it has one) otherwise recursively
1006 // process its children
1007 nsIFrame* childFrame = childFrames.get();
1008 if (childFrame->HasView()) {
1009 PositionFrameView(childFrame);
1010 } else {
1011 PositionChildViews(childFrame);
1018 * The second half of frame reflow. Does the following:
1019 * - sets the frame's bounds
1020 * - sizes and positions (if requested) the frame's view. If the frame's final
1021 * position differs from the current position and the frame itself does not
1022 * have a view, then any child frames with views are positioned so they stay
1023 * in sync
1024 * - sets the view's visibility, opacity, content transparency, and clip
1025 * - invoked the DidReflow() function
1027 * Flags:
1028 * NS_FRAME_NO_MOVE_FRAME - don't move the frame. aX and aY are ignored in this
1029 * case. Also implies NS_FRAME_NO_MOVE_VIEW
1030 * NS_FRAME_NO_MOVE_VIEW - don't position the frame's view. Set this if you
1031 * don't want to automatically sync the frame and view
1032 * NS_FRAME_NO_SIZE_VIEW - don't size the frame's view
1034 nsresult
1035 nsContainerFrame::FinishReflowChild(nsIFrame* aKidFrame,
1036 nsPresContext* aPresContext,
1037 const nsHTMLReflowState* aReflowState,
1038 const nsHTMLReflowMetrics& aDesiredSize,
1039 nscoord aX,
1040 nscoord aY,
1041 uint32_t aFlags)
1043 nsPoint curOrigin = aKidFrame->GetPosition();
1045 if (NS_FRAME_NO_MOVE_FRAME != (aFlags & NS_FRAME_NO_MOVE_FRAME)) {
1046 aKidFrame->SetRect(nsRect(aX, aY, aDesiredSize.width, aDesiredSize.height));
1047 } else {
1048 aKidFrame->SetSize(nsSize(aDesiredSize.width, aDesiredSize.height));
1051 if (aKidFrame->HasView()) {
1052 nsView* view = aKidFrame->GetView();
1053 // Make sure the frame's view is properly sized and positioned and has
1054 // things like opacity correct
1055 SyncFrameViewAfterReflow(aPresContext, aKidFrame, view,
1056 aDesiredSize.VisualOverflow(), aFlags);
1059 if (!(aFlags & NS_FRAME_NO_MOVE_VIEW) &&
1060 (curOrigin.x != aX || curOrigin.y != aY)) {
1061 if (!aKidFrame->HasView()) {
1062 // If the frame has moved, then we need to make sure any child views are
1063 // correctly positioned
1064 PositionChildViews(aKidFrame);
1068 return aKidFrame->DidReflow(aPresContext, aReflowState, nsDidReflowStatus::FINISHED);
1071 nsresult
1072 nsContainerFrame::ReflowOverflowContainerChildren(nsPresContext* aPresContext,
1073 const nsHTMLReflowState& aReflowState,
1074 nsOverflowAreas& aOverflowRects,
1075 uint32_t aFlags,
1076 nsReflowStatus& aStatus)
1078 NS_PRECONDITION(aPresContext, "null pointer");
1079 nsresult rv = NS_OK;
1081 nsFrameList* overflowContainers =
1082 GetPropTableFrames(aPresContext,
1083 OverflowContainersProperty());
1085 NS_ASSERTION(!(overflowContainers && GetPrevInFlow()
1086 && static_cast<nsContainerFrame*>(GetPrevInFlow())
1087 ->GetPropTableFrames(aPresContext,
1088 ExcessOverflowContainersProperty())),
1089 "conflicting overflow containers lists");
1091 if (!overflowContainers) {
1092 // Drain excess from previnflow
1093 nsContainerFrame* prev = (nsContainerFrame*) GetPrevInFlow();
1094 if (prev) {
1095 nsFrameList* excessFrames =
1096 prev->RemovePropTableFrames(aPresContext,
1097 ExcessOverflowContainersProperty());
1098 if (excessFrames) {
1099 excessFrames->ApplySetParent(this);
1100 nsContainerFrame::ReparentFrameViewList(aPresContext, *excessFrames,
1101 prev, this);
1102 overflowContainers = excessFrames;
1103 SetPropTableFrames(aPresContext, overflowContainers,
1104 OverflowContainersProperty());
1109 // Our own excess overflow containers from a previous reflow can still be
1110 // present if our next-in-flow hasn't been reflown yet.
1111 nsFrameList* selfExcessOCFrames =
1112 RemovePropTableFrames(aPresContext, ExcessOverflowContainersProperty());
1113 if (selfExcessOCFrames) {
1114 if (overflowContainers) {
1115 overflowContainers->AppendFrames(nullptr, *selfExcessOCFrames);
1116 selfExcessOCFrames->Delete(aPresContext->PresShell());
1117 } else {
1118 overflowContainers = selfExcessOCFrames;
1119 SetPropTableFrames(aPresContext, overflowContainers,
1120 OverflowContainersProperty());
1123 if (!overflowContainers) {
1124 return NS_OK; // nothing to reflow
1127 nsOverflowContinuationTracker tracker(aPresContext, this, false, false);
1128 bool shouldReflowAllKids = aReflowState.ShouldReflowAllKids();
1130 for (nsIFrame* frame = overflowContainers->FirstChild(); frame;
1131 frame = frame->GetNextSibling()) {
1132 if (frame->GetPrevInFlow()->GetParent() != GetPrevInFlow()) {
1133 // frame's prevInFlow has moved, skip reflowing this frame;
1134 // it will get reflowed once it's been placed
1135 continue;
1137 // If the available vertical height has changed, we need to reflow
1138 // even if the frame isn't dirty.
1139 if (shouldReflowAllKids || NS_SUBTREE_DIRTY(frame)) {
1140 // Get prev-in-flow
1141 nsIFrame* prevInFlow = frame->GetPrevInFlow();
1142 NS_ASSERTION(prevInFlow,
1143 "overflow container frame must have a prev-in-flow");
1144 NS_ASSERTION(frame->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER,
1145 "overflow container frame must have overflow container bit set");
1146 nsRect prevRect = prevInFlow->GetRect();
1148 // Initialize reflow params
1149 nsSize availSpace(prevRect.width, aReflowState.availableHeight);
1150 nsHTMLReflowMetrics desiredSize;
1151 nsHTMLReflowState frameState(aPresContext, aReflowState,
1152 frame, availSpace);
1153 nsReflowStatus frameStatus;
1155 // Reflow
1156 rv = ReflowChild(frame, aPresContext, desiredSize, frameState,
1157 prevRect.x, 0, aFlags, frameStatus, &tracker);
1158 NS_ENSURE_SUCCESS(rv, rv);
1159 //XXXfr Do we need to override any shrinkwrap effects here?
1160 // e.g. desiredSize.width = prevRect.width;
1161 rv = FinishReflowChild(frame, aPresContext, &frameState, desiredSize,
1162 prevRect.x, 0, aFlags);
1163 NS_ENSURE_SUCCESS(rv, rv);
1165 // Handle continuations
1166 if (!NS_FRAME_IS_FULLY_COMPLETE(frameStatus)) {
1167 if (frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
1168 // Abspos frames can't cause their parent to be incomplete,
1169 // only overflow incomplete.
1170 NS_FRAME_SET_OVERFLOW_INCOMPLETE(frameStatus);
1172 else {
1173 NS_ASSERTION(NS_FRAME_IS_COMPLETE(frameStatus),
1174 "overflow container frames can't be incomplete, only overflow-incomplete");
1177 // Acquire a next-in-flow, creating it if necessary
1178 nsIFrame* nif = frame->GetNextInFlow();
1179 if (!nif) {
1180 NS_ASSERTION(frameStatus & NS_FRAME_REFLOW_NEXTINFLOW,
1181 "Someone forgot a REFLOW_NEXTINFLOW flag");
1182 nif = aPresContext->PresShell()->FrameConstructor()->
1183 CreateContinuingFrame(aPresContext, frame, this);
1185 else if (!(nif->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) {
1186 // used to be a normal next-in-flow; steal it from the child list
1187 rv = static_cast<nsContainerFrame*>(nif->GetParent())
1188 ->StealFrame(aPresContext, nif);
1189 NS_ENSURE_SUCCESS(rv, rv);
1192 tracker.Insert(nif, frameStatus);
1194 NS_MergeReflowStatusInto(&aStatus, frameStatus);
1195 // At this point it would be nice to assert !frame->GetOverflowRect().IsEmpty(),
1196 // but we have some unsplittable frames that, when taller than
1197 // availableHeight will push zero-height content into a next-in-flow.
1199 else {
1200 tracker.Skip(frame, aStatus);
1201 if (aReflowState.mFloatManager)
1202 nsBlockFrame::RecoverFloatsFor(frame, *aReflowState.mFloatManager);
1204 ConsiderChildOverflow(aOverflowRects, frame);
1207 return NS_OK;
1210 void
1211 nsContainerFrame::DisplayOverflowContainers(nsDisplayListBuilder* aBuilder,
1212 const nsRect& aDirtyRect,
1213 const nsDisplayListSet& aLists)
1215 nsFrameList* overflowconts =
1216 GetPropTableFrames(PresContext(), OverflowContainersProperty());
1217 if (overflowconts) {
1218 for (nsIFrame* frame = overflowconts->FirstChild(); frame;
1219 frame = frame->GetNextSibling()) {
1220 BuildDisplayListForChild(aBuilder, frame, aDirtyRect, aLists);
1225 static bool
1226 TryRemoveFrame(nsIFrame* aFrame, FramePropertyTable* aPropTable,
1227 const FramePropertyDescriptor* aProp, nsIFrame* aChildToRemove)
1229 nsFrameList* list = static_cast<nsFrameList*>(aPropTable->Get(aFrame, aProp));
1230 if (list && list->StartRemoveFrame(aChildToRemove)) {
1231 // aChildToRemove *may* have been removed from this list.
1232 if (list->IsEmpty()) {
1233 aPropTable->Remove(aFrame, aProp);
1234 list->Delete(aFrame->PresContext()->PresShell());
1236 return true;
1238 return false;
1241 nsresult
1242 nsContainerFrame::StealFrame(nsPresContext* aPresContext,
1243 nsIFrame* aChild,
1244 bool aForceNormal)
1246 #ifdef DEBUG
1247 if (!mFrames.ContainsFrame(aChild)) {
1248 nsFrameList* list = GetOverflowFrames();
1249 if (!list || !list->ContainsFrame(aChild)) {
1250 FramePropertyTable* propTable = aPresContext->PropertyTable();
1251 list = static_cast<nsFrameList*>(
1252 propTable->Get(this, OverflowContainersProperty()));
1253 if (!list || !list->ContainsFrame(aChild)) {
1254 list = static_cast<nsFrameList*>(
1255 propTable->Get(this, ExcessOverflowContainersProperty()));
1256 MOZ_ASSERT(list && list->ContainsFrame(aChild), "aChild isn't our child"
1257 " or on a frame list not supported by StealFrame");
1261 #endif
1263 bool removed;
1264 if ((aChild->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)
1265 && !aForceNormal) {
1266 FramePropertyTable* propTable = aPresContext->PropertyTable();
1267 // Try removing from the overflow container list.
1268 removed = ::TryRemoveFrame(this, propTable, OverflowContainersProperty(),
1269 aChild);
1270 if (!removed) {
1271 // It must be in the excess overflow container list.
1272 removed = ::TryRemoveFrame(this, propTable,
1273 ExcessOverflowContainersProperty(),
1274 aChild);
1276 } else {
1277 removed = mFrames.StartRemoveFrame(aChild);
1278 if (!removed) {
1279 // We didn't find the child in our principal child list.
1280 // Maybe it's on the overflow list?
1281 nsFrameList* frameList = GetOverflowFrames();
1282 if (frameList) {
1283 removed = frameList->ContinueRemoveFrame(aChild);
1284 if (frameList->IsEmpty()) {
1285 DestroyOverflowList(aPresContext);
1291 NS_POSTCONDITION(removed, "StealFrame: can't find aChild");
1292 return removed ? NS_OK : NS_ERROR_UNEXPECTED;
1295 nsFrameList
1296 nsContainerFrame::StealFramesAfter(nsIFrame* aChild)
1298 NS_ASSERTION(!aChild ||
1299 !(aChild->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER),
1300 "StealFramesAfter doesn't handle overflow containers");
1301 NS_ASSERTION(GetType() != nsGkAtoms::blockFrame, "unexpected call");
1303 if (!aChild) {
1304 nsFrameList copy(mFrames);
1305 mFrames.Clear();
1306 return copy;
1309 for (nsFrameList::FrameLinkEnumerator iter(mFrames); !iter.AtEnd();
1310 iter.Next()) {
1311 if (iter.PrevFrame() == aChild) {
1312 return mFrames.ExtractTail(iter);
1316 // We didn't find the child in the principal child list.
1317 // Maybe it's on the overflow list?
1318 nsFrameList* overflowFrames = GetOverflowFrames();
1319 if (overflowFrames) {
1320 for (nsFrameList::FrameLinkEnumerator iter(*overflowFrames); !iter.AtEnd();
1321 iter.Next()) {
1322 if (iter.PrevFrame() == aChild) {
1323 return overflowFrames->ExtractTail(iter);
1328 NS_ERROR("StealFramesAfter: can't find aChild");
1329 return nsFrameList::EmptyList();
1333 * Create a next-in-flow for aFrame. Will return the newly created
1334 * frame in aNextInFlowResult <b>if and only if</b> a new frame is
1335 * created; otherwise nullptr is returned in aNextInFlowResult.
1337 nsresult
1338 nsContainerFrame::CreateNextInFlow(nsPresContext* aPresContext,
1339 nsIFrame* aFrame,
1340 nsIFrame*& aNextInFlowResult)
1342 NS_PRECONDITION(GetType() != nsGkAtoms::blockFrame,
1343 "you should have called nsBlockFrame::CreateContinuationFor instead");
1344 NS_PRECONDITION(mFrames.ContainsFrame(aFrame), "expected an in-flow child frame");
1346 aNextInFlowResult = nullptr;
1348 nsIFrame* nextInFlow = aFrame->GetNextInFlow();
1349 if (nullptr == nextInFlow) {
1350 // Create a continuation frame for the child frame and insert it
1351 // into our child list.
1352 nextInFlow = aPresContext->PresShell()->FrameConstructor()->
1353 CreateContinuingFrame(aPresContext, aFrame, this);
1354 mFrames.InsertFrame(nullptr, aFrame, nextInFlow);
1356 NS_FRAME_LOG(NS_FRAME_TRACE_NEW_FRAMES,
1357 ("nsContainerFrame::CreateNextInFlow: frame=%p nextInFlow=%p",
1358 aFrame, nextInFlow));
1360 aNextInFlowResult = nextInFlow;
1362 return NS_OK;
1366 * Remove and delete aNextInFlow and its next-in-flows. Updates the sibling and flow
1367 * pointers
1369 void
1370 nsContainerFrame::DeleteNextInFlowChild(nsPresContext* aPresContext,
1371 nsIFrame* aNextInFlow,
1372 bool aDeletingEmptyFrames)
1374 #ifdef DEBUG
1375 nsIFrame* prevInFlow = aNextInFlow->GetPrevInFlow();
1376 #endif
1377 NS_PRECONDITION(prevInFlow, "bad prev-in-flow");
1379 // If the next-in-flow has a next-in-flow then delete it, too (and
1380 // delete it first).
1381 // Do this in a loop so we don't overflow the stack for frames
1382 // with very many next-in-flows
1383 nsIFrame* nextNextInFlow = aNextInFlow->GetNextInFlow();
1384 if (nextNextInFlow) {
1385 nsAutoTArray<nsIFrame*, 8> frames;
1386 for (nsIFrame* f = nextNextInFlow; f; f = f->GetNextInFlow()) {
1387 frames.AppendElement(f);
1389 for (int32_t i = frames.Length() - 1; i >= 0; --i) {
1390 nsIFrame* delFrame = frames.ElementAt(i);
1391 static_cast<nsContainerFrame*>(delFrame->GetParent())
1392 ->DeleteNextInFlowChild(aPresContext, delFrame, aDeletingEmptyFrames);
1396 // Take the next-in-flow out of the parent's child list
1397 DebugOnly<nsresult> rv = StealFrame(aPresContext, aNextInFlow);
1398 NS_ASSERTION(NS_SUCCEEDED(rv), "StealFrame failure");
1400 #ifdef DEBUG
1401 if (aDeletingEmptyFrames) {
1402 nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(aNextInFlow);
1404 #endif
1406 // Delete the next-in-flow frame and its descendants. This will also
1407 // remove it from its next-in-flow/prev-in-flow chain.
1408 aNextInFlow->Destroy();
1410 NS_POSTCONDITION(!prevInFlow->GetNextInFlow(), "non null next-in-flow");
1414 * Set the frames on the overflow list
1416 void
1417 nsContainerFrame::SetOverflowFrames(nsPresContext* aPresContext,
1418 const nsFrameList& aOverflowFrames)
1420 NS_PRECONDITION(aOverflowFrames.NotEmpty(), "Shouldn't be called");
1421 nsFrameList* newList = new (aPresContext->PresShell()) nsFrameList(aOverflowFrames);
1423 aPresContext->PropertyTable()->Set(this, OverflowProperty(), newList);
1426 nsFrameList*
1427 nsContainerFrame::GetPropTableFrames(nsPresContext* aPresContext,
1428 const FramePropertyDescriptor* aProperty) const
1430 FramePropertyTable* propTable = aPresContext->PropertyTable();
1431 return static_cast<nsFrameList*>(propTable->Get(this, aProperty));
1434 nsFrameList*
1435 nsContainerFrame::RemovePropTableFrames(nsPresContext* aPresContext,
1436 const FramePropertyDescriptor* aProperty)
1438 FramePropertyTable* propTable = aPresContext->PropertyTable();
1439 return static_cast<nsFrameList*>(propTable->Remove(this, aProperty));
1442 void
1443 nsContainerFrame::SetPropTableFrames(nsPresContext* aPresContext,
1444 nsFrameList* aFrameList,
1445 const FramePropertyDescriptor* aProperty)
1447 NS_PRECONDITION(aPresContext && aProperty && aFrameList, "null ptr");
1448 NS_PRECONDITION(
1449 (aProperty != nsContainerFrame::OverflowContainersProperty() &&
1450 aProperty != nsContainerFrame::ExcessOverflowContainersProperty()) ||
1451 IsFrameOfType(nsIFrame::eCanContainOverflowContainers),
1452 "this type of frame can't have overflow containers");
1453 MOZ_ASSERT(!GetPropTableFrames(aPresContext, aProperty));
1454 aPresContext->PropertyTable()->Set(this, aProperty, aFrameList);
1458 * Push aFromChild and its next siblings to the next-in-flow. Change the
1459 * geometric parent of each frame that's pushed. If there is no next-in-flow
1460 * the frames are placed on the overflow list (and the geometric parent is
1461 * left unchanged).
1463 * Updates the next-in-flow's child count. Does <b>not</b> update the
1464 * pusher's child count.
1466 * @param aFromChild the first child frame to push. It is disconnected from
1467 * aPrevSibling
1468 * @param aPrevSibling aFromChild's previous sibling. Must not be null. It's
1469 * an error to push a parent's first child frame
1471 void
1472 nsContainerFrame::PushChildren(nsPresContext* aPresContext,
1473 nsIFrame* aFromChild,
1474 nsIFrame* aPrevSibling)
1476 NS_PRECONDITION(aFromChild, "null pointer");
1477 NS_PRECONDITION(aPrevSibling, "pushing first child");
1478 NS_PRECONDITION(aPrevSibling->GetNextSibling() == aFromChild, "bad prev sibling");
1480 // Disconnect aFromChild from its previous sibling
1481 nsFrameList tail = mFrames.RemoveFramesAfter(aPrevSibling);
1483 nsContainerFrame* nextInFlow =
1484 static_cast<nsContainerFrame*>(GetNextInFlow());
1485 if (nextInFlow) {
1486 // XXX This is not a very good thing to do. If it gets removed
1487 // then remove the copy of this routine that doesn't do this from
1488 // nsInlineFrame.
1489 // When pushing and pulling frames we need to check for whether any
1490 // views need to be reparented.
1491 for (nsIFrame* f = aFromChild; f; f = f->GetNextSibling()) {
1492 nsContainerFrame::ReparentFrameView(aPresContext, f, this, nextInFlow);
1494 nextInFlow->mFrames.InsertFrames(nextInFlow, nullptr, tail);
1496 else {
1497 // Add the frames to our overflow list
1498 SetOverflowFrames(aPresContext, tail);
1503 * Moves any frames on the overflow lists (the prev-in-flow's overflow list and
1504 * the receiver's overflow list) to the child list.
1506 * Updates this frame's child count and content mapping.
1508 * @return true if any frames were moved and false otherwise
1510 bool
1511 nsContainerFrame::MoveOverflowToChildList(nsPresContext* aPresContext)
1513 bool result = false;
1515 // Check for an overflow list with our prev-in-flow
1516 nsContainerFrame* prevInFlow = (nsContainerFrame*)GetPrevInFlow();
1517 if (nullptr != prevInFlow) {
1518 AutoFrameListPtr prevOverflowFrames(aPresContext,
1519 prevInFlow->StealOverflowFrames());
1520 if (prevOverflowFrames) {
1521 // Tables are special; they can have repeated header/footer
1522 // frames on mFrames at this point.
1523 NS_ASSERTION(mFrames.IsEmpty() || GetType() == nsGkAtoms::tableFrame,
1524 "bad overflow list");
1525 // When pushing and pulling frames we need to check for whether any
1526 // views need to be reparented.
1527 nsContainerFrame::ReparentFrameViewList(aPresContext,
1528 *prevOverflowFrames,
1529 prevInFlow, this);
1530 mFrames.AppendFrames(this, *prevOverflowFrames);
1531 result = true;
1535 // It's also possible that we have an overflow list for ourselves.
1536 return DrainSelfOverflowList() || result;
1539 bool
1540 nsContainerFrame::DrainSelfOverflowList()
1542 AutoFrameListPtr overflowFrames(PresContext(), StealOverflowFrames());
1543 if (overflowFrames) {
1544 NS_ASSERTION(mFrames.NotEmpty(), "overflow list w/o frames");
1545 mFrames.AppendFrames(nullptr, *overflowFrames);
1546 return true;
1548 return false;
1551 nsOverflowContinuationTracker::nsOverflowContinuationTracker(nsPresContext* aPresContext,
1552 nsContainerFrame* aFrame,
1553 bool aWalkOOFFrames,
1554 bool aSkipOverflowContainerChildren)
1555 : mOverflowContList(nullptr),
1556 mPrevOverflowCont(nullptr),
1557 mSentry(nullptr),
1558 mParent(aFrame),
1559 mSkipOverflowContainerChildren(aSkipOverflowContainerChildren),
1560 mWalkOOFFrames(aWalkOOFFrames)
1562 NS_PRECONDITION(aFrame, "null frame pointer");
1563 SetupOverflowContList();
1566 void
1567 nsOverflowContinuationTracker::SetupOverflowContList()
1569 NS_PRECONDITION(mParent, "null frame pointer");
1570 NS_PRECONDITION(!mOverflowContList, "already have list");
1571 nsPresContext* pc = mParent->PresContext();
1572 nsContainerFrame* nif =
1573 static_cast<nsContainerFrame*>(mParent->GetNextInFlow());
1574 if (nif) {
1575 mOverflowContList = nif->GetPropTableFrames(pc,
1576 nsContainerFrame::OverflowContainersProperty());
1577 if (mOverflowContList) {
1578 mParent = nif;
1579 SetUpListWalker();
1582 if (!mOverflowContList) {
1583 mOverflowContList = mParent->GetPropTableFrames(pc,
1584 nsContainerFrame::ExcessOverflowContainersProperty());
1585 if (mOverflowContList) {
1586 SetUpListWalker();
1592 * Helper function to walk past overflow continuations whose prev-in-flow
1593 * isn't a normal child and to set mSentry and mPrevOverflowCont correctly.
1595 void
1596 nsOverflowContinuationTracker::SetUpListWalker()
1598 NS_ASSERTION(!mSentry && !mPrevOverflowCont,
1599 "forgot to reset mSentry or mPrevOverflowCont");
1600 if (mOverflowContList) {
1601 nsIFrame* cur = mOverflowContList->FirstChild();
1602 if (mSkipOverflowContainerChildren) {
1603 while (cur && (cur->GetPrevInFlow()->GetStateBits()
1604 & NS_FRAME_IS_OVERFLOW_CONTAINER)) {
1605 mPrevOverflowCont = cur;
1606 cur = cur->GetNextSibling();
1608 while (cur && (!(cur->GetStateBits() & NS_FRAME_OUT_OF_FLOW)
1609 == mWalkOOFFrames)) {
1610 mPrevOverflowCont = cur;
1611 cur = cur->GetNextSibling();
1614 if (cur) {
1615 mSentry = cur->GetPrevInFlow();
1621 * Helper function to step forward through the overflow continuations list.
1622 * Sets mSentry and mPrevOverflowCont, skipping over OOF or non-OOF frames
1623 * as appropriate. May only be called when we have already set up an
1624 * mOverflowContList; mOverflowContList cannot be null.
1626 void
1627 nsOverflowContinuationTracker::StepForward()
1629 NS_PRECONDITION(mOverflowContList, "null list");
1631 // Step forward
1632 if (mPrevOverflowCont) {
1633 mPrevOverflowCont = mPrevOverflowCont->GetNextSibling();
1635 else {
1636 mPrevOverflowCont = mOverflowContList->FirstChild();
1639 // Skip over oof or non-oof frames as appropriate
1640 if (mSkipOverflowContainerChildren) {
1641 nsIFrame* cur = mPrevOverflowCont->GetNextSibling();
1642 while (cur && (!(cur->GetStateBits() & NS_FRAME_OUT_OF_FLOW)
1643 == mWalkOOFFrames)) {
1644 mPrevOverflowCont = cur;
1645 cur = cur->GetNextSibling();
1649 // Set up the sentry
1650 mSentry = (mPrevOverflowCont->GetNextSibling())
1651 ? mPrevOverflowCont->GetNextSibling()->GetPrevInFlow()
1652 : nullptr;
1655 nsresult
1656 nsOverflowContinuationTracker::Insert(nsIFrame* aOverflowCont,
1657 nsReflowStatus& aReflowStatus)
1659 NS_PRECONDITION(aOverflowCont, "null frame pointer");
1660 NS_PRECONDITION(!mSkipOverflowContainerChildren || mWalkOOFFrames ==
1661 !!(aOverflowCont->GetStateBits() & NS_FRAME_OUT_OF_FLOW),
1662 "shouldn't insert frame that doesn't match walker type");
1663 NS_PRECONDITION(aOverflowCont->GetPrevInFlow(),
1664 "overflow containers must have a prev-in-flow");
1665 nsresult rv = NS_OK;
1666 bool reparented = false;
1667 nsPresContext* presContext = aOverflowCont->PresContext();
1668 bool addToList = !mSentry || aOverflowCont != mSentry->GetNextInFlow();
1670 // If we have a list and aOverflowCont is already in it then don't try to
1671 // add it again.
1672 if (addToList && aOverflowCont->GetParent() == mParent &&
1673 (aOverflowCont->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) &&
1674 mOverflowContList && mOverflowContList->ContainsFrame(aOverflowCont)) {
1675 addToList = false;
1676 mPrevOverflowCont = aOverflowCont->GetPrevSibling();
1679 if (addToList) {
1680 if (aOverflowCont->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) {
1681 // aOverflowCont is in some other overflow container list,
1682 // steal it first
1683 NS_ASSERTION(!(mOverflowContList &&
1684 mOverflowContList->ContainsFrame(aOverflowCont)),
1685 "overflow containers out of order");
1686 rv = static_cast<nsContainerFrame*>(aOverflowCont->GetParent())
1687 ->StealFrame(presContext, aOverflowCont);
1688 NS_ENSURE_SUCCESS(rv, rv);
1690 else {
1691 aOverflowCont->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
1693 if (!mOverflowContList) {
1694 mOverflowContList = new (presContext->PresShell()) nsFrameList();
1695 mParent->SetPropTableFrames(presContext, mOverflowContList,
1696 nsContainerFrame::ExcessOverflowContainersProperty());
1697 SetUpListWalker();
1699 if (aOverflowCont->GetParent() != mParent) {
1700 nsContainerFrame::ReparentFrameView(presContext, aOverflowCont,
1701 aOverflowCont->GetParent(),
1702 mParent);
1703 reparented = true;
1706 // If aOverflowCont has a prev/next-in-flow that might be in
1707 // mOverflowContList we need to find it and insert after/before it to
1708 // maintain the order amongst next-in-flows in this list.
1709 nsIFrame* pif = aOverflowCont->GetPrevInFlow();
1710 nsIFrame* nif = aOverflowCont->GetNextInFlow();
1711 if ((pif && pif->GetParent() == mParent && pif != mPrevOverflowCont) ||
1712 (nif && nif->GetParent() == mParent && mPrevOverflowCont)) {
1713 for (nsFrameList::Enumerator e(*mOverflowContList); !e.AtEnd(); e.Next()) {
1714 nsIFrame* f = e.get();
1715 if (f == pif) {
1716 mPrevOverflowCont = pif;
1717 break;
1719 if (f == nif) {
1720 mPrevOverflowCont = f->GetPrevSibling();
1721 break;
1726 mOverflowContList->InsertFrame(mParent, mPrevOverflowCont, aOverflowCont);
1727 aReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
1730 // If we need to reflow it, mark it dirty
1731 if (aReflowStatus & NS_FRAME_REFLOW_NEXTINFLOW)
1732 aOverflowCont->AddStateBits(NS_FRAME_IS_DIRTY);
1734 // It's in our list, just step forward
1735 StepForward();
1736 NS_ASSERTION(mPrevOverflowCont == aOverflowCont ||
1737 (mSkipOverflowContainerChildren &&
1738 (mPrevOverflowCont->GetStateBits() & NS_FRAME_OUT_OF_FLOW) !=
1739 (aOverflowCont->GetStateBits() & NS_FRAME_OUT_OF_FLOW)),
1740 "OverflowContTracker in unexpected state");
1742 if (addToList) {
1743 // Convert all non-overflow-container continuations of aOverflowCont
1744 // into overflow containers and move them to our overflow
1745 // tracker. This preserves the invariant that the next-continuations
1746 // of an overflow container are also overflow containers.
1747 nsIFrame* f = aOverflowCont->GetNextContinuation();
1748 if (f && (!(f->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) ||
1749 (!reparented && f->GetParent() == mParent) ||
1750 (reparented && f->GetParent() != mParent))) {
1751 if (!(f->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) {
1752 nsContainerFrame* parent = static_cast<nsContainerFrame*>(f->GetParent());
1753 rv = parent->StealFrame(presContext, f);
1754 NS_ENSURE_SUCCESS(rv, rv);
1756 Insert(f, aReflowStatus);
1759 return rv;
1762 void
1763 nsOverflowContinuationTracker::BeginFinish(nsIFrame* aChild)
1765 NS_PRECONDITION(aChild, "null ptr");
1766 NS_PRECONDITION(aChild->GetNextInFlow(),
1767 "supposed to call Finish *before* deleting next-in-flow!");
1768 for (nsIFrame* f = aChild; f; f = f->GetNextInFlow()) {
1769 // We'll update these in EndFinish after the next-in-flows are gone.
1770 if (f == mPrevOverflowCont) {
1771 mSentry = nullptr;
1772 mPrevOverflowCont = nullptr;
1773 break;
1775 if (f == mSentry) {
1776 mSentry = nullptr;
1777 break;
1782 void
1783 nsOverflowContinuationTracker::EndFinish(nsIFrame* aChild)
1785 if (!mOverflowContList) {
1786 return;
1788 // Forget mOverflowContList if it was deleted.
1789 nsPresContext* pc = aChild->PresContext();
1790 FramePropertyTable* propTable = pc->PropertyTable();
1791 nsFrameList* eoc = static_cast<nsFrameList*>(propTable->Get(mParent,
1792 nsContainerFrame::ExcessOverflowContainersProperty()));
1793 if (eoc != mOverflowContList) {
1794 nsFrameList* oc = static_cast<nsFrameList*>(propTable->Get(mParent,
1795 nsContainerFrame::OverflowContainersProperty()));
1796 if (oc != mOverflowContList) {
1797 // mOverflowContList was deleted
1798 mPrevOverflowCont = nullptr;
1799 mSentry = nullptr;
1800 mParent = static_cast<nsContainerFrame*>(aChild->GetParent());
1801 mOverflowContList = nullptr;
1802 SetupOverflowContList();
1803 return;
1806 // The list survived, update mSentry if needed.
1807 if (!mSentry) {
1808 if (!mPrevOverflowCont) {
1809 SetUpListWalker();
1810 } else {
1811 mozilla::AutoRestore<nsIFrame*> saved(mPrevOverflowCont);
1812 // step backward to make StepForward() use our current mPrevOverflowCont
1813 mPrevOverflowCont = mPrevOverflowCont->GetPrevSibling();
1814 StepForward();
1819 /////////////////////////////////////////////////////////////////////////////
1820 // Debugging
1822 #ifdef DEBUG
1823 void
1824 nsContainerFrame::List(FILE* out, int32_t aIndent, uint32_t aFlags) const
1826 ListGeneric(out, aIndent, aFlags);
1828 // Output the children
1829 bool outputOneList = false;
1830 ChildListIterator lists(this);
1831 for (; !lists.IsDone(); lists.Next()) {
1832 if (outputOneList) {
1833 IndentBy(out, aIndent);
1835 if (lists.CurrentID() != kPrincipalList) {
1836 if (!outputOneList) {
1837 fputs("\n", out);
1838 IndentBy(out, aIndent);
1840 fputs(mozilla::layout::ChildListName(lists.CurrentID()), out);
1841 fprintf(out, " %p ", &GetChildList(lists.CurrentID()));
1843 fputs("<\n", out);
1844 nsFrameList::Enumerator childFrames(lists.CurrentList());
1845 for (; !childFrames.AtEnd(); childFrames.Next()) {
1846 nsIFrame* kid = childFrames.get();
1847 // Verify the child frame's parent frame pointer is correct
1848 NS_ASSERTION(kid->GetParent() == this, "bad parent frame pointer");
1850 // Have the child frame list
1851 kid->List(out, aIndent + 1, aFlags);
1853 IndentBy(out, aIndent);
1854 fputs(">\n", out);
1855 outputOneList = true;
1858 if (!outputOneList) {
1859 fputs("<>\n", out);
1862 #endif