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: /* rendering object for css3 multi-column layout */ michael@0: michael@0: #include "mozilla/Attributes.h" michael@0: #include "nsContainerFrame.h" michael@0: michael@0: class nsColumnSetFrame : public nsContainerFrame { michael@0: public: michael@0: NS_DECL_FRAMEARENA_HELPERS michael@0: michael@0: nsColumnSetFrame(nsStyleContext* aContext); michael@0: michael@0: virtual nsresult SetInitialChildList(ChildListID aListID, michael@0: nsFrameList& aChildList) MOZ_OVERRIDE; 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 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 nscoord GetMinWidth(nsRenderingContext *aRenderingContext) MOZ_OVERRIDE; michael@0: virtual nscoord GetPrefWidth(nsRenderingContext *aRenderingContext) MOZ_OVERRIDE; michael@0: michael@0: /** michael@0: * Retrieve the available height for content of this frame. The available content michael@0: * height is the available height for the frame, minus borders and padding. michael@0: */ michael@0: virtual nscoord GetAvailableContentHeight(const nsHTMLReflowState& aReflowState); michael@0: michael@0: virtual nsIFrame* GetContentInsertionFrame() MOZ_OVERRIDE { michael@0: nsIFrame* frame = GetFirstPrincipalChild(); michael@0: michael@0: // if no children return nullptr michael@0: if (!frame) michael@0: return nullptr; michael@0: michael@0: return frame->GetContentInsertionFrame(); michael@0: } michael@0: michael@0: virtual nsresult StealFrame(nsIFrame* aChild, bool aForceNormal) MOZ_OVERRIDE michael@0: { // nsColumnSetFrame keeps overflow containers in main child list michael@0: return nsContainerFrame::StealFrame(aChild, true); michael@0: } michael@0: 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: } 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: virtual nsIAtom* GetType() const MOZ_OVERRIDE; michael@0: michael@0: virtual void PaintColumnRule(nsRenderingContext* aCtx, michael@0: const nsRect& aDirtyRect, michael@0: const nsPoint& aPt); michael@0: michael@0: #ifdef DEBUG_FRAME_DUMP michael@0: virtual nsresult GetFrameName(nsAString& aResult) const MOZ_OVERRIDE { michael@0: return MakeFrameName(NS_LITERAL_STRING("ColumnSet"), aResult); michael@0: } michael@0: #endif michael@0: michael@0: protected: michael@0: nscoord mLastBalanceHeight; michael@0: nsReflowStatus mLastFrameStatus; michael@0: michael@0: /** michael@0: * These are the parameters that control the layout of columns. michael@0: */ michael@0: struct ReflowConfig { michael@0: // The number of columns that we want to balance across. If we're not michael@0: // balancing, this will be set to INT32_MAX. michael@0: int32_t mBalanceColCount; michael@0: michael@0: // The width of each individual column. michael@0: nscoord mColWidth; michael@0: michael@0: // The amount of width that is expected to be left over after all the michael@0: // columns and column gaps are laid out. michael@0: nscoord mExpectedWidthLeftOver; michael@0: michael@0: // The width of each column gap. michael@0: nscoord mColGap; michael@0: michael@0: // The maximum height of any individual column during a reflow iteration. michael@0: // This parameter is set during each iteration of the binary search for michael@0: // the best column height. michael@0: nscoord mColMaxHeight; michael@0: michael@0: // A boolean controlling whether or not we are balancing. This should be michael@0: // equivalent to mBalanceColCount == INT32_MAX. michael@0: bool mIsBalancing; michael@0: michael@0: // The last known column height that was 'feasible'. A column height is michael@0: // feasible if all child content fits within the specified height. michael@0: nscoord mKnownFeasibleHeight; michael@0: michael@0: // The last known height that was 'infeasible'. A column height is michael@0: // infeasible if not all child content fits within the specified height. michael@0: nscoord mKnownInfeasibleHeight; michael@0: michael@0: // Height of the column set frame michael@0: nscoord mComputedHeight; michael@0: michael@0: // The height "consumed" by previous-in-flows. michael@0: // The computed height should be equal to the height of the element (i.e. michael@0: // the computed height itself) plus the consumed height. michael@0: nscoord mConsumedHeight; michael@0: }; michael@0: michael@0: /** michael@0: * Some data that is better calculated during reflow michael@0: */ michael@0: struct ColumnBalanceData { michael@0: // The maximum "content height" of any column michael@0: nscoord mMaxHeight; michael@0: // The sum of the "content heights" for all columns michael@0: nscoord mSumHeight; michael@0: // The "content height" of the last column michael@0: nscoord mLastHeight; michael@0: // The maximum "content height" of all columns that overflowed michael@0: // their available height michael@0: nscoord mMaxOverflowingHeight; michael@0: // This flag determines whether the last reflow of children exceeded the michael@0: // computed height of the column set frame. If so, we set the height to michael@0: // this maximum allowable height, and continue reflow without balancing. michael@0: bool mHasExcessHeight; michael@0: michael@0: void Reset() { michael@0: mMaxHeight = mSumHeight = mLastHeight = mMaxOverflowingHeight = 0; michael@0: mHasExcessHeight = false; michael@0: } michael@0: }; michael@0: michael@0: /** michael@0: * Similar to nsBlockFrame::DrainOverflowLines. Locate any columns not michael@0: * handled by our prev-in-flow, and any columns sitting on our own michael@0: * overflow list, and put them in our primary child list for reflowing. michael@0: */ michael@0: void DrainOverflowColumns(); michael@0: michael@0: bool ReflowColumns(nsHTMLReflowMetrics& aDesiredSize, michael@0: const nsHTMLReflowState& aReflowState, michael@0: nsReflowStatus& aReflowStatus, michael@0: ReflowConfig& aConfig, michael@0: bool aLastColumnUnbounded, michael@0: nsCollapsingMargin* aCarriedOutBottomMargin, michael@0: ColumnBalanceData& aColData); michael@0: michael@0: /** michael@0: * The basic reflow strategy is to call this function repeatedly to michael@0: * obtain specific parameters that determine the layout of the michael@0: * columns. This function will compute those parameters from the CSS michael@0: * style. This function will also be responsible for implementing michael@0: * the state machine that controls column balancing. michael@0: */ michael@0: ReflowConfig ChooseColumnStrategy(const nsHTMLReflowState& aReflowState, michael@0: bool aForceAuto, nscoord aFeasibleHeight, michael@0: nscoord aInfeasibleHeight); michael@0: michael@0: /** michael@0: * Perform the binary search for the best balance height for this column set. michael@0: * michael@0: * @param aReflowState The input parameters for the current reflow iteration. michael@0: * @param aPresContext The presentation context in which the current reflow michael@0: * iteration is occurring. michael@0: * @param aConfig The ReflowConfig object associated with this column set michael@0: * frame, generated by ChooseColumnStrategy(). michael@0: * @param aColData A data structure used to keep track of data needed between michael@0: * successive iterations of the balancing process. michael@0: * @param aDesiredSize The final output size of the column set frame (output michael@0: * of reflow procedure). michael@0: * @param aOutMargin The bottom margin of the column set frame that may be michael@0: * carried out from reflow (and thus collapsed). michael@0: * @param aUnboundedLastColumn A boolean value indicating that the last column michael@0: * can be of any height. Used during the first iteration of the michael@0: * balancing procedure to measure the height of all content in michael@0: * descendant frames of the column set. michael@0: * @param aRunWasFeasible An input/output parameter indicating whether or not michael@0: * the last iteration of the balancing loop was a feasible height to michael@0: * fit all content from descendant frames. michael@0: * @param aStatus A final reflow status of the column set frame, passed in as michael@0: * an output parameter. michael@0: */ michael@0: void FindBestBalanceHeight(const nsHTMLReflowState& aReflowState, michael@0: nsPresContext* aPresContext, michael@0: ReflowConfig& aConfig, michael@0: ColumnBalanceData& aColData, michael@0: nsHTMLReflowMetrics& aDesiredSize, michael@0: nsCollapsingMargin& aOutMargin, michael@0: bool& aUnboundedLastColumn, michael@0: bool& aRunWasFeasible, michael@0: nsReflowStatus& aStatus); michael@0: /** michael@0: * Reflow column children. Returns true iff the content that was reflowed michael@0: * fit into the mColMaxHeight. michael@0: */ michael@0: bool ReflowChildren(nsHTMLReflowMetrics& aDesiredSize, michael@0: const nsHTMLReflowState& aReflowState, michael@0: nsReflowStatus& aStatus, michael@0: const ReflowConfig& aConfig, michael@0: bool aLastColumnUnbounded, michael@0: nsCollapsingMargin* aCarriedOutBottomMargin, michael@0: ColumnBalanceData& aColData); michael@0: };