| Index: third_party/WebKit/Source/core/layout/ColumnBalancer.h
|
| diff --git a/third_party/WebKit/Source/core/layout/ColumnBalancer.h b/third_party/WebKit/Source/core/layout/ColumnBalancer.h
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..938f3a8d207e7c991a956204ad34cab91d862c18
|
| --- /dev/null
|
| +++ b/third_party/WebKit/Source/core/layout/ColumnBalancer.h
|
| @@ -0,0 +1,157 @@
|
| +// Copyright 2015 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "core/layout/MultiColumnFragmentainerGroup.h"
|
| +
|
| +namespace blink {
|
| +
|
| +// A column balancer traverses the portion of the subtree of a flow thread that belongs to a given
|
| +// fragmentainer group, in order to collect certain data to be used for column balancing. This is an
|
| +// abstract class that just walks the subtree and leaves it to subclasses to actualy collect data.
|
| +class ColumnBalancer {
|
| +protected:
|
| + ColumnBalancer(const MultiColumnFragmentainerGroup&);
|
| +
|
| + const MultiColumnFragmentainerGroup& group() const { return m_group; }
|
| +
|
| + // Flow thread offset for the layout object that we're currently examining.
|
| + LayoutUnit flowThreadOffset() const { return m_flowThreadOffset; }
|
| +
|
| + // Return true if the specified offset is at the top of a column, as long as it's not the first
|
| + // column in the multicol container.
|
| + bool isFirstAfterBreak(LayoutUnit flowThreadOffset) const
|
| + {
|
| + if (flowThreadOffset != m_group.columnLogicalTopForOffset(flowThreadOffset))
|
| + return false; // Not at the top of a column.
|
| + // The first column in the first group isn't after any break.
|
| + return flowThreadOffset > m_group.logicalTopInFlowThread() || !m_group.isFirstGroup();
|
| + }
|
| +
|
| + // Examine and collect column balancing data from a layout box that has been found to intersect
|
| + // with this fragmentainer group. Does not recurse into children. flowThreadOffset() will
|
| + // return the offset from |box| to the flow thread. Two hooks are provided here. The first one
|
| + // is called right after entering and before traversing the subtree of the box, and the second
|
| + // one right after having traversed the subtree.
|
| + virtual void examineBoxAfterEntering(const LayoutBox&) = 0;
|
| + virtual void examineBoxBeforeLeaving(const LayoutBox&) = 0;
|
| +
|
| + // Examine and collect column balancing data from a line that has been found to intersect with
|
| + // this fragmentainer group. Does not recurse into layout objects on that line.
|
| + virtual void examineLine(const RootInlineBox&) = 0;
|
| +
|
| + // Examine and collect column balancing data for everything in the fragmentainer group. Will
|
| + // trigger calls to examineBoxAfterEntering(), examineBoxBeforeLeaving() and examineLine() for
|
| + // interesting boxes and lines.
|
| + void traverse();
|
| +
|
| +private:
|
| + void traverseSubtree(const LayoutBox&);
|
| +
|
| + const MultiColumnFragmentainerGroup& m_group;
|
| + LayoutUnit m_flowThreadOffset;
|
| +};
|
| +
|
| +// After an initial layout pass, we know the height of the contents of a flow thread. Based on
|
| +// this, we can estimate an initial minimal column height. This class will collect the necessary
|
| +// information from the layout objects to make this estimate. This estimate may be used to perform
|
| +// another layout iteration. If we after such a layout iteration cannot fit the contents with the
|
| +// given column height without creating overflowing columns, we will have to stretch the columns by
|
| +// some amount and lay out again. We may need to do this several times (but typically not more
|
| +// times than the number of columns that we have). The amount to stretch is provided by the sister
|
| +// of this class, named MinimumSpaceShortageFinder.
|
| +class InitialColumnHeightFinder final : public ColumnBalancer {
|
| +public:
|
| + static LayoutUnit initialMinimalBalancedHeight(const MultiColumnFragmentainerGroup& group)
|
| + {
|
| + return InitialColumnHeightFinder(group).initialMinimalBalancedHeight();
|
| + }
|
| +
|
| +private:
|
| + InitialColumnHeightFinder(const MultiColumnFragmentainerGroup&);
|
| +
|
| + LayoutUnit initialMinimalBalancedHeight() const;
|
| +
|
| + void examineBoxAfterEntering(const LayoutBox&);
|
| + void examineBoxBeforeLeaving(const LayoutBox&);
|
| + void examineLine(const RootInlineBox&);
|
| +
|
| + // Add a content run, specified by its end position. A content run is appended at every
|
| + // forced/explicit break and at the end of the column set. The content runs are used to
|
| + // determine where implicit/soft breaks will occur, in order to calculate an initial column
|
| + // height.
|
| + void addContentRun(LayoutUnit endOffsetInFlowThread);
|
| +
|
| + // Return the index of the content run with the currently tallest columns, taking all implicit
|
| + // breaks assumed so far into account.
|
| + unsigned contentRunIndexWithTallestColumns() const;
|
| +
|
| + // Given the current list of content runs, make assumptions about where we need to insert
|
| + // implicit breaks (if there's room for any at all; depending on the number of explicit breaks),
|
| + // and store the results. This is needed in order to balance the columns.
|
| + void distributeImplicitBreaks();
|
| +
|
| + // A run of content without explicit (forced) breaks; i.e. a flow thread portion between two
|
| + // explicit breaks, between flow thread start and an explicit break, between an explicit break
|
| + // and flow thread end, or, in cases when there are no explicit breaks at all: between flow
|
| + // thread portion start and flow thread portion end. We need to know where the explicit breaks
|
| + // are, in order to figure out where the implicit breaks will end up, so that we get the columns
|
| + // properly balanced. A content run starts out as representing one single column, and will
|
| + // represent one additional column for each implicit break "inserted" there.
|
| + class ContentRun {
|
| + public:
|
| + ContentRun(LayoutUnit breakOffset)
|
| + : m_breakOffset(breakOffset)
|
| + , m_assumedImplicitBreaks(0) { }
|
| +
|
| + unsigned assumedImplicitBreaks() const { return m_assumedImplicitBreaks; }
|
| + void assumeAnotherImplicitBreak() { m_assumedImplicitBreaks++; }
|
| + LayoutUnit breakOffset() const { return m_breakOffset; }
|
| +
|
| + // Return the column height that this content run would require, considering the implicit
|
| + // breaks assumed so far.
|
| + LayoutUnit columnLogicalHeight(LayoutUnit startOffset) const { return ceilf((m_breakOffset - startOffset).toFloat() / float(m_assumedImplicitBreaks + 1)); }
|
| +
|
| + private:
|
| + LayoutUnit m_breakOffset; // Flow thread offset where this run ends.
|
| + unsigned m_assumedImplicitBreaks; // Number of implicit breaks in this run assumed so far.
|
| + };
|
| + Vector<ContentRun, 32> m_contentRuns;
|
| +};
|
| +
|
| +// If we have previously used InitialColumnHeightFinder to estimate an initial column height, and
|
| +// that didn't result in tall enough columns, we need subsequent layout passes where we increase
|
| +// the column height by the minimum space shortage at column breaks. This class finds the minimum
|
| +// space shortage after having laid out with the current column height.
|
| +class MinimumSpaceShortageFinder final : public ColumnBalancer {
|
| +public:
|
| + MinimumSpaceShortageFinder(const MultiColumnFragmentainerGroup&);
|
| +
|
| + LayoutUnit minimumSpaceShortage() const { return m_minimumSpaceShortage; }
|
| + unsigned forcedBreaksCount() const { return m_forcedBreaksCount; }
|
| +
|
| +private:
|
| + void examineBoxAfterEntering(const LayoutBox&);
|
| + void examineBoxBeforeLeaving(const LayoutBox&);
|
| + void examineLine(const RootInlineBox&);
|
| +
|
| + void recordSpaceShortage(LayoutUnit shortage)
|
| + {
|
| + // Only positive values are interesting (and allowed) here. Zero space shortage may
|
| + // be reported when we're at the top of a column and the element has zero
|
| + // height.
|
| + if (shortage > 0)
|
| + m_minimumSpaceShortage = std::min(m_minimumSpaceShortage, shortage);
|
| + }
|
| +
|
| + // The smallest amout of space shortage that caused a column break.
|
| + LayoutUnit m_minimumSpaceShortage;
|
| +
|
| + // Set when breaking before a block, and we're looking for the first unbreakable descendant, in
|
| + // order to report correct space shortage for that one.
|
| + LayoutUnit m_pendingStrut;
|
| +
|
| + unsigned m_forcedBreaksCount;
|
| +};
|
| +
|
| +} // namespace blink
|
|
|