mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-28 23:31:56 +00:00
628cc55f22
Differential Revision: https://phabricator.services.mozilla.com/D108597
1228 lines
52 KiB
C++
1228 lines
52 KiB
C++
/* -*- 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/. */
|
|
|
|
/* base class #1 for rendering objects that have child lists */
|
|
|
|
#ifndef nsContainerFrame_h___
|
|
#define nsContainerFrame_h___
|
|
|
|
#include "mozilla/Attributes.h"
|
|
#include "LayoutConstants.h"
|
|
#include "nsISelectionDisplay.h"
|
|
#include "nsSplittableFrame.h"
|
|
#include "nsFrameList.h"
|
|
#include "nsLineBox.h"
|
|
#include "nsTHashSet.h"
|
|
|
|
class nsOverflowContinuationTracker;
|
|
|
|
namespace mozilla {
|
|
class PresShell;
|
|
} // namespace mozilla
|
|
|
|
// Some macros for container classes to do sanity checking on
|
|
// width/height/x/y values computed during reflow.
|
|
// NOTE: AppUnitsPerCSSPixel value hardwired here to remove the
|
|
// dependency on nsDeviceContext.h. It doesn't matter if it's a
|
|
// little off.
|
|
#ifdef DEBUG
|
|
// 10 million pixels, converted to app units. Note that this a bit larger
|
|
// than 1/4 of nscoord_MAX. So, if any content gets to be this large, we're
|
|
// definitely in danger of grazing up against nscoord_MAX; hence, it's ABSURD.
|
|
# define ABSURD_COORD (10000000 * 60)
|
|
# define ABSURD_SIZE(_x) (((_x) < -ABSURD_COORD) || ((_x) > ABSURD_COORD))
|
|
#endif
|
|
|
|
/**
|
|
* Implementation of a container frame.
|
|
*/
|
|
class nsContainerFrame : public nsSplittableFrame {
|
|
public:
|
|
NS_DECL_ABSTRACT_FRAME(nsContainerFrame)
|
|
NS_DECL_QUERYFRAME_TARGET(nsContainerFrame)
|
|
NS_DECL_QUERYFRAME
|
|
|
|
// nsIFrame overrides
|
|
virtual void Init(nsIContent* aContent, nsContainerFrame* aParent,
|
|
nsIFrame* aPrevInFlow) override;
|
|
virtual nsContainerFrame* GetContentInsertionFrame() override { return this; }
|
|
|
|
virtual const nsFrameList& GetChildList(ChildListID aList) const override;
|
|
virtual void GetChildLists(nsTArray<ChildList>* aLists) const override;
|
|
virtual void DestroyFrom(nsIFrame* aDestructRoot,
|
|
PostDestroyData& aPostDestroyData) override;
|
|
virtual void ChildIsDirty(nsIFrame* aChild) override;
|
|
|
|
virtual FrameSearchResult PeekOffsetNoAmount(bool aForward,
|
|
int32_t* aOffset) override;
|
|
virtual FrameSearchResult PeekOffsetCharacter(
|
|
bool aForward, int32_t* aOffset,
|
|
PeekOffsetCharacterOptions aOptions =
|
|
PeekOffsetCharacterOptions()) override;
|
|
|
|
#ifdef DEBUG_FRAME_DUMP
|
|
void List(FILE* out = stderr, const char* aPrefix = "",
|
|
ListFlags aFlags = ListFlags()) const override;
|
|
void ListWithMatchedRules(FILE* out = stderr,
|
|
const char* aPrefix = "") const override;
|
|
void ListChildLists(FILE* aOut, const char* aPrefix, ListFlags aFlags,
|
|
ChildListIDs aSkippedListIDs) const;
|
|
virtual void ExtraContainerFrameInfo(nsACString& aTo) const;
|
|
#endif
|
|
|
|
// nsContainerFrame methods
|
|
|
|
/**
|
|
* Called to set the initial list of frames. This happens after the frame
|
|
* has been initialized.
|
|
*
|
|
* This is only called once for a given child list, and won't be called
|
|
* at all for child lists with no initial list of frames.
|
|
*
|
|
* @param aListID the child list identifier.
|
|
* @param aChildList list of child frames. Each of the frames has its
|
|
* NS_FRAME_IS_DIRTY bit set. Must not be empty.
|
|
* This method cannot handle the child list returned by
|
|
* GetAbsoluteListID().
|
|
* @see #Init()
|
|
*/
|
|
virtual void SetInitialChildList(ChildListID aListID,
|
|
nsFrameList& aChildList);
|
|
|
|
/**
|
|
* This method is responsible for appending frames to the frame
|
|
* list. The implementation should append the frames to the specified
|
|
* child list and then generate a reflow command.
|
|
*
|
|
* @param aListID the child list identifier.
|
|
* @param aFrameList list of child frames to append. Each of the frames has
|
|
* its NS_FRAME_IS_DIRTY bit set. Must not be empty.
|
|
*/
|
|
virtual void AppendFrames(ChildListID aListID, nsFrameList& aFrameList);
|
|
|
|
/**
|
|
* This method is responsible for inserting frames into the frame
|
|
* list. The implementation should insert the new frames into the specified
|
|
* child list and then generate a reflow command.
|
|
*
|
|
* @param aListID the child list identifier.
|
|
* @param aPrevFrame the frame to insert frames <b>after</b>
|
|
* @param aPrevFrameLine (optional) if present (i.e., not null), the line
|
|
* box that aPrevFrame is part of.
|
|
* @param aFrameList list of child frames to insert <b>after</b> aPrevFrame.
|
|
* Each of the frames has its NS_FRAME_IS_DIRTY bit set
|
|
*/
|
|
virtual void InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame,
|
|
const nsLineList::iterator* aPrevFrameLine,
|
|
nsFrameList& aFrameList);
|
|
|
|
/**
|
|
* This method is responsible for removing a frame in the frame
|
|
* list. The implementation should do something with the removed frame
|
|
* and then generate a reflow command. The implementation is responsible
|
|
* for destroying aOldFrame (the caller mustn't destroy aOldFrame).
|
|
*
|
|
* @param aListID the child list identifier.
|
|
* @param aOldFrame the frame to remove
|
|
*/
|
|
virtual void RemoveFrame(ChildListID aListID, nsIFrame* aOldFrame);
|
|
|
|
/**
|
|
* Helper method to create next-in-flows if necessary. If aFrame
|
|
* already has a next-in-flow then this method does
|
|
* nothing. Otherwise, a new continuation frame is created and
|
|
* linked into the flow. In addition, the new frame is inserted
|
|
* into the principal child list after aFrame.
|
|
* @note calling this method on a block frame is illegal. Use
|
|
* nsBlockFrame::CreateContinuationFor() instead.
|
|
* @return the next-in-flow <b>if and only if</b> one is created. If
|
|
* a next-in-flow already exists, nullptr will be returned.
|
|
*/
|
|
nsIFrame* CreateNextInFlow(nsIFrame* aFrame);
|
|
|
|
/**
|
|
* Delete aNextInFlow and its next-in-flows.
|
|
* @param aDeletingEmptyFrames if set, then the reflow for aNextInFlow's
|
|
* content was complete before aNextInFlow, so aNextInFlow and its
|
|
* next-in-flows no longer map any real content.
|
|
*/
|
|
virtual void DeleteNextInFlowChild(nsIFrame* aNextInFlow,
|
|
bool aDeletingEmptyFrames);
|
|
|
|
// Positions the frame's view based on the frame's origin
|
|
static void PositionFrameView(nsIFrame* aKidFrame);
|
|
|
|
static nsresult ReparentFrameView(nsIFrame* aChildFrame,
|
|
nsIFrame* aOldParentFrame,
|
|
nsIFrame* aNewParentFrame);
|
|
|
|
static void ReparentFrameViewList(const nsFrameList& aChildFrameList,
|
|
nsIFrame* aOldParentFrame,
|
|
nsIFrame* aNewParentFrame);
|
|
|
|
/**
|
|
* Reparent aFrame from aOldParent to aNewParent.
|
|
*/
|
|
static void ReparentFrame(nsIFrame* aFrame, nsContainerFrame* aOldParent,
|
|
nsContainerFrame* aNewParent);
|
|
|
|
/**
|
|
* Reparent all the frames in aFrameList from aOldParent to aNewParent.
|
|
*
|
|
* Note: Reparenting a large frame list can be have huge performance impact.
|
|
* For example, instead of using this method, nsInlineFrame uses a "lazy
|
|
* reparenting" technique that it reparents a child frame just before
|
|
* reflowing the child. (See InlineReflowInput::mSetParentPointer.)
|
|
*/
|
|
static void ReparentFrames(nsFrameList& aFrameList,
|
|
nsContainerFrame* aOldParent,
|
|
nsContainerFrame* aNewParent);
|
|
|
|
// Set the view's size and position after its frame has been reflowed.
|
|
static void SyncFrameViewAfterReflow(
|
|
nsPresContext* aPresContext, nsIFrame* aFrame, nsView* aView,
|
|
const nsRect& aInkOverflowArea,
|
|
ReflowChildFlags aFlags = ReflowChildFlags::Default);
|
|
|
|
// Syncs properties to the top level view and window, like transparency and
|
|
// shadow.
|
|
// The SET_ASYNC indicates that the actual nsIWidget calls to sync the window
|
|
// properties should be done async.
|
|
enum {
|
|
SET_ASYNC = 0x01,
|
|
};
|
|
static void SyncWindowProperties(nsPresContext* aPresContext,
|
|
nsIFrame* aFrame, nsView* aView,
|
|
gfxContext* aRC, uint32_t aFlags);
|
|
|
|
/**
|
|
* Converts the minimum and maximum sizes given in inner window app units to
|
|
* outer window device pixel sizes and assigns these constraints to the
|
|
* widget.
|
|
*
|
|
* @param aPresContext pres context
|
|
* @param aWidget widget for this frame
|
|
* @param minimum size of the window in app units
|
|
* @param maxmimum size of the window in app units
|
|
*/
|
|
static void SetSizeConstraints(nsPresContext* aPresContext,
|
|
nsIWidget* aWidget, const nsSize& aMinSize,
|
|
const nsSize& aMaxSize);
|
|
|
|
/**
|
|
* Helper for calculating intrinsic inline size for inline containers.
|
|
*
|
|
* @param aData the intrinsic inline size data, either an InlineMinISizeData
|
|
* or an InlinePrefISizeData
|
|
* @param aHandleChildren a callback function invoked for each in-flow
|
|
* continuation, with the continuation frame and the intrinsic inline size
|
|
* data passed into it.
|
|
*/
|
|
template <typename ISizeData, typename F>
|
|
void DoInlineIntrinsicISize(ISizeData* aData, F& aHandleChildren);
|
|
|
|
void DoInlineMinISize(gfxContext* aRenderingContext,
|
|
InlineMinISizeData* aData);
|
|
void DoInlinePrefISize(gfxContext* aRenderingContext,
|
|
InlinePrefISizeData* aData);
|
|
|
|
/**
|
|
* This is the CSS block concept of computing 'auto' widths, which most
|
|
* classes derived from nsContainerFrame want.
|
|
*/
|
|
virtual mozilla::LogicalSize ComputeAutoSize(
|
|
gfxContext* aRenderingContext, mozilla::WritingMode aWM,
|
|
const mozilla::LogicalSize& aCBSize, nscoord aAvailableISize,
|
|
const mozilla::LogicalSize& aMargin,
|
|
const mozilla::LogicalSize& aBorderPadding,
|
|
const mozilla::StyleSizeOverrides& aSizeOverrides,
|
|
mozilla::ComputeSizeFlags aFlags) override;
|
|
|
|
/**
|
|
* Positions aKidFrame and its view (if requested), and then calls Reflow().
|
|
* If the reflow status after reflowing the child is FullyComplete then any
|
|
* next-in-flows are deleted using DeleteNextInFlowChild().
|
|
*
|
|
* @param aReflowInput the reflow input for aKidFrame.
|
|
* @param aWM aPos's writing-mode (any writing mode will do).
|
|
* @param aPos Position of the aKidFrame to be moved, in terms of aWM.
|
|
* @param aContainerSize Size of the border-box of the containing frame.
|
|
*
|
|
* Note: If ReflowChildFlags::NoMoveFrame is requested, both aPos and
|
|
* aContainerSize are ignored.
|
|
*/
|
|
void ReflowChild(nsIFrame* aKidFrame, nsPresContext* aPresContext,
|
|
ReflowOutput& aDesiredSize, const ReflowInput& aReflowInput,
|
|
const mozilla::WritingMode& aWM,
|
|
const mozilla::LogicalPoint& aPos,
|
|
const nsSize& aContainerSize, ReflowChildFlags aFlags,
|
|
nsReflowStatus& aStatus,
|
|
nsOverflowContinuationTracker* aTracker = nullptr);
|
|
|
|
/**
|
|
* 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
|
|
*
|
|
* @param aReflowInput the reflow input for aKidFrame.
|
|
* @param aWM aPos's writing-mode (any writing mode will do).
|
|
* @param aPos Position of the aKidFrame to be moved, in terms of aWM.
|
|
* @param aContainerSize Size of the border-box of the containing frame.
|
|
*
|
|
* Note: If ReflowChildFlags::NoMoveFrame is requested, both aPos and
|
|
* aContainerSize are ignored unless
|
|
* ReflowChildFlags::ApplyRelativePositioning is requested.
|
|
*/
|
|
static void FinishReflowChild(
|
|
nsIFrame* aKidFrame, nsPresContext* aPresContext,
|
|
const ReflowOutput& aDesiredSize, const ReflowInput* aReflowInput,
|
|
const mozilla::WritingMode& aWM, const mozilla::LogicalPoint& aPos,
|
|
const nsSize& aContainerSize, ReflowChildFlags aFlags);
|
|
|
|
// XXX temporary: hold on to a copy of the old physical versions of
|
|
// ReflowChild and FinishReflowChild so that we can convert callers
|
|
// incrementally.
|
|
void ReflowChild(nsIFrame* aKidFrame, nsPresContext* aPresContext,
|
|
ReflowOutput& aDesiredSize, const ReflowInput& aReflowInput,
|
|
nscoord aX, nscoord aY, ReflowChildFlags aFlags,
|
|
nsReflowStatus& aStatus,
|
|
nsOverflowContinuationTracker* aTracker = nullptr);
|
|
|
|
static void FinishReflowChild(nsIFrame* aKidFrame,
|
|
nsPresContext* aPresContext,
|
|
const ReflowOutput& aDesiredSize,
|
|
const ReflowInput* aReflowInput, nscoord aX,
|
|
nscoord aY, ReflowChildFlags aFlags);
|
|
|
|
static void PositionChildViews(nsIFrame* aFrame);
|
|
|
|
// ==========================================================================
|
|
/* Overflow containers are continuation frames that hold overflow. They
|
|
* are created when the frame runs out of computed block-size, but still has
|
|
* too much content to fit in the AvailableBSize. The parent creates a
|
|
* continuation as usual, but marks it as NS_FRAME_IS_OVERFLOW_CONTAINER
|
|
* and adds it to its next-in-flow's overflow container list, either by
|
|
* adding it directly or by putting it in its own excess overflow containers
|
|
* list (to be drained by the next-in-flow when it calls
|
|
* ReflowOverflowContainerChildren). The parent continues reflow as if
|
|
* the frame was complete once it ran out of computed block-size, but returns
|
|
* a reflow status with either IsIncomplete() or IsOverflowIncomplete() equal
|
|
* to true to request a next-in-flow. The parent's next-in-flow is then
|
|
* responsible for calling ReflowOverflowContainerChildren to (drain and)
|
|
* reflow these overflow continuations. Overflow containers do not affect
|
|
* other frames' size or position during reflow (but do affect their
|
|
* parent's overflow area).
|
|
*
|
|
* Overflow container continuations are different from normal continuations
|
|
* in that
|
|
* - more than one child of the frame can have its next-in-flow broken
|
|
* off and pushed into the frame's next-in-flow
|
|
* - new continuations may need to be spliced into the middle of the list
|
|
* or deleted continuations slipped out
|
|
* e.g. A, B, C are all fixed-size containers on one page, all have
|
|
* overflow beyond AvailableBSize, and content is dynamically added
|
|
* and removed from B
|
|
* As a result, it is not possible to simply prepend the new continuations
|
|
* to the old list as with the OverflowProperty mechanism. To avoid
|
|
* complicated list splicing, the code assumes only one overflow containers
|
|
* list exists for a given frame: either its own OverflowContainersProperty
|
|
* or its prev-in-flow's ExcessOverflowContainersProperty, not both.
|
|
*
|
|
* The nsOverflowContinuationTracker helper class should be used for tracking
|
|
* overflow containers and adding them to the appropriate list.
|
|
* See nsBlockFrame::Reflow for a sample implementation.
|
|
*
|
|
* For more information, see https://wiki.mozilla.org/Gecko:Continuation_Model
|
|
*
|
|
* Note that Flex/GridContainerFrame doesn't use nsOverflowContinuationTracker
|
|
* so the above doesn't apply. Flex/Grid containers may have items that
|
|
* aren't in document order between fragments, due to the 'order' property,
|
|
* but they do maintain the invariant that children in the same nsFrameList
|
|
* are in document order. This means that when pushing/pulling items or
|
|
* merging lists, the result needs to be sorted to restore the order.
|
|
* However, given that lists are individually sorted, it's a simple merge
|
|
* operation of the two lists to make the result sorted.
|
|
* DrainExcessOverflowContainersList takes a merging function to perform that
|
|
* operation. (By "document order" here we mean normal frame tree order,
|
|
* which is approximately flattened DOM tree order.)
|
|
*/
|
|
|
|
friend class nsOverflowContinuationTracker;
|
|
|
|
typedef void (*ChildFrameMerger)(nsFrameList& aDest, nsFrameList& aSrc,
|
|
nsContainerFrame* aParent);
|
|
static inline void DefaultChildFrameMerge(nsFrameList& aDest,
|
|
nsFrameList& aSrc,
|
|
nsContainerFrame* aParent) {
|
|
aDest.AppendFrames(nullptr, aSrc);
|
|
}
|
|
|
|
/**
|
|
* Reflow overflow container children. They are invisible to normal reflow
|
|
* (i.e. don't affect sizing or placement of other children) and inherit
|
|
* width and horizontal position from their prev-in-flow.
|
|
*
|
|
* This method
|
|
* 1. Pulls excess overflow containers from the prev-in-flow and adds
|
|
* them to our overflow container list
|
|
* 2. Reflows all our overflow container kids
|
|
* 3. Expands aOverflowRect as necessary to accomodate these children.
|
|
* 4. Sets aStatus's mOverflowIncomplete flag (along with
|
|
* mNextInFlowNeedsReflow as necessary) if any overflow children
|
|
* are incomplete and
|
|
* 5. Prepends a list of their continuations to our excess overflow
|
|
* container list, to be drained into our next-in-flow when it is
|
|
* reflowed.
|
|
*
|
|
* The caller is responsible for tracking any new overflow container
|
|
* continuations it makes, removing them from its child list, and
|
|
* making sure they are stored properly in the overflow container lists.
|
|
* The nsOverflowContinuationTracker helper class should be used for this.
|
|
*
|
|
* @param aFlags is passed through to ReflowChild
|
|
* @param aMergeFunc is passed to DrainExcessOverflowContainersList
|
|
* @param aContainerSize is used only for converting logical coordinate to
|
|
* physical coordinate. If a tentative container size is used, caller
|
|
* may need to adjust the position of our overflow container children
|
|
* once the real size is known if our writing mode is vertical-rl.
|
|
*/
|
|
void ReflowOverflowContainerChildren(
|
|
nsPresContext* aPresContext, const ReflowInput& aReflowInput,
|
|
mozilla::OverflowAreas& aOverflowRects, ReflowChildFlags aFlags,
|
|
nsReflowStatus& aStatus,
|
|
ChildFrameMerger aMergeFunc = DefaultChildFrameMerge,
|
|
Maybe<nsSize> aContainerSize = Nothing());
|
|
|
|
/**
|
|
* Move any frames on our overflow list to the end of our principal list.
|
|
* @return true if there were any overflow frames
|
|
*/
|
|
virtual bool DrainSelfOverflowList() override;
|
|
|
|
/**
|
|
* Move all frames on our prev-in-flow's and our own ExcessOverflowContainers
|
|
* lists to our OverflowContainers list. If there are frames on multiple
|
|
* lists they are merged using aMergeFunc.
|
|
* @return a pointer to our OverflowContainers list, if any
|
|
*/
|
|
nsFrameList* DrainExcessOverflowContainersList(
|
|
ChildFrameMerger aMergeFunc = DefaultChildFrameMerge);
|
|
|
|
/**
|
|
* Removes aChild without destroying it and without requesting reflow.
|
|
* Continuations are not affected. Checks the principal and overflow lists,
|
|
* and also the [excess] overflow containers lists if the frame bit
|
|
* NS_FRAME_IS_OVERFLOW_CONTAINER is set. It does not check any other lists.
|
|
* aChild must be in one of the above mentioned lists, or an assertion is
|
|
* triggered.
|
|
*
|
|
* Note: This method can destroy either overflow list or [excess] overflow
|
|
* containers list if aChild is the only child in the list. Any pointer to the
|
|
* list obtained prior to calling this method shouldn't be used.
|
|
*/
|
|
virtual void StealFrame(nsIFrame* aChild);
|
|
|
|
/**
|
|
* Removes the next-siblings of aChild without destroying them and without
|
|
* requesting reflow. Checks the principal and overflow lists (not
|
|
* overflow containers / excess overflow containers). Does not check any
|
|
* other auxiliary lists.
|
|
* @param aChild a child frame or nullptr
|
|
* @return If aChild is non-null, the next-siblings of aChild, if any.
|
|
* If aChild is null, all child frames on the principal list, if any.
|
|
*/
|
|
nsFrameList StealFramesAfter(nsIFrame* aChild);
|
|
|
|
/**
|
|
* Add overflow containers to the display list
|
|
*/
|
|
void DisplayOverflowContainers(nsDisplayListBuilder* aBuilder,
|
|
const nsDisplayListSet& aLists);
|
|
|
|
/**
|
|
* Builds display lists for the children. The background
|
|
* of each child is placed in the Content() list (suitable for inline
|
|
* children and other elements that behave like inlines,
|
|
* but not for in-flow block children of blocks). DOES NOT
|
|
* paint the background/borders/outline of this frame. This should
|
|
* probably be avoided and eventually removed. It's currently here
|
|
* to emulate what nsContainerFrame::Paint did.
|
|
*/
|
|
virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
|
const nsDisplayListSet& aLists) override;
|
|
|
|
static void PlaceFrameView(nsIFrame* aFrame) {
|
|
if (aFrame->HasView())
|
|
nsContainerFrame::PositionFrameView(aFrame);
|
|
else
|
|
nsContainerFrame::PositionChildViews(aFrame);
|
|
}
|
|
|
|
/**
|
|
* Returns a CSS Box Alignment constant which the caller can use to align
|
|
* the absolutely-positioned child (whose ReflowInput is aChildRI) within
|
|
* a CSS Box Alignment area associated with this container.
|
|
*
|
|
* The lower 8 bits of the returned value are guaranteed to form a valid
|
|
* argument for CSSAlignUtils::AlignJustifySelf(). (The upper 8 bits may
|
|
* encode an <overflow-position>.)
|
|
*
|
|
* NOTE: This default nsContainerFrame implementation is a stub, and isn't
|
|
* meant to be called. Subclasses must provide their own implementations, if
|
|
* they use CSS Box Alignment to determine the static position of their
|
|
* absolutely-positioned children. (Though: if subclasses share enough code,
|
|
* maybe this nsContainerFrame impl should include some shared code.)
|
|
*
|
|
* @param aChildRI A ReflowInput for the positioned child frame that's being
|
|
* aligned.
|
|
* @param aLogicalAxis The axis (of this container frame) in which the caller
|
|
* would like to align the child frame.
|
|
*/
|
|
virtual mozilla::StyleAlignFlags CSSAlignmentForAbsPosChild(
|
|
const ReflowInput& aChildRI, mozilla::LogicalAxis aLogicalAxis) const;
|
|
|
|
#define NS_DECLARE_FRAME_PROPERTY_FRAMELIST(prop) \
|
|
NS_DECLARE_FRAME_PROPERTY_WITH_DTOR_NEVER_CALLED(prop, nsFrameList)
|
|
|
|
using FrameListPropertyDescriptor =
|
|
mozilla::FrameProperties::Descriptor<nsFrameList>;
|
|
|
|
NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OverflowProperty)
|
|
NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OverflowContainersProperty)
|
|
NS_DECLARE_FRAME_PROPERTY_FRAMELIST(ExcessOverflowContainersProperty)
|
|
NS_DECLARE_FRAME_PROPERTY_FRAMELIST(BackdropProperty)
|
|
|
|
// Only really used on nsBlockFrame instances, but the caller thinks it could
|
|
// have arbitrary nsContainerFrames.
|
|
NS_DECLARE_FRAME_PROPERTY_WITHOUT_DTOR(FirstLetterProperty, nsIFrame)
|
|
|
|
void SetHasFirstLetterChild() { mHasFirstLetterChild = true; }
|
|
|
|
void ClearHasFirstLetterChild() { mHasFirstLetterChild = false; }
|
|
|
|
#ifdef DEBUG
|
|
// Use this to suppress the ABSURD_SIZE assertions.
|
|
NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(DebugReflowingWithInfiniteISize, bool)
|
|
bool IsAbsurdSizeAssertSuppressed() const {
|
|
return GetProperty(DebugReflowingWithInfiniteISize());
|
|
}
|
|
#endif
|
|
|
|
// Incorporate the child overflow areas into aOverflowAreas.
|
|
// If the child does not have a overflow, use the child area.
|
|
void ConsiderChildOverflow(mozilla::OverflowAreas& aOverflowAreas,
|
|
nsIFrame* aChildFrame);
|
|
|
|
protected:
|
|
nsContainerFrame(ComputedStyle* aStyle, nsPresContext* aPresContext,
|
|
ClassID aID)
|
|
: nsSplittableFrame(aStyle, aPresContext, aID) {}
|
|
|
|
~nsContainerFrame();
|
|
|
|
/**
|
|
* Helper for DestroyFrom. DestroyAbsoluteFrames is called before
|
|
* destroying frames on lists that can contain placeholders.
|
|
* Derived classes must do that too, if they destroy such frame lists.
|
|
* See nsBlockFrame::DestroyFrom for an example.
|
|
*/
|
|
void DestroyAbsoluteFrames(nsIFrame* aDestructRoot,
|
|
PostDestroyData& aPostDestroyData);
|
|
|
|
/**
|
|
* Helper for StealFrame. Returns true if aChild was removed from its list.
|
|
*/
|
|
bool MaybeStealOverflowContainerFrame(nsIFrame* aChild);
|
|
|
|
/**
|
|
* Builds a display list for non-block children that behave like
|
|
* inlines. This puts the background of each child into the
|
|
* Content() list (suitable for inline children but not for
|
|
* in-flow block children of blocks).
|
|
* @param aForcePseudoStack forces each child into a pseudo-stacking-context
|
|
* so its background and all other display items (except for positioned
|
|
* display items) go into the Content() list.
|
|
*/
|
|
void BuildDisplayListForNonBlockChildren(nsDisplayListBuilder* aBuilder,
|
|
const nsDisplayListSet& aLists,
|
|
DisplayChildFlags aFlags = {});
|
|
|
|
/**
|
|
* A version of BuildDisplayList that use DisplayChildFlag::Inline.
|
|
* Intended as a convenience for derived classes.
|
|
*/
|
|
void BuildDisplayListForInline(nsDisplayListBuilder* aBuilder,
|
|
const nsDisplayListSet& aLists) {
|
|
DisplayBorderBackgroundOutline(aBuilder, aLists);
|
|
BuildDisplayListForNonBlockChildren(aBuilder, aLists,
|
|
DisplayChildFlag::Inline);
|
|
}
|
|
|
|
// ==========================================================================
|
|
/* Overflow Frames are frames that did not fit and must be pulled by
|
|
* our next-in-flow during its reflow. (The same concept for overflow
|
|
* containers is called "excess frames". We should probably make the
|
|
* names match.)
|
|
*/
|
|
|
|
/**
|
|
* Get the frames on the overflow list, overflow containers list, or excess
|
|
* overflow containers list. Can return null if there are no frames in the
|
|
* list.
|
|
*
|
|
* The caller does NOT take ownership of the list; it's still owned by this
|
|
* frame. A non-null return value indicates that the list is non-empty.
|
|
*/
|
|
[[nodiscard]] nsFrameList* GetOverflowFrames() const {
|
|
nsFrameList* list = GetProperty(OverflowProperty());
|
|
NS_ASSERTION(!list || !list->IsEmpty(), "Unexpected empty overflow list");
|
|
return list;
|
|
}
|
|
[[nodiscard]] nsFrameList* GetOverflowContainers() const {
|
|
nsFrameList* list = GetProperty(OverflowContainersProperty());
|
|
NS_ASSERTION(!list || !list->IsEmpty(),
|
|
"Unexpected empty overflow containers list");
|
|
return list;
|
|
}
|
|
[[nodiscard]] nsFrameList* GetExcessOverflowContainers() const {
|
|
nsFrameList* list = GetProperty(ExcessOverflowContainersProperty());
|
|
NS_ASSERTION(!list || !list->IsEmpty(),
|
|
"Unexpected empty overflow containers list");
|
|
return list;
|
|
}
|
|
|
|
/**
|
|
* Same as the Get methods above, but also remove and the property from this
|
|
* frame.
|
|
*
|
|
* The caller is responsible for deleting nsFrameList and either passing
|
|
* ownership of the frames to someone else or destroying the frames. A
|
|
* non-null return value indicates that the list is non-empty. The recommended
|
|
* way to use this function it to assign its return value into an
|
|
* AutoFrameListPtr.
|
|
*/
|
|
[[nodiscard]] nsFrameList* StealOverflowFrames() {
|
|
nsFrameList* list = TakeProperty(OverflowProperty());
|
|
NS_ASSERTION(!list || !list->IsEmpty(), "Unexpected empty overflow list");
|
|
return list;
|
|
}
|
|
[[nodiscard]] nsFrameList* StealOverflowContainers() {
|
|
nsFrameList* list = TakeProperty(OverflowContainersProperty());
|
|
NS_ASSERTION(!list || !list->IsEmpty(), "Unexpected empty overflow list");
|
|
return list;
|
|
}
|
|
[[nodiscard]] nsFrameList* StealExcessOverflowContainers() {
|
|
nsFrameList* list = TakeProperty(ExcessOverflowContainersProperty());
|
|
NS_ASSERTION(!list || !list->IsEmpty(), "Unexpected empty overflow list");
|
|
return list;
|
|
}
|
|
|
|
/**
|
|
* Set the overflow list, overflow containers list, or excess overflow
|
|
* containers list. The argument must be a *non-empty* list.
|
|
*
|
|
* After this operation, the argument becomes an empty list.
|
|
*
|
|
* @return the frame list associated with the property.
|
|
*/
|
|
nsFrameList* SetOverflowFrames(nsFrameList&& aOverflowFrames) {
|
|
MOZ_ASSERT(aOverflowFrames.NotEmpty(), "Shouldn't be called");
|
|
auto* list = new (PresShell()) nsFrameList(std::move(aOverflowFrames));
|
|
SetProperty(OverflowProperty(), list);
|
|
return list;
|
|
}
|
|
nsFrameList* SetOverflowContainers(nsFrameList&& aOverflowContainers) {
|
|
MOZ_ASSERT(aOverflowContainers.NotEmpty(), "Shouldn't set an empty list!");
|
|
MOZ_ASSERT(!GetProperty(OverflowContainersProperty()),
|
|
"Shouldn't override existing list!");
|
|
MOZ_ASSERT(IsFrameOfType(nsIFrame::eCanContainOverflowContainers),
|
|
"This type of frame can't have overflow containers!");
|
|
auto* list = new (PresShell()) nsFrameList(std::move(aOverflowContainers));
|
|
SetProperty(OverflowContainersProperty(), list);
|
|
return list;
|
|
}
|
|
nsFrameList* SetExcessOverflowContainers(
|
|
nsFrameList&& aExcessOverflowContainers) {
|
|
MOZ_ASSERT(aExcessOverflowContainers.NotEmpty(),
|
|
"Shouldn't set an empty list!");
|
|
MOZ_ASSERT(!GetProperty(ExcessOverflowContainersProperty()),
|
|
"Shouldn't override existing list!");
|
|
MOZ_ASSERT(IsFrameOfType(nsIFrame::eCanContainOverflowContainers),
|
|
"This type of frame can't have overflow containers!");
|
|
auto* list =
|
|
new (PresShell()) nsFrameList(std::move(aExcessOverflowContainers));
|
|
SetProperty(ExcessOverflowContainersProperty(), list);
|
|
return list;
|
|
}
|
|
|
|
/**
|
|
* Destroy the overflow list, overflow containers list, or excess overflow
|
|
* containers list.
|
|
*
|
|
* The list to be destroyed must be empty. That is, the caller is responsible
|
|
* for either passing ownership of the frames to someone else or destroying
|
|
* the frames before calling these methods.
|
|
*/
|
|
void DestroyOverflowList() {
|
|
nsFrameList* list = TakeProperty(OverflowProperty());
|
|
MOZ_ASSERT(list && list->IsEmpty());
|
|
list->Delete(PresShell());
|
|
}
|
|
void DestroyOverflowContainers() {
|
|
nsFrameList* list = TakeProperty(OverflowContainersProperty());
|
|
MOZ_ASSERT(list && list->IsEmpty());
|
|
list->Delete(PresShell());
|
|
}
|
|
void DestroyExcessOverflowContainers() {
|
|
nsFrameList* list = TakeProperty(ExcessOverflowContainersProperty());
|
|
MOZ_ASSERT(list && list->IsEmpty());
|
|
list->Delete(PresShell());
|
|
}
|
|
|
|
/**
|
|
* Moves any frames on both the prev-in-flow's overflow list and the
|
|
* receiver's overflow to the receiver's child list.
|
|
*
|
|
* Resets the overlist pointers to nullptr, and updates the receiver's child
|
|
* count and content mapping.
|
|
*
|
|
* @return true if any frames were moved and false otherwise
|
|
*/
|
|
bool MoveOverflowToChildList();
|
|
|
|
/**
|
|
* Merge a sorted frame list into our overflow list. aList becomes empty after
|
|
* this call.
|
|
*/
|
|
void MergeSortedOverflow(nsFrameList& aList);
|
|
|
|
/**
|
|
* Merge a sorted frame list into our excess overflow containers list. aList
|
|
* becomes empty after this call.
|
|
*/
|
|
void MergeSortedExcessOverflowContainers(nsFrameList& aList);
|
|
|
|
/**
|
|
* Moves all frames from aSrc into aDest such that the resulting aDest
|
|
* is still sorted in document content order and continuation order. aSrc
|
|
* becomes empty after this call.
|
|
*
|
|
* Precondition: both |aSrc| and |aDest| must be sorted to begin with.
|
|
* @param aCommonAncestor a hint for nsLayoutUtils::CompareTreePosition
|
|
*/
|
|
static void MergeSortedFrameLists(nsFrameList& aDest, nsFrameList& aSrc,
|
|
nsIContent* aCommonAncestor);
|
|
|
|
/**
|
|
* This is intended to be used as a ChildFrameMerger argument for
|
|
* ReflowOverflowContainerChildren() and DrainExcessOverflowContainersList().
|
|
*/
|
|
static inline void MergeSortedFrameListsFor(nsFrameList& aDest,
|
|
nsFrameList& aSrc,
|
|
nsContainerFrame* aParent) {
|
|
MergeSortedFrameLists(aDest, aSrc, aParent->GetContent());
|
|
}
|
|
|
|
/**
|
|
* Basically same as MoveOverflowToChildList, except that this is for
|
|
* handling inline children where children of prev-in-flow can be
|
|
* pushed to overflow list even if a next-in-flow exists.
|
|
*
|
|
* @param aLineContainer the line container of the current frame.
|
|
*
|
|
* @return true if any frames were moved and false otherwise
|
|
*/
|
|
bool MoveInlineOverflowToChildList(nsIFrame* aLineContainer);
|
|
|
|
/**
|
|
* Push aFromChild and its next siblings to the overflow list.
|
|
*
|
|
* @param aFromChild the first child frame to push. It is disconnected
|
|
* from aPrevSibling
|
|
* @param aPrevSibling aFrameChild's previous sibling. Must not be null.
|
|
* It's an error to push a parent's first child frame.
|
|
*/
|
|
void PushChildrenToOverflow(nsIFrame* aFromChild, nsIFrame* aPrevSibling);
|
|
|
|
/**
|
|
* Same as above, except that this pushes frames to the next-in-flow
|
|
* frame and changes the geometric parent of the pushed frames when
|
|
* there is a next-in-flow frame.
|
|
*
|
|
* Updates the next-in-flow's child count. Does <b>not</b> update the
|
|
* pusher's child count.
|
|
*/
|
|
void PushChildren(nsIFrame* aFromChild, nsIFrame* aPrevSibling);
|
|
|
|
/**
|
|
* Iterate our children in our principal child list in the normal document
|
|
* order, and append them (or their next-in-flows) to either our overflow list
|
|
* or excess overflow container list according to their presence in
|
|
* aPushedItems, aIncompleteItems, or aOverflowIncompleteItems.
|
|
*
|
|
* Note: This method is only intended for Grid / Flex containers.
|
|
* aPushedItems, aIncompleteItems, and aOverflowIncompleteItems are expected
|
|
* to contain only Grid / Flex items. That is, they should contain only
|
|
* in-flow children.
|
|
*
|
|
* @return true if any items are moved; false otherwise.
|
|
*/
|
|
using FrameHashtable = nsTHashSet<nsIFrame*>;
|
|
bool PushIncompleteChildren(const FrameHashtable& aPushedItems,
|
|
const FrameHashtable& aIncompleteItems,
|
|
const FrameHashtable& aOverflowIncompleteItems);
|
|
|
|
/**
|
|
* Prepare our child lists so that they are ready to reflow by the following
|
|
* operations:
|
|
*
|
|
* - Merge overflow list from our prev-in-flow into our principal child list.
|
|
* - Merge our own overflow list into our principal child list,
|
|
* - Push any child's next-in-flows in our principal child list to our
|
|
* overflow list.
|
|
* - Pull up any first-in-flow child we might have pushed from our
|
|
* next-in-flows.
|
|
*/
|
|
void NormalizeChildLists();
|
|
|
|
/**
|
|
* Helper to implement AppendFrames / InsertFrames for flex / grid
|
|
* containers.
|
|
*/
|
|
void NoteNewChildren(ChildListID aListID, const nsFrameList& aFrameList);
|
|
|
|
/**
|
|
* Helper to implement DrainSelfOverflowList() for flex / grid containers.
|
|
*/
|
|
bool DrainAndMergeSelfOverflowList();
|
|
|
|
/**
|
|
* Helper to find the first non-anonymous-box frame in the subtree rooted at
|
|
* aFrame.
|
|
*/
|
|
static nsIFrame* GetFirstNonAnonBoxInSubtree(nsIFrame* aFrame);
|
|
|
|
/**
|
|
* Reparent floats whose placeholders are inline descendants of aFrame from
|
|
* whatever block they're currently parented by to aOurBlock.
|
|
* @param aReparentSiblings if this is true, we follow aFrame's
|
|
* GetNextSibling chain reparenting them all
|
|
*/
|
|
static void ReparentFloatsForInlineChild(nsIFrame* aOurBlock,
|
|
nsIFrame* aFrame,
|
|
bool aReparentSiblings);
|
|
|
|
/**
|
|
* Try to remove aChildToRemove from the frame list stored in aProp.
|
|
* If aChildToRemove was removed from the aProp list and that list became
|
|
* empty, then aProp is removed from this frame and deleted.
|
|
* @note if aChildToRemove isn't on the aProp frame list, it might still be
|
|
* removed from whatever list it happens to be on, so use this method
|
|
* carefully. This method is primarily meant for removing frames from the
|
|
* [Excess]OverflowContainers lists.
|
|
* @return true if aChildToRemove was removed from some list
|
|
*/
|
|
bool TryRemoveFrame(FrameListPropertyDescriptor aProp,
|
|
nsIFrame* aChildToRemove);
|
|
|
|
// ==========================================================================
|
|
/*
|
|
* Convenience methods for traversing continuations
|
|
*/
|
|
|
|
struct ContinuationTraversingState {
|
|
nsContainerFrame* mNextInFlow;
|
|
explicit ContinuationTraversingState(nsContainerFrame* aFrame)
|
|
: mNextInFlow(static_cast<nsContainerFrame*>(aFrame->GetNextInFlow())) {
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Find the first frame that is a child of this frame's next-in-flows,
|
|
* considering both their principal child lists and overflow lists.
|
|
*/
|
|
nsIFrame* GetNextInFlowChild(ContinuationTraversingState& aState,
|
|
bool* aIsInOverflow = nullptr);
|
|
|
|
/**
|
|
* Remove the result of GetNextInFlowChild from its current parent and
|
|
* append it to this frame's principal child list.
|
|
*/
|
|
nsIFrame* PullNextInFlowChild(ContinuationTraversingState& aState);
|
|
|
|
/**
|
|
* Safely destroy the frames on the nsFrameList stored on aProp for this
|
|
* frame then remove the property and delete the frame list.
|
|
* Nothing happens if the property doesn't exist.
|
|
*/
|
|
void SafelyDestroyFrameListProp(nsIFrame* aDestructRoot,
|
|
PostDestroyData& aPostDestroyData,
|
|
mozilla::PresShell* aPresShell,
|
|
FrameListPropertyDescriptor aProp);
|
|
|
|
// ==========================================================================
|
|
|
|
// Helper used by Progress and Meter frames. Returns true if the bar should
|
|
// be rendered vertically, based on writing-mode and -moz-orient properties.
|
|
bool ResolvedOrientationIsVertical();
|
|
|
|
/**
|
|
* Calculate the used values for 'width' and 'height' for a replaced element.
|
|
* http://www.w3.org/TR/CSS21/visudet.html#min-max-widths
|
|
*
|
|
* @param aAspectRatio the aspect ratio calculated by GetAspectRatio().
|
|
*/
|
|
mozilla::LogicalSize ComputeSizeWithIntrinsicDimensions(
|
|
gfxContext* aRenderingContext, mozilla::WritingMode aWM,
|
|
const mozilla::IntrinsicSize& aIntrinsicSize,
|
|
const mozilla::AspectRatio& aAspectRatio,
|
|
const mozilla::LogicalSize& aCBSize, const mozilla::LogicalSize& aMargin,
|
|
const mozilla::LogicalSize& aBorderPadding,
|
|
const mozilla::StyleSizeOverrides& aSizeOverrides,
|
|
mozilla::ComputeSizeFlags aFlags);
|
|
|
|
// Compute tight bounds assuming this frame honours its border, background
|
|
// and outline, its children's tight bounds, and nothing else.
|
|
nsRect ComputeSimpleTightBounds(mozilla::gfx::DrawTarget* aDrawTarget) const;
|
|
|
|
/*
|
|
* If this frame is dirty, marks all absolutely-positioned children of this
|
|
* frame dirty. If this frame isn't dirty, or if there are no
|
|
* absolutely-positioned children, does nothing.
|
|
*
|
|
* It's necessary to use PushDirtyBitToAbsoluteFrames() when you plan to
|
|
* reflow this frame's absolutely-positioned children after the dirty bit on
|
|
* this frame has already been cleared, which prevents ReflowInput from
|
|
* propagating the dirty bit normally. This situation generally only arises
|
|
* when a multipass layout algorithm is used.
|
|
*/
|
|
void PushDirtyBitToAbsoluteFrames();
|
|
|
|
// Helper function that tests if the frame tree is too deep; if it is
|
|
// it marks the frame as "unflowable", zeroes out the metrics, sets
|
|
// the reflow status, and returns true. Otherwise, the frame is
|
|
// unmarked "unflowable" and the metrics and reflow status are not
|
|
// touched and false is returned.
|
|
bool IsFrameTreeTooDeep(const ReflowInput& aReflowInput,
|
|
ReflowOutput& aMetrics, nsReflowStatus& aStatus);
|
|
|
|
/**
|
|
* @return true if we should avoid a page/column break in this frame.
|
|
*/
|
|
bool ShouldAvoidBreakInside(const ReflowInput& aReflowInput) const;
|
|
|
|
/**
|
|
* To be called by |BuildDisplayLists| of this class or derived classes to add
|
|
* a translucent overlay if this frame's content is selected.
|
|
* @param aContentType an nsISelectionDisplay DISPLAY_ constant identifying
|
|
* which kind of content this is for
|
|
*/
|
|
void DisplaySelectionOverlay(
|
|
nsDisplayListBuilder* aBuilder, nsDisplayList* aList,
|
|
uint16_t aContentType = nsISelectionDisplay::DISPLAY_FRAMES);
|
|
|
|
// ==========================================================================
|
|
|
|
#ifdef DEBUG
|
|
// A helper for flex / grid container to sanity check child lists before
|
|
// reflow. Intended to be called after calling NormalizeChildLists().
|
|
void SanityCheckChildListsBeforeReflow() const;
|
|
|
|
// A helper to set mDidPushItemsBitMayLie if needed. Intended to be called
|
|
// only in flex / grid container's RemoveFrame.
|
|
void SetDidPushItemsBitIfNeeded(ChildListID aListID, nsIFrame* aOldFrame);
|
|
|
|
// A flag for flex / grid containers. If true, NS_STATE_GRID_DID_PUSH_ITEMS or
|
|
// NS_STATE_FLEX_DID_PUSH_ITEMS may be set even though all pushed frames may
|
|
// have been removed. This is used to suppress an assertion in case
|
|
// RemoveFrame removed all associated child frames.
|
|
bool mDidPushItemsBitMayLie{false};
|
|
#endif
|
|
|
|
nsFrameList mFrames;
|
|
};
|
|
|
|
// ==========================================================================
|
|
/* The out-of-flow-related code below is for a hacky way of splitting
|
|
* absolutely-positioned frames. Basically what we do is split the frame
|
|
* in nsAbsoluteContainingBlock and pretend the continuation is an overflow
|
|
* container. This isn't an ideal solution, but it lets us print the content
|
|
* at least. See bug 154892.
|
|
*/
|
|
|
|
/**
|
|
* Helper class for tracking overflow container continuations during reflow.
|
|
*
|
|
* A frame is related to two sets of overflow containers: those that /are/
|
|
* its own children, and those that are /continuations/ of its children.
|
|
* This tracker walks through those continuations (the frame's NIF's children)
|
|
* and their prev-in-flows (a subset of the frame's normal and overflow
|
|
* container children) in parallel. It allows the reflower to synchronously
|
|
* walk its overflow continuations while it loops through and reflows its
|
|
* children. This makes it possible to insert new continuations at the correct
|
|
* place in the overflow containers list.
|
|
*
|
|
* The reflower is expected to loop through its children in the same order it
|
|
* looped through them the last time (if there was a last time).
|
|
* For each child, the reflower should either
|
|
* - call Skip for the child if was not reflowed in this pass
|
|
* - call Insert for the overflow continuation if the child was reflowed
|
|
* but has incomplete overflow
|
|
* - call Finished for the child if it was reflowed in this pass but
|
|
* is either complete or has a normal next-in-flow. This call can
|
|
* be skipped if the child did not previously have an overflow
|
|
* continuation.
|
|
*/
|
|
class nsOverflowContinuationTracker {
|
|
public:
|
|
/**
|
|
* Initializes an nsOverflowContinuationTracker to help track overflow
|
|
* continuations of aFrame's children. Typically invoked on 'this'.
|
|
*
|
|
* aWalkOOFFrames determines whether the walker skips out-of-flow frames
|
|
* or skips non-out-of-flow frames.
|
|
*
|
|
* Don't set aSkipOverflowContainerChildren to false unless you plan
|
|
* to walk your own overflow container children. (Usually they are handled
|
|
* by calling ReflowOverflowContainerChildren.) aWalkOOFFrames is ignored
|
|
* if aSkipOverflowContainerChildren is false.
|
|
*/
|
|
nsOverflowContinuationTracker(nsContainerFrame* aFrame, bool aWalkOOFFrames,
|
|
bool aSkipOverflowContainerChildren = true);
|
|
/**
|
|
* This function adds an overflow continuation to our running list and
|
|
* sets its NS_FRAME_IS_OVERFLOW_CONTAINER flag.
|
|
*
|
|
* aReflowStatus should preferably be specific to the recently-reflowed
|
|
* child and not influenced by any of its siblings' statuses. This
|
|
* function sets the NS_FRAME_IS_DIRTY bit on aOverflowCont if it needs
|
|
* to be reflowed. (Its need for reflow depends on changes to its
|
|
* prev-in-flow, not to its parent--for whom it is invisible, reflow-wise.)
|
|
*
|
|
* The caller MUST disconnect the frame from its parent's child list
|
|
* if it was not previously an NS_FRAME_IS_OVERFLOW_CONTAINER (because
|
|
* StealFrame is much more inefficient than disconnecting in place
|
|
* during Reflow, which the caller is able to do but we are not).
|
|
*
|
|
* The caller MUST NOT disconnect the frame from its parent's
|
|
* child list if it is already an NS_FRAME_IS_OVERFLOW_CONTAINER.
|
|
* (In this case we will disconnect and reconnect it ourselves.)
|
|
*/
|
|
nsresult Insert(nsIFrame* aOverflowCont, nsReflowStatus& aReflowStatus);
|
|
/**
|
|
* Begin/EndFinish() must be called for each child that is reflowed
|
|
* but no longer has an overflow continuation. (It may be called for
|
|
* other children, but in that case has no effect.) It increments our
|
|
* walker and makes sure we drop any dangling pointers to its
|
|
* next-in-flow. This function MUST be called before stealing or
|
|
* deleting aChild's next-in-flow.
|
|
* The AutoFinish helper object does that for you. Use it like so:
|
|
* if (kidNextInFlow) {
|
|
* nsOverflowContinuationTracker::AutoFinish fini(tracker, kid);
|
|
* ... DeleteNextInFlowChild/StealFrame(kidNextInFlow) here ...
|
|
* }
|
|
*/
|
|
class MOZ_RAII AutoFinish {
|
|
public:
|
|
AutoFinish(nsOverflowContinuationTracker* aTracker, nsIFrame* aChild)
|
|
: mTracker(aTracker), mChild(aChild) {
|
|
if (mTracker) mTracker->BeginFinish(mChild);
|
|
}
|
|
~AutoFinish() {
|
|
if (mTracker) mTracker->EndFinish(mChild);
|
|
}
|
|
|
|
private:
|
|
nsOverflowContinuationTracker* mTracker;
|
|
nsIFrame* mChild;
|
|
};
|
|
|
|
/**
|
|
* This function should be called for each child that isn't reflowed.
|
|
* It increments our walker and sets the mOverflowIncomplete
|
|
* reflow flag if it encounters an overflow continuation so that our
|
|
* next-in-flow doesn't get prematurely deleted. It MUST be called on
|
|
* each unreflowed child that has an overflow container continuation;
|
|
* it MAY be called on other children, but it isn't necessary (doesn't
|
|
* do anything).
|
|
*/
|
|
void Skip(nsIFrame* aChild, nsReflowStatus& aReflowStatus) {
|
|
MOZ_ASSERT(aChild, "null ptr");
|
|
if (aChild == mSentry) {
|
|
StepForward();
|
|
if (aReflowStatus.IsComplete()) {
|
|
aReflowStatus.SetOverflowIncomplete();
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
/**
|
|
* @see class AutoFinish
|
|
*/
|
|
void BeginFinish(nsIFrame* aChild);
|
|
void EndFinish(nsIFrame* aChild);
|
|
|
|
void SetupOverflowContList();
|
|
void SetUpListWalker();
|
|
void StepForward();
|
|
|
|
/* We hold a pointer to either the next-in-flow's overflow containers list
|
|
or, if that doesn't exist, our frame's excess overflow containers list.
|
|
We need to make sure that we drop that pointer if the list becomes
|
|
empty and is deleted elsewhere. */
|
|
nsFrameList* mOverflowContList;
|
|
/* We hold a pointer to the most recently-reflowed child that has an
|
|
overflow container next-in-flow. We do this because it's a known
|
|
good point; this pointer won't be deleted on us. We can use it to
|
|
recover our place in the list. */
|
|
nsIFrame* mPrevOverflowCont;
|
|
/* This is a pointer to the next overflow container's prev-in-flow, which
|
|
is (or should be) a child of our frame. When we hit this, we will need
|
|
to increment this walker to the next overflow container. */
|
|
nsIFrame* mSentry;
|
|
/* Parent of all frames in mOverflowContList. If our mOverflowContList
|
|
is an excessOverflowContainersProperty, or null, then this is our frame
|
|
(the frame that was passed in to our constructor). Otherwise this is
|
|
that frame's next-in-flow, and our mOverflowContList is mParent's
|
|
overflowContainersProperty */
|
|
nsContainerFrame* mParent;
|
|
/* Tells SetUpListWalker whether or not to walk us past any continuations
|
|
of overflow containers. aWalkOOFFrames is ignored when this is false. */
|
|
bool mSkipOverflowContainerChildren;
|
|
/* Tells us whether to pay attention to OOF frames or non-OOF frames */
|
|
bool mWalkOOFFrames;
|
|
};
|
|
|
|
// Start Display Reflow Debugging
|
|
#ifdef DEBUG
|
|
|
|
struct DR_cookie {
|
|
DR_cookie(nsPresContext* aPresContext, nsIFrame* aFrame,
|
|
const mozilla::ReflowInput& aReflowInput,
|
|
mozilla::ReflowOutput& aMetrics, nsReflowStatus& aStatus);
|
|
~DR_cookie();
|
|
void Change() const;
|
|
|
|
nsPresContext* mPresContext;
|
|
nsIFrame* mFrame;
|
|
const mozilla::ReflowInput& mReflowInput;
|
|
mozilla::ReflowOutput& mMetrics;
|
|
nsReflowStatus& mStatus;
|
|
void* mValue;
|
|
};
|
|
|
|
struct DR_layout_cookie {
|
|
explicit DR_layout_cookie(nsIFrame* aFrame);
|
|
~DR_layout_cookie();
|
|
|
|
nsIFrame* mFrame;
|
|
void* mValue;
|
|
};
|
|
|
|
struct DR_intrinsic_inline_size_cookie {
|
|
DR_intrinsic_inline_size_cookie(nsIFrame* aFrame, const char* aType,
|
|
nscoord& aResult);
|
|
~DR_intrinsic_inline_size_cookie();
|
|
|
|
nsIFrame* mFrame;
|
|
const char* mType;
|
|
nscoord& mResult;
|
|
void* mValue;
|
|
};
|
|
|
|
struct DR_intrinsic_size_cookie {
|
|
DR_intrinsic_size_cookie(nsIFrame* aFrame, const char* aType,
|
|
nsSize& aResult);
|
|
~DR_intrinsic_size_cookie();
|
|
|
|
nsIFrame* mFrame;
|
|
const char* mType;
|
|
nsSize& mResult;
|
|
void* mValue;
|
|
};
|
|
|
|
struct DR_init_constraints_cookie {
|
|
DR_init_constraints_cookie(
|
|
nsIFrame* aFrame, mozilla::ReflowInput* aState, nscoord aCBWidth,
|
|
nscoord aCBHeight, const mozilla::Maybe<mozilla::LogicalMargin> aBorder,
|
|
const mozilla::Maybe<mozilla::LogicalMargin> aPadding);
|
|
~DR_init_constraints_cookie();
|
|
|
|
nsIFrame* mFrame;
|
|
mozilla::ReflowInput* mState;
|
|
void* mValue;
|
|
};
|
|
|
|
struct DR_init_offsets_cookie {
|
|
DR_init_offsets_cookie(nsIFrame* aFrame,
|
|
mozilla::SizeComputationInput* aState,
|
|
nscoord aPercentBasis,
|
|
mozilla::WritingMode aCBWritingMode,
|
|
const mozilla::Maybe<mozilla::LogicalMargin> aBorder,
|
|
const mozilla::Maybe<mozilla::LogicalMargin> aPadding);
|
|
~DR_init_offsets_cookie();
|
|
|
|
nsIFrame* mFrame;
|
|
mozilla::SizeComputationInput* mState;
|
|
void* mValue;
|
|
};
|
|
|
|
# define DISPLAY_REFLOW(dr_pres_context, dr_frame, dr_rf_state, \
|
|
dr_rf_metrics, dr_rf_status) \
|
|
DR_cookie dr_cookie(dr_pres_context, dr_frame, dr_rf_state, dr_rf_metrics, \
|
|
dr_rf_status);
|
|
# define DISPLAY_REFLOW_CHANGE() dr_cookie.Change();
|
|
# define DISPLAY_LAYOUT(dr_frame) DR_layout_cookie dr_cookie(dr_frame);
|
|
# define DISPLAY_MIN_INLINE_SIZE(dr_frame, dr_result) \
|
|
DR_intrinsic_inline_size_cookie dr_cookie(dr_frame, "Min", dr_result)
|
|
# define DISPLAY_PREF_INLINE_SIZE(dr_frame, dr_result) \
|
|
DR_intrinsic_inline_size_cookie dr_cookie(dr_frame, "Pref", dr_result)
|
|
# define DISPLAY_PREF_SIZE(dr_frame, dr_result) \
|
|
DR_intrinsic_size_cookie dr_cookie(dr_frame, "Pref", dr_result)
|
|
# define DISPLAY_MIN_SIZE(dr_frame, dr_result) \
|
|
DR_intrinsic_size_cookie dr_cookie(dr_frame, "Min", dr_result)
|
|
# define DISPLAY_MAX_SIZE(dr_frame, dr_result) \
|
|
DR_intrinsic_size_cookie dr_cookie(dr_frame, "Max", dr_result)
|
|
# define DISPLAY_INIT_CONSTRAINTS(dr_frame, dr_state, dr_cbw, dr_cbh, dr_bdr, \
|
|
dr_pad) \
|
|
DR_init_constraints_cookie dr_cookie(dr_frame, dr_state, dr_cbw, dr_cbh, \
|
|
dr_bdr, dr_pad)
|
|
# define DISPLAY_INIT_OFFSETS(dr_frame, dr_state, dr_pb, dr_cbwm, dr_bdr, \
|
|
dr_pad) \
|
|
DR_init_offsets_cookie dr_cookie(dr_frame, dr_state, dr_pb, dr_cbwm, \
|
|
dr_bdr, dr_pad)
|
|
|
|
#else
|
|
|
|
# define DISPLAY_REFLOW(dr_pres_context, dr_frame, dr_rf_state, \
|
|
dr_rf_metrics, dr_rf_status)
|
|
# define DISPLAY_REFLOW_CHANGE()
|
|
# define DISPLAY_LAYOUT(dr_frame) PR_BEGIN_MACRO PR_END_MACRO
|
|
# define DISPLAY_MIN_INLINE_SIZE(dr_frame, dr_result) \
|
|
PR_BEGIN_MACRO PR_END_MACRO
|
|
# define DISPLAY_PREF_INLINE_SIZE(dr_frame, dr_result) \
|
|
PR_BEGIN_MACRO PR_END_MACRO
|
|
# define DISPLAY_PREF_SIZE(dr_frame, dr_result) PR_BEGIN_MACRO PR_END_MACRO
|
|
# define DISPLAY_MIN_SIZE(dr_frame, dr_result) PR_BEGIN_MACRO PR_END_MACRO
|
|
# define DISPLAY_MAX_SIZE(dr_frame, dr_result) PR_BEGIN_MACRO PR_END_MACRO
|
|
# define DISPLAY_INIT_CONSTRAINTS(dr_frame, dr_state, dr_cbw, dr_cbh, dr_bdr, \
|
|
dr_pad) \
|
|
PR_BEGIN_MACRO PR_END_MACRO
|
|
# define DISPLAY_INIT_OFFSETS(dr_frame, dr_state, dr_pb, dr_cbwm, dr_bdr, \
|
|
dr_pad) \
|
|
PR_BEGIN_MACRO PR_END_MACRO
|
|
|
|
#endif
|
|
// End Display Reflow Debugging
|
|
|
|
#endif /* nsContainerFrame_h___ */
|