diff options
Diffstat (limited to 'layout/generic/nsContainerFrame.h')
-rw-r--r-- | layout/generic/nsContainerFrame.h | 923 |
1 files changed, 923 insertions, 0 deletions
diff --git a/layout/generic/nsContainerFrame.h b/layout/generic/nsContainerFrame.h new file mode 100644 index 000000000..a9f6d52df --- /dev/null +++ b/layout/generic/nsContainerFrame.h @@ -0,0 +1,923 @@ +/* -*- 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 */ + +#ifndef nsContainerFrame_h___ +#define nsContainerFrame_h___ + +#include "mozilla/Attributes.h" +#include "nsSplittableFrame.h" +#include "nsFrameList.h" +#include "nsLayoutUtils.h" + +// Option flags for ReflowChild() and FinishReflowChild() +// member functions +#define NS_FRAME_NO_MOVE_VIEW 0x0001 +#define NS_FRAME_NO_MOVE_FRAME (0x0002 | NS_FRAME_NO_MOVE_VIEW) +#define NS_FRAME_NO_SIZE_VIEW 0x0004 +#define NS_FRAME_NO_VISIBILITY 0x0008 +// Only applies to ReflowChild; if true, don't delete the next-in-flow, even +// if the reflow is fully complete. +#define NS_FRAME_NO_DELETE_NEXT_IN_FLOW_CHILD 0x0010 + +class nsOverflowContinuationTracker; +namespace mozilla { +class FramePropertyTable; +} // 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 CRAZY. +#define CRAZY_COORD (10000000*60) +#define CRAZY_SIZE(_x) (((_x) < -CRAZY_COORD) || ((_x) > CRAZY_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) override; + virtual void ChildIsDirty(nsIFrame* aChild) override; + + virtual bool IsLeaf() const override; + virtual FrameSearchResult PeekOffsetNoAmount(bool aForward, int32_t* aOffset) override; + virtual FrameSearchResult PeekOffsetCharacter(bool aForward, int32_t* aOffset, + bool aRespectClusters = true) override; + + virtual nsresult AttributeChanged(int32_t aNameSpaceID, + nsIAtom* aAttribute, + int32_t aModType) override; + +#ifdef DEBUG_FRAME_DUMP + void List(FILE* out = stderr, const char* aPrefix = "", uint32_t aFlags = 0) const override; +#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 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, + 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); + + /** + * Helper method to wrap views around frames. Used by containers + * under special circumstances (can be used by leaf frames as well) + */ + static void CreateViewForFrame(nsIFrame* aFrame, + bool aForce); + + // 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 nsresult ReparentFrameViewList(const nsFrameList& aChildFrameList, + nsIFrame* aOldParentFrame, + nsIFrame* aNewParentFrame); + + // Set the view's size and position after its frame has been reflowed. + // + // Flags: + // 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 view + static void SyncFrameViewAfterReflow(nsPresContext* aPresContext, + nsIFrame* aFrame, + nsView* aView, + const nsRect& aVisualOverflowArea, + uint32_t aFlags = 0); + + // 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, + nsRenderingContext* aRC, + uint32_t aFlags); + + // Sets the view's attributes from the frame style. + // - visibility + // - clip + // Call this when one of these styles changes or when the view has just + // been created. + // @param aStyleContext can be null, in which case the frame's style context is used + static void SyncFrameViewProperties(nsPresContext* aPresContext, + nsIFrame* aFrame, + nsStyleContext* aStyleContext, + nsView* aView, + uint32_t aFlags = 0); + + /** + * 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); + + // Used by both nsInlineFrame and nsFirstLetterFrame. + void DoInlineIntrinsicISize(nsRenderingContext *aRenderingContext, + InlineIntrinsicISizeData *aData, + nsLayoutUtils::IntrinsicISizeType aType); + + /** + * This is the CSS block concept of computing 'auto' widths, which most + * classes derived from nsContainerFrame want. + */ + virtual mozilla::LogicalSize + ComputeAutoSize(nsRenderingContext* aRenderingContext, + mozilla::WritingMode aWM, + const mozilla::LogicalSize& aCBSize, + nscoord aAvailableISize, + const mozilla::LogicalSize& aMargin, + const mozilla::LogicalSize& aBorder, + const mozilla::LogicalSize& aPadding, + ComputeSizeFlags aFlags) override; + + /** + * Positions aChildFrame and its view (if requested), and then calls Reflow(). + * If the reflow status after reflowing the child is FULLY_COMPLETE then any + * next-in-flows are deleted using DeleteNextInFlowChild(). + * + * @param aContainerSize size of the border-box of the containing frame + * + * Flags: + * 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_MOVE_FRAME - don't move the frame. aPos is ignored in this + * case. Also implies NS_FRAME_NO_MOVE_VIEW + */ + void ReflowChild(nsIFrame* aChildFrame, + nsPresContext* aPresContext, + ReflowOutput& aDesiredSize, + const ReflowInput& aReflowInput, + const mozilla::WritingMode& aWM, + const mozilla::LogicalPoint& aPos, + const nsSize& aContainerSize, + uint32_t 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 aContainerSize size of the border-box of the containing frame + * + * Flags: + * NS_FRAME_NO_MOVE_FRAME - don't move the frame. aPos is 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 + */ + static void FinishReflowChild(nsIFrame* aKidFrame, + nsPresContext* aPresContext, + const ReflowOutput& aDesiredSize, + const ReflowInput* aReflowInput, + const mozilla::WritingMode& aWM, + const mozilla::LogicalPoint& aPos, + const nsSize& aContainerSize, + uint32_t 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, + uint32_t aFlags, + nsReflowStatus& aStatus, + nsOverflowContinuationTracker* aTracker = nullptr); + + static void FinishReflowChild(nsIFrame* aKidFrame, + nsPresContext* aPresContext, + const ReflowOutput& aDesiredSize, + const ReflowInput* aReflowInput, + nscoord aX, + nscoord aY, + uint32_t aFlags); + + static void PositionChildViews(nsIFrame* aFrame); + + // ========================================================================== + /* Overflow containers are continuation frames that hold overflow. They + * are created when the frame runs out of computed height, but still has + * too much content to fit in the availableHeight. 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 height, but returns + * either an NS_FRAME_NOT_COMPLETE or NS_FRAME_OVERFLOW_INCOMPLETE reflow + * status 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 availableHeight, 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. + */ + + 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 NS_FRAME_OVERFLOW_IS_INCOMPLETE flag (along with + * NS_FRAME_REFLOW_NEXTINFLOW 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 + */ + void ReflowOverflowContainerChildren(nsPresContext* aPresContext, + const ReflowInput& aReflowInput, + nsOverflowAreas& aOverflowRects, + uint32_t aFlags, + nsReflowStatus& aStatus, + ChildFrameMerger aMergeFunc = + DefaultChildFrameMerge); + + /** + * 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. + * Returns NS_ERROR_UNEXPECTED if aChild wasn't found on any of the lists + * mentioned above. + */ + virtual nsresult 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 nsRect& aDirtyRect, + 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 nsRect& aDirtyRect, + const nsDisplayListSet& aLists) override; + + static void PlaceFrameView(nsIFrame* aFrame) + { + if (aFrame->HasView()) + nsContainerFrame::PositionFrameView(aFrame); + else + nsContainerFrame::PositionChildViews(aFrame); + } + + static bool FrameStartsCounterScope(nsIFrame* aFrame); + + /** + * Renumber the list of the counter scope started by this frame, if any. + * If this returns true, the frame it's called on should get the + * NS_FRAME_HAS_DIRTY_CHILDREN bit set on it by the caller; either directly + * if it's already in reflow, or via calling FrameNeedsReflow() to schedule + * a reflow. + */ + bool RenumberList(); + + /** + * Renumber this frame if it's a list-item, then call RenumberChildFrames. + * @param aOrdinal Ordinal number to start counting at. + * Modifies this number for each associated list + * item. Changes in the numbering due to setting + * the |value| attribute are included if |aForCounting| + * is false. This value is both an input and output + * of this function, with the output value being the + * next ordinal number to be used. + * @param aDepth Current depth in frame tree from root list element. + * @param aIncrement Amount to increase by after visiting each associated + * list item, unless overridden by |value|. + * @param aForCounting Whether we are counting the elements or actually + * restyling them. When true, this simply visits all children, + * ignoring |<li value="..">| changes, effectively counting them + * and storing the result in |aOrdinal|. This is useful for + * |<ol reversed>|, where we need to count the number of + * applicable child list elements before numbering. When false, + * this will restyle all applicable descendants, and the next + * ordinal value will be stored in |aOrdinal|, taking into account + * any changes from |<li value="..">|. + */ + bool RenumberFrameAndDescendants(int32_t* aOrdinal, + int32_t aDepth, + int32_t aIncrement, + bool aForCounting) override; + /** + * Renumber the child frames using RenumberFrameAndDescendants. + * See RenumberFrameAndDescendants for description of parameters. + */ + virtual bool RenumberChildFrames(int32_t* aOrdinal, + int32_t aDepth, + int32_t aIncrement, + bool aForCounting); + + /** + * 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 uint16_t 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) + + typedef PropertyDescriptor<nsFrameList> FrameListPropertyDescriptor; + + NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OverflowProperty) + NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OverflowContainersProperty) + NS_DECLARE_FRAME_PROPERTY_FRAMELIST(ExcessOverflowContainersProperty) + NS_DECLARE_FRAME_PROPERTY_FRAMELIST(BackdropProperty) + +#ifdef DEBUG + // Use this to suppress the CRAZY_SIZE assertions. + NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(DebugReflowingWithInfiniteISize, bool) + bool IsCrazySizeAssertSuppressed() const { + return Properties().Get(DebugReflowingWithInfiniteISize()); + } +#endif + +protected: + explicit nsContainerFrame(nsStyleContext* aContext) : nsSplittableFrame(aContext) {} + ~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); + + /** + * 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 nsRect& aDirtyRect, + const nsDisplayListSet& aLists, + uint32_t aFlags = 0); + + /** + * A version of BuildDisplayList that use DISPLAY_CHILD_INLINE. + * Intended as a convenience for derived classes. + */ + void BuildDisplayListForInline(nsDisplayListBuilder* aBuilder, + const nsRect& aDirtyRect, + const nsDisplayListSet& aLists) { + DisplayBorderBackgroundOutline(aBuilder, aLists); + BuildDisplayListForNonBlockChildren(aBuilder, aDirtyRect, aLists, + DISPLAY_CHILD_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. Can return null if there are no + * overflow frames. 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 nonempty. + */ + inline nsFrameList* GetOverflowFrames() const; + + /** + * As GetOverflowFrames, but removes the overflow frames property. 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 nonempty. The + * recommended way to use this function it to assign its return value + * into an AutoFrameListPtr. + */ + inline nsFrameList* StealOverflowFrames(); + + /** + * Set the overflow list. aOverflowFrames must not be an empty list. + */ + void SetOverflowFrames(const nsFrameList& aOverflowFrames); + + /** + * Destroy the overflow list, which must be empty. + */ + inline void DestroyOverflowList(); + + /** + * 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(); + + /** + * 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 PushChildren(nsIFrame* aFromChild, nsIFrame* aPrevSibling); + + // ========================================================================== + /* + * 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); + + // ========================================================================== + /* + * Convenience methods for nsFrameLists stored in the + * PresContext's proptable + */ + + /** + * Get the PresContext-stored nsFrameList named aPropID for this frame. + * May return null. + */ + nsFrameList* GetPropTableFrames(FrameListPropertyDescriptor aProperty) const; + + /** + * Remove and return the PresContext-stored nsFrameList named aPropID for + * this frame. May return null. + */ + nsFrameList* RemovePropTableFrames(FrameListPropertyDescriptor aProperty); + + /** + * Set the PresContext-stored nsFrameList named aPropID for this frame + * to the given aFrameList, which must not be null. + */ + void SetPropTableFrames(nsFrameList* aFrameList, + FrameListPropertyDescriptor aProperty); + + /** + * 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, + nsIPresShell* aPresShell, + mozilla::FramePropertyTable* aPropTable, + 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(); + + // ========================================================================== + + 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. + */ + +#define IS_TRUE_OVERFLOW_CONTAINER(frame) \ + ( (frame->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) \ + && !( (frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) && \ + frame->IsAbsolutelyPositioned() ) ) +//XXXfr This check isn't quite correct, because it doesn't handle cases +// where the out-of-flow has overflow.. but that's rare. +// We'll need to revisit the way abspos continuations are handled later +// for various reasons, this detail is one of them. 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 NS_FRAME_OVERFLOW_INCOMPLETE + * 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) + { + NS_PRECONDITION(aChild, "null ptr"); + if (aChild == mSentry) { + StepForward(); + NS_MergeReflowStatusInto(&aReflowStatus, NS_FRAME_OVERFLOW_INCOMPLETE); + } + } + +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; +}; + +inline +nsFrameList* +nsContainerFrame::GetOverflowFrames() const +{ + nsFrameList* list = Properties().Get(OverflowProperty()); + NS_ASSERTION(!list || !list->IsEmpty(), "Unexpected empty overflow list"); + return list; +} + +inline +nsFrameList* +nsContainerFrame::StealOverflowFrames() +{ + nsFrameList* list = Properties().Remove(OverflowProperty()); + NS_ASSERTION(!list || !list->IsEmpty(), "Unexpected empty overflow list"); + return list; +} + +inline void +nsContainerFrame::DestroyOverflowList() +{ + nsFrameList* list = RemovePropTableFrames(OverflowProperty()); + MOZ_ASSERT(list && list->IsEmpty()); + list->Delete(PresContext()->PresShell()); +} + +#endif /* nsContainerFrame_h___ */ |