blob: b892c3f20142e9c2202fe30506396d954050c8be [file] [log] [blame]
// Copyright 2020 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.
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_TABLE_NG_TABLE_LAYOUT_ALGORITHM_TYPES_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_TABLE_NG_TABLE_LAYOUT_ALGORITHM_TYPES_H_
#include "base/memory/scoped_refptr.h"
#include "base/optional.h"
#include "third_party/blink/renderer/core/layout/min_max_sizes.h"
#include "third_party/blink/renderer/core/layout/ng/geometry/ng_box_strut.h"
#include "third_party/blink/renderer/core/layout/ng/ng_block_node.h"
#include "third_party/blink/renderer/core/style/computed_style_constants.h"
#include "third_party/blink/renderer/platform/geometry/length.h"
#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
namespace blink {
class ComputedStyle;
class NGBlockNode;
class NGLayoutInputNode;
// Define constraint classes for NGTableLayoutAlgorithm.
class CORE_EXPORT NGTableTypes {
public:
static constexpr LayoutUnit kTableMaxInlineSize = LayoutUnit::Max();
// Inline constraint for a single cell.
// Takes into account the cell style, and min/max content-sizes.
struct CellInlineConstraint {
DISALLOW_NEW();
LayoutUnit min_inline_size;
LayoutUnit max_inline_size;
base::Optional<float> percent; // 100% is stored as 100.0f
LayoutUnit percent_border_padding; // Border/padding used for percentage
// size resolution.
bool is_constrained; // True if this cell has a specified inline-size.
void Encompass(const CellInlineConstraint&);
};
// Inline constraints for a cell that span multiple columns.
struct ColspanCell {
DISALLOW_NEW();
CellInlineConstraint cell_inline_constraint;
wtf_size_t start_column;
wtf_size_t span;
ColspanCell(const CellInlineConstraint& cell_inline_constraint,
unsigned start_column,
unsigned span)
: cell_inline_constraint(cell_inline_constraint),
start_column(start_column),
span(span) {}
};
// Constraint for a column.
struct Column {
DISALLOW_NEW();
Column(const base::Optional<LayoutUnit>& min_inline_size,
const base::Optional<LayoutUnit>& max_inline_size,
const base::Optional<float>& percent,
LayoutUnit percent_border_padding,
bool is_constrained,
bool is_collapsed,
bool is_table_fixed,
bool is_mergeable)
: min_inline_size(min_inline_size),
max_inline_size(max_inline_size),
percent(percent),
percent_border_padding(percent_border_padding),
is_constrained(is_constrained),
is_collapsed(is_collapsed),
is_table_fixed(is_table_fixed),
is_mergeable(is_mergeable) {}
Column() = default;
// These members are initialized from <col> and <colgroup>, then they
// accumulate data from |CellInlineConstraint|s.
base::Optional<LayoutUnit> min_inline_size;
base::Optional<LayoutUnit> max_inline_size;
base::Optional<float> percent; // 100% is stored as 100.0f
LayoutUnit percent_border_padding; // Border/padding used for percentage
// size resolution.
// True if any cell for this column is constrained.
bool is_constrained = false;
bool is_collapsed = false;
bool is_table_fixed = false;
bool is_mergeable = false;
void Encompass(const base::Optional<NGTableTypes::CellInlineConstraint>&);
LayoutUnit ResolvePercentInlineSize(
LayoutUnit percentage_resolution_inline_size) const {
return std::max(
min_inline_size.value_or(LayoutUnit()),
LayoutUnit(*percent * percentage_resolution_inline_size / 100) +
percent_border_padding);
}
bool IsFixed() const {
return is_constrained && !percent && max_inline_size;
}
};
// Block constraint for a single cell.
struct CellBlockConstraint {
DISALLOW_NEW();
LayoutUnit min_block_size;
LayoutUnit baseline;
NGBoxStrut border_box_borders;
wtf_size_t row_index;
wtf_size_t column_index;
wtf_size_t rowspan;
EVerticalAlign vertical_align;
bool is_constrained; // True if this cell has a specified block-size.
CellBlockConstraint(LayoutUnit min_block_size,
LayoutUnit baseline,
NGBoxStrut border_box_borders,
wtf_size_t row_index,
wtf_size_t column_index,
wtf_size_t rowspan,
EVerticalAlign vertical_align,
bool is_constrained)
: min_block_size(min_block_size),
baseline(baseline),
border_box_borders(border_box_borders),
row_index(row_index),
column_index(column_index),
rowspan(rowspan),
vertical_align(vertical_align),
is_constrained(is_constrained) {}
};
// RowspanCells span multiple rows.
struct RowspanCell {
DISALLOW_NEW();
CellBlockConstraint cell_block_constraint;
wtf_size_t start_row;
wtf_size_t span;
RowspanCell(wtf_size_t start_row,
wtf_size_t span,
const CellBlockConstraint& cell_block_constraint)
: cell_block_constraint(cell_block_constraint),
start_row(start_row),
span(span) {}
// Original Legacy sorting criteria from
// CompareRowspanCellsInHeightDistributionOrder
bool operator<(const NGTableTypes::RowspanCell& rhs) const {
// Returns true if a |RowspanCell| is completely contained within another
// |RowspanCell|.
auto IsEnclosed = [](const NGTableTypes::RowspanCell& c1,
const NGTableTypes::RowspanCell& c2) {
return (c1.start_row >= c2.start_row) &&
(c1.start_row + c1.span) <= (c2.start_row + c2.span);
};
// If cells span the same rows, bigger cell is distributed first.
if (start_row == rhs.start_row && span == rhs.span) {
return cell_block_constraint.min_block_size >
rhs.cell_block_constraint.min_block_size;
}
// If one cell is fully enclosed by another, inner cell wins.
if (IsEnclosed(*this, rhs))
return true;
if (IsEnclosed(rhs, *this))
return false;
// Lower rows wins.
return start_row < rhs.start_row;
}
};
struct Row {
DISALLOW_NEW();
LayoutUnit block_size;
LayoutUnit baseline;
base::Optional<float> percent; // 100% is stored as 100.0f
wtf_size_t start_cell_index;
wtf_size_t cell_count;
// |is_constrained| is true if row has specified block-size, or contains
// constrained cells.
bool is_constrained;
bool has_baseline_aligned_percentage_block_size_descendants;
bool has_rowspan_start; // True if row originates a TD with rowspan > 1
bool is_collapsed;
};
struct ColumnLocation {
LayoutUnit offset; // inline offset from table edge.
LayoutUnit size;
bool is_collapsed;
};
struct Section {
wtf_size_t start_row;
wtf_size_t rowspan;
LayoutUnit block_size;
base::Optional<float> percent;
bool is_constrained;
bool is_tbody;
bool needs_redistribution;
};
static Column CreateColumn(const ComputedStyle&,
base::Optional<LayoutUnit> default_inline_size,
bool is_table_fixed);
static CellInlineConstraint CreateCellInlineConstraint(
const NGBlockNode&,
WritingMode table_writing_mode,
bool is_fixed_layout,
const NGBoxStrut& cell_border,
const NGBoxStrut& cell_padding,
bool has_collapsed_borders);
static Section CreateSection(const NGLayoutInputNode&,
wtf_size_t start_row,
wtf_size_t rowspan,
LayoutUnit block_size,
bool treat_as_tbody);
static CellBlockConstraint CreateCellBlockConstraint(
const NGLayoutInputNode&,
LayoutUnit computed_block_size,
LayoutUnit baseline,
const NGBoxStrut& border_box_borders,
wtf_size_t row_index,
wtf_size_t column_index,
wtf_size_t rowspan);
static RowspanCell CreateRowspanCell(
wtf_size_t row_index,
wtf_size_t rowspan,
CellBlockConstraint*,
base::Optional<LayoutUnit> css_block_size);
// Columns are cached by LayoutNGTable, and need to be RefCounted.
typedef base::RefCountedData<WTF::Vector<Column>> Columns;
// Inline constraints are optional because we need to distinguish between an
// empty cell, and a non-existent cell.
using CellInlineConstraints = Vector<base::Optional<CellInlineConstraint>>;
using ColspanCells = Vector<ColspanCell>;
using Caption = MinMaxSizes;
using CellBlockConstraints = Vector<CellBlockConstraint>;
using RowspanCells = Vector<RowspanCell>;
using Rows = Vector<Row>;
using Sections = Vector<Section>;
using ColumnLocations = Vector<ColumnLocation>;
};
class NGTableGroupedChildrenIterator;
// Table's children grouped by type.
// When iterating through members, make sure to handle out_of_flows correctly.
struct NGTableGroupedChildren {
DISALLOW_NEW();
public:
explicit NGTableGroupedChildren(const NGBlockNode& table);
Vector<NGBlockNode> captions; // CAPTION
Vector<NGBlockNode> columns; // COLGROUP, COL
NGBlockNode header; // first THEAD
Vector<NGBlockNode> bodies; // TBODY/multiple THEAD/TFOOT
NGBlockNode footer; // first TFOOT
// Default iterators iterate over tbody-like (THEAD/TBODY/TFOOT) elements.
NGTableGroupedChildrenIterator begin() const;
NGTableGroupedChildrenIterator end() const;
};
// Iterates table's sections in order:
// thead, tbody, tfoot
class NGTableGroupedChildrenIterator {
enum CurrentSection { kNone, kHead, kBody, kFoot, kEnd };
public:
explicit NGTableGroupedChildrenIterator(
const NGTableGroupedChildren& grouped_children,
bool is_end = false);
NGTableGroupedChildrenIterator& operator++();
NGBlockNode operator*() const;
bool operator==(const NGTableGroupedChildrenIterator& rhs) const;
bool operator!=(const NGTableGroupedChildrenIterator& rhs) const;
// True if section should be treated as tbody
bool TreatAsTBody() const { return current_section_ == kBody; }
private:
void AdvanceToNonEmptySection();
const NGTableGroupedChildren& grouped_children_;
Vector<NGBlockNode>::const_iterator body_iterator_;
CurrentSection current_section_{kNone};
};
} // namespace blink
WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(
blink::NGTableTypes::CellInlineConstraint)
WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(
blink::NGTableTypes::ColspanCell)
WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(blink::NGTableTypes::Column)
WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(
blink::NGTableTypes::CellBlockConstraint)
WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(
blink::NGTableTypes::RowspanCell)
WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(blink::NGTableTypes::Row)
WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(
blink::NGTableTypes::ColumnLocation)
WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(blink::NGTableTypes::Section)
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_TABLE_NG_TABLE_LAYOUT_ALGORITHM_TYPES_H_