michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: // vim:cindent:ts=2:et:sw=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: /* michael@0: * rendering object for CSS display:block, inline-block, and list-item michael@0: * boxes, also used for various anonymous boxes michael@0: */ michael@0: michael@0: #ifndef nsBlockFrame_h___ michael@0: #define nsBlockFrame_h___ michael@0: michael@0: #include "nsContainerFrame.h" michael@0: #include "nsHTMLParts.h" michael@0: #include "nsLineBox.h" michael@0: #include "nsCSSPseudoElements.h" michael@0: #include "nsStyleSet.h" michael@0: #include "nsFloatManager.h" michael@0: michael@0: enum LineReflowStatus { michael@0: // The line was completely reflowed and fit in available width, and we should michael@0: // try to pull up content from the next line if possible. michael@0: LINE_REFLOW_OK, michael@0: // The line was completely reflowed and fit in available width, but we should michael@0: // not try to pull up content from the next line. michael@0: LINE_REFLOW_STOP, michael@0: // We need to reflow the line again at its current vertical position. The michael@0: // new reflow should not try to pull up any frames from the next line. michael@0: LINE_REFLOW_REDO_NO_PULL, michael@0: // We need to reflow the line again using the floats from its height michael@0: // this reflow, since its height made it hit floats that were not michael@0: // adjacent to its top. michael@0: LINE_REFLOW_REDO_MORE_FLOATS, michael@0: // We need to reflow the line again at a lower vertical postion where there michael@0: // may be more horizontal space due to different float configuration. michael@0: LINE_REFLOW_REDO_NEXT_BAND, michael@0: // The line did not fit in the available vertical space. Try pushing it to michael@0: // the next page or column if it's not the first line on the current page/column. michael@0: LINE_REFLOW_TRUNCATED michael@0: }; michael@0: michael@0: class nsBlockReflowState; michael@0: class nsBlockInFlowLineIterator; michael@0: class nsBulletFrame; michael@0: class nsFirstLineFrame; michael@0: michael@0: /** michael@0: * Some invariants: michael@0: * -- The overflow out-of-flows list contains the out-of- michael@0: * flow frames whose placeholders are in the overflow list. michael@0: * -- A given piece of content has at most one placeholder michael@0: * frame in a block's normal child list. michael@0: * -- While a block is being reflowed, and from then until michael@0: * its next-in-flow is reflowed it may have a michael@0: * PushedFloatProperty frame property that points to michael@0: * an nsFrameList. This list contains continuations for michael@0: * floats whose prev-in-flow is in the block's regular float michael@0: * list and first-in-flows of floats that did not fit, but michael@0: * whose placeholders are in the block or one of its michael@0: * prev-in-flows. michael@0: * -- In all these frame lists, if there are two frames for michael@0: * the same content appearing in the list, then the frames michael@0: * appear with the prev-in-flow before the next-in-flow. michael@0: * -- While reflowing a block, its overflow line list michael@0: * will usually be empty but in some cases will have lines michael@0: * (while we reflow the block at its shrink-wrap width). michael@0: * In this case any new overflowing content must be michael@0: * prepended to the overflow lines. michael@0: */ michael@0: michael@0: typedef nsContainerFrame nsBlockFrameSuper; michael@0: michael@0: /* michael@0: * Base class for block and inline frames. michael@0: * The block frame has an additional child list, kAbsoluteList, which michael@0: * contains the absolutely positioned frames. michael@0: */ michael@0: class nsBlockFrame : public nsBlockFrameSuper michael@0: { michael@0: public: michael@0: NS_DECL_QUERYFRAME_TARGET(nsBlockFrame) michael@0: NS_DECL_FRAMEARENA_HELPERS michael@0: michael@0: typedef nsLineList::iterator line_iterator; michael@0: typedef nsLineList::const_iterator const_line_iterator; michael@0: typedef nsLineList::reverse_iterator reverse_line_iterator; michael@0: typedef nsLineList::const_reverse_iterator const_reverse_line_iterator; michael@0: michael@0: line_iterator begin_lines() { return mLines.begin(); } michael@0: line_iterator end_lines() { return mLines.end(); } michael@0: const_line_iterator begin_lines() const { return mLines.begin(); } michael@0: const_line_iterator end_lines() const { return mLines.end(); } michael@0: reverse_line_iterator rbegin_lines() { return mLines.rbegin(); } michael@0: reverse_line_iterator rend_lines() { return mLines.rend(); } michael@0: const_reverse_line_iterator rbegin_lines() const { return mLines.rbegin(); } michael@0: const_reverse_line_iterator rend_lines() const { return mLines.rend(); } michael@0: line_iterator line(nsLineBox* aList) { return mLines.begin(aList); } michael@0: reverse_line_iterator rline(nsLineBox* aList) { return mLines.rbegin(aList); } michael@0: michael@0: friend nsIFrame* NS_NewBlockFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, nsFrameState aFlags); michael@0: michael@0: // nsQueryFrame michael@0: NS_DECL_QUERYFRAME michael@0: michael@0: // nsIFrame 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: virtual const nsFrameList& GetChildList(ChildListID aListID) const MOZ_OVERRIDE; michael@0: virtual void GetChildLists(nsTArray* aLists) const MOZ_OVERRIDE; michael@0: virtual nscoord GetBaseline() const MOZ_OVERRIDE; michael@0: virtual nscoord GetCaretBaseline() const MOZ_OVERRIDE; michael@0: virtual void DestroyFrom(nsIFrame* aDestructRoot) MOZ_OVERRIDE; michael@0: virtual nsSplittableType GetSplittableType() const MOZ_OVERRIDE; michael@0: virtual bool IsFloatContainingBlock() const MOZ_OVERRIDE; michael@0: virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder, michael@0: const nsRect& aDirtyRect, michael@0: const nsDisplayListSet& aLists) MOZ_OVERRIDE; michael@0: virtual nsIAtom* GetType() const MOZ_OVERRIDE; michael@0: virtual bool IsFrameOfType(uint32_t aFlags) const MOZ_OVERRIDE michael@0: { michael@0: return nsContainerFrame::IsFrameOfType(aFlags & michael@0: ~(nsIFrame::eCanContainOverflowContainers | michael@0: nsIFrame::eBlockFrame)); michael@0: } michael@0: michael@0: virtual void InvalidateFrame(uint32_t aDisplayItemKey = 0) MOZ_OVERRIDE; michael@0: virtual void InvalidateFrameWithRect(const nsRect& aRect, uint32_t aDisplayItemKey = 0) 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: virtual nsresult GetFrameName(nsAString& aResult) const MOZ_OVERRIDE; michael@0: #endif michael@0: michael@0: #ifdef DEBUG michael@0: virtual nsFrameState GetDebugStateBits() const MOZ_OVERRIDE; michael@0: #endif michael@0: michael@0: #ifdef ACCESSIBILITY michael@0: virtual mozilla::a11y::AccType AccessibleType() MOZ_OVERRIDE; michael@0: #endif michael@0: michael@0: // line cursor methods to speed up searching for the line(s) michael@0: // containing a point. The basic idea is that we set the cursor michael@0: // property if the lines' overflowArea.VisualOverflow().ys and michael@0: // overflowArea.VisualOverflow().yMosts are non-decreasing michael@0: // (considering only non-empty overflowArea.VisualOverflow()s; empty michael@0: // overflowArea.VisualOverflow()s never participate in event handling michael@0: // or painting), and the block has sufficient number of lines. The michael@0: // cursor property points to a "recently used" line. If we get a michael@0: // series of requests that work on lines michael@0: // "near" the cursor, then we can find those nearby lines quickly by michael@0: // starting our search at the cursor. michael@0: michael@0: // Clear out line cursor because we're disturbing the lines (i.e., Reflow) michael@0: void ClearLineCursor(); michael@0: // Get the first line that might contain y-coord 'y', or nullptr if you must search michael@0: // all lines. If nonnull is returned then we guarantee that the lines' michael@0: // combinedArea.ys and combinedArea.yMosts are non-decreasing. michael@0: // The actual line returned might not contain 'y', but if not, it is guaranteed michael@0: // to be before any line which does contain 'y'. michael@0: nsLineBox* GetFirstLineContaining(nscoord y); michael@0: // Set the line cursor to our first line. Only call this if you michael@0: // guarantee that the lines' combinedArea.ys and combinedArea.yMosts michael@0: // are non-decreasing. michael@0: void SetupLineCursor(); michael@0: michael@0: virtual void ChildIsDirty(nsIFrame* aChild) MOZ_OVERRIDE; michael@0: virtual bool IsVisibleInSelection(nsISelection* aSelection) MOZ_OVERRIDE; michael@0: michael@0: virtual bool IsEmpty() MOZ_OVERRIDE; michael@0: virtual bool CachedIsEmpty() MOZ_OVERRIDE; michael@0: virtual bool IsSelfEmpty() MOZ_OVERRIDE; michael@0: michael@0: // Given that we have a bullet, does it actually draw something, i.e., michael@0: // do we have either a 'list-style-type' or 'list-style-image' that is michael@0: // not 'none'? michael@0: bool BulletIsEmpty() const; michael@0: michael@0: /** michael@0: * Return the bullet text equivalent. michael@0: */ michael@0: void GetBulletText(nsAString& aText) const; michael@0: michael@0: /** michael@0: * Return true if there's a bullet. michael@0: */ michael@0: bool HasBullet() const { michael@0: return HasOutsideBullet() || HasInsideBullet(); michael@0: } michael@0: michael@0: /** michael@0: * @return true if this frame has an inside bullet frame. michael@0: */ michael@0: bool HasInsideBullet() const { michael@0: return 0 != (mState & NS_BLOCK_FRAME_HAS_INSIDE_BULLET); michael@0: } michael@0: michael@0: /** michael@0: * @return true if this frame has an outside bullet frame. michael@0: */ michael@0: bool HasOutsideBullet() const { michael@0: return 0 != (mState & NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET); michael@0: } michael@0: michael@0: /** michael@0: * @return the bullet frame or nullptr if we don't have one. michael@0: */ michael@0: nsBulletFrame* GetBullet() const { michael@0: nsBulletFrame* outside = GetOutsideBullet(); michael@0: return outside ? outside : GetInsideBullet(); michael@0: } michael@0: michael@0: virtual void MarkIntrinsicWidthsDirty() MOZ_OVERRIDE; michael@0: private: michael@0: void CheckIntrinsicCacheAgainstShrinkWrapState(); michael@0: public: michael@0: virtual nscoord GetMinWidth(nsRenderingContext *aRenderingContext) MOZ_OVERRIDE; michael@0: virtual nscoord GetPrefWidth(nsRenderingContext *aRenderingContext) MOZ_OVERRIDE; michael@0: michael@0: virtual nsRect ComputeTightBounds(gfxContext* aContext) const MOZ_OVERRIDE; michael@0: michael@0: virtual nsresult GetPrefWidthTightBounds(nsRenderingContext* aContext, michael@0: nscoord* aX, michael@0: nscoord* aXMost) MOZ_OVERRIDE; michael@0: michael@0: /** michael@0: * Compute the final height of this frame. michael@0: * michael@0: * @param aReflowState Data structure passed from parent during reflow. michael@0: * @param aReflowStatus A pointed to the reflow status for when we're finished michael@0: * doing reflow. this will get set appropriately if the height causes michael@0: * us to exceed the current available (page) height. michael@0: * @param aContentHeight The height of content, precomputed outside of this michael@0: * function. The final height that is used in aMetrics will be set to michael@0: * either this or the available height, whichever is larger, in the michael@0: * case where our available height is constrained, and we overflow that michael@0: * available height. michael@0: * @param aBorderPadding The margins representing the border padding for block michael@0: * frames. Can be 0. michael@0: * @param aMetrics Out parameter for final height. Taken as an michael@0: * nsHTMLReflowMetrics object so that aMetrics can be passed in michael@0: * directly during reflow. michael@0: * @param aConsumed The height already consumed by our previous-in-flows. michael@0: */ michael@0: void ComputeFinalHeight(const nsHTMLReflowState& aReflowState, michael@0: nsReflowStatus* aStatus, michael@0: nscoord aContentHeight, michael@0: const nsMargin& aBorderPadding, michael@0: nsHTMLReflowMetrics& aMetrics, michael@0: nscoord aConsumed); michael@0: michael@0: virtual nsresult Reflow(nsPresContext* aPresContext, michael@0: nsHTMLReflowMetrics& aDesiredSize, michael@0: const nsHTMLReflowState& aReflowState, michael@0: nsReflowStatus& aStatus) MOZ_OVERRIDE; michael@0: michael@0: virtual nsresult AttributeChanged(int32_t aNameSpaceID, michael@0: nsIAtom* aAttribute, michael@0: int32_t aModType) MOZ_OVERRIDE; 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: virtual nsresult StealFrame(nsIFrame* aChild, michael@0: bool aForceNormal = false) MOZ_OVERRIDE; michael@0: michael@0: virtual void DeleteNextInFlowChild(nsIFrame* aNextInFlow, michael@0: bool aDeletingEmptyFrames) MOZ_OVERRIDE; michael@0: michael@0: /** michael@0: * This is a special method that allows a child class of nsBlockFrame to michael@0: * return a special, customized nsStyleText object to the nsLineLayout michael@0: * constructor. It is used when the nsBlockFrame child needs to specify its michael@0: * custom rendering style. michael@0: */ michael@0: virtual const nsStyleText* StyleTextForLineLayout(); michael@0: michael@0: /** michael@0: * Determines whether the collapsed margin carried out of the last michael@0: * line includes the margin-top of a line with clearance (in which michael@0: * case we must avoid collapsing that margin with our bottom margin) michael@0: */ michael@0: bool CheckForCollapsedBottomMarginFromClearanceLine(); michael@0: michael@0: static nsresult GetCurrentLine(nsBlockReflowState *aState, nsLineBox **aOutCurrentLine); michael@0: michael@0: /** michael@0: * Determine if this block is a margin root at the top/bottom edges. michael@0: */ michael@0: void IsMarginRoot(bool* aTopMarginRoot, bool* aBottomMarginRoot); michael@0: michael@0: static bool BlockNeedsFloatManager(nsIFrame* aBlock); michael@0: michael@0: /** michael@0: * Returns whether aFrame is a block frame that will wrap its contents michael@0: * around floats intruding on it from the outside. (aFrame need not michael@0: * be a block frame, but if it's not, the result will be false.) michael@0: */ michael@0: static bool BlockCanIntersectFloats(nsIFrame* aFrame); michael@0: michael@0: /** michael@0: * Returns the width that needs to be cleared past floats for blocks michael@0: * that cannot intersect floats. aState must already have michael@0: * GetAvailableSpace called on it for the vertical position that we michael@0: * care about (which need not be its current mY) michael@0: */ michael@0: struct ReplacedElementWidthToClear { michael@0: nscoord marginLeft, borderBoxWidth, marginRight; michael@0: nscoord MarginBoxWidth() const michael@0: { return marginLeft + borderBoxWidth + marginRight; } michael@0: }; michael@0: static ReplacedElementWidthToClear michael@0: WidthToClearPastFloats(nsBlockReflowState& aState, michael@0: const nsRect& aFloatAvailableSpace, michael@0: nsIFrame* aFrame); michael@0: michael@0: /** michael@0: * Creates a contination for aFloat and adds it to the list of overflow floats. michael@0: * Also updates aState.mReflowStatus to include the float's incompleteness. michael@0: * Must only be called while this block frame is in reflow. michael@0: * aFloatStatus must be the float's true, unmodified reflow status. michael@0: * michael@0: */ michael@0: nsresult SplitFloat(nsBlockReflowState& aState, michael@0: nsIFrame* aFloat, michael@0: nsReflowStatus aFloatStatus); michael@0: michael@0: /** michael@0: * Walks up the frame tree, starting with aCandidate, and returns the first michael@0: * block frame that it encounters. michael@0: */ michael@0: static nsBlockFrame* GetNearestAncestorBlock(nsIFrame* aCandidate); michael@0: michael@0: struct FrameLines { michael@0: nsLineList mLines; michael@0: nsFrameList mFrames; michael@0: }; michael@0: michael@0: protected: michael@0: nsBlockFrame(nsStyleContext* aContext) michael@0: : nsContainerFrame(aContext) michael@0: , mMinWidth(NS_INTRINSIC_WIDTH_UNKNOWN) michael@0: , mPrefWidth(NS_INTRINSIC_WIDTH_UNKNOWN) michael@0: { michael@0: #ifdef DEBUG michael@0: InitDebugFlags(); michael@0: #endif michael@0: } michael@0: virtual ~nsBlockFrame(); michael@0: michael@0: #ifdef DEBUG michael@0: already_AddRefed GetFirstLetterStyle(nsPresContext* aPresContext) michael@0: { michael@0: return aPresContext->StyleSet()-> michael@0: ProbePseudoElementStyle(mContent->AsElement(), michael@0: nsCSSPseudoElements::ePseudo_firstLetter, michael@0: mStyleContext); michael@0: } michael@0: #endif michael@0: michael@0: NS_DECLARE_FRAME_PROPERTY(LineCursorProperty, nullptr) michael@0: nsLineBox* GetLineCursor() { michael@0: return (GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR) ? michael@0: static_cast(Properties().Get(LineCursorProperty())) : nullptr; michael@0: } michael@0: michael@0: nsLineBox* NewLineBox(nsIFrame* aFrame, bool aIsBlock) { michael@0: return NS_NewLineBox(PresContext()->PresShell(), aFrame, aIsBlock); michael@0: } michael@0: nsLineBox* NewLineBox(nsLineBox* aFromLine, nsIFrame* aFrame, int32_t aCount) { michael@0: return NS_NewLineBox(PresContext()->PresShell(), aFromLine, aFrame, aCount); michael@0: } michael@0: void FreeLineBox(nsLineBox* aLine) { michael@0: if (aLine == GetLineCursor()) { michael@0: ClearLineCursor(); michael@0: } michael@0: aLine->Destroy(PresContext()->PresShell()); michael@0: } michael@0: /** michael@0: * Helper method for StealFrame. michael@0: */ michael@0: void RemoveFrameFromLine(nsIFrame* aChild, nsLineList::iterator aLine, michael@0: nsFrameList& aFrameList, nsLineList& aLineList); michael@0: michael@0: void TryAllLines(nsLineList::iterator* aIterator, michael@0: nsLineList::iterator* aStartIterator, michael@0: nsLineList::iterator* aEndIterator, michael@0: bool* aInOverflowLines, michael@0: FrameLines** aOverflowLines); michael@0: michael@0: void SetFlags(nsFrameState aFlags) { michael@0: mState &= ~NS_BLOCK_FLAGS_MASK; michael@0: mState |= aFlags; michael@0: } michael@0: michael@0: /** move the frames contained by aLine by aDY michael@0: * if aLine is a block, its child floats are added to the state manager michael@0: */ michael@0: void SlideLine(nsBlockReflowState& aState, michael@0: nsLineBox* aLine, nscoord aDY); michael@0: michael@0: void ComputeFinalSize(const nsHTMLReflowState& aReflowState, michael@0: nsBlockReflowState& aState, michael@0: nsHTMLReflowMetrics& aMetrics, michael@0: nscoord* aBottomEdgeOfChildren); michael@0: michael@0: void ComputeOverflowAreas(const nsRect& aBounds, michael@0: const nsStyleDisplay* aDisplay, michael@0: nscoord aBottomEdgeOfChildren, michael@0: nsOverflowAreas& aOverflowAreas); michael@0: michael@0: /** michael@0: * Add the frames in aFrameList to this block after aPrevSibling. michael@0: * This block thinks in terms of lines, but the frame construction code michael@0: * knows nothing about lines at all so we need to find the line that michael@0: * contains aPrevSibling and add aFrameList after aPrevSibling on that line. michael@0: * New lines are created as necessary to handle block data in aFrameList. michael@0: * This function will clear aFrameList. michael@0: */ michael@0: void AddFrames(nsFrameList& aFrameList, nsIFrame* aPrevSibling); michael@0: michael@0: /** michael@0: * Perform Bidi resolution on this frame michael@0: */ michael@0: nsresult ResolveBidi(); michael@0: michael@0: /** michael@0: * Test whether the frame is a form control in a visual Bidi page. michael@0: * This is necessary for backwards-compatibility, because most visual michael@0: * pages use logical order for form controls so that they will michael@0: * display correctly on native widgets in OSs with Bidi support michael@0: * @param aPresContext the pres context michael@0: * @return whether the frame is a BIDI form control michael@0: */ michael@0: bool IsVisualFormControl(nsPresContext* aPresContext); michael@0: michael@0: public: michael@0: /** michael@0: * Does all the real work for removing aDeletedFrame michael@0: * -- finds the line containing aDeletedFrame michael@0: * -- removes all aDeletedFrame next-in-flows (or all continuations, michael@0: * if REMOVE_FIXED_CONTINUATIONS is given) michael@0: * -- marks lines dirty as needed michael@0: * -- marks textruns dirty (unless FRAMES_ARE_EMPTY is given, in which michael@0: * case textruns do not need to be dirtied) michael@0: * -- destroys all removed frames michael@0: */ michael@0: enum { michael@0: REMOVE_FIXED_CONTINUATIONS = 0x02, michael@0: FRAMES_ARE_EMPTY = 0x04 michael@0: }; michael@0: nsresult DoRemoveFrame(nsIFrame* aDeletedFrame, uint32_t aFlags); michael@0: michael@0: void ReparentFloats(nsIFrame* aFirstFrame, nsBlockFrame* aOldParent, michael@0: bool aReparentSiblings); michael@0: michael@0: virtual bool UpdateOverflow() MOZ_OVERRIDE; michael@0: michael@0: /** Load all of aFrame's floats into the float manager iff aFrame is not a michael@0: * block formatting context. Handles all necessary float manager translations; michael@0: * assumes float manager is in aFrame's parent's coord system. michael@0: * Safe to call on non-blocks (does nothing). michael@0: */ michael@0: static void RecoverFloatsFor(nsIFrame* aFrame, michael@0: nsFloatManager& aFloatManager); michael@0: michael@0: /** michael@0: * Determine if we have any pushed floats from a previous continuation. michael@0: * michael@0: * @returns true, if any of the floats at the beginning of our mFloats list michael@0: * have the NS_FRAME_IS_PUSHED_FLOAT bit set; false otherwise. michael@0: */ michael@0: bool HasPushedFloatsFromPrevContinuation() const { michael@0: if (!mFloats.IsEmpty()) { michael@0: // If we have pushed floats, then they should be at the beginning of our michael@0: // float list. michael@0: if (mFloats.FirstChild()->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT) { michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: // Double-check the above assertion that pushed floats should be at the michael@0: // beginning of our floats list. michael@0: for (nsFrameList::Enumerator e(mFloats); !e.AtEnd(); e.Next()) { michael@0: nsIFrame* f = e.get(); michael@0: NS_ASSERTION(!(f->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT), michael@0: "pushed floats must be at the beginning of the float list"); michael@0: } michael@0: #endif michael@0: return false; michael@0: } michael@0: michael@0: protected: michael@0: michael@0: /** grab overflow lines from this block's prevInFlow, and make them michael@0: * part of this block's mLines list. michael@0: * @return true if any lines were drained. michael@0: */ michael@0: bool DrainOverflowLines(); michael@0: michael@0: /** michael@0: * @return false iff this block does not have a float on any child list. michael@0: * This function is O(1). michael@0: */ michael@0: bool MaybeHasFloats() const { michael@0: if (!mFloats.IsEmpty()) { michael@0: return true; michael@0: } michael@0: // XXX this could be replaced with HasPushedFloats() if we enforced michael@0: // removing the property when the frame list becomes empty. michael@0: nsFrameList* list = GetPushedFloats(); michael@0: if (list && !list->IsEmpty()) { michael@0: return true; michael@0: } michael@0: // For the OverflowOutOfFlowsProperty I think we do enforce that, but it's michael@0: // a mix of out-of-flow frames, so that's why the method name has "Maybe". michael@0: return GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS; michael@0: } michael@0: michael@0: /** grab pushed floats from this block's prevInFlow, and splice michael@0: * them into this block's mFloats list. michael@0: */ michael@0: void DrainPushedFloats(nsBlockReflowState& aState); michael@0: michael@0: /** Load all our floats into the float manager (without reflowing them). michael@0: * Assumes float manager is in our own coordinate system. michael@0: */ michael@0: void RecoverFloats(nsFloatManager& aFloatManager); michael@0: michael@0: /** Reflow pushed floats michael@0: */ michael@0: void ReflowPushedFloats(nsBlockReflowState& aState, michael@0: nsOverflowAreas& aOverflowAreas, michael@0: nsReflowStatus& aStatus); michael@0: michael@0: /** Find any trailing BR clear from the last line of the block (or its PIFs) michael@0: */ michael@0: uint8_t FindTrailingClear(); michael@0: michael@0: /** michael@0: * Remove a float from our float list. michael@0: */ michael@0: void RemoveFloat(nsIFrame* aFloat); michael@0: /** michael@0: * Remove a float from the float cache for the line its placeholder is on. michael@0: */ michael@0: void RemoveFloatFromFloatCache(nsIFrame* aFloat); michael@0: michael@0: void CollectFloats(nsIFrame* aFrame, nsFrameList& aList, michael@0: bool aCollectFromSiblings) { michael@0: if (MaybeHasFloats()) { michael@0: DoCollectFloats(aFrame, aList, aCollectFromSiblings); michael@0: } michael@0: } michael@0: void DoCollectFloats(nsIFrame* aFrame, nsFrameList& aList, michael@0: bool aCollectFromSiblings); michael@0: michael@0: // Remove a float, abs, rel positioned frame from the appropriate block's list michael@0: static void DoRemoveOutOfFlowFrame(nsIFrame* aFrame); michael@0: michael@0: /** set up the conditions necessary for an resize reflow michael@0: * the primary task is to mark the minimumly sufficient lines dirty. michael@0: */ michael@0: void PrepareResizeReflow(nsBlockReflowState& aState); michael@0: michael@0: /** reflow all lines that have been marked dirty */ michael@0: nsresult ReflowDirtyLines(nsBlockReflowState& aState); michael@0: michael@0: /** Mark a given line dirty due to reflow being interrupted on or before it */ michael@0: void MarkLineDirtyForInterrupt(nsLineBox* aLine); michael@0: michael@0: //---------------------------------------- michael@0: // Methods for line reflow michael@0: /** michael@0: * Reflow a line. michael@0: * @param aState the current reflow state michael@0: * @param aLine the line to reflow. can contain a single block frame michael@0: * or contain 1 or more inline frames. michael@0: * @param aKeepReflowGoing [OUT] indicates whether the caller should continue to reflow more lines michael@0: */ michael@0: nsresult ReflowLine(nsBlockReflowState& aState, michael@0: line_iterator aLine, michael@0: bool* aKeepReflowGoing); michael@0: michael@0: // Return false if it needs another reflow because of reduced space michael@0: // between floats that are next to it (but not next to its top), and michael@0: // return true otherwise. michael@0: bool PlaceLine(nsBlockReflowState& aState, michael@0: nsLineLayout& aLineLayout, michael@0: line_iterator aLine, michael@0: nsFloatManager::SavedState* aFloatStateBeforeLine, michael@0: nsRect& aFloatAvailableSpace, /* in-out */ michael@0: nscoord& aAvailableSpaceHeight, /* in-out */ michael@0: bool* aKeepReflowGoing); michael@0: michael@0: /** michael@0: * If NS_BLOCK_LOOK_FOR_DIRTY_FRAMES is set, call MarkLineDirty michael@0: * on any line with a child frame that is dirty. michael@0: */ michael@0: void LazyMarkLinesDirty(); michael@0: michael@0: /** michael@0: * Mark |aLine| dirty, and, if necessary because of possible michael@0: * pull-up, mark the previous line dirty as well. Also invalidates textruns michael@0: * on those lines because the text in the lines might have changed due to michael@0: * addition/removal of frames. michael@0: * @param aLine the line to mark dirty michael@0: * @param aLineList the line list containing that line michael@0: */ michael@0: void MarkLineDirty(line_iterator aLine, const nsLineList* aLineList); michael@0: michael@0: // XXX where to go michael@0: bool IsLastLine(nsBlockReflowState& aState, michael@0: line_iterator aLine); michael@0: michael@0: void DeleteLine(nsBlockReflowState& aState, michael@0: nsLineList::iterator aLine, michael@0: nsLineList::iterator aLineEnd); michael@0: michael@0: //---------------------------------------- michael@0: // Methods for individual frame reflow michael@0: michael@0: bool ShouldApplyTopMargin(nsBlockReflowState& aState, michael@0: nsLineBox* aLine); michael@0: michael@0: nsresult ReflowBlockFrame(nsBlockReflowState& aState, michael@0: line_iterator aLine, michael@0: bool* aKeepGoing); michael@0: michael@0: nsresult ReflowInlineFrames(nsBlockReflowState& aState, michael@0: line_iterator aLine, michael@0: bool* aKeepLineGoing); michael@0: michael@0: nsresult DoReflowInlineFrames(nsBlockReflowState& aState, michael@0: nsLineLayout& aLineLayout, michael@0: line_iterator aLine, michael@0: nsFlowAreaRect& aFloatAvailableSpace, michael@0: nscoord& aAvailableSpaceHeight, michael@0: nsFloatManager::SavedState* michael@0: aFloatStateBeforeLine, michael@0: bool* aKeepReflowGoing, michael@0: LineReflowStatus* aLineReflowStatus, michael@0: bool aAllowPullUp); michael@0: michael@0: nsresult ReflowInlineFrame(nsBlockReflowState& aState, michael@0: nsLineLayout& aLineLayout, michael@0: line_iterator aLine, michael@0: nsIFrame* aFrame, michael@0: LineReflowStatus* aLineReflowStatus); michael@0: michael@0: // Compute the available width for a float. michael@0: nsRect AdjustFloatAvailableSpace(nsBlockReflowState& aState, michael@0: const nsRect& aFloatAvailableSpace, michael@0: nsIFrame* aFloatFrame); michael@0: // Computes the border-box width of the float michael@0: nscoord ComputeFloatWidth(nsBlockReflowState& aState, michael@0: const nsRect& aFloatAvailableSpace, michael@0: nsIFrame* aFloat); michael@0: // An incomplete aReflowStatus indicates the float should be split michael@0: // but only if the available height is constrained. michael@0: // aAdjustedAvailableSpace is the result of calling michael@0: // nsBlockFrame::AdjustFloatAvailableSpace. michael@0: nsresult ReflowFloat(nsBlockReflowState& aState, michael@0: const nsRect& aAdjustedAvailableSpace, michael@0: nsIFrame* aFloat, michael@0: nsMargin& aFloatMargin, michael@0: nsMargin& aFloatOffsets, michael@0: // Whether the float's position michael@0: // (aAdjustedAvailableSpace) has been pushed down michael@0: // due to the presence of other floats. michael@0: bool aFloatPushedDown, michael@0: nsReflowStatus& aReflowStatus); michael@0: michael@0: //---------------------------------------- michael@0: // Methods for pushing/pulling lines/frames michael@0: michael@0: /** michael@0: * Create a next-in-flow, if necessary, for aFrame. If a new frame is michael@0: * created, place it in aLine if aLine is not null. michael@0: * @param aState the block reflow state michael@0: * @param aLine where to put a new frame michael@0: * @param aFrame the frame michael@0: * @return true if a new frame was created, false if not michael@0: */ michael@0: bool CreateContinuationFor(nsBlockReflowState& aState, michael@0: nsLineBox* aLine, michael@0: nsIFrame* aFrame); michael@0: michael@0: /** michael@0: * Push aLine (and any after it), since it cannot be placed on this michael@0: * page/column. Set aKeepReflowGoing to false and set michael@0: * flag aState.mReflowStatus as incomplete. michael@0: */ michael@0: void PushTruncatedLine(nsBlockReflowState& aState, michael@0: line_iterator aLine, michael@0: bool* aKeepReflowGoing); michael@0: michael@0: void SplitLine(nsBlockReflowState& aState, michael@0: nsLineLayout& aLineLayout, michael@0: line_iterator aLine, michael@0: nsIFrame* aFrame, michael@0: LineReflowStatus* aLineReflowStatus); michael@0: michael@0: /** michael@0: * Pull a frame from the next available location (one of our lines or michael@0: * one of our next-in-flows lines). michael@0: * @return the pulled frame or nullptr michael@0: */ michael@0: nsIFrame* PullFrame(nsBlockReflowState& aState, michael@0: line_iterator aLine); michael@0: michael@0: /** michael@0: * Try to pull a frame out of a line pointed at by aFromLine. michael@0: * michael@0: * Note: pulling a frame from a line that is a place-holder frame michael@0: * doesn't automatically remove the corresponding float from the michael@0: * line's float array. This happens indirectly: either the line gets michael@0: * emptied (and destroyed) or the line gets reflowed (because we mark michael@0: * it dirty) and the code at the top of ReflowLine empties the michael@0: * array. So eventually, it will be removed, just not right away. michael@0: * michael@0: * @return the pulled frame or nullptr michael@0: */ michael@0: nsIFrame* PullFrameFrom(nsLineBox* aLine, michael@0: nsBlockFrame* aFromContainer, michael@0: nsLineList::iterator aFromLine); michael@0: michael@0: /** michael@0: * Push the line after aLineBefore to the overflow line list. michael@0: * @param aLineBefore a line in 'mLines' (or begin_lines() when michael@0: * pushing the first line) michael@0: */ michael@0: void PushLines(nsBlockReflowState& aState, michael@0: nsLineList::iterator aLineBefore); michael@0: michael@0: void PropagateFloatDamage(nsBlockReflowState& aState, michael@0: nsLineBox* aLine, michael@0: nscoord aDeltaY); michael@0: michael@0: void CheckFloats(nsBlockReflowState& aState); michael@0: michael@0: //---------------------------------------- michael@0: // List handling kludge michael@0: michael@0: // If this returns true, the block it's called on should get the michael@0: // NS_FRAME_HAS_DIRTY_CHILDREN bit set on it by the caller; either directly michael@0: // if it's already in reflow, or via calling FrameNeedsReflow() to schedule a michael@0: // reflow. michael@0: bool RenumberLists(nsPresContext* aPresContext); michael@0: michael@0: static bool RenumberListsInBlock(nsPresContext* aPresContext, michael@0: nsBlockFrame* aBlockFrame, michael@0: int32_t* aOrdinal, michael@0: int32_t aDepth, michael@0: int32_t aIncrement); michael@0: michael@0: static bool RenumberListsFor(nsPresContext* aPresContext, nsIFrame* aKid, michael@0: int32_t* aOrdinal, int32_t aDepth, michael@0: int32_t aIncrement); michael@0: michael@0: static bool FrameStartsCounterScope(nsIFrame* aFrame); michael@0: michael@0: void ReflowBullet(nsIFrame* aBulletFrame, michael@0: nsBlockReflowState& aState, michael@0: nsHTMLReflowMetrics& aMetrics, michael@0: nscoord aLineTop); michael@0: michael@0: //---------------------------------------- michael@0: michael@0: virtual nsILineIterator* GetLineIterator() MOZ_OVERRIDE; michael@0: michael@0: public: michael@0: bool HasOverflowLines() const { michael@0: return 0 != (GetStateBits() & NS_BLOCK_HAS_OVERFLOW_LINES); michael@0: } michael@0: FrameLines* GetOverflowLines() const; michael@0: protected: michael@0: FrameLines* RemoveOverflowLines(); michael@0: void SetOverflowLines(FrameLines* aOverflowLines); michael@0: void DestroyOverflowLines(); michael@0: michael@0: /** michael@0: * This class is useful for efficiently modifying the out of flow michael@0: * overflow list. It gives the client direct writable access to michael@0: * the frame list temporarily but ensures that property is only michael@0: * written back if absolutely necessary. michael@0: */ michael@0: struct nsAutoOOFFrameList { michael@0: nsFrameList mList; michael@0: michael@0: nsAutoOOFFrameList(nsBlockFrame* aBlock) michael@0: : mPropValue(aBlock->GetOverflowOutOfFlows()) michael@0: , mBlock(aBlock) { michael@0: if (mPropValue) { michael@0: mList = *mPropValue; michael@0: } michael@0: } michael@0: ~nsAutoOOFFrameList() { michael@0: mBlock->SetOverflowOutOfFlows(mList, mPropValue); michael@0: } michael@0: protected: michael@0: nsFrameList* const mPropValue; michael@0: nsBlockFrame* const mBlock; michael@0: }; michael@0: friend struct nsAutoOOFFrameList; michael@0: michael@0: nsFrameList* GetOverflowOutOfFlows() const; michael@0: void SetOverflowOutOfFlows(const nsFrameList& aList, nsFrameList* aPropValue); michael@0: michael@0: /** michael@0: * @return the inside bullet frame or nullptr if we don't have one. michael@0: */ michael@0: nsBulletFrame* GetInsideBullet() const; michael@0: michael@0: /** michael@0: * @return the outside bullet frame or nullptr if we don't have one. michael@0: */ michael@0: nsBulletFrame* GetOutsideBullet() const; michael@0: michael@0: /** michael@0: * @return the outside bullet frame list frame property. michael@0: */ michael@0: nsFrameList* GetOutsideBulletList() const; michael@0: michael@0: /** michael@0: * @return true if this frame has pushed floats. michael@0: */ michael@0: bool HasPushedFloats() const { michael@0: return 0 != (GetStateBits() & NS_BLOCK_HAS_PUSHED_FLOATS); michael@0: } michael@0: michael@0: // Get the pushed floats list, which is used for *temporary* storage michael@0: // of floats during reflow, between when we decide they don't fit in michael@0: // this block until our next continuation takes them. michael@0: nsFrameList* GetPushedFloats() const; michael@0: // Get the pushed floats list, or if there is not currently one, michael@0: // make a new empty one. michael@0: nsFrameList* EnsurePushedFloats(); michael@0: // Remove and return the pushed floats list. michael@0: nsFrameList* RemovePushedFloats(); michael@0: michael@0: #ifdef DEBUG michael@0: void VerifyLines(bool aFinalCheckOK); michael@0: void VerifyOverflowSituation(); michael@0: int32_t GetDepth() const; michael@0: #endif michael@0: michael@0: nscoord mMinWidth, mPrefWidth; michael@0: michael@0: nsLineList mLines; michael@0: michael@0: // List of all floats in this block michael@0: // XXXmats blocks rarely have floats, make it a frame property michael@0: nsFrameList mFloats; michael@0: michael@0: friend class nsBlockReflowState; michael@0: friend class nsBlockInFlowLineIterator; michael@0: michael@0: #ifdef DEBUG michael@0: public: michael@0: static bool gLamePaintMetrics; michael@0: static bool gLameReflowMetrics; michael@0: static bool gNoisy; michael@0: static bool gNoisyDamageRepair; michael@0: static bool gNoisyIntrinsic; michael@0: static bool gNoisyReflow; michael@0: static bool gReallyNoisyReflow; michael@0: static bool gNoisyFloatManager; michael@0: static bool gVerifyLines; michael@0: static bool gDisableResizeOpt; michael@0: michael@0: static int32_t gNoiseIndent; michael@0: michael@0: static const char* kReflowCommandType[]; michael@0: michael@0: protected: michael@0: static void InitDebugFlags(); michael@0: #endif michael@0: }; michael@0: michael@0: #ifdef DEBUG michael@0: class AutoNoisyIndenter { michael@0: public: michael@0: AutoNoisyIndenter(bool aDoIndent) : mIndented(aDoIndent) { michael@0: if (mIndented) { michael@0: nsBlockFrame::gNoiseIndent++; michael@0: } michael@0: } michael@0: ~AutoNoisyIndenter() { michael@0: if (mIndented) { michael@0: nsBlockFrame::gNoiseIndent--; michael@0: } michael@0: } michael@0: private: michael@0: bool mIndented; michael@0: }; michael@0: #endif michael@0: michael@0: /** michael@0: * Iterates over all lines in the prev-in-flows/next-in-flows of this block. michael@0: */ michael@0: class nsBlockInFlowLineIterator { michael@0: public: michael@0: typedef nsBlockFrame::line_iterator line_iterator; michael@0: /** michael@0: * Set up the iterator to point to aLine which must be a normal line michael@0: * in aFrame (not an overflow line). michael@0: */ michael@0: nsBlockInFlowLineIterator(nsBlockFrame* aFrame, line_iterator aLine); michael@0: /** michael@0: * Set up the iterator to point to the first line found starting from michael@0: * aFrame. Sets aFoundValidLine to false if there is no such line. michael@0: * After aFoundValidLine has returned false, don't call any methods on this michael@0: * object again. michael@0: */ michael@0: nsBlockInFlowLineIterator(nsBlockFrame* aFrame, bool* aFoundValidLine); michael@0: /** michael@0: * Set up the iterator to point to the line that contains aFindFrame (either michael@0: * directly or indirectly). If aFrame is out of flow, or contained in an michael@0: * out-of-flow, finds the line containing the out-of-flow's placeholder. If michael@0: * the frame is not found, sets aFoundValidLine to false. After michael@0: * aFoundValidLine has returned false, don't call any methods on this michael@0: * object again. michael@0: */ michael@0: nsBlockInFlowLineIterator(nsBlockFrame* aFrame, nsIFrame* aFindFrame, michael@0: bool* aFoundValidLine); michael@0: michael@0: line_iterator GetLine() { return mLine; } michael@0: bool IsLastLineInList(); michael@0: nsBlockFrame* GetContainer() { return mFrame; } michael@0: bool GetInOverflow() { return mLineList != &mFrame->mLines; } michael@0: michael@0: /** michael@0: * Returns the current line list we're iterating, null means michael@0: * we're iterating |mLines| of the container. michael@0: */ michael@0: nsLineList* GetLineList() { return mLineList; } michael@0: michael@0: /** michael@0: * Returns the end-iterator of whatever line list we're in. michael@0: */ michael@0: line_iterator End(); michael@0: michael@0: /** michael@0: * Returns false if there are no more lines. After this has returned false, michael@0: * don't call any methods on this object again. michael@0: */ michael@0: bool Next(); michael@0: /** michael@0: * Returns false if there are no more lines. After this has returned false, michael@0: * don't call any methods on this object again. michael@0: */ michael@0: bool Prev(); michael@0: michael@0: private: michael@0: friend class nsBlockFrame; michael@0: // XXX nsBlockFrame uses this internally in one place. Try to remove it. michael@0: nsBlockInFlowLineIterator(nsBlockFrame* aFrame, line_iterator aLine, bool aInOverflow); michael@0: michael@0: nsBlockFrame* mFrame; michael@0: line_iterator mLine; michael@0: nsLineList* mLineList; // the line list mLine is in michael@0: michael@0: /** michael@0: * Moves iterator to next valid line reachable from the current block. michael@0: * Returns false if there are no valid lines. michael@0: */ michael@0: bool FindValidLine(); michael@0: }; michael@0: michael@0: #endif /* nsBlockFrame_h___ */