michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* base class #1 for rendering objects that have child lists */ michael@0: michael@0: #include "nsContainerFrame.h" michael@0: michael@0: #include "nsAbsoluteContainingBlock.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsPresContext.h" michael@0: #include "nsStyleContext.h" michael@0: #include "nsRect.h" michael@0: #include "nsPoint.h" michael@0: #include "nsStyleConsts.h" michael@0: #include "nsView.h" michael@0: #include "nsIPresShell.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "nsViewManager.h" michael@0: #include "nsIWidget.h" michael@0: #include "nsCSSRendering.h" michael@0: #include "nsError.h" michael@0: #include "nsDisplayList.h" michael@0: #include "nsIBaseWindow.h" michael@0: #include "nsBoxLayoutState.h" michael@0: #include "nsCSSFrameConstructor.h" michael@0: #include "nsBlockFrame.h" michael@0: #include "mozilla/AutoRestore.h" michael@0: #include "nsIFrameInlines.h" michael@0: #include "nsPrintfCString.h" michael@0: #include michael@0: michael@0: #ifdef DEBUG michael@0: #undef NOISY michael@0: #else michael@0: #undef NOISY michael@0: #endif michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: using namespace mozilla::layout; michael@0: michael@0: NS_IMPL_FRAMEARENA_HELPERS(nsContainerFrame) michael@0: michael@0: nsContainerFrame::~nsContainerFrame() michael@0: { michael@0: } michael@0: michael@0: NS_QUERYFRAME_HEAD(nsContainerFrame) michael@0: NS_QUERYFRAME_ENTRY(nsContainerFrame) michael@0: NS_QUERYFRAME_TAIL_INHERITING(nsSplittableFrame) michael@0: michael@0: void michael@0: nsContainerFrame::Init(nsIContent* aContent, michael@0: nsIFrame* aParent, michael@0: nsIFrame* aPrevInFlow) michael@0: { michael@0: nsSplittableFrame::Init(aContent, aParent, aPrevInFlow); michael@0: if (aPrevInFlow) { michael@0: // Make sure we copy bits from our prev-in-flow that will affect michael@0: // us. A continuation for a container frame needs to know if it michael@0: // has a child with a view so that we'll properly reposition it. michael@0: if (aPrevInFlow->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW) michael@0: AddStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW); michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsContainerFrame::SetInitialChildList(ChildListID aListID, michael@0: nsFrameList& aChildList) michael@0: { michael@0: nsresult result; michael@0: if (mFrames.NotEmpty()) { michael@0: // We already have child frames which means we've already been michael@0: // initialized michael@0: NS_NOTREACHED("unexpected second call to SetInitialChildList"); michael@0: result = NS_ERROR_UNEXPECTED; michael@0: } else if (aListID != kPrincipalList) { michael@0: // All we know about is the principal child list. michael@0: NS_NOTREACHED("unknown frame list"); michael@0: result = NS_ERROR_INVALID_ARG; michael@0: } else { michael@0: #ifdef DEBUG michael@0: nsFrame::VerifyDirtyBitSet(aChildList); michael@0: #endif michael@0: mFrames.SetFrames(aChildList); michael@0: result = NS_OK; michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: nsresult michael@0: nsContainerFrame::AppendFrames(ChildListID aListID, michael@0: nsFrameList& aFrameList) michael@0: { michael@0: if (aListID != kPrincipalList) { michael@0: if (aListID != kNoReflowPrincipalList) michael@0: { michael@0: NS_ERROR("unexpected child list"); michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: } michael@0: if (aFrameList.NotEmpty()) { michael@0: mFrames.AppendFrames(this, aFrameList); michael@0: michael@0: // Ask the parent frame to reflow me. michael@0: if (aListID == kPrincipalList) michael@0: { michael@0: PresContext()->PresShell()-> michael@0: FrameNeedsReflow(this, nsIPresShell::eTreeChange, michael@0: NS_FRAME_HAS_DIRTY_CHILDREN); michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsContainerFrame::InsertFrames(ChildListID aListID, michael@0: nsIFrame* aPrevFrame, michael@0: nsFrameList& aFrameList) michael@0: { michael@0: NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this, michael@0: "inserting after sibling frame with different parent"); michael@0: michael@0: if (aListID != kPrincipalList) { michael@0: if (aListID != kNoReflowPrincipalList) michael@0: { michael@0: NS_ERROR("unexpected child list"); michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: } michael@0: if (aFrameList.NotEmpty()) { michael@0: // Insert frames after aPrevFrame michael@0: mFrames.InsertFrames(this, aPrevFrame, aFrameList); michael@0: michael@0: if (aListID == kPrincipalList) michael@0: { michael@0: PresContext()->PresShell()-> michael@0: FrameNeedsReflow(this, nsIPresShell::eTreeChange, michael@0: NS_FRAME_HAS_DIRTY_CHILDREN); michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsContainerFrame::RemoveFrame(ChildListID aListID, michael@0: nsIFrame* aOldFrame) michael@0: { michael@0: if (aListID != kPrincipalList) { michael@0: if (kNoReflowPrincipalList != aListID) michael@0: { michael@0: NS_ERROR("unexpected child list"); michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: } michael@0: michael@0: // Loop and destroy aOldFrame and all of its continuations. michael@0: // Request a reflow on the parent frames involved unless we were explicitly michael@0: // told not to (kNoReflowPrincipalList). michael@0: bool generateReflowCommand = true; michael@0: if (kNoReflowPrincipalList == aListID) { michael@0: generateReflowCommand = false; michael@0: } michael@0: nsIPresShell* shell = PresContext()->PresShell(); michael@0: nsContainerFrame* lastParent = nullptr; michael@0: while (aOldFrame) { michael@0: //XXXfr probably should use StealFrame here. I'm not sure if we need to michael@0: // check the overflow lists atm, but we'll need a prescontext lookup michael@0: // for overflow containers once we can split abspos elements with michael@0: // inline containing blocks. michael@0: nsIFrame* oldFrameNextContinuation = aOldFrame->GetNextContinuation(); michael@0: nsContainerFrame* parent = michael@0: static_cast(aOldFrame->GetParent()); michael@0: parent->StealFrame(aOldFrame, true); michael@0: aOldFrame->Destroy(); michael@0: aOldFrame = oldFrameNextContinuation; michael@0: if (parent != lastParent && generateReflowCommand) { michael@0: shell->FrameNeedsReflow(parent, nsIPresShell::eTreeChange, michael@0: NS_FRAME_HAS_DIRTY_CHILDREN); michael@0: lastParent = parent; michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsContainerFrame::DestroyAbsoluteFrames(nsIFrame* aDestructRoot) michael@0: { michael@0: if (IsAbsoluteContainer()) { michael@0: GetAbsoluteContainingBlock()->DestroyFrames(this, aDestructRoot); michael@0: MarkAsNotAbsoluteContainingBlock(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsContainerFrame::SafelyDestroyFrameListProp(nsIFrame* aDestructRoot, michael@0: nsIPresShell* aPresShell, michael@0: FramePropertyTable* aPropTable, michael@0: const FramePropertyDescriptor* aProp) michael@0: { michael@0: // Note that the last frame can be removed through another route and thus michael@0: // delete the property -- that's why we fetch the property again before michael@0: // removing each frame rather than fetching it once and iterating the list. michael@0: while (nsFrameList* frameList = michael@0: static_cast(aPropTable->Get(this, aProp))) { michael@0: nsIFrame* frame = frameList->RemoveFirstChild(); michael@0: if (MOZ_LIKELY(frame)) { michael@0: frame->DestroyFrom(aDestructRoot); michael@0: } else { michael@0: aPropTable->Remove(this, aProp); michael@0: frameList->Delete(aPresShell); michael@0: return; michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsContainerFrame::DestroyFrom(nsIFrame* aDestructRoot) michael@0: { michael@0: // Prevent event dispatch during destruction. michael@0: if (HasView()) { michael@0: GetView()->SetFrame(nullptr); michael@0: } michael@0: michael@0: DestroyAbsoluteFrames(aDestructRoot); michael@0: michael@0: // Destroy frames on the principal child list. michael@0: mFrames.DestroyFramesFrom(aDestructRoot); michael@0: michael@0: // Destroy frames on the auxiliary frame lists and delete the lists. michael@0: nsPresContext* pc = PresContext(); michael@0: nsIPresShell* shell = pc->PresShell(); michael@0: FramePropertyTable* props = pc->PropertyTable(); michael@0: SafelyDestroyFrameListProp(aDestructRoot, shell, props, OverflowProperty()); michael@0: michael@0: MOZ_ASSERT(IsFrameOfType(nsIFrame::eCanContainOverflowContainers) || michael@0: !(props->Get(this, nsContainerFrame::OverflowContainersProperty()) || michael@0: props->Get(this, nsContainerFrame::ExcessOverflowContainersProperty())), michael@0: "this type of frame should't have overflow containers"); michael@0: michael@0: SafelyDestroyFrameListProp(aDestructRoot, shell, props, michael@0: OverflowContainersProperty()); michael@0: SafelyDestroyFrameListProp(aDestructRoot, shell, props, michael@0: ExcessOverflowContainersProperty()); michael@0: michael@0: nsSplittableFrame::DestroyFrom(aDestructRoot); michael@0: } michael@0: michael@0: ///////////////////////////////////////////////////////////////////////////// michael@0: // Child frame enumeration michael@0: michael@0: const nsFrameList& michael@0: nsContainerFrame::GetChildList(ChildListID aListID) const michael@0: { michael@0: // We only know about the principal child list and the overflow lists. michael@0: switch (aListID) { michael@0: case kPrincipalList: michael@0: return mFrames; michael@0: case kOverflowList: { michael@0: nsFrameList* list = GetOverflowFrames(); michael@0: return list ? *list : nsFrameList::EmptyList(); michael@0: } michael@0: case kOverflowContainersList: { michael@0: nsFrameList* list = GetPropTableFrames(OverflowContainersProperty()); michael@0: return list ? *list : nsFrameList::EmptyList(); michael@0: } michael@0: case kExcessOverflowContainersList: { michael@0: nsFrameList* list = michael@0: GetPropTableFrames(ExcessOverflowContainersProperty()); michael@0: return list ? *list : nsFrameList::EmptyList(); michael@0: } michael@0: default: michael@0: return nsSplittableFrame::GetChildList(aListID); michael@0: } michael@0: } michael@0: michael@0: static void AppendIfNonempty(const nsIFrame* aFrame, michael@0: FramePropertyTable* aPropTable, michael@0: const FramePropertyDescriptor* aProperty, michael@0: nsTArray* aLists, michael@0: nsIFrame::ChildListID aListID) michael@0: { michael@0: nsFrameList* list = static_cast( michael@0: aPropTable->Get(aFrame, aProperty)); michael@0: if (list) { michael@0: list->AppendIfNonempty(aLists, aListID); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsContainerFrame::GetChildLists(nsTArray* aLists) const michael@0: { michael@0: mFrames.AppendIfNonempty(aLists, kPrincipalList); michael@0: FramePropertyTable* propTable = PresContext()->PropertyTable(); michael@0: ::AppendIfNonempty(this, propTable, OverflowProperty(), michael@0: aLists, kOverflowList); michael@0: if (IsFrameOfType(nsIFrame::eCanContainOverflowContainers)) { michael@0: ::AppendIfNonempty(this, propTable, OverflowContainersProperty(), michael@0: aLists, kOverflowContainersList); michael@0: ::AppendIfNonempty(this, propTable, ExcessOverflowContainersProperty(), michael@0: aLists, kExcessOverflowContainersList); michael@0: } michael@0: nsSplittableFrame::GetChildLists(aLists); michael@0: } michael@0: michael@0: ///////////////////////////////////////////////////////////////////////////// michael@0: // Painting/Events michael@0: michael@0: void michael@0: nsContainerFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, michael@0: const nsRect& aDirtyRect, michael@0: const nsDisplayListSet& aLists) michael@0: { michael@0: DisplayBorderBackgroundOutline(aBuilder, aLists); michael@0: michael@0: BuildDisplayListForNonBlockChildren(aBuilder, aDirtyRect, aLists); michael@0: } michael@0: michael@0: void michael@0: nsContainerFrame::BuildDisplayListForNonBlockChildren(nsDisplayListBuilder* aBuilder, michael@0: const nsRect& aDirtyRect, michael@0: const nsDisplayListSet& aLists, michael@0: uint32_t aFlags) michael@0: { michael@0: nsIFrame* kid = mFrames.FirstChild(); michael@0: // Put each child's background directly onto the content list michael@0: nsDisplayListSet set(aLists, aLists.Content()); michael@0: // The children should be in content order michael@0: while (kid) { michael@0: BuildDisplayListForChild(aBuilder, kid, aDirtyRect, set, aFlags); michael@0: kid = kid->GetNextSibling(); michael@0: } michael@0: } michael@0: michael@0: /* virtual */ void michael@0: nsContainerFrame::ChildIsDirty(nsIFrame* aChild) michael@0: { michael@0: NS_ASSERTION(NS_SUBTREE_DIRTY(aChild), "child isn't actually dirty"); michael@0: michael@0: AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN); michael@0: } michael@0: michael@0: bool michael@0: nsContainerFrame::IsLeaf() const michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: nsIFrame::FrameSearchResult michael@0: nsContainerFrame::PeekOffsetNoAmount(bool aForward, int32_t* aOffset) michael@0: { michael@0: NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range"); michael@0: // Don't allow the caret to stay in an empty (leaf) container frame. michael@0: return CONTINUE_EMPTY; michael@0: } michael@0: michael@0: nsIFrame::FrameSearchResult michael@0: nsContainerFrame::PeekOffsetCharacter(bool aForward, int32_t* aOffset, michael@0: bool aRespectClusters) michael@0: { michael@0: NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range"); michael@0: // Don't allow the caret to stay in an empty (leaf) container frame. michael@0: return CONTINUE_EMPTY; michael@0: } michael@0: michael@0: ///////////////////////////////////////////////////////////////////////////// michael@0: // Helper member functions michael@0: michael@0: static nsresult michael@0: ReparentFrameViewTo(nsIFrame* aFrame, michael@0: nsViewManager* aViewManager, michael@0: nsView* aNewParentView, michael@0: nsView* aOldParentView) michael@0: { michael@0: michael@0: // XXX What to do about placeholder views for "position: fixed" elements? michael@0: // They should be reparented too. michael@0: michael@0: // Does aFrame have a view? michael@0: if (aFrame->HasView()) { michael@0: #ifdef MOZ_XUL michael@0: if (aFrame->GetType() == nsGkAtoms::menuPopupFrame) { michael@0: // This view must be parented by the root view, don't reparent it. michael@0: return NS_OK; michael@0: } michael@0: #endif michael@0: nsView* view = aFrame->GetView(); michael@0: // Verify that the current parent view is what we think it is michael@0: //nsView* parentView; michael@0: //NS_ASSERTION(parentView == aOldParentView, "unexpected parent view"); michael@0: michael@0: aViewManager->RemoveChild(view); michael@0: michael@0: // The view will remember the Z-order and other attributes that have been set on it. michael@0: nsView* insertBefore = nsLayoutUtils::FindSiblingViewFor(aNewParentView, aFrame); michael@0: aViewManager->InsertChild(aNewParentView, view, insertBefore, insertBefore != nullptr); michael@0: } else { michael@0: nsIFrame::ChildListIterator lists(aFrame); michael@0: for (; !lists.IsDone(); lists.Next()) { michael@0: // Iterate the child frames, and check each child frame to see if it has michael@0: // a view michael@0: nsFrameList::Enumerator childFrames(lists.CurrentList()); michael@0: for (; !childFrames.AtEnd(); childFrames.Next()) { michael@0: ReparentFrameViewTo(childFrames.get(), aViewManager, michael@0: aNewParentView, aOldParentView); michael@0: } michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsContainerFrame::CreateViewForFrame(nsIFrame* aFrame, michael@0: bool aForce) michael@0: { michael@0: if (aFrame->HasView()) { michael@0: return; michael@0: } michael@0: michael@0: // If we don't yet have a view, see if we need a view michael@0: if (!aForce && !aFrame->NeedsView()) { michael@0: // don't need a view michael@0: return; michael@0: } michael@0: michael@0: nsView* parentView = aFrame->GetParent()->GetClosestView(); michael@0: NS_ASSERTION(parentView, "no parent with view"); michael@0: michael@0: nsViewManager* viewManager = parentView->GetViewManager(); michael@0: NS_ASSERTION(viewManager, "null view manager"); michael@0: michael@0: // Create a view michael@0: nsView* view = viewManager->CreateView(aFrame->GetRect(), parentView); michael@0: michael@0: SyncFrameViewProperties(aFrame->PresContext(), aFrame, nullptr, view); michael@0: michael@0: nsView* insertBefore = nsLayoutUtils::FindSiblingViewFor(parentView, aFrame); michael@0: // we insert this view 'above' the insertBefore view, unless insertBefore is null, michael@0: // in which case we want to call with aAbove == false to insert at the beginning michael@0: // in document order michael@0: viewManager->InsertChild(parentView, view, insertBefore, insertBefore != nullptr); michael@0: michael@0: // REVIEW: Don't create a widget for fixed-pos elements anymore. michael@0: // ComputeRepaintRegionForCopy will calculate the right area to repaint michael@0: // when we scroll. michael@0: // Reparent views on any child frames (or their descendants) to this michael@0: // view. We can just call ReparentFrameViewTo on this frame because michael@0: // we know this frame has no view, so it will crawl the children. Also, michael@0: // we know that any descendants with views must have 'parentView' as their michael@0: // parent view. michael@0: ReparentFrameViewTo(aFrame, viewManager, view, parentView); michael@0: michael@0: // Remember our view michael@0: aFrame->SetView(view); michael@0: michael@0: NS_FRAME_LOG(NS_FRAME_TRACE_CALLS, michael@0: ("nsContainerFrame::CreateViewForFrame: frame=%p view=%p", michael@0: aFrame)); michael@0: } michael@0: michael@0: /** michael@0: * Position the view associated with |aKidFrame|, if there is one. A michael@0: * container frame should call this method after positioning a frame, michael@0: * but before |Reflow|. michael@0: */ michael@0: void michael@0: nsContainerFrame::PositionFrameView(nsIFrame* aKidFrame) michael@0: { michael@0: nsIFrame* parentFrame = aKidFrame->GetParent(); michael@0: if (!aKidFrame->HasView() || !parentFrame) michael@0: return; michael@0: michael@0: nsView* view = aKidFrame->GetView(); michael@0: nsViewManager* vm = view->GetViewManager(); michael@0: nsPoint pt; michael@0: nsView* ancestorView = parentFrame->GetClosestView(&pt); michael@0: michael@0: if (ancestorView != view->GetParent()) { michael@0: NS_ASSERTION(ancestorView == view->GetParent()->GetParent(), michael@0: "Allowed only one anonymous view between frames"); michael@0: // parentFrame is responsible for positioning aKidFrame's view michael@0: // explicitly michael@0: return; michael@0: } michael@0: michael@0: pt += aKidFrame->GetPosition(); michael@0: vm->MoveViewTo(view, pt.x, pt.y); michael@0: } michael@0: michael@0: nsresult michael@0: nsContainerFrame::ReparentFrameView(nsIFrame* aChildFrame, michael@0: nsIFrame* aOldParentFrame, michael@0: nsIFrame* aNewParentFrame) michael@0: { michael@0: NS_PRECONDITION(aChildFrame, "null child frame pointer"); michael@0: NS_PRECONDITION(aOldParentFrame, "null old parent frame pointer"); michael@0: NS_PRECONDITION(aNewParentFrame, "null new parent frame pointer"); michael@0: NS_PRECONDITION(aOldParentFrame != aNewParentFrame, "same old and new parent frame"); michael@0: michael@0: // See if either the old parent frame or the new parent frame have a view michael@0: while (!aOldParentFrame->HasView() && !aNewParentFrame->HasView()) { michael@0: // Walk up both the old parent frame and the new parent frame nodes michael@0: // stopping when we either find a common parent or views for one michael@0: // or both of the frames. michael@0: // michael@0: // This works well in the common case where we push/pull and the old parent michael@0: // frame and the new parent frame are part of the same flow. They will michael@0: // typically be the same distance (height wise) from the michael@0: aOldParentFrame = aOldParentFrame->GetParent(); michael@0: aNewParentFrame = aNewParentFrame->GetParent(); michael@0: michael@0: // We should never walk all the way to the root frame without finding michael@0: // a view michael@0: NS_ASSERTION(aOldParentFrame && aNewParentFrame, "didn't find view"); michael@0: michael@0: // See if we reached a common ancestor michael@0: if (aOldParentFrame == aNewParentFrame) { michael@0: break; michael@0: } michael@0: } michael@0: michael@0: // See if we found a common parent frame michael@0: if (aOldParentFrame == aNewParentFrame) { michael@0: // We found a common parent and there are no views between the old parent michael@0: // and the common parent or the new parent frame and the common parent. michael@0: // Because neither the old parent frame nor the new parent frame have views, michael@0: // then any child views don't need reparenting michael@0: return NS_OK; michael@0: } michael@0: michael@0: // We found views for one or both of the ancestor frames before we michael@0: // found a common ancestor. michael@0: nsView* oldParentView = aOldParentFrame->GetClosestView(); michael@0: nsView* newParentView = aNewParentFrame->GetClosestView(); michael@0: michael@0: // See if the old parent frame and the new parent frame are in the michael@0: // same view sub-hierarchy. If they are then we don't have to do michael@0: // anything michael@0: if (oldParentView != newParentView) { michael@0: // They're not so we need to reparent any child views michael@0: return ReparentFrameViewTo(aChildFrame, oldParentView->GetViewManager(), newParentView, michael@0: oldParentView); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsContainerFrame::ReparentFrameViewList(const nsFrameList& aChildFrameList, michael@0: nsIFrame* aOldParentFrame, michael@0: nsIFrame* aNewParentFrame) michael@0: { michael@0: NS_PRECONDITION(aChildFrameList.NotEmpty(), "empty child frame list"); michael@0: NS_PRECONDITION(aOldParentFrame, "null old parent frame pointer"); michael@0: NS_PRECONDITION(aNewParentFrame, "null new parent frame pointer"); michael@0: NS_PRECONDITION(aOldParentFrame != aNewParentFrame, "same old and new parent frame"); michael@0: michael@0: // See if either the old parent frame or the new parent frame have a view michael@0: while (!aOldParentFrame->HasView() && !aNewParentFrame->HasView()) { michael@0: // Walk up both the old parent frame and the new parent frame nodes michael@0: // stopping when we either find a common parent or views for one michael@0: // or both of the frames. michael@0: // michael@0: // This works well in the common case where we push/pull and the old parent michael@0: // frame and the new parent frame are part of the same flow. They will michael@0: // typically be the same distance (height wise) from the michael@0: aOldParentFrame = aOldParentFrame->GetParent(); michael@0: aNewParentFrame = aNewParentFrame->GetParent(); michael@0: michael@0: // We should never walk all the way to the root frame without finding michael@0: // a view michael@0: NS_ASSERTION(aOldParentFrame && aNewParentFrame, "didn't find view"); michael@0: michael@0: // See if we reached a common ancestor michael@0: if (aOldParentFrame == aNewParentFrame) { michael@0: break; michael@0: } michael@0: } michael@0: michael@0: michael@0: // See if we found a common parent frame michael@0: if (aOldParentFrame == aNewParentFrame) { michael@0: // We found a common parent and there are no views between the old parent michael@0: // and the common parent or the new parent frame and the common parent. michael@0: // Because neither the old parent frame nor the new parent frame have views, michael@0: // then any child views don't need reparenting michael@0: return NS_OK; michael@0: } michael@0: michael@0: // We found views for one or both of the ancestor frames before we michael@0: // found a common ancestor. michael@0: nsView* oldParentView = aOldParentFrame->GetClosestView(); michael@0: nsView* newParentView = aNewParentFrame->GetClosestView(); michael@0: michael@0: // See if the old parent frame and the new parent frame are in the michael@0: // same view sub-hierarchy. If they are then we don't have to do michael@0: // anything michael@0: if (oldParentView != newParentView) { michael@0: nsViewManager* viewManager = oldParentView->GetViewManager(); michael@0: michael@0: // They're not so we need to reparent any child views michael@0: for (nsFrameList::Enumerator e(aChildFrameList); !e.AtEnd(); e.Next()) { michael@0: ReparentFrameViewTo(e.get(), viewManager, newParentView, oldParentView); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: static nsIWidget* michael@0: GetPresContextContainerWidget(nsPresContext* aPresContext) michael@0: { michael@0: nsCOMPtr container = aPresContext->Document()->GetContainer(); michael@0: nsCOMPtr baseWindow = do_QueryInterface(container); michael@0: if (!baseWindow) michael@0: return nullptr; michael@0: michael@0: nsCOMPtr mainWidget; michael@0: baseWindow->GetMainWidget(getter_AddRefs(mainWidget)); michael@0: return mainWidget; michael@0: } michael@0: michael@0: static bool michael@0: IsTopLevelWidget(nsIWidget* aWidget) michael@0: { michael@0: nsWindowType windowType = aWidget->WindowType(); michael@0: return windowType == eWindowType_toplevel || michael@0: windowType == eWindowType_dialog || michael@0: windowType == eWindowType_sheet; michael@0: // popups aren't toplevel so they're not handled here michael@0: } michael@0: michael@0: void michael@0: nsContainerFrame::SyncWindowProperties(nsPresContext* aPresContext, michael@0: nsIFrame* aFrame, michael@0: nsView* aView, michael@0: nsRenderingContext* aRC) michael@0: { michael@0: #ifdef MOZ_XUL michael@0: if (!aView || !nsCSSRendering::IsCanvasFrame(aFrame) || !aView->HasWidget()) michael@0: return; michael@0: michael@0: nsIWidget* windowWidget = GetPresContextContainerWidget(aPresContext); michael@0: if (!windowWidget || !IsTopLevelWidget(windowWidget)) michael@0: return; michael@0: michael@0: nsViewManager* vm = aView->GetViewManager(); michael@0: nsView* rootView = vm->GetRootView(); michael@0: michael@0: if (aView != rootView) michael@0: return; michael@0: michael@0: Element* rootElement = aPresContext->Document()->GetRootElement(); michael@0: if (!rootElement || !rootElement->IsXUL()) { michael@0: // Scrollframes use native widgets which don't work well with michael@0: // translucent windows, at least in Windows XP. So if the document michael@0: // has a root scrollrame it's useless to try to make it transparent, michael@0: // we'll just get something broken. michael@0: // nsCSSFrameConstructor::ConstructRootFrame constructs root michael@0: // scrollframes whenever the root element is not a XUL element, so michael@0: // we test for that here. We can't just call michael@0: // presShell->GetRootScrollFrame() since that might not have michael@0: // been constructed yet. michael@0: // We can change this to allow translucent toplevel HTML documents michael@0: // (e.g. to do something like Dashboard widgets), once we michael@0: // have broad support for translucent scrolled documents, but be michael@0: // careful because apparently some Firefox extensions expect michael@0: // openDialog("something.html") to produce an opaque window michael@0: // even if the HTML doesn't have a background-color set. michael@0: return; michael@0: } michael@0: michael@0: nsIFrame *rootFrame = aPresContext->PresShell()->FrameConstructor()->GetRootElementStyleFrame(); michael@0: if (!rootFrame) michael@0: return; michael@0: michael@0: nsTransparencyMode mode = nsLayoutUtils::GetFrameTransparency(aFrame, rootFrame); michael@0: nsIWidget* viewWidget = aView->GetWidget(); michael@0: viewWidget->SetTransparencyMode(mode); michael@0: windowWidget->SetWindowShadowStyle(rootFrame->StyleUIReset()->mWindowShadow); michael@0: michael@0: if (!aRC) michael@0: return; michael@0: michael@0: nsBoxLayoutState aState(aPresContext, aRC); michael@0: nsSize minSize = rootFrame->GetMinSize(aState); michael@0: nsSize maxSize = rootFrame->GetMaxSize(aState); michael@0: michael@0: SetSizeConstraints(aPresContext, windowWidget, minSize, maxSize); michael@0: #endif michael@0: } michael@0: michael@0: void nsContainerFrame::SetSizeConstraints(nsPresContext* aPresContext, michael@0: nsIWidget* aWidget, michael@0: const nsSize& aMinSize, michael@0: const nsSize& aMaxSize) michael@0: { michael@0: nsIntSize devMinSize(aPresContext->AppUnitsToDevPixels(aMinSize.width), michael@0: aPresContext->AppUnitsToDevPixels(aMinSize.height)); michael@0: nsIntSize devMaxSize(aMaxSize.width == NS_INTRINSICSIZE ? NS_MAXSIZE : michael@0: aPresContext->AppUnitsToDevPixels(aMaxSize.width), michael@0: aMaxSize.height == NS_INTRINSICSIZE ? NS_MAXSIZE : michael@0: aPresContext->AppUnitsToDevPixels(aMaxSize.height)); michael@0: widget::SizeConstraints constraints(devMinSize, devMaxSize); michael@0: michael@0: // The sizes are in inner window sizes, so convert them into outer window sizes. michael@0: // Use a size of (200, 200) as only the difference between the inner and outer michael@0: // size is needed. michael@0: nsIntSize windowSize = aWidget->ClientToWindowSize(nsIntSize(200, 200)); michael@0: if (constraints.mMinSize.width) michael@0: constraints.mMinSize.width += windowSize.width - 200; michael@0: if (constraints.mMinSize.height) michael@0: constraints.mMinSize.height += windowSize.height - 200; michael@0: if (constraints.mMaxSize.width != NS_MAXSIZE) michael@0: constraints.mMaxSize.width += windowSize.width - 200; michael@0: if (constraints.mMaxSize.height != NS_MAXSIZE) michael@0: constraints.mMaxSize.height += windowSize.height - 200; michael@0: michael@0: aWidget->SetSizeConstraints(constraints); michael@0: } michael@0: michael@0: void michael@0: nsContainerFrame::SyncFrameViewAfterReflow(nsPresContext* aPresContext, michael@0: nsIFrame* aFrame, michael@0: nsView* aView, michael@0: const nsRect& aVisualOverflowArea, michael@0: uint32_t aFlags) michael@0: { michael@0: if (!aView) { michael@0: return; michael@0: } michael@0: michael@0: // Make sure the view is sized and positioned correctly michael@0: if (0 == (aFlags & NS_FRAME_NO_MOVE_VIEW)) { michael@0: PositionFrameView(aFrame); michael@0: } michael@0: michael@0: if (0 == (aFlags & NS_FRAME_NO_SIZE_VIEW)) { michael@0: nsViewManager* vm = aView->GetViewManager(); michael@0: michael@0: vm->ResizeView(aView, aVisualOverflowArea, true); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsContainerFrame::SyncFrameViewProperties(nsPresContext* aPresContext, michael@0: nsIFrame* aFrame, michael@0: nsStyleContext* aStyleContext, michael@0: nsView* aView, michael@0: uint32_t aFlags) michael@0: { michael@0: NS_ASSERTION(!aStyleContext || aFrame->StyleContext() == aStyleContext, michael@0: "Wrong style context for frame?"); michael@0: michael@0: if (!aView) { michael@0: return; michael@0: } michael@0: michael@0: nsViewManager* vm = aView->GetViewManager(); michael@0: michael@0: if (nullptr == aStyleContext) { michael@0: aStyleContext = aFrame->StyleContext(); michael@0: } michael@0: michael@0: // Make sure visibility is correct. This only affects nsSubdocumentFrame. michael@0: if (0 == (aFlags & NS_FRAME_NO_VISIBILITY) && michael@0: !aFrame->SupportsVisibilityHidden()) { michael@0: // See if the view should be hidden or visible michael@0: vm->SetViewVisibility(aView, michael@0: aStyleContext->StyleVisibility()->IsVisible() michael@0: ? nsViewVisibility_kShow : nsViewVisibility_kHide); michael@0: } michael@0: michael@0: // See if the frame is being relatively positioned or absolutely michael@0: // positioned michael@0: bool isPositioned = aFrame->IsPositioned(); michael@0: michael@0: int32_t zIndex = 0; michael@0: bool autoZIndex = false; michael@0: michael@0: if (!isPositioned) { michael@0: autoZIndex = true; michael@0: } else { michael@0: // Make sure z-index is correct michael@0: const nsStylePosition* position = aStyleContext->StylePosition(); michael@0: michael@0: if (position->mZIndex.GetUnit() == eStyleUnit_Integer) { michael@0: zIndex = position->mZIndex.GetIntValue(); michael@0: } else if (position->mZIndex.GetUnit() == eStyleUnit_Auto) { michael@0: autoZIndex = true; michael@0: } michael@0: } michael@0: michael@0: vm->SetViewZIndex(aView, autoZIndex, zIndex); michael@0: } michael@0: michael@0: static nscoord GetCoord(const nsStyleCoord& aCoord, nscoord aIfNotCoord) michael@0: { michael@0: if (aCoord.ConvertsToLength()) { michael@0: return nsRuleNode::ComputeCoordPercentCalc(aCoord, 0); michael@0: } michael@0: return aIfNotCoord; michael@0: } michael@0: michael@0: void michael@0: nsContainerFrame::DoInlineIntrinsicWidth(nsRenderingContext *aRenderingContext, michael@0: InlineIntrinsicWidthData *aData, michael@0: nsLayoutUtils::IntrinsicWidthType aType) michael@0: { michael@0: if (GetPrevInFlow()) michael@0: return; // Already added. michael@0: michael@0: NS_PRECONDITION(aType == nsLayoutUtils::MIN_WIDTH || michael@0: aType == nsLayoutUtils::PREF_WIDTH, "bad type"); michael@0: michael@0: mozilla::css::Side startSide, endSide; michael@0: if (StyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR) { michael@0: startSide = NS_SIDE_LEFT; michael@0: endSide = NS_SIDE_RIGHT; michael@0: } else { michael@0: startSide = NS_SIDE_RIGHT; michael@0: endSide = NS_SIDE_LEFT; michael@0: } michael@0: michael@0: const nsStylePadding *stylePadding = StylePadding(); michael@0: const nsStyleBorder *styleBorder = StyleBorder(); michael@0: const nsStyleMargin *styleMargin = StyleMargin(); michael@0: michael@0: // This goes at the beginning no matter how things are broken and how michael@0: // messy the bidi situations are, since per CSS2.1 section 8.6 michael@0: // (implemented in bug 328168), the startSide border is always on the michael@0: // first line. michael@0: // This frame is a first-in-flow, but it might have a previous bidi michael@0: // continuation, in which case that continuation should handle the startSide michael@0: // border. michael@0: if (!GetPrevContinuation()) { michael@0: aData->currentLine += michael@0: // clamp negative calc() to 0 michael@0: std::max(GetCoord(stylePadding->mPadding.Get(startSide), 0), 0) + michael@0: styleBorder->GetComputedBorderWidth(startSide) + michael@0: GetCoord(styleMargin->mMargin.Get(startSide), 0); michael@0: } michael@0: michael@0: const nsLineList_iterator* savedLine = aData->line; michael@0: nsIFrame* const savedLineContainer = aData->lineContainer; michael@0: michael@0: nsContainerFrame *lastInFlow; michael@0: for (nsContainerFrame *nif = this; nif; michael@0: nif = static_cast(nif->GetNextInFlow())) { michael@0: for (nsIFrame *kid = nif->mFrames.FirstChild(); kid; michael@0: kid = kid->GetNextSibling()) { michael@0: if (aType == nsLayoutUtils::MIN_WIDTH) michael@0: kid->AddInlineMinWidth(aRenderingContext, michael@0: static_cast(aData)); michael@0: else michael@0: kid->AddInlinePrefWidth(aRenderingContext, michael@0: static_cast(aData)); michael@0: } michael@0: michael@0: // After we advance to our next-in-flow, the stored line and line container michael@0: // may no longer be correct. Just forget them. michael@0: aData->line = nullptr; michael@0: aData->lineContainer = nullptr; michael@0: michael@0: lastInFlow = nif; michael@0: } michael@0: michael@0: aData->line = savedLine; michael@0: aData->lineContainer = savedLineContainer; michael@0: michael@0: // This goes at the end no matter how things are broken and how michael@0: // messy the bidi situations are, since per CSS2.1 section 8.6 michael@0: // (implemented in bug 328168), the endSide border is always on the michael@0: // last line. michael@0: // We reached the last-in-flow, but it might have a next bidi michael@0: // continuation, in which case that continuation should handle michael@0: // the endSide border. michael@0: if (!lastInFlow->GetNextContinuation()) { michael@0: aData->currentLine += michael@0: // clamp negative calc() to 0 michael@0: std::max(GetCoord(stylePadding->mPadding.Get(endSide), 0), 0) + michael@0: styleBorder->GetComputedBorderWidth(endSide) + michael@0: GetCoord(styleMargin->mMargin.Get(endSide), 0); michael@0: } michael@0: } michael@0: michael@0: /* virtual */ nsSize michael@0: nsContainerFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext, michael@0: nsSize aCBSize, nscoord aAvailableWidth, michael@0: nsSize aMargin, nsSize aBorder, michael@0: nsSize aPadding, bool aShrinkWrap) michael@0: { michael@0: nsSize result(0xdeadbeef, NS_UNCONSTRAINEDSIZE); michael@0: nscoord availBased = aAvailableWidth - aMargin.width - aBorder.width - michael@0: aPadding.width; michael@0: // replaced elements always shrink-wrap michael@0: if (aShrinkWrap || IsFrameOfType(eReplaced)) { michael@0: // don't bother setting it if the result won't be used michael@0: if (StylePosition()->mWidth.GetUnit() == eStyleUnit_Auto) { michael@0: result.width = ShrinkWidthToFit(aRenderingContext, availBased); michael@0: } michael@0: } else { michael@0: result.width = availBased; michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: /** michael@0: * Invokes the WillReflow() function, positions the frame and its view (if michael@0: * requested), and then calls Reflow(). If the reflow succeeds and the child michael@0: * frame is complete, deletes any next-in-flows using DeleteNextInFlowChild() michael@0: */ michael@0: nsresult michael@0: nsContainerFrame::ReflowChild(nsIFrame* aKidFrame, michael@0: nsPresContext* aPresContext, michael@0: nsHTMLReflowMetrics& aDesiredSize, michael@0: const nsHTMLReflowState& aReflowState, michael@0: nscoord aX, michael@0: nscoord aY, michael@0: uint32_t aFlags, michael@0: nsReflowStatus& aStatus, michael@0: nsOverflowContinuationTracker* aTracker) michael@0: { michael@0: NS_PRECONDITION(aReflowState.frame == aKidFrame, "bad reflow state"); michael@0: michael@0: nsresult result; michael@0: michael@0: // Send the WillReflow() notification, and position the child frame michael@0: // and its view if requested michael@0: aKidFrame->WillReflow(aPresContext); michael@0: michael@0: if (NS_FRAME_NO_MOVE_FRAME != (aFlags & NS_FRAME_NO_MOVE_FRAME)) { michael@0: aKidFrame->SetPosition(nsPoint(aX, aY)); michael@0: } michael@0: michael@0: if (0 == (aFlags & NS_FRAME_NO_MOVE_VIEW)) { michael@0: PositionFrameView(aKidFrame); michael@0: } michael@0: michael@0: // Reflow the child frame michael@0: result = aKidFrame->Reflow(aPresContext, aDesiredSize, aReflowState, michael@0: aStatus); michael@0: michael@0: // If the reflow was successful and the child frame is complete, delete any michael@0: // next-in-flows, but only if the NO_DELETE_NEXT_IN_FLOW flag isn't set. michael@0: if (NS_SUCCEEDED(result) && NS_FRAME_IS_FULLY_COMPLETE(aStatus) && michael@0: !(aFlags & NS_FRAME_NO_DELETE_NEXT_IN_FLOW_CHILD)) { michael@0: nsIFrame* kidNextInFlow = aKidFrame->GetNextInFlow(); michael@0: if (kidNextInFlow) { michael@0: // Remove all of the childs next-in-flows. Make sure that we ask michael@0: // the right parent to do the removal (it's possible that the michael@0: // parent is not this because we are executing pullup code) michael@0: nsOverflowContinuationTracker::AutoFinish fini(aTracker, aKidFrame); michael@0: static_cast(kidNextInFlow->GetParent()) michael@0: ->DeleteNextInFlowChild(kidNextInFlow, true); michael@0: } michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: michael@0: /** michael@0: * Position the views of |aFrame|'s descendants. A container frame michael@0: * should call this method if it moves a frame after |Reflow|. michael@0: */ michael@0: void michael@0: nsContainerFrame::PositionChildViews(nsIFrame* aFrame) michael@0: { michael@0: if (!(aFrame->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW)) { michael@0: return; michael@0: } michael@0: michael@0: // Recursively walk aFrame's child frames. michael@0: // Process the additional child lists, but skip the popup list as the michael@0: // view for popups is managed by the parent. Currently only nsMenuFrame michael@0: // and nsPopupSetFrame have a popupList and during layout will adjust the michael@0: // view manually to position the popup. michael@0: ChildListIterator lists(aFrame); michael@0: for (; !lists.IsDone(); lists.Next()) { michael@0: if (lists.CurrentID() == kPopupList) { michael@0: continue; michael@0: } michael@0: nsFrameList::Enumerator childFrames(lists.CurrentList()); michael@0: for (; !childFrames.AtEnd(); childFrames.Next()) { michael@0: // Position the frame's view (if it has one) otherwise recursively michael@0: // process its children michael@0: nsIFrame* childFrame = childFrames.get(); michael@0: if (childFrame->HasView()) { michael@0: PositionFrameView(childFrame); michael@0: } else { michael@0: PositionChildViews(childFrame); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * The second half of frame reflow. Does the following: michael@0: * - sets the frame's bounds michael@0: * - sizes and positions (if requested) the frame's view. If the frame's final michael@0: * position differs from the current position and the frame itself does not michael@0: * have a view, then any child frames with views are positioned so they stay michael@0: * in sync michael@0: * - sets the view's visibility, opacity, content transparency, and clip michael@0: * - invoked the DidReflow() function michael@0: * michael@0: * Flags: michael@0: * NS_FRAME_NO_MOVE_FRAME - don't move the frame. aX and aY are ignored in this michael@0: * case. Also implies NS_FRAME_NO_MOVE_VIEW michael@0: * NS_FRAME_NO_MOVE_VIEW - don't position the frame's view. Set this if you michael@0: * don't want to automatically sync the frame and view michael@0: * NS_FRAME_NO_SIZE_VIEW - don't size the frame's view michael@0: */ michael@0: nsresult michael@0: nsContainerFrame::FinishReflowChild(nsIFrame* aKidFrame, michael@0: nsPresContext* aPresContext, michael@0: const nsHTMLReflowMetrics& aDesiredSize, michael@0: const nsHTMLReflowState* aReflowState, michael@0: nscoord aX, michael@0: nscoord aY, michael@0: uint32_t aFlags) michael@0: { michael@0: nsPoint curOrigin = aKidFrame->GetPosition(); michael@0: michael@0: if (NS_FRAME_NO_MOVE_FRAME != (aFlags & NS_FRAME_NO_MOVE_FRAME)) { michael@0: aKidFrame->SetRect(nsRect(aX, aY, aDesiredSize.Width(), aDesiredSize.Height())); michael@0: } else { michael@0: aKidFrame->SetSize(nsSize(aDesiredSize.Width(), aDesiredSize.Height())); michael@0: } michael@0: michael@0: if (aKidFrame->HasView()) { michael@0: nsView* view = aKidFrame->GetView(); michael@0: // Make sure the frame's view is properly sized and positioned and has michael@0: // things like opacity correct michael@0: SyncFrameViewAfterReflow(aPresContext, aKidFrame, view, michael@0: aDesiredSize.VisualOverflow(), aFlags); michael@0: } michael@0: michael@0: if (!(aFlags & NS_FRAME_NO_MOVE_VIEW) && michael@0: (curOrigin.x != aX || curOrigin.y != aY)) { michael@0: if (!aKidFrame->HasView()) { michael@0: // If the frame has moved, then we need to make sure any child views are michael@0: // correctly positioned michael@0: PositionChildViews(aKidFrame); michael@0: } michael@0: } michael@0: michael@0: return aKidFrame->DidReflow(aPresContext, aReflowState, nsDidReflowStatus::FINISHED); michael@0: } michael@0: michael@0: nsresult michael@0: nsContainerFrame::ReflowOverflowContainerChildren(nsPresContext* aPresContext, michael@0: const nsHTMLReflowState& aReflowState, michael@0: nsOverflowAreas& aOverflowRects, michael@0: uint32_t aFlags, michael@0: nsReflowStatus& aStatus) michael@0: { michael@0: NS_PRECONDITION(aPresContext, "null pointer"); michael@0: nsresult rv = NS_OK; michael@0: michael@0: nsFrameList* overflowContainers = michael@0: GetPropTableFrames(OverflowContainersProperty()); michael@0: michael@0: NS_ASSERTION(!(overflowContainers && GetPrevInFlow() michael@0: && static_cast(GetPrevInFlow()) michael@0: ->GetPropTableFrames(ExcessOverflowContainersProperty())), michael@0: "conflicting overflow containers lists"); michael@0: michael@0: if (!overflowContainers) { michael@0: // Drain excess from previnflow michael@0: nsContainerFrame* prev = (nsContainerFrame*) GetPrevInFlow(); michael@0: if (prev) { michael@0: nsFrameList* excessFrames = michael@0: prev->RemovePropTableFrames(ExcessOverflowContainersProperty()); michael@0: if (excessFrames) { michael@0: excessFrames->ApplySetParent(this); michael@0: nsContainerFrame::ReparentFrameViewList(*excessFrames, prev, this); michael@0: overflowContainers = excessFrames; michael@0: SetPropTableFrames(overflowContainers, OverflowContainersProperty()); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Our own excess overflow containers from a previous reflow can still be michael@0: // present if our next-in-flow hasn't been reflown yet. michael@0: nsFrameList* selfExcessOCFrames = michael@0: RemovePropTableFrames(ExcessOverflowContainersProperty()); michael@0: if (selfExcessOCFrames) { michael@0: if (overflowContainers) { michael@0: overflowContainers->AppendFrames(nullptr, *selfExcessOCFrames); michael@0: selfExcessOCFrames->Delete(aPresContext->PresShell()); michael@0: } else { michael@0: overflowContainers = selfExcessOCFrames; michael@0: SetPropTableFrames(overflowContainers, OverflowContainersProperty()); michael@0: } michael@0: } michael@0: if (!overflowContainers) { michael@0: return NS_OK; // nothing to reflow michael@0: } michael@0: michael@0: nsOverflowContinuationTracker tracker(this, false, false); michael@0: bool shouldReflowAllKids = aReflowState.ShouldReflowAllKids(); michael@0: michael@0: for (nsIFrame* frame = overflowContainers->FirstChild(); frame; michael@0: frame = frame->GetNextSibling()) { michael@0: if (frame->GetPrevInFlow()->GetParent() != GetPrevInFlow()) { michael@0: // frame's prevInFlow has moved, skip reflowing this frame; michael@0: // it will get reflowed once it's been placed michael@0: continue; michael@0: } michael@0: // If the available vertical height has changed, we need to reflow michael@0: // even if the frame isn't dirty. michael@0: if (shouldReflowAllKids || NS_SUBTREE_DIRTY(frame)) { michael@0: // Get prev-in-flow michael@0: nsIFrame* prevInFlow = frame->GetPrevInFlow(); michael@0: NS_ASSERTION(prevInFlow, michael@0: "overflow container frame must have a prev-in-flow"); michael@0: NS_ASSERTION(frame->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER, michael@0: "overflow container frame must have overflow container bit set"); michael@0: nsRect prevRect = prevInFlow->GetRect(); michael@0: michael@0: // Initialize reflow params michael@0: nsSize availSpace(prevRect.width, aReflowState.AvailableHeight()); michael@0: nsHTMLReflowMetrics desiredSize(aReflowState); michael@0: nsHTMLReflowState frameState(aPresContext, aReflowState, michael@0: frame, availSpace); michael@0: nsReflowStatus frameStatus; michael@0: michael@0: // Reflow michael@0: rv = ReflowChild(frame, aPresContext, desiredSize, frameState, michael@0: prevRect.x, 0, aFlags, frameStatus, &tracker); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: //XXXfr Do we need to override any shrinkwrap effects here? michael@0: // e.g. desiredSize.Width() = prevRect.width; michael@0: rv = FinishReflowChild(frame, aPresContext, desiredSize, &frameState, michael@0: prevRect.x, 0, aFlags); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Handle continuations michael@0: if (!NS_FRAME_IS_FULLY_COMPLETE(frameStatus)) { michael@0: if (frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) { michael@0: // Abspos frames can't cause their parent to be incomplete, michael@0: // only overflow incomplete. michael@0: NS_FRAME_SET_OVERFLOW_INCOMPLETE(frameStatus); michael@0: } michael@0: else { michael@0: NS_ASSERTION(NS_FRAME_IS_COMPLETE(frameStatus), michael@0: "overflow container frames can't be incomplete, only overflow-incomplete"); michael@0: } michael@0: michael@0: // Acquire a next-in-flow, creating it if necessary michael@0: nsIFrame* nif = frame->GetNextInFlow(); michael@0: if (!nif) { michael@0: NS_ASSERTION(frameStatus & NS_FRAME_REFLOW_NEXTINFLOW, michael@0: "Someone forgot a REFLOW_NEXTINFLOW flag"); michael@0: nif = aPresContext->PresShell()->FrameConstructor()-> michael@0: CreateContinuingFrame(aPresContext, frame, this); michael@0: } michael@0: else if (!(nif->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) { michael@0: // used to be a normal next-in-flow; steal it from the child list michael@0: rv = static_cast(nif->GetParent()) michael@0: ->StealFrame(nif); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: tracker.Insert(nif, frameStatus); michael@0: } michael@0: NS_MergeReflowStatusInto(&aStatus, frameStatus); michael@0: // At this point it would be nice to assert !frame->GetOverflowRect().IsEmpty(), michael@0: // but we have some unsplittable frames that, when taller than michael@0: // availableHeight will push zero-height content into a next-in-flow. michael@0: } michael@0: else { michael@0: tracker.Skip(frame, aStatus); michael@0: if (aReflowState.mFloatManager) michael@0: nsBlockFrame::RecoverFloatsFor(frame, *aReflowState.mFloatManager); michael@0: } michael@0: ConsiderChildOverflow(aOverflowRects, frame); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsContainerFrame::DisplayOverflowContainers(nsDisplayListBuilder* aBuilder, michael@0: const nsRect& aDirtyRect, michael@0: const nsDisplayListSet& aLists) michael@0: { michael@0: nsFrameList* overflowconts = GetPropTableFrames(OverflowContainersProperty()); michael@0: if (overflowconts) { michael@0: for (nsIFrame* frame = overflowconts->FirstChild(); frame; michael@0: frame = frame->GetNextSibling()) { michael@0: BuildDisplayListForChild(aBuilder, frame, aDirtyRect, aLists); michael@0: } michael@0: } michael@0: } michael@0: michael@0: static bool michael@0: TryRemoveFrame(nsIFrame* aFrame, FramePropertyTable* aPropTable, michael@0: const FramePropertyDescriptor* aProp, nsIFrame* aChildToRemove) michael@0: { michael@0: nsFrameList* list = static_cast(aPropTable->Get(aFrame, aProp)); michael@0: if (list && list->StartRemoveFrame(aChildToRemove)) { michael@0: // aChildToRemove *may* have been removed from this list. michael@0: if (list->IsEmpty()) { michael@0: aPropTable->Remove(aFrame, aProp); michael@0: list->Delete(aFrame->PresContext()->PresShell()); michael@0: } michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: nsresult michael@0: nsContainerFrame::StealFrame(nsIFrame* aChild, michael@0: bool aForceNormal) michael@0: { michael@0: #ifdef DEBUG michael@0: if (!mFrames.ContainsFrame(aChild)) { michael@0: nsFrameList* list = GetOverflowFrames(); michael@0: if (!list || !list->ContainsFrame(aChild)) { michael@0: FramePropertyTable* propTable = PresContext()->PropertyTable(); michael@0: list = static_cast( michael@0: propTable->Get(this, OverflowContainersProperty())); michael@0: if (!list || !list->ContainsFrame(aChild)) { michael@0: list = static_cast( michael@0: propTable->Get(this, ExcessOverflowContainersProperty())); michael@0: MOZ_ASSERT(list && list->ContainsFrame(aChild), "aChild isn't our child" michael@0: " or on a frame list not supported by StealFrame"); michael@0: } michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: bool removed; michael@0: if ((aChild->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) michael@0: && !aForceNormal) { michael@0: FramePropertyTable* propTable = PresContext()->PropertyTable(); michael@0: // Try removing from the overflow container list. michael@0: removed = ::TryRemoveFrame(this, propTable, OverflowContainersProperty(), michael@0: aChild); michael@0: if (!removed) { michael@0: // It must be in the excess overflow container list. michael@0: removed = ::TryRemoveFrame(this, propTable, michael@0: ExcessOverflowContainersProperty(), michael@0: aChild); michael@0: } michael@0: } else { michael@0: removed = mFrames.StartRemoveFrame(aChild); michael@0: if (!removed) { michael@0: // We didn't find the child in our principal child list. michael@0: // Maybe it's on the overflow list? michael@0: nsFrameList* frameList = GetOverflowFrames(); michael@0: if (frameList) { michael@0: removed = frameList->ContinueRemoveFrame(aChild); michael@0: if (frameList->IsEmpty()) { michael@0: DestroyOverflowList(); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: NS_POSTCONDITION(removed, "StealFrame: can't find aChild"); michael@0: return removed ? NS_OK : NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: nsFrameList michael@0: nsContainerFrame::StealFramesAfter(nsIFrame* aChild) michael@0: { michael@0: NS_ASSERTION(!aChild || michael@0: !(aChild->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER), michael@0: "StealFramesAfter doesn't handle overflow containers"); michael@0: NS_ASSERTION(GetType() != nsGkAtoms::blockFrame, "unexpected call"); michael@0: michael@0: if (!aChild) { michael@0: nsFrameList copy(mFrames); michael@0: mFrames.Clear(); michael@0: return copy; michael@0: } michael@0: michael@0: for (nsFrameList::FrameLinkEnumerator iter(mFrames); !iter.AtEnd(); michael@0: iter.Next()) { michael@0: if (iter.PrevFrame() == aChild) { michael@0: return mFrames.ExtractTail(iter); michael@0: } michael@0: } michael@0: michael@0: // We didn't find the child in the principal child list. michael@0: // Maybe it's on the overflow list? michael@0: nsFrameList* overflowFrames = GetOverflowFrames(); michael@0: if (overflowFrames) { michael@0: for (nsFrameList::FrameLinkEnumerator iter(*overflowFrames); !iter.AtEnd(); michael@0: iter.Next()) { michael@0: if (iter.PrevFrame() == aChild) { michael@0: return overflowFrames->ExtractTail(iter); michael@0: } michael@0: } michael@0: } michael@0: michael@0: NS_ERROR("StealFramesAfter: can't find aChild"); michael@0: return nsFrameList::EmptyList(); michael@0: } michael@0: michael@0: /* michael@0: * Create a next-in-flow for aFrame. Will return the newly created michael@0: * frame in aNextInFlowResult if and only if a new frame is michael@0: * created; otherwise nullptr is returned in aNextInFlowResult. michael@0: */ michael@0: nsresult michael@0: nsContainerFrame::CreateNextInFlow(nsIFrame* aFrame, michael@0: nsIFrame*& aNextInFlowResult) michael@0: { michael@0: NS_PRECONDITION(GetType() != nsGkAtoms::blockFrame, michael@0: "you should have called nsBlockFrame::CreateContinuationFor instead"); michael@0: NS_PRECONDITION(mFrames.ContainsFrame(aFrame), "expected an in-flow child frame"); michael@0: michael@0: nsPresContext* pc = PresContext(); michael@0: aNextInFlowResult = nullptr; michael@0: michael@0: nsIFrame* nextInFlow = aFrame->GetNextInFlow(); michael@0: if (nullptr == nextInFlow) { michael@0: // Create a continuation frame for the child frame and insert it michael@0: // into our child list. michael@0: nextInFlow = pc->PresShell()->FrameConstructor()-> michael@0: CreateContinuingFrame(pc, aFrame, this); michael@0: mFrames.InsertFrame(nullptr, aFrame, nextInFlow); michael@0: michael@0: NS_FRAME_LOG(NS_FRAME_TRACE_NEW_FRAMES, michael@0: ("nsContainerFrame::CreateNextInFlow: frame=%p nextInFlow=%p", michael@0: aFrame, nextInFlow)); michael@0: michael@0: aNextInFlowResult = nextInFlow; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: /** michael@0: * Remove and delete aNextInFlow and its next-in-flows. Updates the sibling and flow michael@0: * pointers michael@0: */ michael@0: void michael@0: nsContainerFrame::DeleteNextInFlowChild(nsIFrame* aNextInFlow, michael@0: bool aDeletingEmptyFrames) michael@0: { michael@0: #ifdef DEBUG michael@0: nsIFrame* prevInFlow = aNextInFlow->GetPrevInFlow(); michael@0: #endif michael@0: NS_PRECONDITION(prevInFlow, "bad prev-in-flow"); michael@0: michael@0: // If the next-in-flow has a next-in-flow then delete it, too (and michael@0: // delete it first). michael@0: // Do this in a loop so we don't overflow the stack for frames michael@0: // with very many next-in-flows michael@0: nsIFrame* nextNextInFlow = aNextInFlow->GetNextInFlow(); michael@0: if (nextNextInFlow) { michael@0: nsAutoTArray frames; michael@0: for (nsIFrame* f = nextNextInFlow; f; f = f->GetNextInFlow()) { michael@0: frames.AppendElement(f); michael@0: } michael@0: for (int32_t i = frames.Length() - 1; i >= 0; --i) { michael@0: nsIFrame* delFrame = frames.ElementAt(i); michael@0: static_cast(delFrame->GetParent()) michael@0: ->DeleteNextInFlowChild(delFrame, aDeletingEmptyFrames); michael@0: } michael@0: } michael@0: michael@0: // Take the next-in-flow out of the parent's child list michael@0: DebugOnly rv = StealFrame(aNextInFlow); michael@0: NS_ASSERTION(NS_SUCCEEDED(rv), "StealFrame failure"); michael@0: michael@0: #ifdef DEBUG michael@0: if (aDeletingEmptyFrames) { michael@0: nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(aNextInFlow); michael@0: } michael@0: #endif michael@0: michael@0: // Delete the next-in-flow frame and its descendants. This will also michael@0: // remove it from its next-in-flow/prev-in-flow chain. michael@0: aNextInFlow->Destroy(); michael@0: michael@0: NS_POSTCONDITION(!prevInFlow->GetNextInFlow(), "non null next-in-flow"); michael@0: } michael@0: michael@0: /** michael@0: * Set the frames on the overflow list michael@0: */ michael@0: void michael@0: nsContainerFrame::SetOverflowFrames(const nsFrameList& aOverflowFrames) michael@0: { michael@0: NS_PRECONDITION(aOverflowFrames.NotEmpty(), "Shouldn't be called"); michael@0: michael@0: nsPresContext* pc = PresContext(); michael@0: nsFrameList* newList = new (pc->PresShell()) nsFrameList(aOverflowFrames); michael@0: michael@0: pc->PropertyTable()->Set(this, OverflowProperty(), newList); michael@0: } michael@0: michael@0: nsFrameList* michael@0: nsContainerFrame::GetPropTableFrames(const FramePropertyDescriptor* aProperty) const michael@0: { michael@0: FramePropertyTable* propTable = PresContext()->PropertyTable(); michael@0: return static_cast(propTable->Get(this, aProperty)); michael@0: } michael@0: michael@0: nsFrameList* michael@0: nsContainerFrame::RemovePropTableFrames(const FramePropertyDescriptor* aProperty) michael@0: { michael@0: FramePropertyTable* propTable = PresContext()->PropertyTable(); michael@0: return static_cast(propTable->Remove(this, aProperty)); michael@0: } michael@0: michael@0: void michael@0: nsContainerFrame::SetPropTableFrames(nsFrameList* aFrameList, michael@0: const FramePropertyDescriptor* aProperty) michael@0: { michael@0: NS_PRECONDITION(aProperty && aFrameList, "null ptr"); michael@0: NS_PRECONDITION( michael@0: (aProperty != nsContainerFrame::OverflowContainersProperty() && michael@0: aProperty != nsContainerFrame::ExcessOverflowContainersProperty()) || michael@0: IsFrameOfType(nsIFrame::eCanContainOverflowContainers), michael@0: "this type of frame can't have overflow containers"); michael@0: MOZ_ASSERT(!GetPropTableFrames(aProperty)); michael@0: PresContext()->PropertyTable()->Set(this, aProperty, aFrameList); michael@0: } michael@0: michael@0: /** michael@0: * Push aFromChild and its next siblings to the next-in-flow. Change the michael@0: * geometric parent of each frame that's pushed. If there is no next-in-flow michael@0: * the frames are placed on the overflow list (and the geometric parent is michael@0: * left unchanged). michael@0: * michael@0: * Updates the next-in-flow's child count. Does not update the michael@0: * pusher's child count. michael@0: * michael@0: * @param aFromChild the first child frame to push. It is disconnected from michael@0: * aPrevSibling michael@0: * @param aPrevSibling aFromChild's previous sibling. Must not be null. It's michael@0: * an error to push a parent's first child frame michael@0: */ michael@0: void michael@0: nsContainerFrame::PushChildren(nsIFrame* aFromChild, michael@0: nsIFrame* aPrevSibling) michael@0: { michael@0: NS_PRECONDITION(aFromChild, "null pointer"); michael@0: NS_PRECONDITION(aPrevSibling, "pushing first child"); michael@0: NS_PRECONDITION(aPrevSibling->GetNextSibling() == aFromChild, "bad prev sibling"); michael@0: michael@0: // Disconnect aFromChild from its previous sibling michael@0: nsFrameList tail = mFrames.RemoveFramesAfter(aPrevSibling); michael@0: michael@0: nsContainerFrame* nextInFlow = michael@0: static_cast(GetNextInFlow()); michael@0: if (nextInFlow) { michael@0: // XXX This is not a very good thing to do. If it gets removed michael@0: // then remove the copy of this routine that doesn't do this from michael@0: // nsInlineFrame. michael@0: // When pushing and pulling frames we need to check for whether any michael@0: // views need to be reparented. michael@0: for (nsIFrame* f = aFromChild; f; f = f->GetNextSibling()) { michael@0: nsContainerFrame::ReparentFrameView(f, this, nextInFlow); michael@0: } michael@0: nextInFlow->mFrames.InsertFrames(nextInFlow, nullptr, tail); michael@0: } michael@0: else { michael@0: // Add the frames to our overflow list michael@0: SetOverflowFrames(tail); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Moves any frames on the overflow lists (the prev-in-flow's overflow list and michael@0: * the receiver's overflow list) to the child list. michael@0: * michael@0: * Updates this frame's child count and content mapping. michael@0: * michael@0: * @return true if any frames were moved and false otherwise michael@0: */ michael@0: bool michael@0: nsContainerFrame::MoveOverflowToChildList() michael@0: { michael@0: bool result = false; michael@0: michael@0: // Check for an overflow list with our prev-in-flow michael@0: nsContainerFrame* prevInFlow = (nsContainerFrame*)GetPrevInFlow(); michael@0: if (nullptr != prevInFlow) { michael@0: AutoFrameListPtr prevOverflowFrames(PresContext(), michael@0: prevInFlow->StealOverflowFrames()); michael@0: if (prevOverflowFrames) { michael@0: // Tables are special; they can have repeated header/footer michael@0: // frames on mFrames at this point. michael@0: NS_ASSERTION(mFrames.IsEmpty() || GetType() == nsGkAtoms::tableFrame, michael@0: "bad overflow list"); michael@0: // When pushing and pulling frames we need to check for whether any michael@0: // views need to be reparented. michael@0: nsContainerFrame::ReparentFrameViewList(*prevOverflowFrames, michael@0: prevInFlow, this); michael@0: mFrames.AppendFrames(this, *prevOverflowFrames); michael@0: result = true; michael@0: } michael@0: } michael@0: michael@0: // It's also possible that we have an overflow list for ourselves. michael@0: return DrainSelfOverflowList() || result; michael@0: } michael@0: michael@0: bool michael@0: nsContainerFrame::DrainSelfOverflowList() michael@0: { michael@0: AutoFrameListPtr overflowFrames(PresContext(), StealOverflowFrames()); michael@0: if (overflowFrames) { michael@0: NS_ASSERTION(mFrames.NotEmpty(), "overflow list w/o frames"); michael@0: mFrames.AppendFrames(nullptr, *overflowFrames); michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: nsOverflowContinuationTracker::nsOverflowContinuationTracker(nsContainerFrame* aFrame, michael@0: bool aWalkOOFFrames, michael@0: bool aSkipOverflowContainerChildren) michael@0: : mOverflowContList(nullptr), michael@0: mPrevOverflowCont(nullptr), michael@0: mSentry(nullptr), michael@0: mParent(aFrame), michael@0: mSkipOverflowContainerChildren(aSkipOverflowContainerChildren), michael@0: mWalkOOFFrames(aWalkOOFFrames) michael@0: { michael@0: NS_PRECONDITION(aFrame, "null frame pointer"); michael@0: SetupOverflowContList(); michael@0: } michael@0: michael@0: void michael@0: nsOverflowContinuationTracker::SetupOverflowContList() michael@0: { michael@0: NS_PRECONDITION(mParent, "null frame pointer"); michael@0: NS_PRECONDITION(!mOverflowContList, "already have list"); michael@0: nsContainerFrame* nif = michael@0: static_cast(mParent->GetNextInFlow()); michael@0: if (nif) { michael@0: mOverflowContList = nif->GetPropTableFrames( michael@0: nsContainerFrame::OverflowContainersProperty()); michael@0: if (mOverflowContList) { michael@0: mParent = nif; michael@0: SetUpListWalker(); michael@0: } michael@0: } michael@0: if (!mOverflowContList) { michael@0: mOverflowContList = mParent->GetPropTableFrames( michael@0: nsContainerFrame::ExcessOverflowContainersProperty()); michael@0: if (mOverflowContList) { michael@0: SetUpListWalker(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Helper function to walk past overflow continuations whose prev-in-flow michael@0: * isn't a normal child and to set mSentry and mPrevOverflowCont correctly. michael@0: */ michael@0: void michael@0: nsOverflowContinuationTracker::SetUpListWalker() michael@0: { michael@0: NS_ASSERTION(!mSentry && !mPrevOverflowCont, michael@0: "forgot to reset mSentry or mPrevOverflowCont"); michael@0: if (mOverflowContList) { michael@0: nsIFrame* cur = mOverflowContList->FirstChild(); michael@0: if (mSkipOverflowContainerChildren) { michael@0: while (cur && (cur->GetPrevInFlow()->GetStateBits() michael@0: & NS_FRAME_IS_OVERFLOW_CONTAINER)) { michael@0: mPrevOverflowCont = cur; michael@0: cur = cur->GetNextSibling(); michael@0: } michael@0: while (cur && (!(cur->GetStateBits() & NS_FRAME_OUT_OF_FLOW) michael@0: == mWalkOOFFrames)) { michael@0: mPrevOverflowCont = cur; michael@0: cur = cur->GetNextSibling(); michael@0: } michael@0: } michael@0: if (cur) { michael@0: mSentry = cur->GetPrevInFlow(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Helper function to step forward through the overflow continuations list. michael@0: * Sets mSentry and mPrevOverflowCont, skipping over OOF or non-OOF frames michael@0: * as appropriate. May only be called when we have already set up an michael@0: * mOverflowContList; mOverflowContList cannot be null. michael@0: */ michael@0: void michael@0: nsOverflowContinuationTracker::StepForward() michael@0: { michael@0: NS_PRECONDITION(mOverflowContList, "null list"); michael@0: michael@0: // Step forward michael@0: if (mPrevOverflowCont) { michael@0: mPrevOverflowCont = mPrevOverflowCont->GetNextSibling(); michael@0: } michael@0: else { michael@0: mPrevOverflowCont = mOverflowContList->FirstChild(); michael@0: } michael@0: michael@0: // Skip over oof or non-oof frames as appropriate michael@0: if (mSkipOverflowContainerChildren) { michael@0: nsIFrame* cur = mPrevOverflowCont->GetNextSibling(); michael@0: while (cur && (!(cur->GetStateBits() & NS_FRAME_OUT_OF_FLOW) michael@0: == mWalkOOFFrames)) { michael@0: mPrevOverflowCont = cur; michael@0: cur = cur->GetNextSibling(); michael@0: } michael@0: } michael@0: michael@0: // Set up the sentry michael@0: mSentry = (mPrevOverflowCont->GetNextSibling()) michael@0: ? mPrevOverflowCont->GetNextSibling()->GetPrevInFlow() michael@0: : nullptr; michael@0: } michael@0: michael@0: nsresult michael@0: nsOverflowContinuationTracker::Insert(nsIFrame* aOverflowCont, michael@0: nsReflowStatus& aReflowStatus) michael@0: { michael@0: NS_PRECONDITION(aOverflowCont, "null frame pointer"); michael@0: NS_PRECONDITION(!mSkipOverflowContainerChildren || mWalkOOFFrames == michael@0: !!(aOverflowCont->GetStateBits() & NS_FRAME_OUT_OF_FLOW), michael@0: "shouldn't insert frame that doesn't match walker type"); michael@0: NS_PRECONDITION(aOverflowCont->GetPrevInFlow(), michael@0: "overflow containers must have a prev-in-flow"); michael@0: nsresult rv = NS_OK; michael@0: bool reparented = false; michael@0: nsPresContext* presContext = aOverflowCont->PresContext(); michael@0: bool addToList = !mSentry || aOverflowCont != mSentry->GetNextInFlow(); michael@0: michael@0: // If we have a list and aOverflowCont is already in it then don't try to michael@0: // add it again. michael@0: if (addToList && aOverflowCont->GetParent() == mParent && michael@0: (aOverflowCont->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) && michael@0: mOverflowContList && mOverflowContList->ContainsFrame(aOverflowCont)) { michael@0: addToList = false; michael@0: mPrevOverflowCont = aOverflowCont->GetPrevSibling(); michael@0: } michael@0: michael@0: if (addToList) { michael@0: if (aOverflowCont->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) { michael@0: // aOverflowCont is in some other overflow container list, michael@0: // steal it first michael@0: NS_ASSERTION(!(mOverflowContList && michael@0: mOverflowContList->ContainsFrame(aOverflowCont)), michael@0: "overflow containers out of order"); michael@0: rv = static_cast(aOverflowCont->GetParent()) michael@0: ->StealFrame(aOverflowCont); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: else { michael@0: aOverflowCont->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER); michael@0: } michael@0: if (!mOverflowContList) { michael@0: mOverflowContList = new (presContext->PresShell()) nsFrameList(); michael@0: mParent->SetPropTableFrames(mOverflowContList, michael@0: nsContainerFrame::ExcessOverflowContainersProperty()); michael@0: SetUpListWalker(); michael@0: } michael@0: if (aOverflowCont->GetParent() != mParent) { michael@0: nsContainerFrame::ReparentFrameView(aOverflowCont, michael@0: aOverflowCont->GetParent(), michael@0: mParent); michael@0: reparented = true; michael@0: } michael@0: michael@0: // If aOverflowCont has a prev/next-in-flow that might be in michael@0: // mOverflowContList we need to find it and insert after/before it to michael@0: // maintain the order amongst next-in-flows in this list. michael@0: nsIFrame* pif = aOverflowCont->GetPrevInFlow(); michael@0: nsIFrame* nif = aOverflowCont->GetNextInFlow(); michael@0: if ((pif && pif->GetParent() == mParent && pif != mPrevOverflowCont) || michael@0: (nif && nif->GetParent() == mParent && mPrevOverflowCont)) { michael@0: for (nsFrameList::Enumerator e(*mOverflowContList); !e.AtEnd(); e.Next()) { michael@0: nsIFrame* f = e.get(); michael@0: if (f == pif) { michael@0: mPrevOverflowCont = pif; michael@0: break; michael@0: } michael@0: if (f == nif) { michael@0: mPrevOverflowCont = f->GetPrevSibling(); michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: mOverflowContList->InsertFrame(mParent, mPrevOverflowCont, aOverflowCont); michael@0: aReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW; michael@0: } michael@0: michael@0: // If we need to reflow it, mark it dirty michael@0: if (aReflowStatus & NS_FRAME_REFLOW_NEXTINFLOW) michael@0: aOverflowCont->AddStateBits(NS_FRAME_IS_DIRTY); michael@0: michael@0: // It's in our list, just step forward michael@0: StepForward(); michael@0: NS_ASSERTION(mPrevOverflowCont == aOverflowCont || michael@0: (mSkipOverflowContainerChildren && michael@0: (mPrevOverflowCont->GetStateBits() & NS_FRAME_OUT_OF_FLOW) != michael@0: (aOverflowCont->GetStateBits() & NS_FRAME_OUT_OF_FLOW)), michael@0: "OverflowContTracker in unexpected state"); michael@0: michael@0: if (addToList) { michael@0: // Convert all non-overflow-container continuations of aOverflowCont michael@0: // into overflow containers and move them to our overflow michael@0: // tracker. This preserves the invariant that the next-continuations michael@0: // of an overflow container are also overflow containers. michael@0: nsIFrame* f = aOverflowCont->GetNextContinuation(); michael@0: if (f && (!(f->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) || michael@0: (!reparented && f->GetParent() == mParent) || michael@0: (reparented && f->GetParent() != mParent))) { michael@0: if (!(f->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) { michael@0: nsContainerFrame* parent = static_cast(f->GetParent()); michael@0: rv = parent->StealFrame(f); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: Insert(f, aReflowStatus); michael@0: } michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: void michael@0: nsOverflowContinuationTracker::BeginFinish(nsIFrame* aChild) michael@0: { michael@0: NS_PRECONDITION(aChild, "null ptr"); michael@0: NS_PRECONDITION(aChild->GetNextInFlow(), michael@0: "supposed to call Finish *before* deleting next-in-flow!"); michael@0: for (nsIFrame* f = aChild; f; f = f->GetNextInFlow()) { michael@0: // We'll update these in EndFinish after the next-in-flows are gone. michael@0: if (f == mPrevOverflowCont) { michael@0: mSentry = nullptr; michael@0: mPrevOverflowCont = nullptr; michael@0: break; michael@0: } michael@0: if (f == mSentry) { michael@0: mSentry = nullptr; michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsOverflowContinuationTracker::EndFinish(nsIFrame* aChild) michael@0: { michael@0: if (!mOverflowContList) { michael@0: return; michael@0: } michael@0: // Forget mOverflowContList if it was deleted. michael@0: nsPresContext* pc = aChild->PresContext(); michael@0: FramePropertyTable* propTable = pc->PropertyTable(); michael@0: nsFrameList* eoc = static_cast(propTable->Get(mParent, michael@0: nsContainerFrame::ExcessOverflowContainersProperty())); michael@0: if (eoc != mOverflowContList) { michael@0: nsFrameList* oc = static_cast(propTable->Get(mParent, michael@0: nsContainerFrame::OverflowContainersProperty())); michael@0: if (oc != mOverflowContList) { michael@0: // mOverflowContList was deleted michael@0: mPrevOverflowCont = nullptr; michael@0: mSentry = nullptr; michael@0: mParent = static_cast(aChild->GetParent()); michael@0: mOverflowContList = nullptr; michael@0: SetupOverflowContList(); michael@0: return; michael@0: } michael@0: } michael@0: // The list survived, update mSentry if needed. michael@0: if (!mSentry) { michael@0: if (!mPrevOverflowCont) { michael@0: SetUpListWalker(); michael@0: } else { michael@0: mozilla::AutoRestore saved(mPrevOverflowCont); michael@0: // step backward to make StepForward() use our current mPrevOverflowCont michael@0: mPrevOverflowCont = mPrevOverflowCont->GetPrevSibling(); michael@0: StepForward(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: ///////////////////////////////////////////////////////////////////////////// michael@0: // Debugging michael@0: michael@0: #ifdef DEBUG_FRAME_DUMP michael@0: void michael@0: nsContainerFrame::List(FILE* out, const char* aPrefix, uint32_t aFlags) const michael@0: { michael@0: nsCString str; michael@0: ListGeneric(str, aPrefix, aFlags); michael@0: michael@0: // Output the children michael@0: bool outputOneList = false; michael@0: ChildListIterator lists(this); michael@0: for (; !lists.IsDone(); lists.Next()) { michael@0: if (outputOneList) { michael@0: str += aPrefix; michael@0: } michael@0: if (lists.CurrentID() != kPrincipalList) { michael@0: if (!outputOneList) { michael@0: str += "\n"; michael@0: str += aPrefix; michael@0: } michael@0: str += nsPrintfCString("%s %p ", mozilla::layout::ChildListName(lists.CurrentID()), michael@0: &GetChildList(lists.CurrentID())); michael@0: } michael@0: fprintf_stderr(out, "%s<\n", str.get()); michael@0: str = ""; michael@0: nsFrameList::Enumerator childFrames(lists.CurrentList()); michael@0: for (; !childFrames.AtEnd(); childFrames.Next()) { michael@0: nsIFrame* kid = childFrames.get(); michael@0: // Verify the child frame's parent frame pointer is correct michael@0: NS_ASSERTION(kid->GetParent() == this, "bad parent frame pointer"); michael@0: michael@0: // Have the child frame list michael@0: nsCString pfx(aPrefix); michael@0: pfx += " "; michael@0: kid->List(out, pfx.get(), aFlags); michael@0: } michael@0: fprintf_stderr(out, "%s>\n", aPrefix); michael@0: outputOneList = true; michael@0: } michael@0: michael@0: if (!outputOneList) { michael@0: fprintf_stderr(out, "%s<>\n", str.get()); michael@0: } michael@0: } michael@0: #endif