/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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/. */ /* * code for managing absolutely positioned children of a rendering * object that is a containing block for them */ #ifndef LAYOUT_GENERIC_ABSOLUTE_CONTAINING_BLOCK_H_ #define LAYOUT_GENERIC_ABSOLUTE_CONTAINING_BLOCK_H_ #include "nsFrameList.h" #include "nsIFrame.h" class nsContainerFrame; class nsPresContext; namespace mozilla { enum class AbsPosReflowFlag : uint8_t { // Allow the children in the absolute containing block to fragment. Without // this flag, the children will be monolithic. AllowFragmentation, CBWidthChanged, CBHeightChanged, IsGridContainerCB, }; using AbsPosReflowFlags = EnumSet; struct StylePositionArea; /** * This class contains the logic for being an absolute containing block. This * class is used within viewport frames (for frames representing content with * fixed position) and blocks (for frames representing absolutely positioned * content), since each set of frames is absolutely positioned with respect to * its parent. * * There is no principal child list, just a named child list which contains * the absolutely positioned frames (FrameChildListID::Absolute or * FrameChildListID::Fixed). * * All functions include as the first argument the frame that is delegating * the request. */ class AbsoluteContainingBlock { public: explicit AbsoluteContainingBlock(FrameChildListID aChildListID) #ifdef DEBUG : mChildListID(aChildListID) #endif { MOZ_ASSERT(mChildListID == FrameChildListID::Absolute || mChildListID == FrameChildListID::Fixed, "should either represent position:fixed or absolute content"); } const nsFrameList& GetChildList() const { return mAbsoluteFrames; } const nsFrameList& GetPushedChildList() const { return mPushedAbsoluteFrames; } void SetInitialChildList(nsIFrame* aDelegatingFrame, FrameChildListID aListID, nsFrameList&& aChildList); void AppendFrames(nsIFrame* aDelegatingFrame, FrameChildListID aListID, nsFrameList&& aFrameList); void InsertFrames(nsIFrame* aDelegatingFrame, FrameChildListID aListID, nsIFrame* aPrevFrame, nsFrameList&& aFrameList); void RemoveFrame(FrameDestroyContext&, FrameChildListID, nsIFrame*); /** * Return the pushed absolute frames. The caller is responsible for passing * the ownership of the frames to someone else, or destroying them. */ [[nodiscard]] nsFrameList StealPushedChildList(); /** * Prepare our absolute child list so that it is ready to reflow by moving all * the pushed absolute frames in aDelegatingFrame's prev-in-flow's absCB, and * some in our own pushed absolute child list, to our absolute child list. * * @return true if we have absolute frames after we return. */ bool PrepareAbsoluteFrames(nsContainerFrame* aDelegatingFrame); /** * Return true if we have absolute frames. * * Note: During reflow, consider calling PrepareAbsoluteFrames() rather than * this method; it moves absolute frames from other lists to mAbsoluteFrames, * which may be needed to get the correct result. */ bool HasAbsoluteFrames() const { return mAbsoluteFrames.NotEmpty(); } /** * Called by the delegating frame after it has done its reflow first. This * function will reflow any absolutely positioned child frames that need to * be reflowed, e.g., because the absolutely positioned child frame has * 'auto' for an offset, or a percentage based width or height. * * @param aOverflowAreas, if non-null, is unioned with (in the local * coordinate space) the overflow areas of the absolutely positioned * children. * @param aReflowStatus This function merges in the statuses of the absolutely * positioned children's reflows. * @param aContainingBlock Rect representing the area where absolute * positioned children can be positioned. Generally, this is the padding rect * of `aDelegatingFrame` (Which would not have a valid mRect set during * reflow), offset against the `aDelegatingFrame`'s border rect. * @param aFlags zero or more AbsPosReflowFlags */ void Reflow(nsContainerFrame* aDelegatingFrame, nsPresContext* aPresContext, const ReflowInput& aReflowInput, nsReflowStatus& aReflowStatus, const nsRect& aContainingBlock, AbsPosReflowFlags aFlags, OverflowAreas* aOverflowAreas); using DestroyContext = nsIFrame::DestroyContext; void DestroyFrames(DestroyContext&); /** * Mark our size-dependent absolute frames with NS_FRAME_HAS_DIRTY_CHILDREN * so that we'll make sure to reflow them. */ void MarkSizeDependentFramesDirty(); /** * Mark all our absolute frames with NS_FRAME_IS_DIRTY. */ void MarkAllFramesDirty(); /** * Rects for abspos frames to position against. Differences are relevant * for containing blocks that scroll. * * See https://drafts.csswg.org/css-position-4/#scrollable-cb */ struct ContainingBlockRects { nsRect mLocal; nsRect mScrollable; }; protected: /** * Returns true if the position of aFrame depends on the position of * its placeholder or if the position or size of aFrame depends on a * containing block dimension that changed. */ bool FrameDependsOnContainer( nsIFrame* aFrame, bool aCBWidthChanged, bool aCBHeightChanged, mozilla::AnchorPosResolutionCache* aAnchorPosResolutionCache = nullptr); /** * After an abspos child's size is known, this method can be used to * resolve size-dependent values in the ComputedLogicalOffsets on its * reflow input. * * aCBSize, aKidSize, aMargin, aOffsets, are all expected in the absolute * containing block's writing-mode. * * aOffset is an outparam. */ void ResolveSizeDependentOffsets( ReflowInput& aKidReflowInput, const LogicalSize& aCBSize, const LogicalSize& aKidSize, const LogicalMargin& aMargin, const StylePositionArea& aResolvedPositionArea, LogicalMargin& aOffsets); /** * For frames that have intrinsic block sizes, since we want to use the * frame's actual instrinsic block-size, we don't compute margins in * InitAbsoluteConstraints because the block-size isn't computed yet. This * method computes the margins for them after layout. * * aCBSize, aKidSize, aMargin, aOffsets, are all expected in the absolute * containing block's writing-mode. * * aMargin and aOffsets are both outparams (though we only touch aOffsets if * the position is overconstrained) */ void ResolveAutoMarginsAfterLayout(ReflowInput& aKidReflowInput, const LogicalSize& aCBSize, const LogicalSize& aKidSize, LogicalMargin& aMargin, const LogicalMargin& aOffsets); void ReflowAbsoluteFrame( nsContainerFrame* aDelegatingFrame, nsPresContext* aPresContext, const ReflowInput& aReflowInput, const ContainingBlockRects& aContainingBlockRects, AbsPosReflowFlags aFlags, nsIFrame* aKidFrame, nsReflowStatus& aStatus, OverflowAreas* aOverflowAreas, const ContainingBlockRects* aFragmentedContainingBlockRects, mozilla::AnchorPosResolutionCache* aAnchorPosResolutionCache = nullptr); /** * Mark our absolute frames dirty. * @param aMarkAllDirty if true, all will be marked with NS_FRAME_IS_DIRTY. * Otherwise, the size-dependant ones will be marked with * NS_FRAME_HAS_DIRTY_CHILDREN. */ void DoMarkFramesDirty(bool aMarkAllDirty); /** * Remove aFrame from one of our frame lists without destroying it. */ void StealFrame(nsIFrame* aFrame); /** * Move any frame in our pushed absolute list into our absolute child list, if * it is a first-in-flow, or if its prev-in-flow is not present in our * absolute child list. * * @param aDelegatingFrame the frame that owns us. */ void DrainPushedChildList(const nsIFrame* aDelegatingFrame); // Stores the abspos frames that have been placed in this containing block. nsFrameList mAbsoluteFrames; // A temporary frame list used during reflow, storing abspos frames that need // to be reflowed by the delegating frame's next-in-flow after transferring // them to its own AbsoluteContainingBlock. nsFrameList mPushedAbsoluteFrames; // Suppose D is the distance from an absolute containing block fragment's // border-box block-start edge to whichever is larger of either (a) its // border-box block-end edge, or (b) the available space's block-end // edge. // // TODO (TYLin, Bug 2009647): We currently assume (a) cannot be bigger than // (b), but it can if there is an unfragmentable in-flow element. // // This variable stores the sum of the D values for the current absolute // containing block fragment and for all its previous fragments. It represents // the offset from the start of the theoretical unfragmented abspos containing // block to the start of the current fragment. During reflow, we subtract this // value from abspos frames' unfragmented positions to get their local // coordinate space position in the current fragment. nscoord mCumulativeContainingBlockBSize = 0; #ifdef DEBUG void SanityCheckChildListsBeforeReflow( const nsIFrame* aDelegatingFrame) const; // FrameChildListID::Fixed or FrameChildListID::Absolute FrameChildListID const mChildListID; #endif }; } // namespace mozilla #endif /* LAYOUT_GENERIC_ABSOLUTE_CONTAINING_BLOCK_H_ */