diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /layout/generic/nsContainerFrame.cpp | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'layout/generic/nsContainerFrame.cpp')
-rw-r--r-- | layout/generic/nsContainerFrame.cpp | 2332 |
1 files changed, 2332 insertions, 0 deletions
diff --git a/layout/generic/nsContainerFrame.cpp b/layout/generic/nsContainerFrame.cpp new file mode 100644 index 000000000..813d19dfe --- /dev/null +++ b/layout/generic/nsContainerFrame.cpp @@ -0,0 +1,2332 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* base class #1 for rendering objects that have child lists */ + +#include "nsContainerFrame.h" + +#include "mozilla/dom/HTMLDetailsElement.h" +#include "mozilla/dom/HTMLSummaryElement.h" +#include "nsAbsoluteContainingBlock.h" +#include "nsIDocument.h" +#include "nsPresContext.h" +#include "nsStyleContext.h" +#include "nsRect.h" +#include "nsPoint.h" +#include "nsStyleConsts.h" +#include "nsView.h" +#include "nsIPresShell.h" +#include "nsCOMPtr.h" +#include "nsGkAtoms.h" +#include "nsViewManager.h" +#include "nsIWidget.h" +#include "nsCSSRendering.h" +#include "nsError.h" +#include "nsDisplayList.h" +#include "nsIBaseWindow.h" +#include "nsBoxLayoutState.h" +#include "nsCSSFrameConstructor.h" +#include "nsBlockFrame.h" +#include "nsPlaceholderFrame.h" +#include "mozilla/AutoRestore.h" +#include "nsIFrameInlines.h" +#include "nsPrintfCString.h" +#include <algorithm> + +using namespace mozilla; +using namespace mozilla::dom; +using namespace mozilla::layout; + +nsContainerFrame::~nsContainerFrame() +{ +} + +NS_QUERYFRAME_HEAD(nsContainerFrame) + NS_QUERYFRAME_ENTRY(nsContainerFrame) +NS_QUERYFRAME_TAIL_INHERITING(nsSplittableFrame) + +void +nsContainerFrame::Init(nsIContent* aContent, + nsContainerFrame* aParent, + nsIFrame* aPrevInFlow) +{ + nsSplittableFrame::Init(aContent, aParent, aPrevInFlow); + if (aPrevInFlow) { + // Make sure we copy bits from our prev-in-flow that will affect + // us. A continuation for a container frame needs to know if it + // has a child with a view so that we'll properly reposition it. + if (aPrevInFlow->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW) + AddStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW); + } +} + +void +nsContainerFrame::SetInitialChildList(ChildListID aListID, + nsFrameList& aChildList) +{ +#ifdef DEBUG + nsFrame::VerifyDirtyBitSet(aChildList); +#endif + if (aListID == kPrincipalList) { + MOZ_ASSERT(mFrames.IsEmpty(), + "unexpected second call to SetInitialChildList"); + mFrames.SetFrames(aChildList); + } else if (aListID == kBackdropList) { + MOZ_ASSERT(StyleDisplay()->mTopLayer != NS_STYLE_TOP_LAYER_NONE, + "Only top layer frames should have backdrop"); + MOZ_ASSERT(GetStateBits() & NS_FRAME_OUT_OF_FLOW, + "Top layer frames should be out-of-flow"); + MOZ_ASSERT(!Properties().Get(BackdropProperty()), + "We shouldn't have setup backdrop frame list before"); +#ifdef DEBUG + { + nsIFrame* placeholder = aChildList.FirstChild(); + MOZ_ASSERT(aChildList.OnlyChild(), "Should have only one backdrop"); + MOZ_ASSERT(placeholder->GetType() == nsGkAtoms::placeholderFrame, + "The frame to be stored should be a placeholder"); + MOZ_ASSERT(static_cast<nsPlaceholderFrame*>(placeholder)-> + GetOutOfFlowFrame()->GetType() == nsGkAtoms::backdropFrame, + "The placeholder should points to a backdrop frame"); + } +#endif + nsFrameList* list = + new (PresContext()->PresShell()) nsFrameList(aChildList); + Properties().Set(BackdropProperty(), list); + } else { + MOZ_ASSERT_UNREACHABLE("Unexpected child list"); + } +} + +void +nsContainerFrame::AppendFrames(ChildListID aListID, + nsFrameList& aFrameList) +{ + MOZ_ASSERT(aListID == kPrincipalList || aListID == kNoReflowPrincipalList, + "unexpected child list"); + + if (MOZ_UNLIKELY(aFrameList.IsEmpty())) { + return; + } + + DrainSelfOverflowList(); // ensure the last frame is in mFrames + mFrames.AppendFrames(this, aFrameList); + + if (aListID != kNoReflowPrincipalList) { + PresContext()->PresShell()-> + FrameNeedsReflow(this, nsIPresShell::eTreeChange, + NS_FRAME_HAS_DIRTY_CHILDREN); + } +} + +void +nsContainerFrame::InsertFrames(ChildListID aListID, + nsIFrame* aPrevFrame, + nsFrameList& aFrameList) +{ + MOZ_ASSERT(aListID == kPrincipalList || aListID == kNoReflowPrincipalList, + "unexpected child list"); + NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this, + "inserting after sibling frame with different parent"); + + if (MOZ_UNLIKELY(aFrameList.IsEmpty())) { + return; + } + + DrainSelfOverflowList(); // ensure aPrevFrame is in mFrames + mFrames.InsertFrames(this, aPrevFrame, aFrameList); + + if (aListID != kNoReflowPrincipalList) { + PresContext()->PresShell()-> + FrameNeedsReflow(this, nsIPresShell::eTreeChange, + NS_FRAME_HAS_DIRTY_CHILDREN); + } +} + +void +nsContainerFrame::RemoveFrame(ChildListID aListID, + nsIFrame* aOldFrame) +{ + MOZ_ASSERT(aListID == kPrincipalList || aListID == kNoReflowPrincipalList, + "unexpected child list"); + + // Loop and destroy aOldFrame and all of its continuations. + // Request a reflow on the parent frames involved unless we were explicitly + // told not to (kNoReflowPrincipalList). + bool generateReflowCommand = true; + if (kNoReflowPrincipalList == aListID) { + generateReflowCommand = false; + } + nsIPresShell* shell = PresContext()->PresShell(); + nsContainerFrame* lastParent = nullptr; + while (aOldFrame) { + nsIFrame* oldFrameNextContinuation = aOldFrame->GetNextContinuation(); + nsContainerFrame* parent = aOldFrame->GetParent(); + // Please note that 'parent' may not actually be where 'aOldFrame' lives. + // We really MUST use StealFrame() and nothing else here. + // @see nsInlineFrame::StealFrame for details. + parent->StealFrame(aOldFrame); + aOldFrame->Destroy(); + aOldFrame = oldFrameNextContinuation; + if (parent != lastParent && generateReflowCommand) { + shell->FrameNeedsReflow(parent, nsIPresShell::eTreeChange, + NS_FRAME_HAS_DIRTY_CHILDREN); + lastParent = parent; + } + } +} + +void +nsContainerFrame::DestroyAbsoluteFrames(nsIFrame* aDestructRoot) +{ + if (IsAbsoluteContainer()) { + GetAbsoluteContainingBlock()->DestroyFrames(this, aDestructRoot); + MarkAsNotAbsoluteContainingBlock(); + } +} + +void +nsContainerFrame::SafelyDestroyFrameListProp(nsIFrame* aDestructRoot, + nsIPresShell* aPresShell, + FramePropertyTable* aPropTable, + FrameListPropertyDescriptor aProp) +{ + // Note that the last frame can be removed through another route and thus + // delete the property -- that's why we fetch the property again before + // removing each frame rather than fetching it once and iterating the list. + while (nsFrameList* frameList = aPropTable->Get(this, aProp)) { + nsIFrame* frame = frameList->RemoveFirstChild(); + if (MOZ_LIKELY(frame)) { + frame->DestroyFrom(aDestructRoot); + } else { + aPropTable->Remove(this, aProp); + frameList->Delete(aPresShell); + return; + } + } +} + +void +nsContainerFrame::DestroyFrom(nsIFrame* aDestructRoot) +{ + // Prevent event dispatch during destruction. + if (HasView()) { + GetView()->SetFrame(nullptr); + } + + DestroyAbsoluteFrames(aDestructRoot); + + // Destroy frames on the principal child list. + mFrames.DestroyFramesFrom(aDestructRoot); + + // Destroy frames on the auxiliary frame lists and delete the lists. + nsPresContext* pc = PresContext(); + nsIPresShell* shell = pc->PresShell(); + FramePropertyTable* props = pc->PropertyTable(); + SafelyDestroyFrameListProp(aDestructRoot, shell, props, OverflowProperty()); + + MOZ_ASSERT(IsFrameOfType(nsIFrame::eCanContainOverflowContainers) || + !(props->Get(this, nsContainerFrame::OverflowContainersProperty()) || + props->Get(this, nsContainerFrame::ExcessOverflowContainersProperty())), + "this type of frame should't have overflow containers"); + SafelyDestroyFrameListProp(aDestructRoot, shell, props, + OverflowContainersProperty()); + SafelyDestroyFrameListProp(aDestructRoot, shell, props, + ExcessOverflowContainersProperty()); + + MOZ_ASSERT(!props->Get(this, BackdropProperty()) || + StyleDisplay()->mTopLayer != NS_STYLE_TOP_LAYER_NONE, + "only top layer frame may have backdrop"); + SafelyDestroyFrameListProp(aDestructRoot, shell, props, BackdropProperty()); + + nsSplittableFrame::DestroyFrom(aDestructRoot); +} + +///////////////////////////////////////////////////////////////////////////// +// Child frame enumeration + +const nsFrameList& +nsContainerFrame::GetChildList(ChildListID aListID) const +{ + // We only know about the principal child list, the overflow lists, + // and the backdrop list. + switch (aListID) { + case kPrincipalList: + return mFrames; + case kOverflowList: { + nsFrameList* list = GetOverflowFrames(); + return list ? *list : nsFrameList::EmptyList(); + } + case kOverflowContainersList: { + nsFrameList* list = GetPropTableFrames(OverflowContainersProperty()); + return list ? *list : nsFrameList::EmptyList(); + } + case kExcessOverflowContainersList: { + nsFrameList* list = + GetPropTableFrames(ExcessOverflowContainersProperty()); + return list ? *list : nsFrameList::EmptyList(); + } + case kBackdropList: { + nsFrameList* list = GetPropTableFrames(BackdropProperty()); + return list ? *list : nsFrameList::EmptyList(); + } + default: + return nsSplittableFrame::GetChildList(aListID); + } +} + +static void +AppendIfNonempty(const nsIFrame* aFrame, + FramePropertyTable* aPropTable, + nsContainerFrame::FrameListPropertyDescriptor aProperty, + nsTArray<nsIFrame::ChildList>* aLists, + nsIFrame::ChildListID aListID) +{ + if (nsFrameList* list = aPropTable->Get(aFrame, aProperty)) { + list->AppendIfNonempty(aLists, aListID); + } +} + +void +nsContainerFrame::GetChildLists(nsTArray<ChildList>* aLists) const +{ + mFrames.AppendIfNonempty(aLists, kPrincipalList); + FramePropertyTable* propTable = PresContext()->PropertyTable(); + ::AppendIfNonempty(this, propTable, OverflowProperty(), + aLists, kOverflowList); + if (IsFrameOfType(nsIFrame::eCanContainOverflowContainers)) { + ::AppendIfNonempty(this, propTable, OverflowContainersProperty(), + aLists, kOverflowContainersList); + ::AppendIfNonempty(this, propTable, ExcessOverflowContainersProperty(), + aLists, kExcessOverflowContainersList); + } + // Bypass BackdropProperty hashtable lookup for any in-flow frames + // since frames in the top layer (only which can have backdrop) are + // definitely out-of-flow. + if (GetStateBits() & NS_FRAME_OUT_OF_FLOW) { + ::AppendIfNonempty(this, propTable, BackdropProperty(), + aLists, kBackdropList); + } + nsSplittableFrame::GetChildLists(aLists); +} + +///////////////////////////////////////////////////////////////////////////// +// Painting/Events + +void +nsContainerFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, + const nsRect& aDirtyRect, + const nsDisplayListSet& aLists) +{ + DisplayBorderBackgroundOutline(aBuilder, aLists); + + BuildDisplayListForNonBlockChildren(aBuilder, aDirtyRect, aLists); +} + +void +nsContainerFrame::BuildDisplayListForNonBlockChildren(nsDisplayListBuilder* aBuilder, + const nsRect& aDirtyRect, + const nsDisplayListSet& aLists, + uint32_t aFlags) +{ + nsIFrame* kid = mFrames.FirstChild(); + // Put each child's background directly onto the content list + nsDisplayListSet set(aLists, aLists.Content()); + // The children should be in content order + while (kid) { + BuildDisplayListForChild(aBuilder, kid, aDirtyRect, set, aFlags); + kid = kid->GetNextSibling(); + } +} + +/* virtual */ void +nsContainerFrame::ChildIsDirty(nsIFrame* aChild) +{ + NS_ASSERTION(NS_SUBTREE_DIRTY(aChild), "child isn't actually dirty"); + + AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN); +} + +bool +nsContainerFrame::IsLeaf() const +{ + return false; +} + +nsIFrame::FrameSearchResult +nsContainerFrame::PeekOffsetNoAmount(bool aForward, int32_t* aOffset) +{ + NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range"); + // Don't allow the caret to stay in an empty (leaf) container frame. + return CONTINUE_EMPTY; +} + +nsIFrame::FrameSearchResult +nsContainerFrame::PeekOffsetCharacter(bool aForward, int32_t* aOffset, + bool aRespectClusters) +{ + NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range"); + // Don't allow the caret to stay in an empty (leaf) container frame. + return CONTINUE_EMPTY; +} + +///////////////////////////////////////////////////////////////////////////// +// Helper member functions + +static void +ReparentFrameViewTo(nsIFrame* aFrame, + nsViewManager* aViewManager, + nsView* aNewParentView, + nsView* aOldParentView) +{ + if (aFrame->HasView()) { +#ifdef MOZ_XUL + if (aFrame->GetType() == nsGkAtoms::menuPopupFrame) { + // This view must be parented by the root view, don't reparent it. + return; + } +#endif + nsView* view = aFrame->GetView(); + // Verify that the current parent view is what we think it is + //nsView* parentView; + //NS_ASSERTION(parentView == aOldParentView, "unexpected parent view"); + + aViewManager->RemoveChild(view); + + // The view will remember the Z-order and other attributes that have been set on it. + nsView* insertBefore = nsLayoutUtils::FindSiblingViewFor(aNewParentView, aFrame); + aViewManager->InsertChild(aNewParentView, view, insertBefore, insertBefore != nullptr); + } else if (aFrame->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW) { + nsIFrame::ChildListIterator lists(aFrame); + for (; !lists.IsDone(); lists.Next()) { + // Iterate the child frames, and check each child frame to see if it has + // a view + nsFrameList::Enumerator childFrames(lists.CurrentList()); + for (; !childFrames.AtEnd(); childFrames.Next()) { + ReparentFrameViewTo(childFrames.get(), aViewManager, + aNewParentView, aOldParentView); + } + } + } +} + +void +nsContainerFrame::CreateViewForFrame(nsIFrame* aFrame, + bool aForce) +{ + if (aFrame->HasView()) { + return; + } + + // If we don't yet have a view, see if we need a view + if (!aForce && !aFrame->NeedsView()) { + // don't need a view + return; + } + + nsView* parentView = aFrame->GetParent()->GetClosestView(); + NS_ASSERTION(parentView, "no parent with view"); + + nsViewManager* viewManager = parentView->GetViewManager(); + NS_ASSERTION(viewManager, "null view manager"); + + // Create a view + nsView* view = viewManager->CreateView(aFrame->GetRect(), parentView); + + SyncFrameViewProperties(aFrame->PresContext(), aFrame, nullptr, view); + + nsView* insertBefore = nsLayoutUtils::FindSiblingViewFor(parentView, aFrame); + // we insert this view 'above' the insertBefore view, unless insertBefore is null, + // in which case we want to call with aAbove == false to insert at the beginning + // in document order + viewManager->InsertChild(parentView, view, insertBefore, insertBefore != nullptr); + + // REVIEW: Don't create a widget for fixed-pos elements anymore. + // ComputeRepaintRegionForCopy will calculate the right area to repaint + // when we scroll. + // Reparent views on any child frames (or their descendants) to this + // view. We can just call ReparentFrameViewTo on this frame because + // we know this frame has no view, so it will crawl the children. Also, + // we know that any descendants with views must have 'parentView' as their + // parent view. + ReparentFrameViewTo(aFrame, viewManager, view, parentView); + + // Remember our view + aFrame->SetView(view); + + NS_FRAME_LOG(NS_FRAME_TRACE_CALLS, + ("nsContainerFrame::CreateViewForFrame: frame=%p view=%p", + aFrame, view)); +} + +/** + * Position the view associated with |aKidFrame|, if there is one. A + * container frame should call this method after positioning a frame, + * but before |Reflow|. + */ +void +nsContainerFrame::PositionFrameView(nsIFrame* aKidFrame) +{ + nsIFrame* parentFrame = aKidFrame->GetParent(); + if (!aKidFrame->HasView() || !parentFrame) + return; + + nsView* view = aKidFrame->GetView(); + nsViewManager* vm = view->GetViewManager(); + nsPoint pt; + nsView* ancestorView = parentFrame->GetClosestView(&pt); + + if (ancestorView != view->GetParent()) { + NS_ASSERTION(ancestorView == view->GetParent()->GetParent(), + "Allowed only one anonymous view between frames"); + // parentFrame is responsible for positioning aKidFrame's view + // explicitly + return; + } + + pt += aKidFrame->GetPosition(); + vm->MoveViewTo(view, pt.x, pt.y); +} + +nsresult +nsContainerFrame::ReparentFrameView(nsIFrame* aChildFrame, + nsIFrame* aOldParentFrame, + nsIFrame* aNewParentFrame) +{ + NS_PRECONDITION(aChildFrame, "null child frame pointer"); + NS_PRECONDITION(aOldParentFrame, "null old parent frame pointer"); + NS_PRECONDITION(aNewParentFrame, "null new parent frame pointer"); + NS_PRECONDITION(aOldParentFrame != aNewParentFrame, "same old and new parent frame"); + + // See if either the old parent frame or the new parent frame have a view + while (!aOldParentFrame->HasView() && !aNewParentFrame->HasView()) { + // Walk up both the old parent frame and the new parent frame nodes + // stopping when we either find a common parent or views for one + // or both of the frames. + // + // This works well in the common case where we push/pull and the old parent + // frame and the new parent frame are part of the same flow. They will + // typically be the same distance (height wise) from the + aOldParentFrame = aOldParentFrame->GetParent(); + aNewParentFrame = aNewParentFrame->GetParent(); + + // We should never walk all the way to the root frame without finding + // a view + NS_ASSERTION(aOldParentFrame && aNewParentFrame, "didn't find view"); + + // See if we reached a common ancestor + if (aOldParentFrame == aNewParentFrame) { + break; + } + } + + // See if we found a common parent frame + if (aOldParentFrame == aNewParentFrame) { + // We found a common parent and there are no views between the old parent + // and the common parent or the new parent frame and the common parent. + // Because neither the old parent frame nor the new parent frame have views, + // then any child views don't need reparenting + return NS_OK; + } + + // We found views for one or both of the ancestor frames before we + // found a common ancestor. + nsView* oldParentView = aOldParentFrame->GetClosestView(); + nsView* newParentView = aNewParentFrame->GetClosestView(); + + // See if the old parent frame and the new parent frame are in the + // same view sub-hierarchy. If they are then we don't have to do + // anything + if (oldParentView != newParentView) { + // They're not so we need to reparent any child views + ReparentFrameViewTo(aChildFrame, oldParentView->GetViewManager(), newParentView, + oldParentView); + } + + return NS_OK; +} + +nsresult +nsContainerFrame::ReparentFrameViewList(const nsFrameList& aChildFrameList, + nsIFrame* aOldParentFrame, + nsIFrame* aNewParentFrame) +{ + NS_PRECONDITION(aChildFrameList.NotEmpty(), "empty child frame list"); + NS_PRECONDITION(aOldParentFrame, "null old parent frame pointer"); + NS_PRECONDITION(aNewParentFrame, "null new parent frame pointer"); + NS_PRECONDITION(aOldParentFrame != aNewParentFrame, "same old and new parent frame"); + + // See if either the old parent frame or the new parent frame have a view + while (!aOldParentFrame->HasView() && !aNewParentFrame->HasView()) { + // Walk up both the old parent frame and the new parent frame nodes + // stopping when we either find a common parent or views for one + // or both of the frames. + // + // This works well in the common case where we push/pull and the old parent + // frame and the new parent frame are part of the same flow. They will + // typically be the same distance (height wise) from the + aOldParentFrame = aOldParentFrame->GetParent(); + aNewParentFrame = aNewParentFrame->GetParent(); + + // We should never walk all the way to the root frame without finding + // a view + NS_ASSERTION(aOldParentFrame && aNewParentFrame, "didn't find view"); + + // See if we reached a common ancestor + if (aOldParentFrame == aNewParentFrame) { + break; + } + } + + + // See if we found a common parent frame + if (aOldParentFrame == aNewParentFrame) { + // We found a common parent and there are no views between the old parent + // and the common parent or the new parent frame and the common parent. + // Because neither the old parent frame nor the new parent frame have views, + // then any child views don't need reparenting + return NS_OK; + } + + // We found views for one or both of the ancestor frames before we + // found a common ancestor. + nsView* oldParentView = aOldParentFrame->GetClosestView(); + nsView* newParentView = aNewParentFrame->GetClosestView(); + + // See if the old parent frame and the new parent frame are in the + // same view sub-hierarchy. If they are then we don't have to do + // anything + if (oldParentView != newParentView) { + nsViewManager* viewManager = oldParentView->GetViewManager(); + + // They're not so we need to reparent any child views + for (nsFrameList::Enumerator e(aChildFrameList); !e.AtEnd(); e.Next()) { + ReparentFrameViewTo(e.get(), viewManager, newParentView, oldParentView); + } + } + + return NS_OK; +} + +static nsIWidget* +GetPresContextContainerWidget(nsPresContext* aPresContext) +{ + nsCOMPtr<nsISupports> container = aPresContext->Document()->GetContainer(); + nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(container); + if (!baseWindow) + return nullptr; + + nsCOMPtr<nsIWidget> mainWidget; + baseWindow->GetMainWidget(getter_AddRefs(mainWidget)); + return mainWidget; +} + +static bool +IsTopLevelWidget(nsIWidget* aWidget) +{ + nsWindowType windowType = aWidget->WindowType(); + return windowType == eWindowType_toplevel || + windowType == eWindowType_dialog || + windowType == eWindowType_popup || + windowType == eWindowType_sheet; +} + +void +nsContainerFrame::SyncWindowProperties(nsPresContext* aPresContext, + nsIFrame* aFrame, + nsView* aView, + nsRenderingContext* aRC, + uint32_t aFlags) +{ +#ifdef MOZ_XUL + if (!aView || !nsCSSRendering::IsCanvasFrame(aFrame) || !aView->HasWidget()) + return; + + nsCOMPtr<nsIWidget> windowWidget = GetPresContextContainerWidget(aPresContext); + if (!windowWidget || !IsTopLevelWidget(windowWidget)) + return; + + nsViewManager* vm = aView->GetViewManager(); + nsView* rootView = vm->GetRootView(); + + if (aView != rootView) + return; + + Element* rootElement = aPresContext->Document()->GetRootElement(); + if (!rootElement || !rootElement->IsXULElement()) { + // Scrollframes use native widgets which don't work well with + // translucent windows, at least in Windows XP. So if the document + // has a root scrollrame it's useless to try to make it transparent, + // we'll just get something broken. + // nsCSSFrameConstructor::ConstructRootFrame constructs root + // scrollframes whenever the root element is not a XUL element, so + // we test for that here. We can't just call + // presShell->GetRootScrollFrame() since that might not have + // been constructed yet. + // We can change this to allow translucent toplevel HTML documents + // (e.g. to do something like Dashboard widgets), once we + // have broad support for translucent scrolled documents, but be + // careful because apparently some Firefox extensions expect + // openDialog("something.html") to produce an opaque window + // even if the HTML doesn't have a background-color set. + return; + } + + nsIFrame *rootFrame = aPresContext->PresShell()->FrameConstructor()->GetRootElementStyleFrame(); + if (!rootFrame) + return; + + if (aFlags & SET_ASYNC) { + aView->SetNeedsWindowPropertiesSync(); + return; + } + + RefPtr<nsPresContext> kungFuDeathGrip(aPresContext); + nsWeakFrame weak(rootFrame); + + nsTransparencyMode mode = nsLayoutUtils::GetFrameTransparency(aFrame, rootFrame); + int32_t shadow = rootFrame->StyleUIReset()->mWindowShadow; + nsCOMPtr<nsIWidget> viewWidget = aView->GetWidget(); + viewWidget->SetTransparencyMode(mode); + windowWidget->SetWindowShadowStyle(shadow); + + if (!aRC) + return; + + if (!weak.IsAlive()) { + return; + } + + nsBoxLayoutState aState(aPresContext, aRC); + nsSize minSize = rootFrame->GetXULMinSize(aState); + nsSize maxSize = rootFrame->GetXULMaxSize(aState); + + SetSizeConstraints(aPresContext, windowWidget, minSize, maxSize); +#endif +} + +void nsContainerFrame::SetSizeConstraints(nsPresContext* aPresContext, + nsIWidget* aWidget, + const nsSize& aMinSize, + const nsSize& aMaxSize) +{ + LayoutDeviceIntSize devMinSize(aPresContext->AppUnitsToDevPixels(aMinSize.width), + aPresContext->AppUnitsToDevPixels(aMinSize.height)); + LayoutDeviceIntSize devMaxSize(aMaxSize.width == NS_INTRINSICSIZE ? NS_MAXSIZE : + aPresContext->AppUnitsToDevPixels(aMaxSize.width), + aMaxSize.height == NS_INTRINSICSIZE ? NS_MAXSIZE : + aPresContext->AppUnitsToDevPixels(aMaxSize.height)); + + // MinSize has a priority over MaxSize + if (devMinSize.width > devMaxSize.width) + devMaxSize.width = devMinSize.width; + if (devMinSize.height > devMaxSize.height) + devMaxSize.height = devMinSize.height; + + widget::SizeConstraints constraints(devMinSize, devMaxSize); + + // The sizes are in inner window sizes, so convert them into outer window sizes. + // Use a size of (200, 200) as only the difference between the inner and outer + // size is needed. + LayoutDeviceIntSize windowSize = + aWidget->ClientToWindowSize(LayoutDeviceIntSize(200, 200)); + if (constraints.mMinSize.width) + constraints.mMinSize.width += windowSize.width - 200; + if (constraints.mMinSize.height) + constraints.mMinSize.height += windowSize.height - 200; + if (constraints.mMaxSize.width != NS_MAXSIZE) + constraints.mMaxSize.width += windowSize.width - 200; + if (constraints.mMaxSize.height != NS_MAXSIZE) + constraints.mMaxSize.height += windowSize.height - 200; + + aWidget->SetSizeConstraints(constraints); +} + +void +nsContainerFrame::SyncFrameViewAfterReflow(nsPresContext* aPresContext, + nsIFrame* aFrame, + nsView* aView, + const nsRect& aVisualOverflowArea, + uint32_t aFlags) +{ + if (!aView) { + return; + } + + // Make sure the view is sized and positioned correctly + if (0 == (aFlags & NS_FRAME_NO_MOVE_VIEW)) { + PositionFrameView(aFrame); + } + + if (0 == (aFlags & NS_FRAME_NO_SIZE_VIEW)) { + nsViewManager* vm = aView->GetViewManager(); + + vm->ResizeView(aView, aVisualOverflowArea, true); + } +} + +void +nsContainerFrame::SyncFrameViewProperties(nsPresContext* aPresContext, + nsIFrame* aFrame, + nsStyleContext* aStyleContext, + nsView* aView, + uint32_t aFlags) +{ + NS_ASSERTION(!aStyleContext || aFrame->StyleContext() == aStyleContext, + "Wrong style context for frame?"); + + if (!aView) { + return; + } + + nsViewManager* vm = aView->GetViewManager(); + + if (nullptr == aStyleContext) { + aStyleContext = aFrame->StyleContext(); + } + + // Make sure visibility is correct. This only affects nsSubdocumentFrame. + if (0 == (aFlags & NS_FRAME_NO_VISIBILITY) && + !aFrame->SupportsVisibilityHidden()) { + // See if the view should be hidden or visible + vm->SetViewVisibility(aView, + aStyleContext->StyleVisibility()->IsVisible() + ? nsViewVisibility_kShow : nsViewVisibility_kHide); + } + + int32_t zIndex = 0; + bool autoZIndex = false; + + if (aFrame->IsAbsPosContainingBlock()) { + // Make sure z-index is correct + const nsStylePosition* position = aStyleContext->StylePosition(); + + if (position->mZIndex.GetUnit() == eStyleUnit_Integer) { + zIndex = position->mZIndex.GetIntValue(); + } else if (position->mZIndex.GetUnit() == eStyleUnit_Auto) { + autoZIndex = true; + } + } else { + autoZIndex = true; + } + + vm->SetViewZIndex(aView, autoZIndex, zIndex); +} + +static nscoord GetCoord(const nsStyleCoord& aCoord, nscoord aIfNotCoord) +{ + if (aCoord.ConvertsToLength()) { + return nsRuleNode::ComputeCoordPercentCalc(aCoord, 0); + } + return aIfNotCoord; +} + +void +nsContainerFrame::DoInlineIntrinsicISize(nsRenderingContext *aRenderingContext, + InlineIntrinsicISizeData *aData, + nsLayoutUtils::IntrinsicISizeType aType) +{ + if (GetPrevInFlow()) + return; // Already added. + + NS_PRECONDITION(aType == nsLayoutUtils::MIN_ISIZE || + aType == nsLayoutUtils::PREF_ISIZE, "bad type"); + + WritingMode wm = GetWritingMode(); + mozilla::css::Side startSide = + wm.PhysicalSideForInlineAxis(eLogicalEdgeStart); + mozilla::css::Side endSide = + wm.PhysicalSideForInlineAxis(eLogicalEdgeEnd); + + const nsStylePadding *stylePadding = StylePadding(); + const nsStyleBorder *styleBorder = StyleBorder(); + const nsStyleMargin *styleMargin = StyleMargin(); + + // This goes at the beginning no matter how things are broken and how + // messy the bidi situations are, since per CSS2.1 section 8.6 + // (implemented in bug 328168), the startSide border is always on the + // first line. + // This frame is a first-in-flow, but it might have a previous bidi + // continuation, in which case that continuation should handle the startSide + // border. + // For box-decoration-break:clone we setup clonePBM = startPBM + endPBM and + // add that to each line. For box-decoration-break:slice clonePBM is zero. + nscoord clonePBM = 0; // PBM = PaddingBorderMargin + const bool sliceBreak = + styleBorder->mBoxDecorationBreak == StyleBoxDecorationBreak::Slice; + if (!GetPrevContinuation()) { + nscoord startPBM = + // clamp negative calc() to 0 + std::max(GetCoord(stylePadding->mPadding.Get(startSide), 0), 0) + + styleBorder->GetComputedBorderWidth(startSide) + + GetCoord(styleMargin->mMargin.Get(startSide), 0); + if (MOZ_LIKELY(sliceBreak)) { + aData->mCurrentLine += startPBM; + } else { + clonePBM = startPBM; + } + } + + nscoord endPBM = + // clamp negative calc() to 0 + std::max(GetCoord(stylePadding->mPadding.Get(endSide), 0), 0) + + styleBorder->GetComputedBorderWidth(endSide) + + GetCoord(styleMargin->mMargin.Get(endSide), 0); + if (MOZ_UNLIKELY(!sliceBreak)) { + clonePBM += endPBM; + } + + const nsLineList_iterator* savedLine = aData->mLine; + nsIFrame* const savedLineContainer = aData->LineContainer(); + + nsContainerFrame *lastInFlow; + for (nsContainerFrame *nif = this; nif; + nif = static_cast<nsContainerFrame*>(nif->GetNextInFlow())) { + if (aData->mCurrentLine == 0) { + aData->mCurrentLine = clonePBM; + } + for (nsIFrame* kid : nif->mFrames) { + if (aType == nsLayoutUtils::MIN_ISIZE) + kid->AddInlineMinISize(aRenderingContext, + static_cast<InlineMinISizeData*>(aData)); + else + kid->AddInlinePrefISize(aRenderingContext, + static_cast<InlinePrefISizeData*>(aData)); + } + + // After we advance to our next-in-flow, the stored line and line container + // may no longer be correct. Just forget them. + aData->mLine = nullptr; + aData->SetLineContainer(nullptr); + + lastInFlow = nif; + } + + aData->mLine = savedLine; + aData->SetLineContainer(savedLineContainer); + + // This goes at the end no matter how things are broken and how + // messy the bidi situations are, since per CSS2.1 section 8.6 + // (implemented in bug 328168), the endSide border is always on the + // last line. + // We reached the last-in-flow, but it might have a next bidi + // continuation, in which case that continuation should handle + // the endSide border. + if (MOZ_LIKELY(!lastInFlow->GetNextContinuation() && sliceBreak)) { + aData->mCurrentLine += endPBM; + } +} + +/* virtual */ +LogicalSize +nsContainerFrame::ComputeAutoSize(nsRenderingContext* aRenderingContext, + WritingMode aWM, + const LogicalSize& aCBSize, + nscoord aAvailableISize, + const LogicalSize& aMargin, + const LogicalSize& aBorder, + const LogicalSize& aPadding, + ComputeSizeFlags aFlags) +{ + LogicalSize result(aWM, 0xdeadbeef, NS_UNCONSTRAINEDSIZE); + nscoord availBased = aAvailableISize - aMargin.ISize(aWM) - + aBorder.ISize(aWM) - aPadding.ISize(aWM); + // replaced elements always shrink-wrap + if ((aFlags & ComputeSizeFlags::eShrinkWrap) || IsFrameOfType(eReplaced)) { + // don't bother setting it if the result won't be used + if (StylePosition()->ISize(aWM).GetUnit() == eStyleUnit_Auto) { + result.ISize(aWM) = ShrinkWidthToFit(aRenderingContext, availBased, aFlags); + } + } else { + result.ISize(aWM) = availBased; + } + + if (IsTableCaption()) { + // If we're a container for font size inflation, then shrink + // wrapping inside of us should not apply font size inflation. + AutoMaybeDisableFontInflation an(this); + + WritingMode tableWM = GetParent()->GetWritingMode(); + uint8_t captionSide = StyleTableBorder()->mCaptionSide; + + if (aWM.IsOrthogonalTo(tableWM)) { + if (captionSide == NS_STYLE_CAPTION_SIDE_TOP || + captionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE || + captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM || + captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE) { + // For an orthogonal caption on a block-dir side of the table, + // shrink-wrap to min-isize. + result.ISize(aWM) = GetMinISize(aRenderingContext); + } else { + // An orthogonal caption on an inline-dir side of the table + // is constrained to the containing block. + nscoord pref = GetPrefISize(aRenderingContext); + if (pref > aCBSize.ISize(aWM)) { + pref = aCBSize.ISize(aWM); + } + if (pref < result.ISize(aWM)) { + result.ISize(aWM) = pref; + } + } + } else { + if (captionSide == NS_STYLE_CAPTION_SIDE_LEFT || + captionSide == NS_STYLE_CAPTION_SIDE_RIGHT) { + result.ISize(aWM) = GetMinISize(aRenderingContext); + } else if (captionSide == NS_STYLE_CAPTION_SIDE_TOP || + captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) { + // The outer frame constrains our available isize to the isize of + // the table. Grow if our min-isize is bigger than that, but not + // larger than the containing block isize. (It would really be nice + // to transmit that information another way, so we could grow up to + // the table's available isize, but that's harder.) + nscoord min = GetMinISize(aRenderingContext); + if (min > aCBSize.ISize(aWM)) { + min = aCBSize.ISize(aWM); + } + if (min > result.ISize(aWM)) { + result.ISize(aWM) = min; + } + } + } + } + return result; +} + +void +nsContainerFrame::ReflowChild(nsIFrame* aKidFrame, + nsPresContext* aPresContext, + ReflowOutput& aDesiredSize, + const ReflowInput& aReflowInput, + const WritingMode& aWM, + const LogicalPoint& aPos, + const nsSize& aContainerSize, + uint32_t aFlags, + nsReflowStatus& aStatus, + nsOverflowContinuationTracker* aTracker) +{ + NS_PRECONDITION(aReflowInput.mFrame == aKidFrame, "bad reflow state"); + if (aWM.IsVerticalRL() || (!aWM.IsVertical() && !aWM.IsBidiLTR())) { + NS_ASSERTION(aContainerSize.width != NS_UNCONSTRAINEDSIZE, + "ReflowChild with unconstrained container width!"); + } + + // Position the child frame and its view if requested. + if (NS_FRAME_NO_MOVE_FRAME != (aFlags & NS_FRAME_NO_MOVE_FRAME)) { + aKidFrame->SetPosition(aWM, aPos, aContainerSize); + } + + if (0 == (aFlags & NS_FRAME_NO_MOVE_VIEW)) { + PositionFrameView(aKidFrame); + } + + // Reflow the child frame + aKidFrame->Reflow(aPresContext, aDesiredSize, aReflowInput, aStatus); + + // If the child frame is complete, delete any next-in-flows, + // but only if the NO_DELETE_NEXT_IN_FLOW flag isn't set. + if (!NS_INLINE_IS_BREAK_BEFORE(aStatus) && + NS_FRAME_IS_FULLY_COMPLETE(aStatus) && + !(aFlags & NS_FRAME_NO_DELETE_NEXT_IN_FLOW_CHILD)) { + nsIFrame* kidNextInFlow = aKidFrame->GetNextInFlow(); + if (kidNextInFlow) { + // Remove all of the childs next-in-flows. Make sure that we ask + // the right parent to do the removal (it's possible that the + // parent is not this because we are executing pullup code) + nsOverflowContinuationTracker::AutoFinish fini(aTracker, aKidFrame); + kidNextInFlow->GetParent()->DeleteNextInFlowChild(kidNextInFlow, true); + } + } +} + +//XXX temporary: hold on to a copy of the old physical version of +// ReflowChild so that we can convert callers incrementally. +void +nsContainerFrame::ReflowChild(nsIFrame* aKidFrame, + nsPresContext* aPresContext, + ReflowOutput& aDesiredSize, + const ReflowInput& aReflowInput, + nscoord aX, + nscoord aY, + uint32_t aFlags, + nsReflowStatus& aStatus, + nsOverflowContinuationTracker* aTracker) +{ + NS_PRECONDITION(aReflowInput.mFrame == aKidFrame, "bad reflow state"); + + // Position the child frame and its view if requested. + if (NS_FRAME_NO_MOVE_FRAME != (aFlags & NS_FRAME_NO_MOVE_FRAME)) { + aKidFrame->SetPosition(nsPoint(aX, aY)); + } + + if (0 == (aFlags & NS_FRAME_NO_MOVE_VIEW)) { + PositionFrameView(aKidFrame); + } + + // Reflow the child frame + aKidFrame->Reflow(aPresContext, aDesiredSize, aReflowInput, aStatus); + + // If the child frame is complete, delete any next-in-flows, + // but only if the NO_DELETE_NEXT_IN_FLOW flag isn't set. + if (NS_FRAME_IS_FULLY_COMPLETE(aStatus) && + !(aFlags & NS_FRAME_NO_DELETE_NEXT_IN_FLOW_CHILD)) { + nsIFrame* kidNextInFlow = aKidFrame->GetNextInFlow(); + if (kidNextInFlow) { + // Remove all of the childs next-in-flows. Make sure that we ask + // the right parent to do the removal (it's possible that the + // parent is not this because we are executing pullup code) + nsOverflowContinuationTracker::AutoFinish fini(aTracker, aKidFrame); + kidNextInFlow->GetParent()->DeleteNextInFlowChild(kidNextInFlow, true); + } + } +} + + +/** + * Position the views of |aFrame|'s descendants. A container frame + * should call this method if it moves a frame after |Reflow|. + */ +void +nsContainerFrame::PositionChildViews(nsIFrame* aFrame) +{ + if (!(aFrame->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW)) { + return; + } + + // Recursively walk aFrame's child frames. + // Process the additional child lists, but skip the popup list as the + // view for popups is managed by the parent. Currently only nsMenuFrame + // and nsPopupSetFrame have a popupList and during layout will adjust the + // view manually to position the popup. + ChildListIterator lists(aFrame); + for (; !lists.IsDone(); lists.Next()) { + if (lists.CurrentID() == kPopupList) { + continue; + } + nsFrameList::Enumerator childFrames(lists.CurrentList()); + for (; !childFrames.AtEnd(); childFrames.Next()) { + // Position the frame's view (if it has one) otherwise recursively + // process its children + nsIFrame* childFrame = childFrames.get(); + if (childFrame->HasView()) { + PositionFrameView(childFrame); + } else { + PositionChildViews(childFrame); + } + } + } +} + +/** + * The second half of frame reflow. Does the following: + * - sets the frame's bounds + * - sizes and positions (if requested) the frame's view. If the frame's final + * position differs from the current position and the frame itself does not + * have a view, then any child frames with views are positioned so they stay + * in sync + * - sets the view's visibility, opacity, content transparency, and clip + * - invoked the DidReflow() function + * + * Flags: + * NS_FRAME_NO_MOVE_FRAME - don't move the frame. aX and aY are ignored in this + * case. Also implies NS_FRAME_NO_MOVE_VIEW + * NS_FRAME_NO_MOVE_VIEW - don't position the frame's view. Set this if you + * don't want to automatically sync the frame and view + * NS_FRAME_NO_SIZE_VIEW - don't size the frame's view + */ +void +nsContainerFrame::FinishReflowChild(nsIFrame* aKidFrame, + nsPresContext* aPresContext, + const ReflowOutput& aDesiredSize, + const ReflowInput* aReflowInput, + const WritingMode& aWM, + const LogicalPoint& aPos, + const nsSize& aContainerSize, + uint32_t aFlags) +{ + if (aWM.IsVerticalRL() || (!aWM.IsVertical() && !aWM.IsBidiLTR())) { + NS_ASSERTION(aContainerSize.width != NS_UNCONSTRAINEDSIZE, + "FinishReflowChild with unconstrained container width!"); + } + + nsPoint curOrigin = aKidFrame->GetPosition(); + WritingMode outerWM = aDesiredSize.GetWritingMode(); + LogicalSize convertedSize = aDesiredSize.Size(outerWM).ConvertTo(aWM, + outerWM); + + if (NS_FRAME_NO_MOVE_FRAME != (aFlags & NS_FRAME_NO_MOVE_FRAME)) { + aKidFrame->SetRect(aWM, LogicalRect(aWM, aPos, convertedSize), + aContainerSize); + } else { + aKidFrame->SetSize(aWM, convertedSize); + } + + if (aKidFrame->HasView()) { + nsView* view = aKidFrame->GetView(); + // Make sure the frame's view is properly sized and positioned and has + // things like opacity correct + SyncFrameViewAfterReflow(aPresContext, aKidFrame, view, + aDesiredSize.VisualOverflow(), aFlags); + } + + nsPoint newOrigin = aKidFrame->GetPosition(); + if (!(aFlags & NS_FRAME_NO_MOVE_VIEW) && curOrigin != newOrigin) { + if (!aKidFrame->HasView()) { + // If the frame has moved, then we need to make sure any child views are + // correctly positioned + PositionChildViews(aKidFrame); + } + } + + aKidFrame->DidReflow(aPresContext, aReflowInput, nsDidReflowStatus::FINISHED); +} + +//XXX temporary: hold on to a copy of the old physical version of +// FinishReflowChild so that we can convert callers incrementally. +void +nsContainerFrame::FinishReflowChild(nsIFrame* aKidFrame, + nsPresContext* aPresContext, + const ReflowOutput& aDesiredSize, + const ReflowInput* aReflowInput, + nscoord aX, + nscoord aY, + uint32_t aFlags) +{ + nsPoint curOrigin = aKidFrame->GetPosition(); + + if (NS_FRAME_NO_MOVE_FRAME != (aFlags & NS_FRAME_NO_MOVE_FRAME)) { + aKidFrame->SetRect(nsRect(aX, aY, aDesiredSize.Width(), aDesiredSize.Height())); + } else { + aKidFrame->SetSize(nsSize(aDesiredSize.Width(), aDesiredSize.Height())); + } + + if (aKidFrame->HasView()) { + nsView* view = aKidFrame->GetView(); + // Make sure the frame's view is properly sized and positioned and has + // things like opacity correct + SyncFrameViewAfterReflow(aPresContext, aKidFrame, view, + aDesiredSize.VisualOverflow(), aFlags); + } + + if (!(aFlags & NS_FRAME_NO_MOVE_VIEW) && + (curOrigin.x != aX || curOrigin.y != aY)) { + if (!aKidFrame->HasView()) { + // If the frame has moved, then we need to make sure any child views are + // correctly positioned + PositionChildViews(aKidFrame); + } + } + + aKidFrame->DidReflow(aPresContext, aReflowInput, nsDidReflowStatus::FINISHED); +} + +void +nsContainerFrame::ReflowOverflowContainerChildren(nsPresContext* aPresContext, + const ReflowInput& aReflowInput, + nsOverflowAreas& aOverflowRects, + uint32_t aFlags, + nsReflowStatus& aStatus, + ChildFrameMerger aMergeFunc) +{ + NS_PRECONDITION(aPresContext, "null pointer"); + + nsFrameList* overflowContainers = DrainExcessOverflowContainersList(aMergeFunc); + if (!overflowContainers) { + return; // nothing to reflow + } + + nsOverflowContinuationTracker tracker(this, false, false); + bool shouldReflowAllKids = aReflowInput.ShouldReflowAllKids(); + + for (nsIFrame* frame : *overflowContainers) { + if (frame->GetPrevInFlow()->GetParent() != GetPrevInFlow()) { + // frame's prevInFlow has moved, skip reflowing this frame; + // it will get reflowed once it's been placed + continue; + } + // If the available vertical height has changed, we need to reflow + // even if the frame isn't dirty. + if (shouldReflowAllKids || NS_SUBTREE_DIRTY(frame)) { + // Get prev-in-flow + nsIFrame* prevInFlow = frame->GetPrevInFlow(); + NS_ASSERTION(prevInFlow, + "overflow container frame must have a prev-in-flow"); + NS_ASSERTION(frame->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER, + "overflow container frame must have overflow container bit set"); + WritingMode wm = frame->GetWritingMode(); + nsSize containerSize = aReflowInput.AvailableSize(wm).GetPhysicalSize(wm); + LogicalRect prevRect = prevInFlow->GetLogicalRect(wm, containerSize); + + // Initialize reflow params + LogicalSize availSpace(wm, prevRect.ISize(wm), + aReflowInput.AvailableSize(wm).BSize(wm)); + ReflowOutput desiredSize(aReflowInput); + ReflowInput frameState(aPresContext, aReflowInput, + frame, availSpace); + nsReflowStatus frameStatus; + + // Reflow + LogicalPoint pos(wm, prevRect.IStart(wm), 0); + ReflowChild(frame, aPresContext, desiredSize, frameState, + wm, pos, containerSize, aFlags, frameStatus, &tracker); + //XXXfr Do we need to override any shrinkwrap effects here? + // e.g. desiredSize.Width() = prevRect.width; + FinishReflowChild(frame, aPresContext, desiredSize, &frameState, + wm, pos, containerSize, aFlags); + + // Handle continuations + if (!NS_FRAME_IS_FULLY_COMPLETE(frameStatus)) { + if (frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) { + // Abspos frames can't cause their parent to be incomplete, + // only overflow incomplete. + NS_FRAME_SET_OVERFLOW_INCOMPLETE(frameStatus); + } + else { + NS_ASSERTION(NS_FRAME_IS_COMPLETE(frameStatus), + "overflow container frames can't be incomplete, only overflow-incomplete"); + } + + // Acquire a next-in-flow, creating it if necessary + nsIFrame* nif = frame->GetNextInFlow(); + if (!nif) { + NS_ASSERTION(frameStatus & NS_FRAME_REFLOW_NEXTINFLOW, + "Someone forgot a REFLOW_NEXTINFLOW flag"); + nif = aPresContext->PresShell()->FrameConstructor()-> + CreateContinuingFrame(aPresContext, frame, this); + } + else if (!(nif->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) { + // used to be a normal next-in-flow; steal it from the child list + nsresult rv = nif->GetParent()->StealFrame(nif); + if (NS_FAILED(rv)) { + return; + } + } + + tracker.Insert(nif, frameStatus); + } + NS_MergeReflowStatusInto(&aStatus, frameStatus); + // At this point it would be nice to assert !frame->GetOverflowRect().IsEmpty(), + // but we have some unsplittable frames that, when taller than + // availableHeight will push zero-height content into a next-in-flow. + } + else { + tracker.Skip(frame, aStatus); + if (aReflowInput.mFloatManager) { + nsBlockFrame::RecoverFloatsFor(frame, *aReflowInput.mFloatManager, + aReflowInput.GetWritingMode(), + aReflowInput.ComputedPhysicalSize()); + } + } + ConsiderChildOverflow(aOverflowRects, frame); + } +} + +void +nsContainerFrame::DisplayOverflowContainers(nsDisplayListBuilder* aBuilder, + const nsRect& aDirtyRect, + const nsDisplayListSet& aLists) +{ + nsFrameList* overflowconts = GetPropTableFrames(OverflowContainersProperty()); + if (overflowconts) { + for (nsIFrame* frame : *overflowconts) { + BuildDisplayListForChild(aBuilder, frame, aDirtyRect, aLists); + } + } +} + +static bool +TryRemoveFrame(nsIFrame* aFrame, FramePropertyTable* aPropTable, + nsContainerFrame::FrameListPropertyDescriptor aProp, + nsIFrame* aChildToRemove) +{ + nsFrameList* list = aPropTable->Get(aFrame, aProp); + if (list && list->StartRemoveFrame(aChildToRemove)) { + // aChildToRemove *may* have been removed from this list. + if (list->IsEmpty()) { + aPropTable->Remove(aFrame, aProp); + list->Delete(aFrame->PresContext()->PresShell()); + } + return true; + } + return false; +} + +bool +nsContainerFrame::MaybeStealOverflowContainerFrame(nsIFrame* aChild) +{ + bool removed = false; + if (MOZ_UNLIKELY(aChild->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) { + FramePropertyTable* propTable = PresContext()->PropertyTable(); + // Try removing from the overflow container list. + removed = ::TryRemoveFrame(this, propTable, OverflowContainersProperty(), + aChild); + if (!removed) { + // It might be in the excess overflow container list. + removed = ::TryRemoveFrame(this, propTable, + ExcessOverflowContainersProperty(), + aChild); + } + } + return removed; +} + +nsresult +nsContainerFrame::StealFrame(nsIFrame* aChild) +{ +#ifdef DEBUG + if (!mFrames.ContainsFrame(aChild)) { + nsFrameList* list = GetOverflowFrames(); + if (!list || !list->ContainsFrame(aChild)) { + FramePropertyTable* propTable = PresContext()->PropertyTable(); + list = propTable->Get(this, OverflowContainersProperty()); + if (!list || !list->ContainsFrame(aChild)) { + list = propTable->Get(this, ExcessOverflowContainersProperty()); + MOZ_ASSERT(list && list->ContainsFrame(aChild), "aChild isn't our child" + " or on a frame list not supported by StealFrame"); + } + } + } +#endif + + bool removed = MaybeStealOverflowContainerFrame(aChild); + if (!removed) { + // NOTE nsColumnSetFrame and nsCanvasFrame have their overflow containers + // on the normal lists so we might get here also if the frame bit + // NS_FRAME_IS_OVERFLOW_CONTAINER is set. + removed = mFrames.StartRemoveFrame(aChild); + if (!removed) { + // We didn't find the child in our principal child list. + // Maybe it's on the overflow list? + nsFrameList* frameList = GetOverflowFrames(); + if (frameList) { + removed = frameList->ContinueRemoveFrame(aChild); + if (frameList->IsEmpty()) { + DestroyOverflowList(); + } + } + } + } + + NS_POSTCONDITION(removed, "StealFrame: can't find aChild"); + return removed ? NS_OK : NS_ERROR_UNEXPECTED; +} + +nsFrameList +nsContainerFrame::StealFramesAfter(nsIFrame* aChild) +{ + NS_ASSERTION(!aChild || + !(aChild->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER), + "StealFramesAfter doesn't handle overflow containers"); + NS_ASSERTION(GetType() != nsGkAtoms::blockFrame, "unexpected call"); + + if (!aChild) { + nsFrameList copy(mFrames); + mFrames.Clear(); + return copy; + } + + for (nsFrameList::FrameLinkEnumerator iter(mFrames); !iter.AtEnd(); + iter.Next()) { + if (iter.PrevFrame() == aChild) { + return mFrames.ExtractTail(iter); + } + } + + // We didn't find the child in the principal child list. + // Maybe it's on the overflow list? + nsFrameList* overflowFrames = GetOverflowFrames(); + if (overflowFrames) { + for (nsFrameList::FrameLinkEnumerator iter(*overflowFrames); !iter.AtEnd(); + iter.Next()) { + if (iter.PrevFrame() == aChild) { + return overflowFrames->ExtractTail(iter); + } + } + } + + NS_ERROR("StealFramesAfter: can't find aChild"); + return nsFrameList::EmptyList(); +} + +/* + * Create a next-in-flow for aFrame. Will return the newly created + * frame <b>if and only if</b> a new frame is created; otherwise + * nullptr is returned. + */ +nsIFrame* +nsContainerFrame::CreateNextInFlow(nsIFrame* aFrame) +{ + NS_PRECONDITION(GetType() != nsGkAtoms::blockFrame, + "you should have called nsBlockFrame::CreateContinuationFor instead"); + NS_PRECONDITION(mFrames.ContainsFrame(aFrame), "expected an in-flow child frame"); + + nsPresContext* pc = PresContext(); + nsIFrame* nextInFlow = aFrame->GetNextInFlow(); + if (nullptr == nextInFlow) { + // Create a continuation frame for the child frame and insert it + // into our child list. + nextInFlow = pc->PresShell()->FrameConstructor()-> + CreateContinuingFrame(pc, aFrame, this); + mFrames.InsertFrame(nullptr, aFrame, nextInFlow); + + NS_FRAME_LOG(NS_FRAME_TRACE_NEW_FRAMES, + ("nsContainerFrame::CreateNextInFlow: frame=%p nextInFlow=%p", + aFrame, nextInFlow)); + + return nextInFlow; + } + return nullptr; +} + +/** + * Remove and delete aNextInFlow and its next-in-flows. Updates the sibling and flow + * pointers + */ +void +nsContainerFrame::DeleteNextInFlowChild(nsIFrame* aNextInFlow, + bool aDeletingEmptyFrames) +{ +#ifdef DEBUG + nsIFrame* prevInFlow = aNextInFlow->GetPrevInFlow(); +#endif + NS_PRECONDITION(prevInFlow, "bad prev-in-flow"); + + // If the next-in-flow has a next-in-flow then delete it, too (and + // delete it first). + // Do this in a loop so we don't overflow the stack for frames + // with very many next-in-flows + nsIFrame* nextNextInFlow = aNextInFlow->GetNextInFlow(); + if (nextNextInFlow) { + AutoTArray<nsIFrame*, 8> frames; + for (nsIFrame* f = nextNextInFlow; f; f = f->GetNextInFlow()) { + frames.AppendElement(f); + } + for (int32_t i = frames.Length() - 1; i >= 0; --i) { + nsIFrame* delFrame = frames.ElementAt(i); + delFrame->GetParent()-> + DeleteNextInFlowChild(delFrame, aDeletingEmptyFrames); + } + } + + // Take the next-in-flow out of the parent's child list + DebugOnly<nsresult> rv = StealFrame(aNextInFlow); + NS_ASSERTION(NS_SUCCEEDED(rv), "StealFrame failure"); + +#ifdef DEBUG + if (aDeletingEmptyFrames) { + nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(aNextInFlow); + } +#endif + + // Delete the next-in-flow frame and its descendants. This will also + // remove it from its next-in-flow/prev-in-flow chain. + aNextInFlow->Destroy(); + + NS_POSTCONDITION(!prevInFlow->GetNextInFlow(), "non null next-in-flow"); +} + +/** + * Set the frames on the overflow list + */ +void +nsContainerFrame::SetOverflowFrames(const nsFrameList& aOverflowFrames) +{ + NS_PRECONDITION(aOverflowFrames.NotEmpty(), "Shouldn't be called"); + + nsPresContext* pc = PresContext(); + nsFrameList* newList = new (pc->PresShell()) nsFrameList(aOverflowFrames); + + pc->PropertyTable()->Set(this, OverflowProperty(), newList); +} + +nsFrameList* +nsContainerFrame::GetPropTableFrames( + FrameListPropertyDescriptor aProperty) const +{ + return PresContext()->PropertyTable()->Get(this, aProperty); +} + +nsFrameList* +nsContainerFrame::RemovePropTableFrames(FrameListPropertyDescriptor aProperty) +{ + return PresContext()->PropertyTable()->Remove(this, aProperty); +} + +void +nsContainerFrame::SetPropTableFrames(nsFrameList* aFrameList, + FrameListPropertyDescriptor aProperty) +{ + NS_PRECONDITION(aProperty && aFrameList, "null ptr"); + NS_PRECONDITION( + (aProperty != nsContainerFrame::OverflowContainersProperty() && + aProperty != nsContainerFrame::ExcessOverflowContainersProperty()) || + IsFrameOfType(nsIFrame::eCanContainOverflowContainers), + "this type of frame can't have overflow containers"); + MOZ_ASSERT(!GetPropTableFrames(aProperty)); + PresContext()->PropertyTable()->Set(this, aProperty, aFrameList); +} + +/** + * Push aFromChild and its next siblings to the next-in-flow. Change the + * geometric parent of each frame that's pushed. If there is no next-in-flow + * the frames are placed on the overflow list (and the geometric parent is + * left unchanged). + * + * Updates the next-in-flow's child count. Does <b>not</b> update the + * pusher's child count. + * + * @param aFromChild the first child frame to push. It is disconnected from + * aPrevSibling + * @param aPrevSibling aFromChild's previous sibling. Must not be null. It's + * an error to push a parent's first child frame + */ +void +nsContainerFrame::PushChildren(nsIFrame* aFromChild, + nsIFrame* aPrevSibling) +{ + NS_PRECONDITION(aFromChild, "null pointer"); + NS_PRECONDITION(aPrevSibling, "pushing first child"); + NS_PRECONDITION(aPrevSibling->GetNextSibling() == aFromChild, "bad prev sibling"); + + // Disconnect aFromChild from its previous sibling + nsFrameList tail = mFrames.RemoveFramesAfter(aPrevSibling); + + nsContainerFrame* nextInFlow = + static_cast<nsContainerFrame*>(GetNextInFlow()); + if (nextInFlow) { + // XXX This is not a very good thing to do. If it gets removed + // then remove the copy of this routine that doesn't do this from + // nsInlineFrame. + // When pushing and pulling frames we need to check for whether any + // views need to be reparented. + for (nsIFrame* f = aFromChild; f; f = f->GetNextSibling()) { + nsContainerFrame::ReparentFrameView(f, this, nextInFlow); + } + nextInFlow->mFrames.InsertFrames(nextInFlow, nullptr, tail); + } + else { + // Add the frames to our overflow list + SetOverflowFrames(tail); + } +} + +/** + * Moves any frames on the overflow lists (the prev-in-flow's overflow list and + * the receiver's overflow list) to the child list. + * + * Updates this frame's child count and content mapping. + * + * @return true if any frames were moved and false otherwise + */ +bool +nsContainerFrame::MoveOverflowToChildList() +{ + bool result = false; + + // Check for an overflow list with our prev-in-flow + nsContainerFrame* prevInFlow = (nsContainerFrame*)GetPrevInFlow(); + if (nullptr != prevInFlow) { + AutoFrameListPtr prevOverflowFrames(PresContext(), + prevInFlow->StealOverflowFrames()); + if (prevOverflowFrames) { + // Tables are special; they can have repeated header/footer + // frames on mFrames at this point. + NS_ASSERTION(mFrames.IsEmpty() || GetType() == nsGkAtoms::tableFrame, + "bad overflow list"); + // When pushing and pulling frames we need to check for whether any + // views need to be reparented. + nsContainerFrame::ReparentFrameViewList(*prevOverflowFrames, + prevInFlow, this); + mFrames.AppendFrames(this, *prevOverflowFrames); + result = true; + } + } + + // It's also possible that we have an overflow list for ourselves. + return DrainSelfOverflowList() || result; +} + +bool +nsContainerFrame::DrainSelfOverflowList() +{ + AutoFrameListPtr overflowFrames(PresContext(), StealOverflowFrames()); + if (overflowFrames) { + mFrames.AppendFrames(nullptr, *overflowFrames); + return true; + } + return false; +} + +nsFrameList* +nsContainerFrame::DrainExcessOverflowContainersList(ChildFrameMerger aMergeFunc) +{ + nsFrameList* overflowContainers = + GetPropTableFrames(OverflowContainersProperty()); + + NS_ASSERTION(!(overflowContainers && GetPrevInFlow() + && static_cast<nsContainerFrame*>(GetPrevInFlow()) + ->GetPropTableFrames(ExcessOverflowContainersProperty())), + "conflicting overflow containers lists"); + + if (!overflowContainers) { + // Drain excess from previnflow + nsContainerFrame* prev = (nsContainerFrame*) GetPrevInFlow(); + if (prev) { + nsFrameList* excessFrames = + prev->RemovePropTableFrames(ExcessOverflowContainersProperty()); + if (excessFrames) { + excessFrames->ApplySetParent(this); + nsContainerFrame::ReparentFrameViewList(*excessFrames, prev, this); + overflowContainers = excessFrames; + SetPropTableFrames(overflowContainers, OverflowContainersProperty()); + } + } + } + + // Our own excess overflow containers from a previous reflow can still be + // present if our next-in-flow hasn't been reflown yet. Move any children + // from it that don't have a continuation in this frame to the + // OverflowContainers list. + nsFrameList* selfExcessOCFrames = + RemovePropTableFrames(ExcessOverflowContainersProperty()); + if (selfExcessOCFrames) { + nsFrameList toMove; + auto child = selfExcessOCFrames->FirstChild(); + while (child) { + auto next = child->GetNextSibling(); + MOZ_ASSERT(child->GetPrevInFlow(), + "ExcessOverflowContainers frames must be continuations"); + if (child->GetPrevInFlow()->GetParent() != this) { + selfExcessOCFrames->RemoveFrame(child); + toMove.AppendFrame(nullptr, child); + } + child = next; + } + if (toMove.IsEmpty()) { + SetPropTableFrames(selfExcessOCFrames, ExcessOverflowContainersProperty()); + } else if (overflowContainers) { + aMergeFunc(*overflowContainers, toMove, this); + if (selfExcessOCFrames->IsEmpty()) { + selfExcessOCFrames->Delete(PresContext()->PresShell()); + } else { + SetPropTableFrames(selfExcessOCFrames, ExcessOverflowContainersProperty()); + } + } else { + if (selfExcessOCFrames->IsEmpty()) { + *selfExcessOCFrames = toMove; + overflowContainers = selfExcessOCFrames; + } else { + SetPropTableFrames(selfExcessOCFrames, ExcessOverflowContainersProperty()); + auto shell = PresContext()->PresShell(); + overflowContainers = new (shell) nsFrameList(toMove); + } + SetPropTableFrames(overflowContainers, OverflowContainersProperty()); + } + } + + return overflowContainers; +} + +nsIFrame* +nsContainerFrame::GetNextInFlowChild(ContinuationTraversingState& aState, + bool* aIsInOverflow) +{ + nsContainerFrame*& nextInFlow = aState.mNextInFlow; + while (nextInFlow) { + // See if there is any frame in the container + nsIFrame* frame = nextInFlow->mFrames.FirstChild(); + if (frame) { + if (aIsInOverflow) { + *aIsInOverflow = false; + } + return frame; + } + // No frames in the principal list, try its overflow list + nsFrameList* overflowFrames = nextInFlow->GetOverflowFrames(); + if (overflowFrames) { + if (aIsInOverflow) { + *aIsInOverflow = true; + } + return overflowFrames->FirstChild(); + } + nextInFlow = static_cast<nsContainerFrame*>(nextInFlow->GetNextInFlow()); + } + return nullptr; +} + +nsIFrame* +nsContainerFrame::PullNextInFlowChild(ContinuationTraversingState& aState) +{ + bool isInOverflow; + nsIFrame* frame = GetNextInFlowChild(aState, &isInOverflow); + if (frame) { + nsContainerFrame* nextInFlow = aState.mNextInFlow; + if (isInOverflow) { + nsFrameList* overflowFrames = nextInFlow->GetOverflowFrames(); + overflowFrames->RemoveFirstChild(); + if (overflowFrames->IsEmpty()) { + nextInFlow->DestroyOverflowList(); + } + } else { + nextInFlow->mFrames.RemoveFirstChild(); + } + + // Move the frame to the principal frame list of this container + mFrames.AppendFrame(this, frame); + // AppendFrame has reparented the frame, we need + // to reparent the frame view then. + nsContainerFrame::ReparentFrameView(frame, nextInFlow, this); + } + return frame; +} + +bool +nsContainerFrame::ResolvedOrientationIsVertical() +{ + StyleOrient orient = StyleDisplay()->mOrient; + switch (orient) { + case StyleOrient::Horizontal: + return false; + case StyleOrient::Vertical: + return true; + case StyleOrient::Inline: + return GetWritingMode().IsVertical(); + case StyleOrient::Block: + return !GetWritingMode().IsVertical(); + } + NS_NOTREACHED("unexpected -moz-orient value"); + return false; +} + +// static +bool +nsContainerFrame::FrameStartsCounterScope(nsIFrame* aFrame) +{ + nsIContent* content = aFrame->GetContent(); + if (!content || !content->IsHTMLElement()) + return false; + + nsIAtom* localName = content->NodeInfo()->NameAtom(); + return localName == nsGkAtoms::ol || + localName == nsGkAtoms::ul || + localName == nsGkAtoms::dir || + localName == nsGkAtoms::menu; +} + +bool +nsContainerFrame::RenumberList() +{ + if (!FrameStartsCounterScope(this)) { + // If this frame doesn't start a counter scope then we don't need + // to renumber child list items. + return false; + } + + MOZ_ASSERT(mContent->IsHTMLElement(), + "FrameStartsCounterScope should only return true for HTML elements"); + + // Setup initial list ordinal value + // XXX Map html's start property to counter-reset style + int32_t ordinal = 1; + int32_t increment; + if (mContent->IsHTMLElement(nsGkAtoms::ol) && + mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::reversed)) { + increment = -1; + } else { + increment = 1; + } + + nsGenericHTMLElement* hc = nsGenericHTMLElement::FromContent(mContent); + // Must be non-null, since FrameStartsCounterScope only returns true + // for HTML elements. + MOZ_ASSERT(hc, "How is mContent not HTML?"); + const nsAttrValue* attr = hc->GetParsedAttr(nsGkAtoms::start); + nsContainerFrame* fif = static_cast<nsContainerFrame*>(FirstInFlow()); + if (attr && attr->Type() == nsAttrValue::eInteger) { + ordinal = attr->GetIntegerValue(); + } else if (increment < 0) { + // <ol reversed> case, or some other case with a negative increment: count + // up the child list + ordinal = 0; + fif->RenumberChildFrames(&ordinal, 0, -increment, true); + } + + return fif->RenumberChildFrames(&ordinal, 0, increment, false); +} + +// add in a sanity check for absurdly deep frame trees. See bug 42138 +// can't just use IsFrameTreeTooDeep() because that method has side effects we don't want +#define MAX_DEPTH_FOR_LIST_RENUMBERING 200 // 200 open displayable tags is pretty unrealistic + +bool +nsContainerFrame::RenumberFrameAndDescendants(int32_t* aOrdinal, + int32_t aDepth, + int32_t aIncrement, + bool aForCounting) +{ + NS_PRECONDITION(aOrdinal, "null params are immoral!"); + + // add in a sanity check for absurdly deep frame trees. See bug 42138 + if (MAX_DEPTH_FOR_LIST_RENUMBERING < aDepth) { + return false; + } + const nsStyleDisplay* display = StyleDisplay(); + + // drill down through any wrappers to the real frame + nsIFrame* kid = GetContentInsertionFrame(); + if (!kid) { + return false; + } + + // Do not renumber list for summary elements. + if (HTMLDetailsElement::IsDetailsEnabled()) { + HTMLSummaryElement* summary = + HTMLSummaryElement::FromContent(kid->GetContent()); + if (summary && summary->IsMainSummary()) { + return false; + } + } + + bool kidRenumberedABullet = false; + + // If the frame is a list-item and the frame implements our + // block frame API then get its bullet and set the list item + // ordinal. + if (mozilla::StyleDisplay::ListItem == display->mDisplay) { + // Make certain that the frame is a block frame in case + // something foreign has crept in. + nsBlockFrame* listItem = nsLayoutUtils::GetAsBlock(kid); + if (listItem) { + nsBulletFrame* bullet = listItem->GetBullet(); + if (bullet) { + if (!aForCounting) { + bool changed; + *aOrdinal = bullet->SetListItemOrdinal(*aOrdinal, &changed, aIncrement); + if (changed) { + kidRenumberedABullet = true; + + // The ordinal changed - mark the bullet frame, and any + // intermediate frames between it and the block (are there + // ever any?), dirty. + // The calling code will make the necessary FrameNeedsReflow + // call for the list ancestor. + bullet->AddStateBits(NS_FRAME_IS_DIRTY); + nsIFrame *f = bullet; + do { + nsIFrame *parent = f->GetParent(); + parent->ChildIsDirty(f); + f = parent; + } while (f != listItem); + } + } else { + // We're only counting the number of children, + // not restyling them. Don't take |value| + // into account when incrementing the ordinal + // or dirty the bullet. + *aOrdinal += aIncrement; + } + } + + // XXX temporary? if the list-item has child list-items they + // should be numbered too; especially since the list-item is + // itself (ASSUMED!) not to be a counter-resetter. + bool meToo = listItem->RenumberChildFrames(aOrdinal, aDepth + 1, + aIncrement, aForCounting); + if (meToo) { + kidRenumberedABullet = true; + } + } + } else if (display->mDisplay == mozilla::StyleDisplay::Block || + display->mDisplay == mozilla::StyleDisplay::Flex || + display->mDisplay == mozilla::StyleDisplay::Grid) { + if (FrameStartsCounterScope(kid)) { + // Don't bother recursing into a frame that is a new counter scope. + // Any list-items in there will be handled by it. + } else { + nsContainerFrame* container = do_QueryFrame(kid); + if (container) { + kidRenumberedABullet = + container->RenumberChildFrames(aOrdinal, aDepth + 1, + aIncrement, aForCounting); + } + } + } + return kidRenumberedABullet; +} + +bool +nsContainerFrame::RenumberChildFrames(int32_t* aOrdinal, + int32_t aDepth, + int32_t aIncrement, + bool aForCounting) +{ + bool renumbered = false; + for (auto kid : mFrames) { + bool kidRenumbered = + kid->RenumberFrameAndDescendants(aOrdinal, aDepth, aIncrement, aForCounting); + if (!aForCounting && kidRenumbered) { + renumbered = true; + } + } + + // We need to set NS_FRAME_HAS_DIRTY_CHILDREN bits up the tree between + // the bullet and the caller of RenumberLists. But the caller itself + // has to be responsible for setting the bit itself, since that caller + // might be making a FrameNeedsReflow call, which requires that the + // bit not be set yet. + if (renumbered && aDepth != 0) { + AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN); + } + + return renumbered; +} + +uint16_t +nsContainerFrame::CSSAlignmentForAbsPosChild(const ReflowInput& aChildRI, + LogicalAxis aLogicalAxis) const +{ + MOZ_ASSERT(aChildRI.mFrame->IsAbsolutelyPositioned(), + "This method should only be called for abspos children"); + NS_ERROR("Child classes that use css box alignment for abspos children " + "should provide their own implementation of this method!"); + + // In the unexpected/unlikely event that this implementation gets invoked, + // just use "start" alignment. + return NS_STYLE_ALIGN_START; +} + +nsresult +nsContainerFrame::AttributeChanged(int32_t aNameSpaceID, + nsIAtom* aAttribute, + int32_t aModType) +{ + nsresult rv = nsSplittableFrame::AttributeChanged(aNameSpaceID, + aAttribute, aModType); + if (NS_FAILED(rv)) { + return rv; + } + if (nsGkAtoms::start == aAttribute || + (nsGkAtoms::reversed == aAttribute && + mContent->IsHTMLElement(nsGkAtoms::ol))) { + + // XXX Not sure if this is necessary anymore + if (RenumberList()) { + PresContext()->PresShell()-> + FrameNeedsReflow(this, nsIPresShell::eStyleChange, + NS_FRAME_HAS_DIRTY_CHILDREN); + } + } + return rv; +} + +nsOverflowContinuationTracker::nsOverflowContinuationTracker(nsContainerFrame* aFrame, + bool aWalkOOFFrames, + bool aSkipOverflowContainerChildren) + : mOverflowContList(nullptr), + mPrevOverflowCont(nullptr), + mSentry(nullptr), + mParent(aFrame), + mSkipOverflowContainerChildren(aSkipOverflowContainerChildren), + mWalkOOFFrames(aWalkOOFFrames) +{ + NS_PRECONDITION(aFrame, "null frame pointer"); + SetupOverflowContList(); +} + +void +nsOverflowContinuationTracker::SetupOverflowContList() +{ + NS_PRECONDITION(mParent, "null frame pointer"); + NS_PRECONDITION(!mOverflowContList, "already have list"); + nsContainerFrame* nif = + static_cast<nsContainerFrame*>(mParent->GetNextInFlow()); + if (nif) { + mOverflowContList = nif->GetPropTableFrames( + nsContainerFrame::OverflowContainersProperty()); + if (mOverflowContList) { + mParent = nif; + SetUpListWalker(); + } + } + if (!mOverflowContList) { + mOverflowContList = mParent->GetPropTableFrames( + nsContainerFrame::ExcessOverflowContainersProperty()); + if (mOverflowContList) { + SetUpListWalker(); + } + } +} + +/** + * Helper function to walk past overflow continuations whose prev-in-flow + * isn't a normal child and to set mSentry and mPrevOverflowCont correctly. + */ +void +nsOverflowContinuationTracker::SetUpListWalker() +{ + NS_ASSERTION(!mSentry && !mPrevOverflowCont, + "forgot to reset mSentry or mPrevOverflowCont"); + if (mOverflowContList) { + nsIFrame* cur = mOverflowContList->FirstChild(); + if (mSkipOverflowContainerChildren) { + while (cur && (cur->GetPrevInFlow()->GetStateBits() + & NS_FRAME_IS_OVERFLOW_CONTAINER)) { + mPrevOverflowCont = cur; + cur = cur->GetNextSibling(); + } + while (cur && (!(cur->GetStateBits() & NS_FRAME_OUT_OF_FLOW) + == mWalkOOFFrames)) { + mPrevOverflowCont = cur; + cur = cur->GetNextSibling(); + } + } + if (cur) { + mSentry = cur->GetPrevInFlow(); + } + } +} + +/** + * Helper function to step forward through the overflow continuations list. + * Sets mSentry and mPrevOverflowCont, skipping over OOF or non-OOF frames + * as appropriate. May only be called when we have already set up an + * mOverflowContList; mOverflowContList cannot be null. + */ +void +nsOverflowContinuationTracker::StepForward() +{ + NS_PRECONDITION(mOverflowContList, "null list"); + + // Step forward + if (mPrevOverflowCont) { + mPrevOverflowCont = mPrevOverflowCont->GetNextSibling(); + } + else { + mPrevOverflowCont = mOverflowContList->FirstChild(); + } + + // Skip over oof or non-oof frames as appropriate + if (mSkipOverflowContainerChildren) { + nsIFrame* cur = mPrevOverflowCont->GetNextSibling(); + while (cur && (!(cur->GetStateBits() & NS_FRAME_OUT_OF_FLOW) + == mWalkOOFFrames)) { + mPrevOverflowCont = cur; + cur = cur->GetNextSibling(); + } + } + + // Set up the sentry + mSentry = (mPrevOverflowCont->GetNextSibling()) + ? mPrevOverflowCont->GetNextSibling()->GetPrevInFlow() + : nullptr; +} + +nsresult +nsOverflowContinuationTracker::Insert(nsIFrame* aOverflowCont, + nsReflowStatus& aReflowStatus) +{ + NS_PRECONDITION(aOverflowCont, "null frame pointer"); + NS_PRECONDITION(!mSkipOverflowContainerChildren || mWalkOOFFrames == + !!(aOverflowCont->GetStateBits() & NS_FRAME_OUT_OF_FLOW), + "shouldn't insert frame that doesn't match walker type"); + NS_PRECONDITION(aOverflowCont->GetPrevInFlow(), + "overflow containers must have a prev-in-flow"); + nsresult rv = NS_OK; + bool reparented = false; + nsPresContext* presContext = aOverflowCont->PresContext(); + bool addToList = !mSentry || aOverflowCont != mSentry->GetNextInFlow(); + + // If we have a list and aOverflowCont is already in it then don't try to + // add it again. + if (addToList && aOverflowCont->GetParent() == mParent && + (aOverflowCont->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) && + mOverflowContList && mOverflowContList->ContainsFrame(aOverflowCont)) { + addToList = false; + mPrevOverflowCont = aOverflowCont->GetPrevSibling(); + } + + if (addToList) { + if (aOverflowCont->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) { + // aOverflowCont is in some other overflow container list, + // steal it first + NS_ASSERTION(!(mOverflowContList && + mOverflowContList->ContainsFrame(aOverflowCont)), + "overflow containers out of order"); + rv = aOverflowCont->GetParent()->StealFrame(aOverflowCont); + NS_ENSURE_SUCCESS(rv, rv); + } + else { + aOverflowCont->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER); + } + if (!mOverflowContList) { + mOverflowContList = new (presContext->PresShell()) nsFrameList(); + mParent->SetPropTableFrames(mOverflowContList, + nsContainerFrame::ExcessOverflowContainersProperty()); + SetUpListWalker(); + } + if (aOverflowCont->GetParent() != mParent) { + nsContainerFrame::ReparentFrameView(aOverflowCont, + aOverflowCont->GetParent(), + mParent); + reparented = true; + } + + // If aOverflowCont has a prev/next-in-flow that might be in + // mOverflowContList we need to find it and insert after/before it to + // maintain the order amongst next-in-flows in this list. + nsIFrame* pif = aOverflowCont->GetPrevInFlow(); + nsIFrame* nif = aOverflowCont->GetNextInFlow(); + if ((pif && pif->GetParent() == mParent && pif != mPrevOverflowCont) || + (nif && nif->GetParent() == mParent && mPrevOverflowCont)) { + for (nsFrameList::Enumerator e(*mOverflowContList); !e.AtEnd(); e.Next()) { + nsIFrame* f = e.get(); + if (f == pif) { + mPrevOverflowCont = pif; + break; + } + if (f == nif) { + mPrevOverflowCont = f->GetPrevSibling(); + break; + } + } + } + + mOverflowContList->InsertFrame(mParent, mPrevOverflowCont, aOverflowCont); + aReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW; + } + + // If we need to reflow it, mark it dirty + if (aReflowStatus & NS_FRAME_REFLOW_NEXTINFLOW) + aOverflowCont->AddStateBits(NS_FRAME_IS_DIRTY); + + // It's in our list, just step forward + StepForward(); + NS_ASSERTION(mPrevOverflowCont == aOverflowCont || + (mSkipOverflowContainerChildren && + (mPrevOverflowCont->GetStateBits() & NS_FRAME_OUT_OF_FLOW) != + (aOverflowCont->GetStateBits() & NS_FRAME_OUT_OF_FLOW)), + "OverflowContTracker in unexpected state"); + + if (addToList) { + // Convert all non-overflow-container continuations of aOverflowCont + // into overflow containers and move them to our overflow + // tracker. This preserves the invariant that the next-continuations + // of an overflow container are also overflow containers. + nsIFrame* f = aOverflowCont->GetNextContinuation(); + if (f && (!(f->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) || + (!reparented && f->GetParent() == mParent) || + (reparented && f->GetParent() != mParent))) { + if (!(f->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) { + rv = f->GetParent()->StealFrame(f); + NS_ENSURE_SUCCESS(rv, rv); + } + Insert(f, aReflowStatus); + } + } + return rv; +} + +void +nsOverflowContinuationTracker::BeginFinish(nsIFrame* aChild) +{ + NS_PRECONDITION(aChild, "null ptr"); + NS_PRECONDITION(aChild->GetNextInFlow(), + "supposed to call Finish *before* deleting next-in-flow!"); + for (nsIFrame* f = aChild; f; f = f->GetNextInFlow()) { + // We'll update these in EndFinish after the next-in-flows are gone. + if (f == mPrevOverflowCont) { + mSentry = nullptr; + mPrevOverflowCont = nullptr; + break; + } + if (f == mSentry) { + mSentry = nullptr; + break; + } + } +} + +void +nsOverflowContinuationTracker::EndFinish(nsIFrame* aChild) +{ + if (!mOverflowContList) { + return; + } + // Forget mOverflowContList if it was deleted. + nsPresContext* pc = aChild->PresContext(); + FramePropertyTable* propTable = pc->PropertyTable(); + nsFrameList* eoc = propTable->Get( + mParent, nsContainerFrame::ExcessOverflowContainersProperty()); + if (eoc != mOverflowContList) { + nsFrameList* oc = static_cast<nsFrameList*>(propTable->Get(mParent, + nsContainerFrame::OverflowContainersProperty())); + if (oc != mOverflowContList) { + // mOverflowContList was deleted + mPrevOverflowCont = nullptr; + mSentry = nullptr; + mParent = aChild->GetParent(); + mOverflowContList = nullptr; + SetupOverflowContList(); + return; + } + } + // The list survived, update mSentry if needed. + if (!mSentry) { + if (!mPrevOverflowCont) { + SetUpListWalker(); + } else { + mozilla::AutoRestore<nsIFrame*> saved(mPrevOverflowCont); + // step backward to make StepForward() use our current mPrevOverflowCont + mPrevOverflowCont = mPrevOverflowCont->GetPrevSibling(); + StepForward(); + } + } +} + +///////////////////////////////////////////////////////////////////////////// +// Debugging + +#ifdef DEBUG_FRAME_DUMP +void +nsContainerFrame::List(FILE* out, const char* aPrefix, uint32_t aFlags) const +{ + nsCString str; + ListGeneric(str, aPrefix, aFlags); + + // Output the children + bool outputOneList = false; + ChildListIterator lists(this); + for (; !lists.IsDone(); lists.Next()) { + if (outputOneList) { + str += aPrefix; + } + if (lists.CurrentID() != kPrincipalList) { + if (!outputOneList) { + str += "\n"; + str += aPrefix; + } + str += nsPrintfCString("%s %p ", mozilla::layout::ChildListName(lists.CurrentID()), + &GetChildList(lists.CurrentID())); + } + fprintf_stderr(out, "%s<\n", str.get()); + str = ""; + nsFrameList::Enumerator childFrames(lists.CurrentList()); + for (; !childFrames.AtEnd(); childFrames.Next()) { + nsIFrame* kid = childFrames.get(); + // Verify the child frame's parent frame pointer is correct + NS_ASSERTION(kid->GetParent() == this, "bad parent frame pointer"); + + // Have the child frame list + nsCString pfx(aPrefix); + pfx += " "; + kid->List(out, pfx.get(), aFlags); + } + fprintf_stderr(out, "%s>\n", aPrefix); + outputOneList = true; + } + + if (!outputOneList) { + fprintf_stderr(out, "%s<>\n", str.get()); + } +} +#endif |