1/*
2 * Copyright (C) 2012 Apple Inc.  All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26
27#ifndef RenderMultiColumnFlowThread_h
28#define RenderMultiColumnFlowThread_h
29
30#include "core/rendering/RenderFlowThread.h"
31
32namespace blink {
33
34class RenderMultiColumnSet;
35
36// Flow thread implementation for CSS multicol. This will be inserted as an anonymous child block of
37// the actual multicol container (i.e. the RenderBlockFlow whose style computes to non-auto
38// column-count and/or column-width). RenderMultiColumnFlowThread is the heart of the multicol
39// implementation, and there is only one instance per multicol container. Child content of the
40// multicol container is parented into the flow thread at the time of renderer insertion.
41//
42// Apart from this flow thread child, the multicol container will also have RenderMultiColumnSet
43// "region" children, which are used to position the columns visually. The flow thread is in charge
44// of layout, and, after having calculated the column width, it lays out content as if everything
45// were in one tall single column, except that there will typically be some amount of blank space
46// (also known as pagination struts) at the offsets where the actual column boundaries are. This
47// way, content that needs to be preceded by a break will appear at the top of the next
48// column. Content needs to be preceded by a break when there's a forced break or when the content
49// is unbreakable and cannot fully fit in the same column as the preceding piece of
50// content. Although a RenderMultiColumnFlowThread is laid out, it does not take up any space in its
51// container. It's the RenderMultiColumnSet objects that take up the necessary amount of space, and
52// make sure that the columns are painted and hit-tested correctly.
53//
54// The width of the flow thread is the same as the column width. The width of a column set is the
55// same as the content box width of the multicol container; in other words exactly enough to hold
56// the number of columns to be used, stacked horizontally, plus column gaps between them.
57//
58// Since it's the first child of the multicol container, the flow thread is laid out first, albeit
59// in a slightly special way, since it's not to take up any space in its ancestors. Afterwards, the
60// column sets are laid out. They get their height from the columns that they hold. In single
61// column-row constrained height non-balancing cases this will simply be the same as the content
62// height of the multicol container itself. In most other cases we'll have to calculate optimal
63// column heights ourselves, though. This process is referred to as column balancing, and then we
64// infer the column set height from the flow thread's height.
65//
66// More on column balancing: the columns' height is unknown in the first layout pass when
67// balancing. This means that we cannot insert any implicit (soft / unforced) breaks (and pagination
68// struts) when laying out the contents of the flow thread. We'll just lay out everything in tall
69// single strip. After the initial flow thread layout pass we can determine a tentative / minimal /
70// initial column height. This is calculated by simply dividing the flow thread's height by the
71// number of specified columns. In the layout pass that follows, we can insert breaks (and
72// pagination struts) at column boundaries, since we now have a column height. It may very easily
73// turn out that the calculated height wasn't enough, though. We'll notice this at end of layout. If
74// we end up with too many columns (i.e. columns overflowing the multicol container), it wasn't
75// enough. In this case we need to increase the column heights. We'll increase them by the lowest
76// amount of space that could possibly affect where the breaks occur (see
77// RenderMultiColumnSet::recordSpaceShortage()). We'll relayout (to find new break points and the
78// new lowest amount of space increase that could affect where they occur, in case we need another
79// round) until we've reached an acceptable height (where everything fits perfectly in the number of
80// columns that we have specified). The rule of thumb is that we shouldn't have to perform more of
81// such iterations than the number of columns that we have.
82//
83// For each layout iteration done for column balancing, the flow thread will need a deep layout if
84// column heights changed in the previous pass, since column height changes may affect break points
85// and pagination struts anywhere in the tree, and currently no way exists to do this in a more
86// optimized manner.
87class RenderMultiColumnFlowThread : public RenderFlowThread {
88public:
89    virtual ~RenderMultiColumnFlowThread();
90
91    static RenderMultiColumnFlowThread* createAnonymous(Document&, RenderStyle* parentStyle);
92
93    virtual bool isRenderMultiColumnFlowThread() const OVERRIDE FINAL { return true; }
94
95    RenderBlockFlow* multiColumnBlockFlow() const { return toRenderBlockFlow(parent()); }
96
97    RenderMultiColumnSet* firstMultiColumnSet() const;
98    RenderMultiColumnSet* lastMultiColumnSet() const;
99
100    virtual void addChild(RenderObject* newChild, RenderObject* beforeChild = 0) OVERRIDE;
101
102    // Populate the flow thread with what's currently its siblings. Called when a regular block
103    // becomes a multicol container.
104    void populate();
105
106    // Empty the flow thread by moving everything to the parent. Remove all multicol specific
107    // renderers. Then destroy the flow thread. Called when a multicol container becomes a regular
108    // block.
109    void evacuateAndDestroy();
110
111    unsigned columnCount() const { return m_columnCount; }
112    LayoutUnit columnHeightAvailable() const { return m_columnHeightAvailable; }
113    void setColumnHeightAvailable(LayoutUnit available) { m_columnHeightAvailable = available; }
114    virtual bool heightIsAuto() const { return !columnHeightAvailable() || multiColumnBlockFlow()->style()->columnFill() == ColumnFillBalance; }
115    bool progressionIsInline() const { return m_progressionIsInline; }
116
117    virtual LayoutSize columnOffset(const LayoutPoint&) const OVERRIDE FINAL;
118
119    // Do we need to set a new width and lay out?
120    virtual bool needsNewWidth() const;
121
122    void layoutColumns(bool relayoutChildren, SubtreeLayoutScope&);
123
124    bool recalculateColumnHeights();
125
126protected:
127    RenderMultiColumnFlowThread();
128    void setProgressionIsInline(bool isInline) { m_progressionIsInline = isInline; }
129
130    virtual void layout() OVERRIDE;
131
132private:
133    void calculateColumnCountAndWidth(LayoutUnit& width, unsigned& count) const;
134
135    virtual const char* renderName() const OVERRIDE;
136    virtual void addRegionToThread(RenderMultiColumnSet*) OVERRIDE;
137    virtual void willBeRemovedFromTree() OVERRIDE;
138    virtual void computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues&) const OVERRIDE;
139    virtual void updateLogicalWidth() OVERRIDE;
140    virtual void setPageBreak(LayoutUnit offset, LayoutUnit spaceShortage) OVERRIDE;
141    virtual void updateMinimumPageHeight(LayoutUnit offset, LayoutUnit minHeight) OVERRIDE;
142    virtual RenderMultiColumnSet* columnSetAtBlockOffset(LayoutUnit) const OVERRIDE;
143    virtual bool addForcedRegionBreak(LayoutUnit, RenderObject* breakChild, bool isBefore, LayoutUnit* offsetBreakAdjustment = 0) OVERRIDE;
144    virtual bool isPageLogicalHeightKnown() const OVERRIDE;
145
146    unsigned m_columnCount; // The used value of column-count
147    LayoutUnit m_columnHeightAvailable; // Total height available to columns, or 0 if auto.
148    bool m_inBalancingPass; // Set when relayouting for column balancing.
149    bool m_needsColumnHeightsRecalculation; // Set when we need to recalculate the column set heights after layout.
150    bool m_progressionIsInline; // Always true for regular multicol. False for paged-y overflow.
151};
152
153} // namespace blink
154
155#endif // RenderMultiColumnFlowThread_h
156
157