michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* base class #1 for rendering objects that have child lists */ michael@0: michael@0: #ifndef nsContainerFrame_h___ michael@0: #define nsContainerFrame_h___ michael@0: michael@0: #include "mozilla/Attributes.h" michael@0: #include "nsSplittableFrame.h" michael@0: #include "nsFrameList.h" michael@0: #include "nsLayoutUtils.h" michael@0: michael@0: // Option flags for ReflowChild() and FinishReflowChild() michael@0: // member functions michael@0: #define NS_FRAME_NO_MOVE_VIEW 0x0001 michael@0: #define NS_FRAME_NO_MOVE_FRAME (0x0002 | NS_FRAME_NO_MOVE_VIEW) michael@0: #define NS_FRAME_NO_SIZE_VIEW 0x0004 michael@0: #define NS_FRAME_NO_VISIBILITY 0x0008 michael@0: // Only applies to ReflowChild; if true, don't delete the next-in-flow, even michael@0: // if the reflow is fully complete. michael@0: #define NS_FRAME_NO_DELETE_NEXT_IN_FLOW_CHILD 0x0010 michael@0: michael@0: class nsOverflowContinuationTracker; michael@0: namespace mozilla { michael@0: class FramePropertyTable; michael@0: } michael@0: michael@0: // Some macros for container classes to do sanity checking on michael@0: // width/height/x/y values computed during reflow. michael@0: // NOTE: AppUnitsPerCSSPixel value hardwired here to remove the michael@0: // dependency on nsDeviceContext.h. It doesn't matter if it's a michael@0: // little off. michael@0: #ifdef DEBUG michael@0: #define CRAZY_COORD (1000000*60) michael@0: #define CRAZY_SIZE(_x) (((_x) < -CRAZY_COORD) || ((_x) > CRAZY_COORD)) michael@0: #endif michael@0: michael@0: /** michael@0: * Implementation of a container frame. michael@0: */ michael@0: class nsContainerFrame : public nsSplittableFrame michael@0: { michael@0: public: michael@0: NS_DECL_FRAMEARENA_HELPERS michael@0: NS_DECL_QUERYFRAME_TARGET(nsContainerFrame) michael@0: NS_DECL_QUERYFRAME michael@0: michael@0: // nsIFrame overrides michael@0: virtual void Init(nsIContent* aContent, michael@0: nsIFrame* aParent, michael@0: nsIFrame* aPrevInFlow) MOZ_OVERRIDE; michael@0: virtual nsresult SetInitialChildList(ChildListID aListID, michael@0: nsFrameList& aChildList) MOZ_OVERRIDE; michael@0: virtual nsresult AppendFrames(ChildListID aListID, michael@0: nsFrameList& aFrameList) MOZ_OVERRIDE; michael@0: virtual nsresult InsertFrames(ChildListID aListID, michael@0: nsIFrame* aPrevFrame, michael@0: nsFrameList& aFrameList) MOZ_OVERRIDE; michael@0: virtual nsresult RemoveFrame(ChildListID aListID, michael@0: nsIFrame* aOldFrame) MOZ_OVERRIDE; michael@0: michael@0: virtual const nsFrameList& GetChildList(ChildListID aList) const MOZ_OVERRIDE; michael@0: virtual void GetChildLists(nsTArray* aLists) const MOZ_OVERRIDE; michael@0: virtual void DestroyFrom(nsIFrame* aDestructRoot) MOZ_OVERRIDE; michael@0: virtual void ChildIsDirty(nsIFrame* aChild) MOZ_OVERRIDE; michael@0: michael@0: virtual bool IsLeaf() const MOZ_OVERRIDE; michael@0: virtual FrameSearchResult PeekOffsetNoAmount(bool aForward, int32_t* aOffset) MOZ_OVERRIDE; michael@0: virtual FrameSearchResult PeekOffsetCharacter(bool aForward, int32_t* aOffset, michael@0: bool aRespectClusters = true) MOZ_OVERRIDE; michael@0: michael@0: #ifdef DEBUG_FRAME_DUMP michael@0: void List(FILE* out = stderr, const char* aPrefix = "", uint32_t aFlags = 0) const MOZ_OVERRIDE; michael@0: #endif michael@0: michael@0: // nsContainerFrame methods michael@0: michael@0: /** michael@0: * Helper method to create next-in-flows if necessary. If aFrame michael@0: * already has a next-in-flow then this method does michael@0: * nothing. Otherwise, a new continuation frame is created and michael@0: * linked into the flow. In addition, the new frame is inserted michael@0: * into the principal child list after aFrame. michael@0: * @note calling this method on a block frame is illegal. Use michael@0: * nsBlockFrame::CreateContinuationFor() instead. michael@0: * @param aNextInFlowResult will contain the next-in-flow michael@0: * if and only if one is created. If a next-in-flow already michael@0: * exists aNextInFlowResult is set to nullptr. michael@0: * @return NS_OK if a next-in-flow already exists or is successfully created. michael@0: */ michael@0: nsresult CreateNextInFlow(nsIFrame* aFrame, nsIFrame*& aNextInFlowResult); michael@0: michael@0: /** michael@0: * Delete aNextInFlow and its next-in-flows. michael@0: * @param aDeletingEmptyFrames if set, then the reflow for aNextInFlow's michael@0: * content was complete before aNextInFlow, so aNextInFlow and its michael@0: * next-in-flows no longer map any real content. michael@0: */ michael@0: virtual void DeleteNextInFlowChild(nsIFrame* aNextInFlow, michael@0: bool aDeletingEmptyFrames); michael@0: michael@0: /** michael@0: * Helper method to wrap views around frames. Used by containers michael@0: * under special circumstances (can be used by leaf frames as well) michael@0: */ michael@0: static void CreateViewForFrame(nsIFrame* aFrame, michael@0: bool aForce); michael@0: michael@0: // Positions the frame's view based on the frame's origin michael@0: static void PositionFrameView(nsIFrame* aKidFrame); michael@0: michael@0: static nsresult ReparentFrameView(nsIFrame* aChildFrame, michael@0: nsIFrame* aOldParentFrame, michael@0: nsIFrame* aNewParentFrame); michael@0: michael@0: static nsresult ReparentFrameViewList(const nsFrameList& aChildFrameList, michael@0: nsIFrame* aOldParentFrame, michael@0: nsIFrame* aNewParentFrame); michael@0: michael@0: // Set the view's size and position after its frame has been reflowed. michael@0: // michael@0: // Flags: michael@0: // NS_FRAME_NO_MOVE_VIEW - don't position the frame's view. Set this if you michael@0: // don't want to automatically sync the frame and view michael@0: // NS_FRAME_NO_SIZE_VIEW - don't size the view michael@0: static void SyncFrameViewAfterReflow(nsPresContext* aPresContext, michael@0: nsIFrame* aFrame, michael@0: nsView* aView, michael@0: const nsRect& aVisualOverflowArea, michael@0: uint32_t aFlags = 0); michael@0: michael@0: // Syncs properties to the top level view and window, like transparency and michael@0: // shadow. michael@0: static void SyncWindowProperties(nsPresContext* aPresContext, michael@0: nsIFrame* aFrame, michael@0: nsView* aView, michael@0: nsRenderingContext* aRC = nullptr); michael@0: michael@0: // Sets the view's attributes from the frame style. michael@0: // - visibility michael@0: // - clip michael@0: // Call this when one of these styles changes or when the view has just michael@0: // been created. michael@0: // @param aStyleContext can be null, in which case the frame's style context is used michael@0: static void SyncFrameViewProperties(nsPresContext* aPresContext, michael@0: nsIFrame* aFrame, michael@0: nsStyleContext* aStyleContext, michael@0: nsView* aView, michael@0: uint32_t aFlags = 0); michael@0: michael@0: /** michael@0: * Converts the minimum and maximum sizes given in inner window app units to michael@0: * outer window device pixel sizes and assigns these constraints to the widget. michael@0: * michael@0: * @param aPresContext pres context michael@0: * @param aWidget widget for this frame michael@0: * @param minimum size of the window in app units michael@0: * @param maxmimum size of the window in app units michael@0: */ michael@0: static void SetSizeConstraints(nsPresContext* aPresContext, michael@0: nsIWidget* aWidget, michael@0: const nsSize& aMinSize, michael@0: const nsSize& aMaxSize); michael@0: michael@0: // Used by both nsInlineFrame and nsFirstLetterFrame. michael@0: void DoInlineIntrinsicWidth(nsRenderingContext *aRenderingContext, michael@0: InlineIntrinsicWidthData *aData, michael@0: nsLayoutUtils::IntrinsicWidthType aType); michael@0: michael@0: /** michael@0: * This is the CSS block concept of computing 'auto' widths, which most michael@0: * classes derived from nsContainerFrame want. michael@0: */ michael@0: virtual nsSize ComputeAutoSize(nsRenderingContext *aRenderingContext, michael@0: nsSize aCBSize, nscoord aAvailableWidth, michael@0: nsSize aMargin, nsSize aBorder, michael@0: nsSize aPadding, bool aShrinkWrap) MOZ_OVERRIDE; michael@0: michael@0: /** michael@0: * Invokes the WillReflow() function, positions the frame and its view (if michael@0: * requested), and then calls Reflow(). If the reflow succeeds and the child michael@0: * frame is complete, deletes any next-in-flows using DeleteNextInFlowChild() michael@0: * michael@0: * Flags: michael@0: * NS_FRAME_NO_MOVE_VIEW - don't position the frame's view. Set this if you michael@0: * don't want to automatically sync the frame and view michael@0: * NS_FRAME_NO_MOVE_FRAME - don't move the frame. aX and aY are ignored in this michael@0: * case. Also implies NS_FRAME_NO_MOVE_VIEW michael@0: */ michael@0: nsresult ReflowChild(nsIFrame* aKidFrame, michael@0: nsPresContext* aPresContext, michael@0: nsHTMLReflowMetrics& aDesiredSize, michael@0: const nsHTMLReflowState& aReflowState, michael@0: nscoord aX, michael@0: nscoord aY, michael@0: uint32_t aFlags, michael@0: nsReflowStatus& aStatus, michael@0: nsOverflowContinuationTracker* aTracker = nullptr); michael@0: michael@0: /** michael@0: * The second half of frame reflow. Does the following: michael@0: * - sets the frame's bounds michael@0: * - sizes and positions (if requested) the frame's view. If the frame's final michael@0: * position differs from the current position and the frame itself does not michael@0: * have a view, then any child frames with views are positioned so they stay michael@0: * in sync michael@0: * - sets the view's visibility, opacity, content transparency, and clip michael@0: * - invoked the DidReflow() function michael@0: * michael@0: * Flags: michael@0: * NS_FRAME_NO_MOVE_FRAME - don't move the frame. aX and aY are ignored in this michael@0: * case. Also implies NS_FRAME_NO_MOVE_VIEW michael@0: * NS_FRAME_NO_MOVE_VIEW - don't position the frame's view. Set this if you michael@0: * don't want to automatically sync the frame and view michael@0: * NS_FRAME_NO_SIZE_VIEW - don't size the frame's view michael@0: */ michael@0: static nsresult FinishReflowChild(nsIFrame* aKidFrame, michael@0: nsPresContext* aPresContext, michael@0: const nsHTMLReflowMetrics& aDesiredSize, michael@0: const nsHTMLReflowState* aReflowState, michael@0: nscoord aX, michael@0: nscoord aY, michael@0: uint32_t aFlags); michael@0: michael@0: michael@0: static void PositionChildViews(nsIFrame* aFrame); michael@0: michael@0: // ========================================================================== michael@0: /* Overflow containers are continuation frames that hold overflow. They michael@0: * are created when the frame runs out of computed height, but still has michael@0: * too much content to fit in the availableHeight. The parent creates a michael@0: * continuation as usual, but marks it as NS_FRAME_IS_OVERFLOW_CONTAINER michael@0: * and adds it to its next-in-flow's overflow container list, either by michael@0: * adding it directly or by putting it in its own excess overflow containers michael@0: * list (to be drained by the next-in-flow when it calls michael@0: * ReflowOverflowContainerChildren). The parent continues reflow as if michael@0: * the frame was complete once it ran out of computed height, but returns michael@0: * either an NS_FRAME_NOT_COMPLETE or NS_FRAME_OVERFLOW_INCOMPLETE reflow michael@0: * status to request a next-in-flow. The parent's next-in-flow is then michael@0: * responsible for calling ReflowOverflowContainerChildren to (drain and) michael@0: * reflow these overflow continuations. Overflow containers do not affect michael@0: * other frames' size or position during reflow (but do affect their michael@0: * parent's overflow area). michael@0: * michael@0: * Overflow container continuations are different from normal continuations michael@0: * in that michael@0: * - more than one child of the frame can have its next-in-flow broken michael@0: * off and pushed into the frame's next-in-flow michael@0: * - new continuations may need to be spliced into the middle of the list michael@0: * or deleted continuations slipped out michael@0: * e.g. A, B, C are all fixed-size containers on one page, all have michael@0: * overflow beyond availableHeight, and content is dynamically added michael@0: * and removed from B michael@0: * As a result, it is not possible to simply prepend the new continuations michael@0: * to the old list as with the overflowProperty mechanism. To avoid michael@0: * complicated list splicing, the code assumes only one overflow containers michael@0: * list exists for a given frame: either its own overflowContainersProperty michael@0: * or its prev-in-flow's excessOverflowContainersProperty, not both. michael@0: * michael@0: * The nsOverflowContinuationTracker helper class should be used for tracking michael@0: * overflow containers and adding them to the appropriate list. michael@0: * See nsBlockFrame::Reflow for a sample implementation. michael@0: */ michael@0: michael@0: friend class nsOverflowContinuationTracker; michael@0: michael@0: /** michael@0: * Reflow overflow container children. They are invisible to normal reflow michael@0: * (i.e. don't affect sizing or placement of other children) and inherit michael@0: * width and horizontal position from their prev-in-flow. michael@0: * michael@0: * This method michael@0: * 1. Pulls excess overflow containers from the prev-in-flow and adds michael@0: * them to our overflow container list michael@0: * 2. Reflows all our overflow container kids michael@0: * 3. Expands aOverflowRect as necessary to accomodate these children. michael@0: * 4. Sets aStatus's NS_FRAME_OVERFLOW_IS_INCOMPLETE flag (along with michael@0: * NS_FRAME_REFLOW_NEXTINFLOW as necessary) if any overflow children michael@0: * are incomplete and michael@0: * 5. Prepends a list of their continuations to our excess overflow michael@0: * container list, to be drained into our next-in-flow when it is michael@0: * reflowed. michael@0: * michael@0: * The caller is responsible for tracking any new overflow container michael@0: * continuations it makes, removing them from its child list, and michael@0: * making sure they are stored properly in the overflow container lists. michael@0: * The nsOverflowContinuationTracker helper class should be used for this. michael@0: * michael@0: * (aFlags just gets passed through to ReflowChild) michael@0: */ michael@0: nsresult ReflowOverflowContainerChildren(nsPresContext* aPresContext, michael@0: const nsHTMLReflowState& aReflowState, michael@0: nsOverflowAreas& aOverflowRects, michael@0: uint32_t aFlags, michael@0: nsReflowStatus& aStatus); michael@0: michael@0: /** michael@0: * Move any frames on our overflow list to the end of our principal list. michael@0: * @return true if there were any overflow frames michael@0: */ michael@0: virtual bool DrainSelfOverflowList() MOZ_OVERRIDE; michael@0: michael@0: /** michael@0: * Removes aChild without destroying it and without requesting reflow. michael@0: * Continuations are not affected. Checks the primary and overflow michael@0: * or overflow containers and excess overflow containers lists, depending michael@0: * on whether the NS_FRAME_IS_OVERFLOW_CONTAINER flag is set. Does not michael@0: * check any other auxiliary lists. michael@0: * Returns NS_ERROR_UNEXPECTED if we failed to remove aChild. michael@0: * Returns other error codes if we failed to put back a proptable list. michael@0: * If aForceNormal is true, only checks the primary and overflow lists michael@0: * even when the NS_FRAME_IS_OVERFLOW_CONTAINER flag is set. michael@0: */ michael@0: virtual nsresult StealFrame(nsIFrame* aChild, michael@0: bool aForceNormal = false); michael@0: michael@0: /** michael@0: * Removes the next-siblings of aChild without destroying them and without michael@0: * requesting reflow. Checks the principal and overflow lists (not michael@0: * overflow containers / excess overflow containers). Does not check any michael@0: * other auxiliary lists. michael@0: * @param aChild a child frame or nullptr michael@0: * @return If aChild is non-null, the next-siblings of aChild, if any. michael@0: * If aChild is null, all child frames on the principal list, if any. michael@0: */ michael@0: nsFrameList StealFramesAfter(nsIFrame* aChild); michael@0: michael@0: /** michael@0: * Add overflow containers to the display list michael@0: */ michael@0: void DisplayOverflowContainers(nsDisplayListBuilder* aBuilder, michael@0: const nsRect& aDirtyRect, michael@0: const nsDisplayListSet& aLists); michael@0: michael@0: /** michael@0: * Builds display lists for the children. The background michael@0: * of each child is placed in the Content() list (suitable for inline michael@0: * children and other elements that behave like inlines, michael@0: * but not for in-flow block children of blocks). DOES NOT michael@0: * paint the background/borders/outline of this frame. This should michael@0: * probably be avoided and eventually removed. It's currently here michael@0: * to emulate what nsContainerFrame::Paint did. michael@0: */ michael@0: virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder, michael@0: const nsRect& aDirtyRect, michael@0: const nsDisplayListSet& aLists) MOZ_OVERRIDE; michael@0: michael@0: /** michael@0: * Destructor function for the proptable-stored framelists -- michael@0: * it should never be called. michael@0: */ michael@0: static void DestroyFrameList(void* aPropertyValue) michael@0: { michael@0: MOZ_ASSERT(false, "The owning frame should destroy its nsFrameList props"); michael@0: } michael@0: michael@0: static void PlaceFrameView(nsIFrame* aFrame) michael@0: { michael@0: if (aFrame->HasView()) michael@0: nsContainerFrame::PositionFrameView(aFrame); michael@0: else michael@0: nsContainerFrame::PositionChildViews(aFrame); michael@0: } michael@0: michael@0: #define NS_DECLARE_FRAME_PROPERTY_FRAMELIST(prop) \ michael@0: NS_DECLARE_FRAME_PROPERTY(prop, nsContainerFrame::DestroyFrameList) michael@0: michael@0: NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OverflowProperty) michael@0: NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OverflowContainersProperty) michael@0: NS_DECLARE_FRAME_PROPERTY_FRAMELIST(ExcessOverflowContainersProperty) michael@0: michael@0: protected: michael@0: nsContainerFrame(nsStyleContext* aContext) : nsSplittableFrame(aContext) {} michael@0: ~nsContainerFrame(); michael@0: michael@0: /** michael@0: * Helper for DestroyFrom. DestroyAbsoluteFrames is called before michael@0: * destroying frames on lists that can contain placeholders. michael@0: * Derived classes must do that too, if they destroy such frame lists. michael@0: * See nsBlockFrame::DestroyFrom for an example. michael@0: */ michael@0: void DestroyAbsoluteFrames(nsIFrame* aDestructRoot); michael@0: michael@0: /** michael@0: * Builds a display list for non-block children that behave like michael@0: * inlines. This puts the background of each child into the michael@0: * Content() list (suitable for inline children but not for michael@0: * in-flow block children of blocks). michael@0: * @param aForcePseudoStack forces each child into a pseudo-stacking-context michael@0: * so its background and all other display items (except for positioned michael@0: * display items) go into the Content() list. michael@0: */ michael@0: void BuildDisplayListForNonBlockChildren(nsDisplayListBuilder* aBuilder, michael@0: const nsRect& aDirtyRect, michael@0: const nsDisplayListSet& aLists, michael@0: uint32_t aFlags = 0); michael@0: michael@0: /** michael@0: * A version of BuildDisplayList that use DISPLAY_CHILD_INLINE. michael@0: * Intended as a convenience for derived classes. michael@0: */ michael@0: void BuildDisplayListForInline(nsDisplayListBuilder* aBuilder, michael@0: const nsRect& aDirtyRect, michael@0: const nsDisplayListSet& aLists) { michael@0: DisplayBorderBackgroundOutline(aBuilder, aLists); michael@0: BuildDisplayListForNonBlockChildren(aBuilder, aDirtyRect, aLists, michael@0: DISPLAY_CHILD_INLINE); michael@0: } michael@0: michael@0: michael@0: // ========================================================================== michael@0: /* Overflow Frames are frames that did not fit and must be pulled by michael@0: * our next-in-flow during its reflow. (The same concept for overflow michael@0: * containers is called "excess frames". We should probably make the michael@0: * names match.) michael@0: */ michael@0: michael@0: /** michael@0: * Get the frames on the overflow list. Can return null if there are no michael@0: * overflow frames. The caller does NOT take ownership of the list; it's michael@0: * still owned by this frame. A non-null return value indicates that the michael@0: * list is nonempty. michael@0: */ michael@0: inline nsFrameList* GetOverflowFrames() const; michael@0: michael@0: /** michael@0: * As GetOverflowFrames, but removes the overflow frames property. The michael@0: * caller is responsible for deleting nsFrameList and either passing michael@0: * ownership of the frames to someone else or destroying the frames. michael@0: * A non-null return value indicates that the list is nonempty. The michael@0: * recommended way to use this function it to assign its return value michael@0: * into an AutoFrameListPtr. michael@0: */ michael@0: inline nsFrameList* StealOverflowFrames(); michael@0: michael@0: /** michael@0: * Set the overflow list. aOverflowFrames must not be an empty list. michael@0: */ michael@0: void SetOverflowFrames(const nsFrameList& aOverflowFrames); michael@0: michael@0: /** michael@0: * Destroy the overflow list, which must be empty. michael@0: */ michael@0: inline void DestroyOverflowList(); michael@0: michael@0: /** michael@0: * Moves any frames on both the prev-in-flow's overflow list and the michael@0: * receiver's overflow to the receiver's child list. michael@0: * michael@0: * Resets the overlist pointers to nullptr, and updates the receiver's child michael@0: * count and content mapping. michael@0: * michael@0: * @return true if any frames were moved and false otherwise michael@0: */ michael@0: bool MoveOverflowToChildList(); michael@0: michael@0: /** michael@0: * Push aFromChild and its next siblings to the next-in-flow. Change michael@0: * the geometric parent of each frame that's pushed. If there is no michael@0: * next-in-flow the frames are placed on the overflow list (and the michael@0: * geometric parent is left unchanged). michael@0: * michael@0: * Updates the next-in-flow's child count. Does not update the michael@0: * pusher's child count. michael@0: * michael@0: * @param aFromChild the first child frame to push. It is disconnected from michael@0: * aPrevSibling michael@0: * @param aPrevSibling aFromChild's previous sibling. Must not be null. michael@0: * It's an error to push a parent's first child frame michael@0: */ michael@0: void PushChildren(nsIFrame* aFromChild, nsIFrame* aPrevSibling); michael@0: michael@0: // ========================================================================== michael@0: /* michael@0: * Convenience methods for nsFrameLists stored in the michael@0: * PresContext's proptable michael@0: */ michael@0: michael@0: /** michael@0: * Get the PresContext-stored nsFrameList named aPropID for this frame. michael@0: * May return null. michael@0: */ michael@0: nsFrameList* GetPropTableFrames(const FramePropertyDescriptor* aProperty) const; michael@0: michael@0: /** michael@0: * Remove and return the PresContext-stored nsFrameList named aPropID for michael@0: * this frame. May return null. michael@0: */ michael@0: nsFrameList* RemovePropTableFrames(const FramePropertyDescriptor* aProperty); michael@0: michael@0: /** michael@0: * Set the PresContext-stored nsFrameList named aPropID for this frame michael@0: * to the given aFrameList, which must not be null. michael@0: */ michael@0: void SetPropTableFrames(nsFrameList* aFrameList, michael@0: const FramePropertyDescriptor* aProperty); michael@0: michael@0: /** michael@0: * Safely destroy the frames on the nsFrameList stored on aProp for this michael@0: * frame then remove the property and delete the frame list. michael@0: * Nothing happens if the property doesn't exist. michael@0: */ michael@0: void SafelyDestroyFrameListProp(nsIFrame* aDestructRoot, michael@0: nsIPresShell* aPresShell, michael@0: mozilla::FramePropertyTable* aPropTable, michael@0: const FramePropertyDescriptor* aProp); michael@0: michael@0: // ========================================================================== michael@0: michael@0: nsFrameList mFrames; michael@0: }; michael@0: michael@0: // ========================================================================== michael@0: /* The out-of-flow-related code below is for a hacky way of splitting michael@0: * absolutely-positioned frames. Basically what we do is split the frame michael@0: * in nsAbsoluteContainingBlock and pretend the continuation is an overflow michael@0: * container. This isn't an ideal solution, but it lets us print the content michael@0: * at least. See bug 154892. michael@0: */ michael@0: michael@0: #define IS_TRUE_OVERFLOW_CONTAINER(frame) \ michael@0: ( (frame->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) \ michael@0: && !( (frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) && \ michael@0: frame->IsAbsolutelyPositioned() ) ) michael@0: //XXXfr This check isn't quite correct, because it doesn't handle cases michael@0: // where the out-of-flow has overflow.. but that's rare. michael@0: // We'll need to revisit the way abspos continuations are handled later michael@0: // for various reasons, this detail is one of them. See bug 154892 michael@0: michael@0: /** michael@0: * Helper class for tracking overflow container continuations during reflow. michael@0: * michael@0: * A frame is related to two sets of overflow containers: those that /are/ michael@0: * its own children, and those that are /continuations/ of its children. michael@0: * This tracker walks through those continuations (the frame's NIF's children) michael@0: * and their prev-in-flows (a subset of the frame's normal and overflow michael@0: * container children) in parallel. It allows the reflower to synchronously michael@0: * walk its overflow continuations while it loops through and reflows its michael@0: * children. This makes it possible to insert new continuations at the correct michael@0: * place in the overflow containers list. michael@0: * michael@0: * The reflower is expected to loop through its children in the same order it michael@0: * looped through them the last time (if there was a last time). michael@0: * For each child, the reflower should either michael@0: * - call Skip for the child if was not reflowed in this pass michael@0: * - call Insert for the overflow continuation if the child was reflowed michael@0: * but has incomplete overflow michael@0: * - call Finished for the child if it was reflowed in this pass but michael@0: * is either complete or has a normal next-in-flow. This call can michael@0: * be skipped if the child did not previously have an overflow michael@0: * continuation. michael@0: */ michael@0: class nsOverflowContinuationTracker { michael@0: public: michael@0: /** michael@0: * Initializes an nsOverflowContinuationTracker to help track overflow michael@0: * continuations of aFrame's children. Typically invoked on 'this'. michael@0: * michael@0: * aWalkOOFFrames determines whether the walker skips out-of-flow frames michael@0: * or skips non-out-of-flow frames. michael@0: * michael@0: * Don't set aSkipOverflowContainerChildren to false unless you plan michael@0: * to walk your own overflow container children. (Usually they are handled michael@0: * by calling ReflowOverflowContainerChildren.) aWalkOOFFrames is ignored michael@0: * if aSkipOverflowContainerChildren is false. michael@0: */ michael@0: nsOverflowContinuationTracker(nsContainerFrame* aFrame, michael@0: bool aWalkOOFFrames, michael@0: bool aSkipOverflowContainerChildren = true); michael@0: /** michael@0: * This function adds an overflow continuation to our running list and michael@0: * sets its NS_FRAME_IS_OVERFLOW_CONTAINER flag. michael@0: * michael@0: * aReflowStatus should preferably be specific to the recently-reflowed michael@0: * child and not influenced by any of its siblings' statuses. This michael@0: * function sets the NS_FRAME_IS_DIRTY bit on aOverflowCont if it needs michael@0: * to be reflowed. (Its need for reflow depends on changes to its michael@0: * prev-in-flow, not to its parent--for whom it is invisible, reflow-wise.) michael@0: * michael@0: * The caller MUST disconnect the frame from its parent's child list michael@0: * if it was not previously an NS_FRAME_IS_OVERFLOW_CONTAINER (because michael@0: * StealFrame is much more inefficient than disconnecting in place michael@0: * during Reflow, which the caller is able to do but we are not). michael@0: * michael@0: * The caller MUST NOT disconnect the frame from its parent's michael@0: * child list if it is already an NS_FRAME_IS_OVERFLOW_CONTAINER. michael@0: * (In this case we will disconnect and reconnect it ourselves.) michael@0: */ michael@0: nsresult Insert(nsIFrame* aOverflowCont, michael@0: nsReflowStatus& aReflowStatus); michael@0: /** michael@0: * Begin/EndFinish() must be called for each child that is reflowed michael@0: * but no longer has an overflow continuation. (It may be called for michael@0: * other children, but in that case has no effect.) It increments our michael@0: * walker and makes sure we drop any dangling pointers to its michael@0: * next-in-flow. This function MUST be called before stealing or michael@0: * deleting aChild's next-in-flow. michael@0: * The AutoFinish helper object does that for you. Use it like so: michael@0: * if (kidNextInFlow) { michael@0: * nsOverflowContinuationTracker::AutoFinish fini(tracker, kid); michael@0: * ... DeleteNextInFlowChild/StealFrame(kidNextInFlow) here ... michael@0: * } michael@0: */ michael@0: class MOZ_STACK_CLASS AutoFinish { michael@0: public: michael@0: AutoFinish(nsOverflowContinuationTracker* aTracker, nsIFrame* aChild) michael@0: : mTracker(aTracker), mChild(aChild) michael@0: { michael@0: if (mTracker) mTracker->BeginFinish(mChild); michael@0: } michael@0: ~AutoFinish() michael@0: { michael@0: if (mTracker) mTracker->EndFinish(mChild); michael@0: } michael@0: private: michael@0: nsOverflowContinuationTracker* mTracker; michael@0: nsIFrame* mChild; michael@0: }; michael@0: michael@0: /** michael@0: * This function should be called for each child that isn't reflowed. michael@0: * It increments our walker and sets the NS_FRAME_OVERFLOW_INCOMPLETE michael@0: * reflow flag if it encounters an overflow continuation so that our michael@0: * next-in-flow doesn't get prematurely deleted. It MUST be called on michael@0: * each unreflowed child that has an overflow container continuation; michael@0: * it MAY be called on other children, but it isn't necessary (doesn't michael@0: * do anything). michael@0: */ michael@0: void Skip(nsIFrame* aChild, nsReflowStatus& aReflowStatus) michael@0: { michael@0: NS_PRECONDITION(aChild, "null ptr"); michael@0: if (aChild == mSentry) { michael@0: StepForward(); michael@0: NS_MergeReflowStatusInto(&aReflowStatus, NS_FRAME_OVERFLOW_INCOMPLETE); michael@0: } michael@0: } michael@0: michael@0: private: michael@0: michael@0: /** michael@0: * @see class AutoFinish michael@0: */ michael@0: void BeginFinish(nsIFrame* aChild); michael@0: void EndFinish(nsIFrame* aChild); michael@0: michael@0: void SetupOverflowContList(); michael@0: void SetUpListWalker(); michael@0: void StepForward(); michael@0: michael@0: /* We hold a pointer to either the next-in-flow's overflow containers list michael@0: or, if that doesn't exist, our frame's excess overflow containers list. michael@0: We need to make sure that we drop that pointer if the list becomes michael@0: empty and is deleted elsewhere. */ michael@0: nsFrameList* mOverflowContList; michael@0: /* We hold a pointer to the most recently-reflowed child that has an michael@0: overflow container next-in-flow. We do this because it's a known michael@0: good point; this pointer won't be deleted on us. We can use it to michael@0: recover our place in the list. */ michael@0: nsIFrame* mPrevOverflowCont; michael@0: /* This is a pointer to the next overflow container's prev-in-flow, which michael@0: is (or should be) a child of our frame. When we hit this, we will need michael@0: to increment this walker to the next overflow container. */ michael@0: nsIFrame* mSentry; michael@0: /* Parent of all frames in mOverflowContList. If our mOverflowContList michael@0: is an excessOverflowContainersProperty, or null, then this is our frame michael@0: (the frame that was passed in to our constructor). Otherwise this is michael@0: that frame's next-in-flow, and our mOverflowContList is mParent's michael@0: overflowContainersProperty */ michael@0: nsContainerFrame* mParent; michael@0: /* Tells SetUpListWalker whether or not to walk us past any continuations michael@0: of overflow containers. aWalkOOFFrames is ignored when this is false. */ michael@0: bool mSkipOverflowContainerChildren; michael@0: /* Tells us whether to pay attention to OOF frames or non-OOF frames */ michael@0: bool mWalkOOFFrames; michael@0: }; michael@0: michael@0: inline michael@0: nsFrameList* michael@0: nsContainerFrame::GetOverflowFrames() const michael@0: { michael@0: nsFrameList* list = michael@0: static_cast(Properties().Get(OverflowProperty())); michael@0: NS_ASSERTION(!list || !list->IsEmpty(), "Unexpected empty overflow list"); michael@0: return list; michael@0: } michael@0: michael@0: inline michael@0: nsFrameList* michael@0: nsContainerFrame::StealOverflowFrames() michael@0: { michael@0: nsFrameList* list = michael@0: static_cast(Properties().Remove(OverflowProperty())); michael@0: NS_ASSERTION(!list || !list->IsEmpty(), "Unexpected empty overflow list"); michael@0: return list; michael@0: } michael@0: michael@0: inline void michael@0: nsContainerFrame::DestroyOverflowList() michael@0: { michael@0: nsFrameList* list = RemovePropTableFrames(OverflowProperty()); michael@0: MOZ_ASSERT(list && list->IsEmpty()); michael@0: list->Delete(PresContext()->PresShell()); michael@0: } michael@0: michael@0: #endif /* nsContainerFrame_h___ */