chromium/third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.h

/*
 * 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 THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_MULTI_COLUMN_FLOW_THREAD_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_MULTI_COLUMN_FLOW_THREAD_H_

#include "base/dcheck_is_on.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/layout/layout_flow_thread.h"

namespace blink {

class LayoutMultiColumnSet;
class LayoutMultiColumnSpannerPlaceholder;

// Flow thread implementation for CSS multicol. This will be inserted as an
// anonymous child block of the actual multicol container (i.e. the
// LayoutBlockFlow whose style computes to non-auto column-count and/or
// column-width). LayoutMultiColumnFlowThread 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 layoutObject insertion.
//
// Apart from this flow thread child, the multicol container will also have
// LayoutMultiColumnSet 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 LayoutMultiColumnFlowThread is laid out, it does not take up any
// space in its container. It's the LayoutMultiColumnSet 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
// LayoutMultiColumnSet. 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 (LayoutMultiColumnSpannerPlaceholder), 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.
//
// Some invariants for the layout tree structure for multicol:
// - A multicol container is always a LayoutBlockFlow
// - Every multicol container has one and only one LayoutMultiColumnFlowThread
// - All multicol DOM children and pseudo-elements associated with the multicol
//   container are reparented into the flow thread.
// - The LayoutMultiColumnFlowThread is the first child of the multicol
//   container.
// - A multicol container may only have LayoutMultiColumnFlowThread,
//   LayoutMultiColumnSet and LayoutMultiColumnSpannerPlaceholder children.
// - A LayoutMultiColumnSet may not be adjacent to another LayoutMultiColumnSet;
//   there are no use-cases for it, and there are also implementation
//   limitations behind this requirement.
// - The flow thread is not in the containing block chain for children that are
//   not to be laid out in columns. This means column spanners and absolutely
//   positioned children whose containing block is outside column content
// - Each spanner (column-span:all) establishes a
//   LayoutMultiColumnSpannerPlaceholder
//
// 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. 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://www.chromium.org/developers/design-documents/multi-column-layout
class CORE_EXPORT LayoutMultiColumnFlowThread final : public LayoutFlowThread {};

template <>
struct DowncastTraits<LayoutMultiColumnFlowThread> {};

}  // namespace blink

#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_MULTI_COLUMN_FLOW_THREAD_H_