File: RenderMultiColumnFlowThread.h

package info (click to toggle)
chromium-browser 41.0.2272.118-1
  • links: PTS, VCS
  • area: main
  • in suites: jessie-kfreebsd
  • size: 2,189,132 kB
  • sloc: cpp: 9,691,462; ansic: 3,341,451; python: 712,689; asm: 518,779; xml: 208,926; java: 169,820; sh: 119,353; perl: 68,907; makefile: 28,311; yacc: 13,305; objc: 11,385; tcl: 3,186; cs: 2,225; sql: 2,217; lex: 2,215; lisp: 1,349; pascal: 1,256; awk: 407; ruby: 155; sed: 53; php: 14; exp: 11
file content (190 lines) | stat: -rw-r--r-- 11,106 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
/*
 * Copyright (C) 2012 Apple Inc.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


#ifndef RenderMultiColumnFlowThread_h
#define RenderMultiColumnFlowThread_h

#include "core/rendering/RenderFlowThread.h"

namespace blink {

class RenderMultiColumnSet;
class RenderMultiColumnSpannerPlaceholder;

// Flow thread implementation for CSS multicol. This will be inserted as an anonymous child block of
// the actual multicol container (i.e. the RenderBlockFlow whose style computes to non-auto
// column-count and/or column-width). RenderMultiColumnFlowThread is the heart of the multicol
// implementation, and there is only one instance per multicol container. Child content of the
// multicol container is parented into the flow thread at the time of renderer insertion.
//
// Apart from this flow thread child, the multicol container will also have RenderMultiColumnSet
// "region" children, which are used to position the columns visually. The flow thread is in charge
// of layout, and, after having calculated the column width, it lays out content as if everything
// were in one tall single column, except that there will typically be some amount of blank space
// (also known as pagination struts) at the offsets where the actual column boundaries are. This
// way, content that needs to be preceded by a break will appear at the top of the next
// column. Content needs to be preceded by a break when there's a forced break or when the content
// is unbreakable and cannot fully fit in the same column as the preceding piece of
// content. Although a RenderMultiColumnFlowThread is laid out, it does not take up any space in its
// container. It's the RenderMultiColumnSet objects that take up the necessary amount of space, and
// make sure that the columns are painted and hit-tested correctly.
//
// If there is any column content inside the multicol container, we create a
// RenderMultiColumnSet. We only need to create multiple sets if there are spanners
// (column-span:all) in the multicol container. When a spanner is inserted, content preceding it
// gets its own set, and content succeeding it will get another set. The spanner itself will also
// get its own placeholder between the sets (RenderMultiColumnSpannerPlaceholder), so that it gets
// positioned and sized correctly. The column-span:all element is inside the flow thread, but its
// containing block is the multicol container.
//
// The width of the flow thread is the same as the column width. The width of a column set is the
// same as the content box width of the multicol container; in other words exactly enough to hold
// the number of columns to be used, stacked horizontally, plus column gaps between them.
//
// Since it's the first child of the multicol container, the flow thread is laid out first, albeit
// in a slightly special way, since it's not to take up any space in its ancestors. Afterwards, the
// column sets are laid out. Column sets get their height from the columns that they hold. In single
// column-row constrained height non-balancing cases without spanners this will simply be the same
// as the content height of the multicol container itself. In most other cases we'll have to
// calculate optimal column heights ourselves, though. This process is referred to as column
// balancing, and then we infer the column set height from the height of the flow thread portion
// occupied by each set.
//
// More on column balancing: the columns' height is unknown in the first layout pass when
// balancing. This means that we cannot insert any implicit (soft / unforced) breaks (and pagination
// struts) when laying out the contents of the flow thread. We'll just lay out everything in tall
// single strip. After the initial flow thread layout pass we can determine a tentative / minimal /
// initial column height. This is calculated by simply dividing the flow thread's height by the
// number of specified columns. In the layout pass that follows, we can insert breaks (and
// pagination struts) at column boundaries, since we now have a column height. It may very easily
// turn out that the calculated height wasn't enough, though. We'll notice this at end of layout. If
// we end up with too many columns (i.e. columns overflowing the multicol container), it wasn't
// enough. In this case we need to increase the column heights. We'll increase them by the lowest
// amount of space that could possibly affect where the breaks occur (see
// RenderMultiColumnSet::recordSpaceShortage()). We'll relayout (to find new break points and the
// new lowest amount of space increase that could affect where they occur, in case we need another
// round) until we've reached an acceptable height (where everything fits perfectly in the number of
// columns that we have specified). The rule of thumb is that we shouldn't have to perform more of
// such iterations than the number of columns that we have.
//
// For each layout iteration done for column balancing, the flow thread will need a deep layout if
// column heights changed in the previous pass, since column height changes may affect break points
// and pagination struts anywhere in the tree, and currently no way exists to do this in a more
// optimized manner.
//
// There's also some documentation online:
// https://sites.google.com/a/chromium.org/dev/developers/design-documents/multi-column-layout
class RenderMultiColumnFlowThread : public RenderFlowThread {
public:
    virtual ~RenderMultiColumnFlowThread();

    static RenderMultiColumnFlowThread* createAnonymous(Document&, RenderStyle* parentStyle);

    virtual bool isRenderMultiColumnFlowThread() const override final { return true; }

    RenderBlockFlow* multiColumnBlockFlow() const { return toRenderBlockFlow(parent()); }

    RenderMultiColumnSet* firstMultiColumnSet() const;
    RenderMultiColumnSet* lastMultiColumnSet() const;

    // Return the first column set or spanner placeholder.
    RenderBox* firstMultiColumnBox() const
    {
        return nextSiblingBox();
    }
    // Return the last column set or spanner placeholder.
    RenderBox* lastMultiColumnBox() const
    {
        RenderBox* lastSiblingBox = multiColumnBlockFlow()->lastChildBox();
        // The flow thread is the first child of the multicol container. If the flow thread is also
        // the last child, it means that there are no siblings; i.e. we have no column boxes.
        return lastSiblingBox != this ? lastSiblingBox : 0;
    }

    // Return the spanner placeholder that belongs to the spanner in the containing block chain, if
    // any. This includes the renderer for the element that actually establishes the spanner too.
    RenderMultiColumnSpannerPlaceholder* containingColumnSpannerPlaceholder(const RenderObject* descendant) const;

    // Populate the flow thread with what's currently its siblings. Called when a regular block
    // becomes a multicol container.
    void populate();

    // Empty the flow thread by moving everything to the parent. Remove all multicol specific
    // renderers. Then destroy the flow thread. Called when a multicol container becomes a regular
    // block.
    void evacuateAndDestroy();

    unsigned columnCount() const { return m_columnCount; }
    LayoutUnit columnHeightAvailable() const { return m_columnHeightAvailable; }
    void setColumnHeightAvailable(LayoutUnit available) { m_columnHeightAvailable = available; }
    virtual bool heightIsAuto() const { return !columnHeightAvailable() || multiColumnBlockFlow()->style()->columnFill() == ColumnFillBalance; }
    bool progressionIsInline() const { return m_progressionIsInline; }

    virtual LayoutSize columnOffset(const LayoutPoint&) const override final;

    // Do we need to set a new width and lay out?
    virtual bool needsNewWidth() const;

    void layoutColumns(bool relayoutChildren, SubtreeLayoutScope&);

    bool recalculateColumnHeights();

protected:
    RenderMultiColumnFlowThread();
    void setProgressionIsInline(bool isInline) { m_progressionIsInline = isInline; }

    virtual void layout() override;

private:
    void calculateColumnCountAndWidth(LayoutUnit& width, unsigned& count) const;
    void createAndInsertMultiColumnSet();
    void createAndInsertSpannerPlaceholder(RenderBox* spanner);
    virtual bool descendantIsValidColumnSpanner(RenderObject* descendant) const;

    virtual const char* renderName() const override;
    virtual void addRegionToThread(RenderMultiColumnSet*) override;
    virtual void willBeRemovedFromTree() override;
    virtual void flowThreadDescendantWasInserted(RenderObject*) override;
    virtual void flowThreadDescendantWillBeRemoved(RenderObject*) override;
    virtual void computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues&) const override;
    virtual void updateLogicalWidth() override;
    virtual void setPageBreak(LayoutUnit offset, LayoutUnit spaceShortage) override;
    virtual void updateMinimumPageHeight(LayoutUnit offset, LayoutUnit minHeight) override;
    virtual RenderMultiColumnSet* columnSetAtBlockOffset(LayoutUnit) const override;
    virtual bool addForcedRegionBreak(LayoutUnit, RenderObject* breakChild, bool isBefore, LayoutUnit* offsetBreakAdjustment = 0) override;
    virtual bool isPageLogicalHeightKnown() const override;

    unsigned m_columnCount; // The used value of column-count
    LayoutUnit m_columnHeightAvailable; // Total height available to columns, or 0 if auto.
    bool m_inBalancingPass; // Set when relayouting for column balancing.
    bool m_needsColumnHeightsRecalculation; // Set when we need to recalculate the column set heights after layout.
    bool m_progressionIsInline; // Always true for regular multicol. False for paged-y overflow.
    bool m_isBeingEvacuated;
};

} // namespace blink

#endif // RenderMultiColumnFlowThread_h