Bug 508473 part III: Pass destruction root to frame destruction methods r=bz sr=roc
[gecko.git] / layout / generic / nsContainerFrame.cpp
blobc18d21f4d0313321c96a8eb489617bc7c3c98d68
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Pierre Phaneuf <pp@ludusdesign.com>
24 * Elika J. Etemad ("fantasai") <fantasai@inkedblade.net>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 /* base class #1 for rendering objects that have child lists */
42 #include "nsContainerFrame.h"
43 #include "nsHTMLContainerFrame.h"
44 #include "nsIContent.h"
45 #include "nsIDocument.h"
46 #include "nsPresContext.h"
47 #include "nsIRenderingContext.h"
48 #include "nsStyleContext.h"
49 #include "nsRect.h"
50 #include "nsPoint.h"
51 #include "nsGUIEvent.h"
52 #include "nsStyleConsts.h"
53 #include "nsIView.h"
54 #include "nsHTMLContainerFrame.h"
55 #include "nsFrameManager.h"
56 #include "nsIPresShell.h"
57 #include "nsCOMPtr.h"
58 #include "nsGkAtoms.h"
59 #include "nsCSSAnonBoxes.h"
60 #include "nsIViewManager.h"
61 #include "nsIWidget.h"
62 #include "nsGfxCIID.h"
63 #include "nsIServiceManager.h"
64 #include "nsCSSRendering.h"
65 #include "nsTransform2D.h"
66 #include "nsRegion.h"
67 #include "nsLayoutErrors.h"
68 #include "nsDisplayList.h"
69 #include "nsContentErrors.h"
70 #include "nsIEventStateManager.h"
71 #include "nsListControlFrame.h"
72 #include "nsIBaseWindow.h"
73 #include "nsThemeConstants.h"
74 #include "nsCSSFrameConstructor.h"
75 #include "nsThemeConstants.h"
77 #ifdef NS_DEBUG
78 #undef NOISY
79 #else
80 #undef NOISY
81 #endif
83 NS_IMPL_FRAMEARENA_HELPERS(nsContainerFrame)
85 nsContainerFrame::~nsContainerFrame()
89 NS_QUERYFRAME_HEAD(nsContainerFrame)
90 NS_QUERYFRAME_ENTRY(nsContainerFrame)
91 NS_QUERYFRAME_TAIL_INHERITING(nsSplittableFrame)
93 NS_IMETHODIMP
94 nsContainerFrame::Init(nsIContent* aContent,
95 nsIFrame* aParent,
96 nsIFrame* aPrevInFlow)
98 nsresult rv = nsSplittableFrame::Init(aContent, aParent, aPrevInFlow);
99 if (aPrevInFlow) {
100 // Make sure we copy bits from our prev-in-flow that will affect
101 // us. A continuation for a container frame needs to know if it
102 // has a child with a view so that we'll properly reposition it.
103 if (aPrevInFlow->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW)
104 AddStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW);
106 return rv;
109 NS_IMETHODIMP
110 nsContainerFrame::SetInitialChildList(nsIAtom* aListName,
111 nsFrameList& aChildList)
113 nsresult result;
114 if (mFrames.NotEmpty()) {
115 // We already have child frames which means we've already been
116 // initialized
117 NS_NOTREACHED("unexpected second call to SetInitialChildList");
118 result = NS_ERROR_UNEXPECTED;
119 } else if (aListName) {
120 // All we know about is the unnamed principal child list
121 NS_NOTREACHED("unknown frame list");
122 result = NS_ERROR_INVALID_ARG;
123 } else {
124 #ifdef NS_DEBUG
125 nsFrame::VerifyDirtyBitSet(aChildList);
126 #endif
127 mFrames.SetFrames(aChildList);
128 result = NS_OK;
130 return result;
133 NS_IMETHODIMP
134 nsContainerFrame::AppendFrames(nsIAtom* aListName,
135 nsFrameList& aFrameList)
137 if (nsnull != aListName) {
138 #ifdef IBMBIDI
139 if (aListName != nsGkAtoms::nextBidi)
140 #endif
142 NS_ERROR("unexpected child list");
143 return NS_ERROR_INVALID_ARG;
146 if (aFrameList.NotEmpty()) {
147 mFrames.AppendFrames(this, aFrameList);
149 // Ask the parent frame to reflow me.
150 #ifdef IBMBIDI
151 if (nsnull == aListName)
152 #endif
154 PresContext()->PresShell()->
155 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
156 NS_FRAME_HAS_DIRTY_CHILDREN);
159 return NS_OK;
162 NS_IMETHODIMP
163 nsContainerFrame::InsertFrames(nsIAtom* aListName,
164 nsIFrame* aPrevFrame,
165 nsFrameList& aFrameList)
167 NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
168 "inserting after sibling frame with different parent");
170 if (nsnull != aListName) {
171 #ifdef IBMBIDI
172 if (aListName != nsGkAtoms::nextBidi)
173 #endif
175 NS_ERROR("unexpected child list");
176 return NS_ERROR_INVALID_ARG;
179 if (aFrameList.NotEmpty()) {
180 // Insert frames after aPrevFrame
181 mFrames.InsertFrames(this, aPrevFrame, aFrameList);
183 #ifdef IBMBIDI
184 if (nsnull == aListName)
185 #endif
187 PresContext()->PresShell()->
188 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
189 NS_FRAME_HAS_DIRTY_CHILDREN);
192 return NS_OK;
195 NS_IMETHODIMP
196 nsContainerFrame::RemoveFrame(nsIAtom* aListName,
197 nsIFrame* aOldFrame)
199 if (nsnull != aListName) {
200 #ifdef IBMBIDI
201 if (nsGkAtoms::nextBidi != aListName)
202 #endif
204 NS_ERROR("unexpected child list");
205 return NS_ERROR_INVALID_ARG;
209 if (aOldFrame) {
210 // Loop and destroy the frame and all of its continuations.
211 // If the frame we are removing is a brFrame, we need a reflow so
212 // the line the brFrame was on can attempt to pull up any frames
213 // that can fit from lines below it.
214 PRBool generateReflowCommand = PR_TRUE;
215 #ifdef IBMBIDI
216 if (nsGkAtoms::nextBidi == aListName) {
217 generateReflowCommand = PR_FALSE;
219 #endif
220 nsContainerFrame* parent = static_cast<nsContainerFrame*>(aOldFrame->GetParent());
221 while (aOldFrame) {
222 // When the parent is an inline frame we have a simple task - just
223 // remove the frame from its parents list and generate a reflow
224 // command.
225 nsIFrame* oldFrameNextContinuation = aOldFrame->GetNextContinuation();
226 //XXXfr probably should use StealFrame here. I'm not sure if we need to
227 // check the overflow lists atm, but we'll need a prescontext lookup
228 // for overflow containers once we can split abspos elements with
229 // inline containing blocks.
230 if (parent == this) {
231 if (!parent->mFrames.DestroyFrameIfPresent(aOldFrame)) {
232 // Try to remove it from our overflow list, if we have one.
233 // The simplest way is to reuse StealFrame.
234 StealFrame(PresContext(), aOldFrame, PR_TRUE);
235 aOldFrame->Destroy();
237 } else {
238 // This recursive call takes care of all continuations after aOldFrame,
239 // so we don't need to loop anymore.
240 parent->RemoveFrame(nsnull, aOldFrame);
241 break;
243 aOldFrame = oldFrameNextContinuation;
244 if (aOldFrame) {
245 parent = static_cast<nsContainerFrame*>(aOldFrame->GetParent());
249 if (generateReflowCommand) {
250 PresContext()->PresShell()->
251 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
252 NS_FRAME_HAS_DIRTY_CHILDREN);
256 return NS_OK;
259 void
260 nsContainerFrame::DestroyFrom(nsIFrame* aDestructRoot)
262 // Prevent event dispatch during destruction
263 if (HasView()) {
264 GetView()->SetClientData(nsnull);
267 // Delete the primary child list
268 mFrames.DestroyFramesFrom(aDestructRoot);
270 // Destroy auxiliary frame lists
271 nsPresContext* prescontext = PresContext();
273 DestroyOverflowList(prescontext, aDestructRoot);
275 if (IsFrameOfType(nsIFrame::eCanContainOverflowContainers)) {
276 nsFrameList* frameList = RemovePropTableFrames(prescontext,
277 nsGkAtoms::overflowContainersProperty);
278 if (frameList)
279 frameList->DestroyFrom(aDestructRoot);
281 frameList = RemovePropTableFrames(prescontext,
282 nsGkAtoms::excessOverflowContainersProperty);
283 if (frameList)
284 frameList->DestroyFrom(aDestructRoot);
287 if (IsGeneratedContentFrame()) {
288 // Make sure all the content nodes for the generated content inside
289 // this frame know it's going away.
290 // See also nsCSSFrameConstructor::CreateGeneratedContentFrame which
291 // created this frame.
292 nsCOMArray<nsIContent>* generatedContent =
293 static_cast<nsCOMArray<nsIContent>*>(
294 UnsetProperty(nsGkAtoms::generatedContent));
296 if (generatedContent) {
297 for (int i = generatedContent->Count() - 1; i >= 0; --i) {
298 nsIContent* content = generatedContent->ObjectAt(i);
299 // Tell the ESM that this content is going away now, so it'll update
300 // its hover content, etc.
301 PresContext()->EventStateManager()->
302 ContentRemoved(content->GetCurrentDoc(), content);
303 content->UnbindFromTree();
305 delete generatedContent;
309 // Destroy the frame and remove the flow pointers
310 nsSplittableFrame::DestroyFrom(aDestructRoot);
313 /////////////////////////////////////////////////////////////////////////////
314 // Child frame enumeration
316 nsFrameList
317 nsContainerFrame::GetChildList(nsIAtom* aListName) const
319 // We only know about the unnamed principal child list and the overflow
320 // lists
321 if (nsnull == aListName) {
322 return mFrames;
325 if (nsGkAtoms::overflowList == aListName) {
326 nsFrameList* frameList = GetOverflowFrames();
327 return frameList ? *frameList : nsFrameList::EmptyList();
330 if (nsGkAtoms::overflowContainersList == aListName) {
331 nsFrameList* list = GetPropTableFrames(PresContext(),
332 nsGkAtoms::overflowContainersProperty);
333 return list ? *list : nsFrameList::EmptyList();
336 if (nsGkAtoms::excessOverflowContainersList == aListName) {
337 nsFrameList* list = GetPropTableFrames(PresContext(),
338 nsGkAtoms::excessOverflowContainersProperty);
339 return list ? *list : nsFrameList::EmptyList();
342 return nsFrameList::EmptyList();
345 #define NS_CONTAINER_FRAME_OVERFLOW_LIST_INDEX 0
346 #define NS_CONTAINER_FRAME_OVERFLOW_CONTAINERS_LIST_INDEX 1
347 #define NS_CONTAINER_FRAME_EXCESS_OVERFLOW_CONTAINERS_LIST_INDEX 2
348 // If adding/removing lists, don't forget to update count in .h file
351 nsIAtom*
352 nsContainerFrame::GetAdditionalChildListName(PRInt32 aIndex) const
354 if (NS_CONTAINER_FRAME_OVERFLOW_LIST_INDEX == aIndex)
355 return nsGkAtoms::overflowList;
356 else if (IsFrameOfType(nsIFrame::eCanContainOverflowContainers)) {
357 if (NS_CONTAINER_FRAME_OVERFLOW_CONTAINERS_LIST_INDEX == aIndex)
358 return nsGkAtoms::overflowContainersList;
359 else if (NS_CONTAINER_FRAME_EXCESS_OVERFLOW_CONTAINERS_LIST_INDEX == aIndex)
360 return nsGkAtoms::excessOverflowContainersList;
362 return nsnull;
365 /////////////////////////////////////////////////////////////////////////////
366 // Painting/Events
368 NS_IMETHODIMP
369 nsContainerFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
370 const nsRect& aDirtyRect,
371 const nsDisplayListSet& aLists)
373 nsresult rv = DisplayBorderBackgroundOutline(aBuilder, aLists);
374 NS_ENSURE_SUCCESS(rv, rv);
376 return BuildDisplayListForNonBlockChildren(aBuilder, aDirtyRect, aLists);
379 nsresult
380 nsContainerFrame::BuildDisplayListForNonBlockChildren(nsDisplayListBuilder* aBuilder,
381 const nsRect& aDirtyRect,
382 const nsDisplayListSet& aLists,
383 PRUint32 aFlags)
385 nsIFrame* kid = mFrames.FirstChild();
386 // Put each child's background directly onto the content list
387 nsDisplayListSet set(aLists, aLists.Content());
388 // The children should be in content order
389 while (kid) {
390 nsresult rv = BuildDisplayListForChild(aBuilder, kid, aDirtyRect, set, aFlags);
391 NS_ENSURE_SUCCESS(rv, rv);
392 kid = kid->GetNextSibling();
394 return NS_OK;
397 /* virtual */ void
398 nsContainerFrame::ChildIsDirty(nsIFrame* aChild)
400 AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
403 PRBool
404 nsContainerFrame::IsLeaf() const
406 return PR_FALSE;
409 PRBool
410 nsContainerFrame::PeekOffsetNoAmount(PRBool aForward, PRInt32* aOffset)
412 NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range");
413 // Don't allow the caret to stay in an empty (leaf) container frame.
414 return PR_FALSE;
417 PRBool
418 nsContainerFrame::PeekOffsetCharacter(PRBool aForward, PRInt32* aOffset)
420 NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range");
421 // Don't allow the caret to stay in an empty (leaf) container frame.
422 return PR_FALSE;
425 /////////////////////////////////////////////////////////////////////////////
426 // Helper member functions
429 * Position the view associated with |aKidFrame|, if there is one. A
430 * container frame should call this method after positioning a frame,
431 * but before |Reflow|.
433 void
434 nsContainerFrame::PositionFrameView(nsIFrame* aKidFrame)
436 nsIFrame* parentFrame = aKidFrame->GetParent();
437 if (!aKidFrame->HasView() || !parentFrame)
438 return;
440 nsIView* view = aKidFrame->GetView();
441 nsIViewManager* vm = view->GetViewManager();
442 nsPoint pt;
443 nsIView* ancestorView = parentFrame->GetClosestView(&pt);
445 if (ancestorView != view->GetParent()) {
446 NS_ASSERTION(ancestorView == view->GetParent()->GetParent(),
447 "Allowed only one anonymous view between frames");
448 // parentFrame is responsible for positioning aKidFrame's view
449 // explicitly
450 return;
453 pt += aKidFrame->GetPosition();
454 vm->MoveViewTo(view, pt.x, pt.y);
457 static nsIWidget*
458 GetPresContextContainerWidget(nsPresContext* aPresContext)
460 nsCOMPtr<nsISupports> container = aPresContext->Document()->GetContainer();
461 nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(container);
462 if (!baseWindow)
463 return nsnull;
465 nsCOMPtr<nsIWidget> mainWidget;
466 baseWindow->GetMainWidget(getter_AddRefs(mainWidget));
467 return mainWidget;
470 static PRBool
471 IsTopLevelWidget(nsIWidget* aWidget)
473 nsWindowType windowType;
474 aWidget->GetWindowType(windowType);
475 return windowType == eWindowType_toplevel ||
476 windowType == eWindowType_dialog ||
477 windowType == eWindowType_sheet;
478 // popups aren't toplevel so they're not handled here
481 void
482 nsContainerFrame::SyncWindowProperties(nsPresContext* aPresContext,
483 nsIFrame* aFrame,
484 nsIView* aView)
486 #ifdef MOZ_XUL
487 if (!aView || !nsCSSRendering::IsCanvasFrame(aFrame) || !aView->HasWidget())
488 return;
490 nsIWidget* windowWidget = GetPresContextContainerWidget(aPresContext);
491 if (!windowWidget || !IsTopLevelWidget(windowWidget))
492 return;
494 nsIViewManager* vm = aView->GetViewManager();
495 nsIView* rootView;
496 vm->GetRootView(rootView);
498 if (aView != rootView)
499 return;
501 nsIContent* rootContent = aPresContext->Document()->GetRootContent();
502 if (!rootContent || !rootContent->IsXUL()) {
503 // Scrollframes use native widgets which don't work well with
504 // translucent windows, at least in Windows XP. So if the document
505 // has a root scrollrame it's useless to try to make it transparent,
506 // we'll just get something broken.
507 // nsCSSFrameConstructor::ConstructRootFrame constructs root
508 // scrollframes whenever the root element is not a XUL element, so
509 // we test for that here. We can't just call
510 // presShell->GetRootScrollFrame() since that might not have
511 // been constructed yet.
512 // We can change this to allow translucent toplevel HTML documents
513 // (e.g. to do something like Dashboard widgets), once we
514 // have broad support for translucent scrolled documents, but be
515 // careful because apparently some Firefox extensions expect
516 // openDialog("something.html") to produce an opaque window
517 // even if the HTML doesn't have a background-color set.
518 return;
521 nsIFrame *rootFrame = aPresContext->PresShell()->FrameConstructor()->GetRootElementStyleFrame();
522 if (!rootFrame)
523 return;
525 nsTransparencyMode mode = nsLayoutUtils::GetFrameTransparency(aFrame, rootFrame);
526 nsIWidget* viewWidget = aView->GetWidget();
527 viewWidget->SetTransparencyMode(mode);
528 windowWidget->SetWindowShadowStyle(rootFrame->GetStyleUIReset()->mWindowShadow);
529 #endif
532 void
533 nsContainerFrame::SyncFrameViewAfterReflow(nsPresContext* aPresContext,
534 nsIFrame* aFrame,
535 nsIView* aView,
536 const nsRect* aCombinedArea,
537 PRUint32 aFlags)
539 if (!aView) {
540 return;
543 NS_ASSERTION(aCombinedArea, "Combined area must be passed in now");
545 // Make sure the view is sized and positioned correctly
546 if (0 == (aFlags & NS_FRAME_NO_MOVE_VIEW)) {
547 PositionFrameView(aFrame);
550 if (0 == (aFlags & NS_FRAME_NO_SIZE_VIEW)) {
551 nsIViewManager* vm = aView->GetViewManager();
553 vm->ResizeView(aView, *aCombinedArea, PR_TRUE);
557 void
558 nsContainerFrame::SyncFrameViewProperties(nsPresContext* aPresContext,
559 nsIFrame* aFrame,
560 nsStyleContext* aStyleContext,
561 nsIView* aView,
562 PRUint32 aFlags)
564 NS_ASSERTION(!aStyleContext || aFrame->GetStyleContext() == aStyleContext,
565 "Wrong style context for frame?");
567 if (!aView) {
568 return;
571 nsIViewManager* vm = aView->GetViewManager();
573 /* If this frame has a -moz-transform property, tell it to invalidate on a scroll
574 * rather than doing a BitBlt.
576 if (aFrame->GetStyleDisplay()->HasTransform())
577 aView->SetInvalidateFrameOnScroll();
579 if (nsnull == aStyleContext) {
580 aStyleContext = aFrame->GetStyleContext();
583 // Make sure visibility is correct. This only affects nsSubdocumentFrame.
584 if (0 == (aFlags & NS_FRAME_NO_VISIBILITY) &&
585 !aFrame->SupportsVisibilityHidden()) {
586 // See if the view should be hidden or visible
587 vm->SetViewVisibility(aView,
588 aStyleContext->GetStyleVisibility()->IsVisible()
589 ? nsViewVisibility_kShow : nsViewVisibility_kHide);
592 // See if the frame is being relatively positioned or absolutely
593 // positioned
594 PRBool isPositioned = aStyleContext->GetStyleDisplay()->IsPositioned();
596 PRInt32 zIndex = 0;
597 PRBool autoZIndex = PR_FALSE;
599 if (!isPositioned) {
600 autoZIndex = PR_TRUE;
601 } else {
602 // Make sure z-index is correct
603 const nsStylePosition* position = aStyleContext->GetStylePosition();
605 if (position->mZIndex.GetUnit() == eStyleUnit_Integer) {
606 zIndex = position->mZIndex.GetIntValue();
607 } else if (position->mZIndex.GetUnit() == eStyleUnit_Auto) {
608 autoZIndex = PR_TRUE;
612 vm->SetViewZIndex(aView, autoZIndex, zIndex, isPositioned);
615 PRBool
616 nsContainerFrame::FrameNeedsView(nsIFrame* aFrame)
618 // XXX Check needed because frame construction can't properly figure out when
619 // a frame is the child of a scrollframe
620 if (aFrame->GetStyleContext()->GetPseudo() ==
621 nsCSSAnonBoxes::scrolledContent) {
622 return PR_TRUE;
624 return aFrame->NeedsView() || aFrame->GetStyleDisplay()->HasTransform();
627 static nscoord GetCoord(const nsStyleCoord& aCoord, nscoord aIfNotCoord)
629 return aCoord.GetUnit() == eStyleUnit_Coord
630 ? aCoord.GetCoordValue()
631 : aIfNotCoord;
634 void
635 nsContainerFrame::DoInlineIntrinsicWidth(nsIRenderingContext *aRenderingContext,
636 InlineIntrinsicWidthData *aData,
637 nsLayoutUtils::IntrinsicWidthType aType)
639 if (GetPrevInFlow())
640 return; // Already added.
642 NS_PRECONDITION(aType == nsLayoutUtils::MIN_WIDTH ||
643 aType == nsLayoutUtils::PREF_WIDTH, "bad type");
645 PRUint8 startSide, endSide;
646 if (GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR) {
647 startSide = NS_SIDE_LEFT;
648 endSide = NS_SIDE_RIGHT;
649 } else {
650 startSide = NS_SIDE_RIGHT;
651 endSide = NS_SIDE_LEFT;
654 const nsStylePadding *stylePadding = GetStylePadding();
655 const nsStyleBorder *styleBorder = GetStyleBorder();
656 const nsStyleMargin *styleMargin = GetStyleMargin();
658 // This goes at the beginning no matter how things are broken and how
659 // messy the bidi situations are, since per CSS2.1 section 8.6
660 // (implemented in bug 328168), the startSide border is always on the
661 // first line.
662 // This frame is a first-in-flow, but it might have a previous bidi
663 // continuation, in which case that continuation should handle the startSide
664 // border.
665 if (!GetPrevContinuation()) {
666 aData->currentLine +=
667 GetCoord(stylePadding->mPadding.Get(startSide), 0) +
668 styleBorder->GetActualBorderWidth(startSide) +
669 GetCoord(styleMargin->mMargin.Get(startSide), 0);
672 const nsLineList_iterator* savedLine = aData->line;
673 nsIFrame* const savedLineContainer = aData->lineContainer;
675 nsContainerFrame *lastInFlow;
676 for (nsContainerFrame *nif = this; nif;
677 nif = static_cast<nsContainerFrame*>(nif->GetNextInFlow())) {
678 for (nsIFrame *kid = nif->mFrames.FirstChild(); kid;
679 kid = kid->GetNextSibling()) {
680 if (aType == nsLayoutUtils::MIN_WIDTH)
681 kid->AddInlineMinWidth(aRenderingContext,
682 static_cast<InlineMinWidthData*>(aData));
683 else
684 kid->AddInlinePrefWidth(aRenderingContext,
685 static_cast<InlinePrefWidthData*>(aData));
688 // After we advance to our next-in-flow, the stored line and line container
689 // may no longer be correct. Just forget them.
690 aData->line = nsnull;
691 aData->lineContainer = nsnull;
693 lastInFlow = nif;
696 aData->line = savedLine;
697 aData->lineContainer = savedLineContainer;
699 // This goes at the end no matter how things are broken and how
700 // messy the bidi situations are, since per CSS2.1 section 8.6
701 // (implemented in bug 328168), the endSide border is always on the
702 // last line.
703 // We reached the last-in-flow, but it might have a next bidi
704 // continuation, in which case that continuation should handle
705 // the endSide border.
706 if (!lastInFlow->GetNextContinuation()) {
707 aData->currentLine +=
708 GetCoord(stylePadding->mPadding.Get(endSide), 0) +
709 styleBorder->GetActualBorderWidth(endSide) +
710 GetCoord(styleMargin->mMargin.Get(endSide), 0);
714 /* virtual */ nsSize
715 nsContainerFrame::ComputeAutoSize(nsIRenderingContext *aRenderingContext,
716 nsSize aCBSize, nscoord aAvailableWidth,
717 nsSize aMargin, nsSize aBorder,
718 nsSize aPadding, PRBool aShrinkWrap)
720 nsSize result(0xdeadbeef, NS_UNCONSTRAINEDSIZE);
721 nscoord availBased = aAvailableWidth - aMargin.width - aBorder.width -
722 aPadding.width;
723 // replaced elements always shrink-wrap
724 if (aShrinkWrap || IsFrameOfType(eReplaced)) {
725 // don't bother setting it if the result won't be used
726 if (GetStylePosition()->mWidth.GetUnit() == eStyleUnit_Auto) {
727 result.width = ShrinkWidthToFit(aRenderingContext, availBased);
729 } else {
730 result.width = availBased;
732 return result;
736 * Invokes the WillReflow() function, positions the frame and its view (if
737 * requested), and then calls Reflow(). If the reflow succeeds and the child
738 * frame is complete, deletes any next-in-flows using DeleteNextInFlowChild()
740 nsresult
741 nsContainerFrame::ReflowChild(nsIFrame* aKidFrame,
742 nsPresContext* aPresContext,
743 nsHTMLReflowMetrics& aDesiredSize,
744 const nsHTMLReflowState& aReflowState,
745 nscoord aX,
746 nscoord aY,
747 PRUint32 aFlags,
748 nsReflowStatus& aStatus,
749 nsOverflowContinuationTracker* aTracker)
751 NS_PRECONDITION(aReflowState.frame == aKidFrame, "bad reflow state");
753 nsresult result;
755 // Send the WillReflow() notification, and position the child frame
756 // and its view if requested
757 aKidFrame->WillReflow(aPresContext);
759 if (NS_FRAME_NO_MOVE_FRAME != (aFlags & NS_FRAME_NO_MOVE_FRAME)) {
760 if ((aFlags & NS_FRAME_INVALIDATE_ON_MOVE) &&
761 !(aKidFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) &&
762 aKidFrame->GetPosition() != nsPoint(aX, aY)) {
763 aKidFrame->InvalidateOverflowRect();
765 aKidFrame->SetPosition(nsPoint(aX, aY));
768 if (0 == (aFlags & NS_FRAME_NO_MOVE_VIEW)) {
769 PositionFrameView(aKidFrame);
772 // Reflow the child frame
773 result = aKidFrame->Reflow(aPresContext, aDesiredSize, aReflowState,
774 aStatus);
776 // If the reflow was successful and the child frame is complete, delete any
777 // next-in-flows
778 if (NS_SUCCEEDED(result) && NS_FRAME_IS_FULLY_COMPLETE(aStatus)) {
779 nsIFrame* kidNextInFlow = aKidFrame->GetNextInFlow();
780 if (nsnull != kidNextInFlow) {
781 // Remove all of the childs next-in-flows. Make sure that we ask
782 // the right parent to do the removal (it's possible that the
783 // parent is not this because we are executing pullup code)
784 if (aTracker) aTracker->Finish(aKidFrame);
785 static_cast<nsContainerFrame*>(kidNextInFlow->GetParent())
786 ->DeleteNextInFlowChild(aPresContext, kidNextInFlow, PR_TRUE);
789 return result;
794 * Position the views of |aFrame|'s descendants. A container frame
795 * should call this method if it moves a frame after |Reflow|.
797 void
798 nsContainerFrame::PositionChildViews(nsIFrame* aFrame)
800 if (!(aFrame->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW)) {
801 return;
804 nsIAtom* childListName = nsnull;
805 PRInt32 childListIndex = 0;
807 do {
808 // Recursively walk aFrame's child frames
809 nsIFrame* childFrame = aFrame->GetFirstChild(childListName);
810 while (childFrame) {
811 // Position the frame's view (if it has one) otherwise recursively
812 // process its children
813 if (childFrame->HasView()) {
814 PositionFrameView(childFrame);
815 } else {
816 PositionChildViews(childFrame);
819 // Get the next sibling child frame
820 childFrame = childFrame->GetNextSibling();
823 // also process the additional child lists, but skip the popup list as the
824 // view for popups is managed by the parent. Currently only nsMenuFrame
825 // has a popupList and during layout will call nsMenuPopupFrame::AdjustView.
826 do {
827 childListName = aFrame->GetAdditionalChildListName(childListIndex++);
828 } while (childListName == nsGkAtoms::popupList);
829 } while (childListName);
833 * The second half of frame reflow. Does the following:
834 * - sets the frame's bounds
835 * - sizes and positions (if requested) the frame's view. If the frame's final
836 * position differs from the current position and the frame itself does not
837 * have a view, then any child frames with views are positioned so they stay
838 * in sync
839 * - sets the view's visibility, opacity, content transparency, and clip
840 * - invoked the DidReflow() function
842 * Flags:
843 * NS_FRAME_NO_MOVE_FRAME - don't move the frame. aX and aY are ignored in this
844 * case. Also implies NS_FRAME_NO_MOVE_VIEW
845 * NS_FRAME_NO_MOVE_VIEW - don't position the frame's view. Set this if you
846 * don't want to automatically sync the frame and view
847 * NS_FRAME_NO_SIZE_VIEW - don't size the frame's view
849 nsresult
850 nsContainerFrame::FinishReflowChild(nsIFrame* aKidFrame,
851 nsPresContext* aPresContext,
852 const nsHTMLReflowState* aReflowState,
853 const nsHTMLReflowMetrics& aDesiredSize,
854 nscoord aX,
855 nscoord aY,
856 PRUint32 aFlags)
858 nsPoint curOrigin = aKidFrame->GetPosition();
859 nsRect bounds(aX, aY, aDesiredSize.width, aDesiredSize.height);
861 aKidFrame->SetRect(bounds);
863 if (aKidFrame->HasView()) {
864 nsIView* view = aKidFrame->GetView();
865 // Make sure the frame's view is properly sized and positioned and has
866 // things like opacity correct
867 SyncFrameViewAfterReflow(aPresContext, aKidFrame, view,
868 &aDesiredSize.mOverflowArea,
869 aFlags);
872 if (!(aFlags & NS_FRAME_NO_MOVE_VIEW) &&
873 (curOrigin.x != aX || curOrigin.y != aY)) {
874 if (!aKidFrame->HasView()) {
875 // If the frame has moved, then we need to make sure any child views are
876 // correctly positioned
877 PositionChildViews(aKidFrame);
880 // We also need to redraw everything associated with the frame
881 // because if the frame's Reflow issued any invalidates, then they
882 // will be at the wrong offset ... note that this includes
883 // invalidates issued against the frame's children, so we need to
884 // invalidate the overflow area too.
885 aKidFrame->Invalidate(aDesiredSize.mOverflowArea);
888 return aKidFrame->DidReflow(aPresContext, aReflowState, NS_FRAME_REFLOW_FINISHED);
891 nsresult
892 nsContainerFrame::ReflowOverflowContainerChildren(nsPresContext* aPresContext,
893 const nsHTMLReflowState& aReflowState,
894 nsRect& aOverflowRect,
895 PRUint32 aFlags,
896 nsReflowStatus& aStatus)
898 NS_PRECONDITION(aPresContext, "null pointer");
899 nsresult rv = NS_OK;
901 nsFrameList* overflowContainers =
902 GetPropTableFrames(aPresContext,
903 nsGkAtoms::overflowContainersProperty);
905 NS_ASSERTION(!(overflowContainers && GetPrevInFlow()
906 && static_cast<nsContainerFrame*>(GetPrevInFlow())
907 ->GetPropTableFrames(aPresContext,
908 nsGkAtoms::excessOverflowContainersProperty)),
909 "conflicting overflow containers lists");
911 if (!overflowContainers) {
912 // Drain excess from previnflow
913 nsContainerFrame* prev = (nsContainerFrame*) GetPrevInFlow();
914 if (prev) {
915 nsFrameList* excessFrames =
916 prev->RemovePropTableFrames(aPresContext,
917 nsGkAtoms::excessOverflowContainersProperty);
918 if (excessFrames) {
919 excessFrames->ApplySetParent(this);
920 nsHTMLContainerFrame::ReparentFrameViewList(aPresContext, *excessFrames,
921 prev, this);
922 overflowContainers = excessFrames;
923 rv = SetPropTableFrames(aPresContext, overflowContainers,
924 nsGkAtoms::overflowContainersProperty);
925 if (NS_FAILED(rv)) {
926 excessFrames->DestroyFrames();
927 delete excessFrames;
928 return rv;
934 if (!overflowContainers)
935 return NS_OK; // nothing to reflow
937 nsOverflowContinuationTracker tracker(aPresContext, this, PR_FALSE, PR_FALSE);
938 for (nsIFrame* frame = overflowContainers->FirstChild(); frame;
939 frame = frame->GetNextSibling()) {
940 if (frame->GetPrevInFlow()->GetParent() != GetPrevInFlow()) {
941 // frame's prevInFlow has moved, skip reflowing this frame;
942 // it will get reflowed once it's been placed
943 continue;
945 if (NS_SUBTREE_DIRTY(frame)) {
946 // Get prev-in-flow
947 nsIFrame* prevInFlow = frame->GetPrevInFlow();
948 NS_ASSERTION(prevInFlow,
949 "overflow container frame must have a prev-in-flow");
950 NS_ASSERTION(frame->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER,
951 "overflow container frame must have overflow container bit set");
952 nsRect prevRect = prevInFlow->GetRect();
954 // Initialize reflow params
955 nsSize availSpace(prevRect.width, aReflowState.availableHeight);
956 nsHTMLReflowMetrics desiredSize;
957 nsHTMLReflowState frameState(aPresContext, aReflowState,
958 frame, availSpace);
959 nsReflowStatus frameStatus = NS_FRAME_COMPLETE;
961 // Cache old bounds
962 nsRect oldRect = frame->GetRect();
963 nsRect oldOverflow = frame->GetOverflowRect();
965 // Reflow
966 rv = ReflowChild(frame, aPresContext, desiredSize, frameState,
967 prevRect.x, 0, aFlags, frameStatus, &tracker);
968 NS_ENSURE_SUCCESS(rv, rv);
969 //XXXfr Do we need to override any shrinkwrap effects here?
970 // e.g. desiredSize.width = prevRect.width;
971 rv = FinishReflowChild(frame, aPresContext, &frameState, desiredSize,
972 prevRect.x, 0, aFlags);
973 NS_ENSURE_SUCCESS(rv, rv);
975 // Invalidate if there was a position or size change
976 nsRect rect = frame->GetRect();
977 if (rect != oldRect) {
978 nsRect dirtyRect = oldOverflow;
979 dirtyRect.MoveBy(oldRect.x, oldRect.y);
980 Invalidate(dirtyRect);
982 dirtyRect = frame->GetOverflowRect();
983 dirtyRect.MoveBy(rect.x, rect.y);
984 Invalidate(dirtyRect);
987 // Handle continuations
988 if (!NS_FRAME_IS_FULLY_COMPLETE(frameStatus)) {
989 if (frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
990 // Abspos frames can't cause their parent to be incomplete,
991 // only overflow incomplete.
992 NS_FRAME_SET_OVERFLOW_INCOMPLETE(frameStatus);
994 else {
995 NS_ASSERTION(NS_FRAME_IS_COMPLETE(frameStatus),
996 "overflow container frames can't be incomplete, only overflow-incomplete");
999 // Acquire a next-in-flow, creating it if necessary
1000 nsIFrame* nif = frame->GetNextInFlow();
1001 if (!nif) {
1002 NS_ASSERTION(frameStatus & NS_FRAME_REFLOW_NEXTINFLOW,
1003 "Someone forgot a REFLOW_NEXTINFLOW flag");
1004 rv = aPresContext->PresShell()->FrameConstructor()->
1005 CreateContinuingFrame(aPresContext, frame, this, &nif);
1006 NS_ENSURE_SUCCESS(rv, rv);
1008 else if (!(nif->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) {
1009 // used to be a normal next-in-flow; steal it from the child list
1010 rv = static_cast<nsContainerFrame*>(nif->GetParent())
1011 ->StealFrame(aPresContext, nif);
1012 NS_ENSURE_SUCCESS(rv, rv);
1015 tracker.Insert(nif, frameStatus);
1017 NS_MergeReflowStatusInto(&aStatus, frameStatus);
1018 // At this point it would be nice to assert !frame->GetOverflowRect().IsEmpty(),
1019 // but we have some unsplittable frames that, when taller than
1020 // availableHeight will push zero-height content into a next-in-flow.
1022 else {
1023 tracker.Skip(frame, aStatus);
1024 if (aReflowState.mFloatManager)
1025 nsBlockFrame::RecoverFloatsFor(frame, *aReflowState.mFloatManager);
1027 ConsiderChildOverflow(aOverflowRect, frame);
1030 return NS_OK;
1033 void
1034 nsContainerFrame::DisplayOverflowContainers(nsDisplayListBuilder* aBuilder,
1035 const nsRect& aDirtyRect,
1036 const nsDisplayListSet& aLists)
1038 nsFrameList* overflowconts = GetPropTableFrames(PresContext(),
1039 nsGkAtoms::overflowContainersProperty);
1040 if (overflowconts) {
1041 for (nsIFrame* frame = overflowconts->FirstChild(); frame;
1042 frame = frame->GetNextSibling()) {
1043 BuildDisplayListForChild(aBuilder, frame, aDirtyRect, aLists);
1048 nsresult
1049 nsContainerFrame::StealFrame(nsPresContext* aPresContext,
1050 nsIFrame* aChild,
1051 PRBool aForceNormal)
1053 PRBool removed = PR_TRUE;
1054 if ((aChild->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)
1055 && !aForceNormal) {
1056 // Try removing from the overflow container list
1057 if (!RemovePropTableFrame(aPresContext, aChild,
1058 nsGkAtoms::overflowContainersProperty)) {
1059 // It must be in the excess overflow container list
1060 removed = RemovePropTableFrame(aPresContext, aChild,
1061 nsGkAtoms::excessOverflowContainersProperty);
1064 else {
1065 if (!mFrames.RemoveFrameIfPresent(aChild)) {
1066 removed = PR_FALSE;
1067 // We didn't find the child in the parent's principal child list.
1068 // Maybe it's on the overflow list?
1069 nsFrameList* frameList = GetOverflowFrames();
1070 if (frameList) {
1071 removed = frameList->RemoveFrameIfPresent(aChild);
1072 if (frameList->IsEmpty()) {
1073 DestroyOverflowList(aPresContext);
1079 NS_POSTCONDITION(removed, "StealFrame: can't find aChild");
1080 return removed ? NS_OK : NS_ERROR_UNEXPECTED;
1083 nsFrameList
1084 nsContainerFrame::StealFramesAfter(nsIFrame* aChild)
1086 NS_ASSERTION(!aChild ||
1087 !(aChild->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER),
1088 "StealFramesAfter doesn't handle overflow containers");
1089 NS_ASSERTION(GetType() != nsGkAtoms::blockFrame, "unexpected call");
1091 if (!aChild) {
1092 nsFrameList copy(mFrames);
1093 mFrames.Clear();
1094 return copy;
1097 for (nsFrameList::FrameLinkEnumerator iter(mFrames); !iter.AtEnd();
1098 iter.Next()) {
1099 if (iter.PrevFrame() == aChild) {
1100 return mFrames.ExtractTail(iter);
1104 // We didn't find the child in the principal child list.
1105 // Maybe it's on the overflow list?
1106 nsFrameList* overflowFrames = GetOverflowFrames();
1107 if (overflowFrames) {
1108 for (nsFrameList::FrameLinkEnumerator iter(*overflowFrames); !iter.AtEnd();
1109 iter.Next()) {
1110 if (iter.PrevFrame() == aChild) {
1111 return overflowFrames->ExtractTail(iter);
1116 NS_ERROR("StealFramesAfter: can't find aChild");
1117 return nsFrameList::EmptyList();
1120 void
1121 nsContainerFrame::DestroyOverflowList(nsPresContext* aPresContext,
1122 nsIFrame* aDestructRoot)
1124 nsFrameList* list =
1125 RemovePropTableFrames(aPresContext, nsGkAtoms::overflowProperty);
1126 if (list)
1127 list->DestroyFrom(aDestructRoot);
1131 * Remove and delete aNextInFlow and its next-in-flows. Updates the sibling and flow
1132 * pointers
1134 void
1135 nsContainerFrame::DeleteNextInFlowChild(nsPresContext* aPresContext,
1136 nsIFrame* aNextInFlow,
1137 PRBool aDeletingEmptyFrames)
1139 #ifdef DEBUG
1140 nsIFrame* prevInFlow = aNextInFlow->GetPrevInFlow();
1141 #endif
1142 NS_PRECONDITION(prevInFlow, "bad prev-in-flow");
1144 // If the next-in-flow has a next-in-flow then delete it, too (and
1145 // delete it first).
1146 // Do this in a loop so we don't overflow the stack for frames
1147 // with very many next-in-flows
1148 nsIFrame* nextNextInFlow = aNextInFlow->GetNextInFlow();
1149 if (nextNextInFlow) {
1150 nsAutoTArray<nsIFrame*, 8> frames;
1151 for (nsIFrame* f = nextNextInFlow; f; f = f->GetNextInFlow()) {
1152 frames.AppendElement(f);
1154 for (PRInt32 i = frames.Length() - 1; i >= 0; --i) {
1155 nsIFrame* delFrame = frames.ElementAt(i);
1156 static_cast<nsContainerFrame*>(delFrame->GetParent())
1157 ->DeleteNextInFlowChild(aPresContext, delFrame, aDeletingEmptyFrames);
1161 aNextInFlow->Invalidate(aNextInFlow->GetOverflowRect());
1163 // Take the next-in-flow out of the parent's child list
1164 #ifdef DEBUG
1165 nsresult rv =
1166 #endif
1167 StealFrame(aPresContext, aNextInFlow);
1168 NS_ASSERTION(NS_SUCCEEDED(rv), "StealFrame failure");
1170 // Delete the next-in-flow frame and its descendants. This will also
1171 // remove it from its next-in-flow/prev-in-flow chain.
1172 aNextInFlow->Destroy();
1174 NS_POSTCONDITION(!prevInFlow->GetNextInFlow(), "non null next-in-flow");
1177 // Destructor function for the proptable-stored framelists
1178 static void
1179 DestroyFrameList(void* aFrame,
1180 nsIAtom* aPropertyName,
1181 void* aPropertyValue,
1182 void* aDtorData)
1184 if (aPropertyValue)
1185 static_cast<nsFrameList*>(aPropertyValue)->Destroy();
1189 * Set the frames on the overflow list
1191 nsresult
1192 nsContainerFrame::SetOverflowFrames(nsPresContext* aPresContext,
1193 const nsFrameList& aOverflowFrames)
1195 NS_PRECONDITION(aOverflowFrames.NotEmpty(), "Shouldn't be called");
1196 nsFrameList* newList = new nsFrameList(aOverflowFrames);
1197 if (!newList) {
1198 // XXXbz should really destroy the frames here, but callers are holding
1199 // pointers to them.... We should switch all callers to framelists, then
1200 // audit and do that.
1201 return NS_ERROR_OUT_OF_MEMORY;
1204 nsresult rv =
1205 aPresContext->PropertyTable()->SetProperty(this,
1206 nsGkAtoms::overflowProperty,
1207 newList,
1208 DestroyFrameList,
1209 nsnull);
1210 if (NS_FAILED(rv)) {
1211 newList->Destroy();
1214 // Verify that we didn't overwrite an existing overflow list
1215 NS_ASSERTION(rv != NS_PROPTABLE_PROP_OVERWRITTEN, "existing overflow list");
1217 return rv;
1220 nsFrameList*
1221 nsContainerFrame::GetPropTableFrames(nsPresContext* aPresContext,
1222 nsIAtom* aPropID) const
1224 nsPropertyTable* propTable = aPresContext->PropertyTable();
1225 return static_cast<nsFrameList*>(propTable->GetProperty(this, aPropID));
1228 nsFrameList*
1229 nsContainerFrame::RemovePropTableFrames(nsPresContext* aPresContext,
1230 nsIAtom* aPropID) const
1232 nsPropertyTable* propTable = aPresContext->PropertyTable();
1233 return static_cast<nsFrameList*>(propTable->UnsetProperty(this, aPropID));
1236 PRBool
1237 nsContainerFrame::RemovePropTableFrame(nsPresContext* aPresContext,
1238 nsIFrame* aFrame,
1239 nsIAtom* aPropID) const
1241 nsFrameList* frameList = RemovePropTableFrames(aPresContext, aPropID);
1242 if (!frameList) {
1243 // No such list
1244 return PR_FALSE;
1246 if (!frameList->RemoveFrameIfPresent(aFrame)) {
1247 // Found list, but it doesn't have the frame. Put list back.
1248 SetPropTableFrames(aPresContext, frameList, aPropID);
1249 return PR_FALSE;
1252 if (frameList->IsEmpty()) {
1253 // Removed frame and now list is empty. Delete it.
1254 delete frameList;
1256 else {
1257 // Removed frame, but list not empty. Put it back.
1258 SetPropTableFrames(aPresContext, frameList, aPropID);
1260 return PR_TRUE;
1263 nsresult
1264 nsContainerFrame::SetPropTableFrames(nsPresContext* aPresContext,
1265 nsFrameList* aFrameList,
1266 nsIAtom* aPropID) const
1268 NS_PRECONDITION(aPresContext && aPropID && aFrameList, "null ptr");
1269 nsresult rv = aPresContext->PropertyTable()->SetProperty(this, aPropID,
1270 aFrameList, DestroyFrameList, nsnull);
1271 NS_ASSERTION(rv != NS_PROPTABLE_PROP_OVERWRITTEN, "existing framelist");
1272 return rv;
1276 * Push aFromChild and its next siblings to the next-in-flow. Change the
1277 * geometric parent of each frame that's pushed. If there is no next-in-flow
1278 * the frames are placed on the overflow list (and the geometric parent is
1279 * left unchanged).
1281 * Updates the next-in-flow's child count. Does <b>not</b> update the
1282 * pusher's child count.
1284 * @param aFromChild the first child frame to push. It is disconnected from
1285 * aPrevSibling
1286 * @param aPrevSibling aFromChild's previous sibling. Must not be null. It's
1287 * an error to push a parent's first child frame
1289 void
1290 nsContainerFrame::PushChildren(nsPresContext* aPresContext,
1291 nsIFrame* aFromChild,
1292 nsIFrame* aPrevSibling)
1294 NS_PRECONDITION(aFromChild, "null pointer");
1295 NS_PRECONDITION(aPrevSibling, "pushing first child");
1296 NS_PRECONDITION(aPrevSibling->GetNextSibling() == aFromChild, "bad prev sibling");
1298 // Disconnect aFromChild from its previous sibling
1299 nsFrameList tail = mFrames.RemoveFramesAfter(aPrevSibling);
1301 nsContainerFrame* nextInFlow =
1302 static_cast<nsContainerFrame*>(GetNextInFlow());
1303 if (nextInFlow) {
1304 // XXX This is not a very good thing to do. If it gets removed
1305 // then remove the copy of this routine that doesn't do this from
1306 // nsInlineFrame.
1307 // When pushing and pulling frames we need to check for whether any
1308 // views need to be reparented.
1309 for (nsIFrame* f = aFromChild; f; f = f->GetNextSibling()) {
1310 nsHTMLContainerFrame::ReparentFrameView(aPresContext, f, this, nextInFlow);
1312 nextInFlow->mFrames.InsertFrames(nextInFlow, nsnull, tail);
1314 else {
1315 // Add the frames to our overflow list
1316 SetOverflowFrames(aPresContext, tail);
1321 * Moves any frames on the overflow lists (the prev-in-flow's overflow list and
1322 * the receiver's overflow list) to the child list.
1324 * Updates this frame's child count and content mapping.
1326 * @return PR_TRUE if any frames were moved and PR_FALSE otherwise
1328 PRBool
1329 nsContainerFrame::MoveOverflowToChildList(nsPresContext* aPresContext)
1331 PRBool result = PR_FALSE;
1333 // Check for an overflow list with our prev-in-flow
1334 nsContainerFrame* prevInFlow = (nsContainerFrame*)GetPrevInFlow();
1335 if (nsnull != prevInFlow) {
1336 nsAutoPtr<nsFrameList> prevOverflowFrames(prevInFlow->StealOverflowFrames());
1337 if (prevOverflowFrames) {
1338 // Tables are special; they can have repeated header/footer
1339 // frames on mFrames at this point.
1340 NS_ASSERTION(mFrames.IsEmpty() || GetType() == nsGkAtoms::tableFrame,
1341 "bad overflow list");
1342 // When pushing and pulling frames we need to check for whether any
1343 // views need to be reparented.
1344 nsHTMLContainerFrame::ReparentFrameViewList(aPresContext,
1345 *prevOverflowFrames,
1346 prevInFlow, this);
1347 mFrames.AppendFrames(this, *prevOverflowFrames);
1348 result = PR_TRUE;
1352 // It's also possible that we have an overflow list for ourselves
1353 nsAutoPtr<nsFrameList> overflowFrames(StealOverflowFrames());
1354 if (overflowFrames) {
1355 NS_ASSERTION(mFrames.NotEmpty(), "overflow list w/o frames");
1356 mFrames.AppendFrames(nsnull, *overflowFrames);
1357 result = PR_TRUE;
1359 return result;
1362 nsOverflowContinuationTracker::nsOverflowContinuationTracker(nsPresContext* aPresContext,
1363 nsContainerFrame* aFrame,
1364 PRBool aWalkOOFFrames,
1365 PRBool aSkipOverflowContainerChildren)
1366 : mOverflowContList(nsnull),
1367 mPrevOverflowCont(nsnull),
1368 mSentry(nsnull),
1369 mParent(aFrame),
1370 mSkipOverflowContainerChildren(aSkipOverflowContainerChildren),
1371 mWalkOOFFrames(aWalkOOFFrames)
1373 NS_PRECONDITION(aFrame, "null frame pointer");
1374 nsContainerFrame* next = static_cast<nsContainerFrame*>
1375 (aFrame->GetNextInFlow());
1376 if (next) {
1377 mOverflowContList =
1378 next->GetPropTableFrames(aPresContext,
1379 nsGkAtoms::overflowContainersProperty);
1380 if (mOverflowContList) {
1381 mParent = next;
1382 SetUpListWalker();
1385 if (!mOverflowContList) {
1386 mOverflowContList =
1387 mParent->GetPropTableFrames(aPresContext,
1388 nsGkAtoms::excessOverflowContainersProperty);
1389 if (mOverflowContList) {
1390 SetUpListWalker();
1396 * Helper function to walk past overflow continuations whose prev-in-flow
1397 * isn't a normal child and to set mSentry and mPrevOverflowCont correctly.
1399 void
1400 nsOverflowContinuationTracker::SetUpListWalker()
1402 NS_ASSERTION(!mSentry && !mPrevOverflowCont,
1403 "forgot to reset mSentry or mPrevOverflowCont");
1404 if (mOverflowContList) {
1405 nsIFrame* cur = mOverflowContList->FirstChild();
1406 if (mSkipOverflowContainerChildren) {
1407 while (cur && (cur->GetPrevInFlow()->GetStateBits()
1408 & NS_FRAME_IS_OVERFLOW_CONTAINER)) {
1409 mPrevOverflowCont = cur;
1410 cur = cur->GetNextSibling();
1412 while (cur && (!(cur->GetStateBits() & NS_FRAME_OUT_OF_FLOW)
1413 == mWalkOOFFrames)) {
1414 mPrevOverflowCont = cur;
1415 cur = cur->GetNextSibling();
1418 if (cur) {
1419 mSentry = cur->GetPrevInFlow();
1425 * Helper function to step forward through the overflow continuations list.
1426 * Sets mSentry and mPrevOverflowCont, skipping over OOF or non-OOF frames
1427 * as appropriate. May only be called when we have already set up an
1428 * mOverflowContList; mOverflowContList cannot be null.
1430 void
1431 nsOverflowContinuationTracker::StepForward()
1433 NS_PRECONDITION(mOverflowContList, "null list");
1435 // Step forward
1436 if (mPrevOverflowCont) {
1437 mPrevOverflowCont = mPrevOverflowCont->GetNextSibling();
1439 else {
1440 mPrevOverflowCont = mOverflowContList->FirstChild();
1443 // Skip over oof or non-oof frames as appropriate
1444 if (mSkipOverflowContainerChildren) {
1445 nsIFrame* cur = mPrevOverflowCont->GetNextSibling();
1446 while (cur && (!(cur->GetStateBits() & NS_FRAME_OUT_OF_FLOW)
1447 == mWalkOOFFrames)) {
1448 mPrevOverflowCont = cur;
1449 cur = cur->GetNextSibling();
1453 // Set up the sentry
1454 mSentry = (mPrevOverflowCont->GetNextSibling())
1455 ? mPrevOverflowCont->GetNextSibling()->GetPrevInFlow()
1456 : nsnull;
1459 nsresult
1460 nsOverflowContinuationTracker::Insert(nsIFrame* aOverflowCont,
1461 nsReflowStatus& aReflowStatus)
1463 NS_PRECONDITION(aOverflowCont, "null frame pointer");
1464 NS_PRECONDITION(!mSkipOverflowContainerChildren || mWalkOOFFrames ==
1465 !!(aOverflowCont->GetStateBits() & NS_FRAME_OUT_OF_FLOW),
1466 "shouldn't insert frame that doesn't match walker type");
1467 NS_PRECONDITION(aOverflowCont->GetPrevInFlow(),
1468 "overflow containers must have a prev-in-flow");
1469 nsresult rv = NS_OK;
1470 PRBool convertedToOverflowContainer = PR_FALSE;
1471 nsPresContext* presContext = aOverflowCont->PresContext();
1472 if (!mSentry || aOverflowCont != mSentry->GetNextInFlow()) {
1473 // Not in our list, so we need to add it
1474 if (aOverflowCont->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) {
1475 // aOverflowCont is in some other overflow container list,
1476 // steal it first
1477 NS_ASSERTION(!(mOverflowContList &&
1478 mOverflowContList->ContainsFrame(aOverflowCont)),
1479 "overflow containers out of order");
1480 rv = static_cast<nsContainerFrame*>(aOverflowCont->GetParent())
1481 ->StealFrame(presContext, aOverflowCont);
1482 NS_ENSURE_SUCCESS(rv, rv);
1484 else {
1485 aOverflowCont->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
1486 convertedToOverflowContainer = PR_TRUE;
1488 if (!mOverflowContList) {
1489 mOverflowContList = new nsFrameList();
1490 rv = mParent->SetPropTableFrames(presContext,
1491 mOverflowContList, nsGkAtoms::excessOverflowContainersProperty);
1492 NS_ENSURE_SUCCESS(rv, rv);
1493 SetUpListWalker();
1495 if (aOverflowCont->GetParent() != mParent) {
1496 nsHTMLContainerFrame::ReparentFrameView(presContext, aOverflowCont,
1497 aOverflowCont->GetParent(),
1498 mParent);
1500 mOverflowContList->InsertFrame(mParent, mPrevOverflowCont, aOverflowCont);
1501 aReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
1504 // If we need to reflow it, mark it dirty
1505 if (aReflowStatus & NS_FRAME_REFLOW_NEXTINFLOW)
1506 aOverflowCont->AddStateBits(NS_FRAME_IS_DIRTY);
1508 // It's in our list, just step forward
1509 StepForward();
1510 NS_ASSERTION(mPrevOverflowCont == aOverflowCont ||
1511 (mSkipOverflowContainerChildren &&
1512 (mPrevOverflowCont->GetStateBits() & NS_FRAME_OUT_OF_FLOW) !=
1513 (aOverflowCont->GetStateBits() & NS_FRAME_OUT_OF_FLOW)),
1514 "OverflowContTracker in unexpected state");
1516 if (convertedToOverflowContainer) {
1517 // Convert all non-overflow-container continuations of aOverflowCont
1518 // into overflow containers and move them to our overflow
1519 // tracker. This preserves the invariant that the next-continuations
1520 // of an overflow container are also overflow containers.
1521 nsIFrame* f = aOverflowCont->GetNextContinuation();
1522 if (f && !(f->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) {
1523 nsContainerFrame* parent = static_cast<nsContainerFrame*>(f->GetParent());
1524 rv = parent->StealFrame(presContext, f);
1525 NS_ENSURE_SUCCESS(rv, rv);
1526 Insert(f, aReflowStatus);
1529 return rv;
1532 void
1533 nsOverflowContinuationTracker::Finish(nsIFrame* aChild)
1535 NS_PRECONDITION(aChild, "null ptr");
1536 NS_PRECONDITION(aChild->GetNextInFlow(),
1537 "supposed to call Finish *before* deleting next-in-flow!");
1539 for (nsIFrame* f = aChild; f; f = f->GetNextInFlow()) {
1540 if (f == mSentry) {
1541 // Make sure we drop all references if this was the only frame
1542 // in the overflow containers list
1543 if (mOverflowContList->FirstChild() == f->GetNextInFlow()
1544 && !f->GetNextInFlow()->GetNextSibling()) {
1545 mOverflowContList = nsnull;
1546 mPrevOverflowCont = nsnull;
1547 mSentry = nsnull;
1548 mParent = static_cast<nsContainerFrame*>(f->GetParent());
1549 break;
1551 else {
1552 // Step past aChild
1553 nsIFrame* prevOverflowCont = mPrevOverflowCont;
1554 StepForward();
1555 if (mPrevOverflowCont == f->GetNextInFlow()) {
1556 // Pull mPrevOverflowChild back to aChild's prevSibling:
1557 // aChild will be removed from our list by our caller
1558 mPrevOverflowCont = prevOverflowCont;
1565 /////////////////////////////////////////////////////////////////////////////
1566 // Debugging
1568 #ifdef NS_DEBUG
1569 NS_IMETHODIMP
1570 nsContainerFrame::List(FILE* out, PRInt32 aIndent) const
1572 IndentBy(out, aIndent);
1573 ListTag(out);
1574 #ifdef DEBUG_waterson
1575 fprintf(out, " [parent=%p]", static_cast<void*>(mParent));
1576 #endif
1577 if (HasView()) {
1578 fprintf(out, " [view=%p]", static_cast<void*>(GetView()));
1580 if (GetNextSibling()) {
1581 fprintf(out, " next=%p", static_cast<void*>(GetNextSibling()));
1583 if (nsnull != GetPrevContinuation()) {
1584 fprintf(out, " prev-continuation=%p", static_cast<void*>(GetPrevContinuation()));
1586 if (nsnull != GetNextContinuation()) {
1587 fprintf(out, " next-continuation=%p", static_cast<void*>(GetNextContinuation()));
1589 void* IBsibling = GetProperty(nsGkAtoms::IBSplitSpecialSibling);
1590 if (IBsibling) {
1591 fprintf(out, " IBSplitSpecialSibling=%p", IBsibling);
1593 void* IBprevsibling = GetProperty(nsGkAtoms::IBSplitSpecialPrevSibling);
1594 if (IBprevsibling) {
1595 fprintf(out, " IBSplitSpecialPrevSibling=%p", IBprevsibling);
1597 fprintf(out, " {%d,%d,%d,%d}", mRect.x, mRect.y, mRect.width, mRect.height);
1598 if (0 != mState) {
1599 fprintf(out, " [state=%08x]", mState);
1601 fprintf(out, " [content=%p]", static_cast<void*>(mContent));
1602 nsContainerFrame* f = const_cast<nsContainerFrame*>(this);
1603 if (f->HasOverflowRect()) {
1604 nsRect overflowArea = f->GetOverflowRect();
1605 fprintf(out, " [overflow=%d,%d,%d,%d]", overflowArea.x, overflowArea.y,
1606 overflowArea.width, overflowArea.height);
1608 fprintf(out, " [sc=%p]", static_cast<void*>(mStyleContext));
1609 nsIAtom* pseudoTag = mStyleContext->GetPseudo();
1610 if (pseudoTag) {
1611 nsAutoString atomString;
1612 pseudoTag->ToString(atomString);
1613 fprintf(out, " pst=%s",
1614 NS_LossyConvertUTF16toASCII(atomString).get());
1617 // Output the children
1618 nsIAtom* listName = nsnull;
1619 PRInt32 listIndex = 0;
1620 PRBool outputOneList = PR_FALSE;
1621 do {
1622 nsIFrame* kid = GetFirstChild(listName);
1623 if (nsnull != kid) {
1624 if (outputOneList) {
1625 IndentBy(out, aIndent);
1627 outputOneList = PR_TRUE;
1628 nsAutoString tmp;
1629 if (nsnull != listName) {
1630 listName->ToString(tmp);
1631 fputs(NS_LossyConvertUTF16toASCII(tmp).get(), out);
1633 fputs("<\n", out);
1634 while (nsnull != kid) {
1635 // Verify the child frame's parent frame pointer is correct
1636 NS_ASSERTION(kid->GetParent() == (nsIFrame*)this, "bad parent frame pointer");
1638 // Have the child frame list
1639 kid->List(out, aIndent + 1);
1640 kid = kid->GetNextSibling();
1642 IndentBy(out, aIndent);
1643 fputs(">\n", out);
1645 listName = GetAdditionalChildListName(listIndex++);
1646 } while(nsnull != listName);
1648 if (!outputOneList) {
1649 fputs("<>\n", out);
1652 return NS_OK;
1654 #endif