blob: a67b9e0acfe867d1ecc0c25bc2c7c04e6882bb95 [file] [log] [blame]
/*
* 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 LayoutMultiColumnSet_h
#define LayoutMultiColumnSet_h
#include "core/CoreExport.h"
#include "core/layout/LayoutMultiColumnFlowThread.h"
#include "core/layout/MultiColumnFragmentainerGroup.h"
#include "wtf/Vector.h"
namespace blink {
// A set of columns in a multicol container. A column set is inserted as an anonymous child of the
// actual multicol container (i.e. the layoutObject whose style computes to non-auto column-count and/or
// column-width), next to the flow thread. There'll be one column set for each contiguous run of
// column content. The only thing that can interrupt a contiguous run of column content is a column
// spanner, which means that if there are no spanners, there'll only be one column set.
//
// Since a spanner interrupts an otherwise contiguous run of column content, inserting one may
// result in the creation of additional new column sets. A placeholder for the spanning layoutObject has
// to be placed in between the column sets that come before and after the spanner, if there's
// actually column content both before and after the spanner.
//
// A column set has no children on its own, but is merely used to slice a portion of the tall
// "single-column" flow thread into actual columns visually, to convert from flow thread coordinates
// to visual ones. It is in charge of both positioning columns correctly relatively to the parent
// multicol container, and to calculate the correct translation for each column's contents, and to
// paint any rules between them. LayoutMultiColumnSet objects are used for painting, hit testing,
// and any other type of operation that requires mapping from flow thread coordinates to visual
// coordinates.
//
// Columns are normally laid out in the inline progression direction, but if the multicol container
// is inside another fragmentation context (e.g. paged media, or an another multicol container), we
// may need to group the columns, so that we get one MultiColumnFragmentainerGroup for each outer
// fragmentainer (page / column) that the inner multicol container lives in. Each fragmentainer
// group has its own column height, but the column height is uniform within a group.
class CORE_EXPORT LayoutMultiColumnSet : public LayoutBlockFlow {
public:
static LayoutMultiColumnSet* createAnonymous(LayoutFlowThread&, const ComputedStyle& parentStyle);
const MultiColumnFragmentainerGroup& firstFragmentainerGroup() const { return m_fragmentainerGroups.first(); }
const MultiColumnFragmentainerGroup& lastFragmentainerGroup() const { return m_fragmentainerGroups.last(); }
MultiColumnFragmentainerGroup& fragmentainerGroupAtFlowThreadOffset(LayoutUnit flowThreadOffset)
{
return m_fragmentainerGroups[fragmentainerGroupIndexAtFlowThreadOffset(flowThreadOffset)];
}
const MultiColumnFragmentainerGroup& fragmentainerGroupAtFlowThreadOffset(LayoutUnit flowThreadOffset) const
{
return m_fragmentainerGroups[fragmentainerGroupIndexAtFlowThreadOffset(flowThreadOffset)];
}
const MultiColumnFragmentainerGroup& fragmentainerGroupAtVisualPoint(const LayoutPoint&) const;
const MultiColumnFragmentainerGroupList& fragmentainerGroups() const { return m_fragmentainerGroups; }
bool isOfType(LayoutObjectType type) const override { return type == LayoutObjectLayoutMultiColumnSet || LayoutBlockFlow::isOfType(type); }
bool canHaveChildren() const final { return false; }
// Return the width and height of a single column or page in the set.
LayoutUnit pageLogicalWidth() const { return flowThread()->logicalWidth(); }
LayoutUnit pageLogicalHeightForOffset(LayoutUnit) const;
LayoutUnit pageRemainingLogicalHeightForOffset(LayoutUnit, PageBoundaryRule) const;
bool isPageLogicalHeightKnown() const;
LayoutUnit tallestUnbreakableLogicalHeight() const { return m_tallestUnbreakableLogicalHeight; }
void propagateTallestUnbreakableLogicalHeight(LayoutUnit value) { m_tallestUnbreakableLogicalHeight = std::max(value, m_tallestUnbreakableLogicalHeight); }
LayoutUnit nextLogicalTopForUnbreakableContent(LayoutUnit flowThreadOffset, LayoutUnit contentLogicalHeight) const;
LayoutFlowThread* flowThread() const { return m_flowThread; }
LayoutBlockFlow* multiColumnBlockFlow() const { return toLayoutBlockFlow(parent()); }
LayoutMultiColumnFlowThread* multiColumnFlowThread() const { return toLayoutMultiColumnFlowThread(flowThread()); }
LayoutMultiColumnSet* nextSiblingMultiColumnSet() const;
LayoutMultiColumnSet* previousSiblingMultiColumnSet() const;
// Return true if we have a fragmentainer group that can hold a column at the specified flow thread block offset.
bool hasFragmentainerGroupForColumnAt(LayoutUnit bottomOffsetInFlowThread, PageBoundaryRule) const;
MultiColumnFragmentainerGroup& appendNewFragmentainerGroup();
// Logical top relative to the content edge of the multicol container.
LayoutUnit logicalTopFromMulticolContentEdge() const;
LayoutUnit logicalTopInFlowThread() const;
LayoutUnit logicalBottomInFlowThread() const;
LayoutUnit logicalHeightInFlowThread() const { return logicalBottomInFlowThread() - logicalTopInFlowThread(); }
// Return the amount of flow thread contents that the specified fragmentainer group can hold
// without overflowing.
LayoutUnit fragmentainerGroupCapacity(const MultiColumnFragmentainerGroup &group) const { return group.logicalHeight() * usedColumnCount(); }
LayoutRect flowThreadPortionRect() const;
LayoutRect flowThreadPortionOverflowRect() const;
LayoutRect overflowRectForFlowThreadPortion(const LayoutRect& flowThreadPortionRect, bool isFirstPortion, bool isLastPortion) const;
// The used CSS value of column-count, i.e. how many columns there are room for without overflowing.
unsigned usedColumnCount() const { return multiColumnFlowThread()->columnCount(); }
bool heightIsAuto() const;
// Find the column that contains the given block offset, and return the translation needed to
// get from flow thread coordinates to visual coordinates.
LayoutSize flowThreadTranslationAtOffset(LayoutUnit) const;
LayoutPoint visualPointToFlowThreadPoint(const LayoutPoint& visualPoint) const;
// (Re-)calculate the column height if it's auto. This is first and foremost needed by sets that
// are to balance the column height, but even when it isn't to be balanced, this is necessary if
// the multicol container's height is constrained.
bool recalculateColumnHeight();
// Reset previously calculated column height. Will mark for layout if needed.
void resetColumnHeight();
void storeOldPosition() { m_oldLogicalTop = logicalTop(); }
bool isInitialHeightCalculated() const { return m_initialHeightCalculated; }
// Layout of flow thread content that's to be rendered inside this column set begins. This
// happens at the beginning of flow thread layout, and when advancing from a previous column set
// or spanner to this one.
void beginFlow(LayoutUnit offsetInFlowThread);
// Layout of flow thread content that was to be rendered inside this column set has
// finished. This happens at end of flow thread layout, and when advancing to the next column
// set or spanner.
void endFlow(LayoutUnit offsetInFlowThread);
void styleDidChange(StyleDifference, const ComputedStyle* oldStyle) override;
void layout() override;
void computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const final;
void attachToFlowThread();
void detachFromFlowThread();
// The top of the page nearest to the specified block offset. All in flowthread coordinates.
LayoutUnit pageLogicalTopForOffset(LayoutUnit offset) const;
LayoutRect fragmentsBoundingBox(const LayoutRect& boundingBoxInFlowThread) const;
void collectLayerFragments(PaintLayerFragments&, const LayoutRect& layerBoundingBox, const LayoutRect& dirtyRect);
LayoutUnit columnGap() const;
// The "CSS actual" value of column-count. This includes overflowing columns, if any.
unsigned actualColumnCount() const;
const char* name() const override { return "LayoutMultiColumnSet"; }
protected:
LayoutMultiColumnSet(LayoutFlowThread*);
private:
unsigned fragmentainerGroupIndexAtFlowThreadOffset(LayoutUnit) const;
void insertedIntoTree() final;
void willBeRemovedFromTree() final;
bool isSelfCollapsingBlock() const override { return false; }
void computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues&) const override;
PositionWithAffinity positionForPoint(const LayoutPoint&) override;
void paintObject(const PaintInfo&, const LayoutPoint& paintOffset) const override;
void addOverflowFromChildren() override;
MultiColumnFragmentainerGroupList m_fragmentainerGroups;
LayoutFlowThread* m_flowThread;
// Height of the tallest piece of unbreakable content. This is the minimum column logical height
// required to avoid fragmentation where it shouldn't occur (inside unbreakable content, between
// orphans and widows, etc.). We only store this so that outer fragmentation contexts (if any)
// can query this when calculating their own minimum. Note that we don't store this value in
// every fragmentainer group (but rather here, in the column set), since we only need the
// largest one among them.
LayoutUnit m_tallestUnbreakableLogicalHeight;
// Logical top in previous layout pass.
LayoutUnit m_oldLogicalTop;
bool m_initialHeightCalculated;
};
DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutMultiColumnSet, isLayoutMultiColumnSet());
} // namespace blink
#endif // LayoutMultiColumnSet_h