blob: 2968a2022ef5d3d187933a5eefd605b95f9da999 [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.
#include "third_party/blink/renderer/core/layout/ng/table/layout_ng_table_cell.h"
#include "third_party/blink/renderer/core/html/html_table_cell_element.h"
#include "third_party/blink/renderer/core/html/table_constants.h"
#include "third_party/blink/renderer/core/layout/layout_analyzer.h"
#include "third_party/blink/renderer/core/layout/layout_object_factory.h"
#include "third_party/blink/renderer/core/layout/layout_view.h"
#include "third_party/blink/renderer/core/layout/ng/ng_block_node.h"
#include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h"
#include "third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_node.h"
#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
#include "third_party/blink/renderer/core/layout/ng/table/layout_ng_table.h"
#include "third_party/blink/renderer/core/layout/ng/table/layout_ng_table_row.h"
#include "third_party/blink/renderer/core/paint/ng/ng_table_cell_paint_invalidator.h"
namespace blink {
LayoutNGTableCell::LayoutNGTableCell(Element* element)
: LayoutNGBlockFlowMixin<LayoutBlockFlow>(element) {
UpdateColAndRowSpanFlags();
}
void LayoutNGTableCell::InvalidateLayoutResultCacheAfterMeasure() const {
NOT_DESTROYED();
if (LayoutBox* row = ParentBox()) {
DCHECK(row->IsTableRow());
row->ClearLayoutResults();
if (LayoutBox* section = row->ParentBox()) {
DCHECK(section->IsTableSection());
section->ClearLayoutResults();
}
}
}
LayoutRectOutsets LayoutNGTableCell::BorderBoxOutsets() const {
NOT_DESTROYED();
// TODO(1061423) This function should not be called before layout.
// ScrollAnchor::Examine does. Example trigger:
// ScrollTimelineTest.TimelineInvalidationWhenScrollerDisplayPropertyChanges
// DCHECK_GE(PhysicalFragmentCount(), 0u);
if (PhysicalFragmentCount() > 0)
return GetPhysicalFragment(0)->Borders().ToLayoutRectOutsets();
return LayoutNGBlockFlowMixin<LayoutBlockFlow>::BorderBoxOutsets();
}
LayoutUnit LayoutNGTableCell::BorderTop() const {
NOT_DESTROYED();
// TODO(1061423) Should return cell border, not fragment border.
// To compute cell border, cell needs to know its starting row
// and column, which are not available here.
// PhysicalFragmentCount() > 0 check should not be necessary,
// but it is because of TextAutosizer/ScrollAnchoring.
if (Table()->ShouldCollapseBorders() && PhysicalFragmentCount() > 0) {
return GetPhysicalFragment(0)->Borders().top;
}
return LayoutNGBlockFlowMixin<LayoutBlockFlow>::BorderTop();
}
LayoutUnit LayoutNGTableCell::BorderBottom() const {
NOT_DESTROYED();
// TODO(1061423) Should return cell border, not fragment border.
if (Table()->ShouldCollapseBorders() && PhysicalFragmentCount() > 0) {
return GetPhysicalFragment(0)->Borders().bottom;
}
return LayoutNGBlockFlowMixin<LayoutBlockFlow>::BorderBottom();
}
LayoutUnit LayoutNGTableCell::BorderLeft() const {
NOT_DESTROYED();
// TODO(1061423) Should return cell border, not fragment border.
if (Table()->ShouldCollapseBorders() && PhysicalFragmentCount() > 0) {
return GetPhysicalFragment(0)->Borders().left;
}
return LayoutNGBlockFlowMixin<LayoutBlockFlow>::BorderLeft();
}
LayoutUnit LayoutNGTableCell::BorderRight() const {
NOT_DESTROYED();
// TODO(1061423) Should return cell border, not fragment border.
if (Table()->ShouldCollapseBorders() && PhysicalFragmentCount() > 0) {
return GetPhysicalFragment(0)->Borders().right;
}
return LayoutNGBlockFlowMixin<LayoutBlockFlow>::BorderRight();
}
LayoutNGTable* LayoutNGTableCell::Table() const {
NOT_DESTROYED();
if (LayoutObject* parent = Parent()) {
if (LayoutObject* grandparent = parent->Parent()) {
return To<LayoutNGTable>(grandparent->Parent());
}
}
return nullptr;
}
void LayoutNGTableCell::UpdateBlockLayout(bool relayout_children) {
NOT_DESTROYED();
LayoutAnalyzer::BlockScope analyzer(*this);
if (IsOutOfFlowPositioned()) {
UpdateOutOfFlowBlockLayout();
return;
}
UpdateInFlowBlockLayout();
}
void LayoutNGTableCell::StyleDidChange(StyleDifference diff,
const ComputedStyle* old_style) {
NOT_DESTROYED();
if (LayoutNGTable* table = Table()) {
if ((old_style && !old_style->BorderVisuallyEqual(StyleRef())) ||
(diff.TextDecorationOrColorChanged() &&
StyleRef().HasBorderColorReferencingCurrentColor())) {
table->GridBordersChanged();
}
}
LayoutNGBlockFlowMixin<LayoutBlockFlow>::StyleDidChange(diff, old_style);
}
void LayoutNGTableCell::WillBeRemovedFromTree() {
NOT_DESTROYED();
if (LayoutNGTable* table = Table())
table->TableGridStructureChanged();
LayoutNGMixin<LayoutBlockFlow>::WillBeRemovedFromTree();
}
void LayoutNGTableCell::ColSpanOrRowSpanChanged() {
NOT_DESTROYED();
UpdateColAndRowSpanFlags();
if (LayoutNGTable* table = Table()) {
table->SetNeedsLayoutAndIntrinsicWidthsRecalc(
layout_invalidation_reason::kTableChanged);
table->TableGridStructureChanged();
}
}
LayoutBox* LayoutNGTableCell::CreateAnonymousBoxWithSameTypeAs(
const LayoutObject* parent) const {
NOT_DESTROYED();
return LayoutObjectFactory::CreateAnonymousTableCellWithParent(*parent);
}
LayoutBlock* LayoutNGTableCell::StickyContainer() const {
NOT_DESTROYED();
return Table();
}
void LayoutNGTableCell::InvalidatePaint(
const PaintInvalidatorContext& context) const {
NOT_DESTROYED();
NGTableCellPaintInvalidator(*this, context).InvalidatePaint();
}
bool LayoutNGTableCell::BackgroundIsKnownToBeOpaqueInRect(
const PhysicalRect& local_rect) const {
NOT_DESTROYED();
// If this object has layer, the area of collapsed borders should be
// transparent to expose the collapsed borders painted on the underlying
// layer.
if (HasLayer() && Table()->ShouldCollapseBorders())
return false;
return LayoutNGBlockFlowMixin<
LayoutBlockFlow>::BackgroundIsKnownToBeOpaqueInRect(local_rect);
}
Length LayoutNGTableCell::StyleOrColLogicalWidth() const {
NOT_DESTROYED();
// TODO(atotic) TablesNG cannot easily get col width before layout.
return StyleRef().LogicalWidth();
}
// TODO(crbug.com/1079133): Used by AXLayoutObject::RowIndex,
// verify behaviour is correct.
unsigned LayoutNGTableCell::RowIndex() const {
NOT_DESTROYED();
return To<LayoutNGTableRow>(Parent())->RowIndex();
}
// TODO(crbug.com/1079133): Used by AXLayoutObject::CellForColumnAndRow,
// verify behaviour is correct.
unsigned LayoutNGTableCell::ResolvedRowSpan() const {
NOT_DESTROYED();
return ParsedRowSpan();
}
unsigned LayoutNGTableCell::AbsoluteColumnIndex() const {
NOT_DESTROYED();
if (PhysicalFragmentCount() > 0) {
return GetPhysicalFragment(0)->TableCellColumnIndex();
}
NOTREACHED() << "AbsoluteColumnIndex did not find cell";
return 0;
}
unsigned LayoutNGTableCell::ColSpan() const {
NOT_DESTROYED();
if (!has_col_span_)
return 1;
return ParseColSpanFromDOM();
}
unsigned LayoutNGTableCell::ParseColSpanFromDOM() const {
NOT_DESTROYED();
if (const auto* cell_element = DynamicTo<HTMLTableCellElement>(GetNode())) {
unsigned span = cell_element->colSpan();
DCHECK_GE(span, kMinColSpan);
DCHECK_LE(span, kMaxColSpan);
return span;
}
return kDefaultRowSpan;
}
unsigned LayoutNGTableCell::ParseRowSpanFromDOM() const {
NOT_DESTROYED();
if (const auto* cell_element = DynamicTo<HTMLTableCellElement>(GetNode())) {
unsigned span = cell_element->rowSpan();
DCHECK_GE(span, kMinRowSpan);
DCHECK_LE(span, kMaxRowSpan);
return span;
}
return kDefaultColSpan;
}
void LayoutNGTableCell::UpdateColAndRowSpanFlags() {
NOT_DESTROYED();
// Colspan or rowspan are rare, so we keep the values in DOM.
has_col_span_ = ParseColSpanFromDOM() != kDefaultColSpan;
has_rowspan_ = ParseRowSpanFromDOM() != kDefaultRowSpan;
}
LayoutNGTableInterface* LayoutNGTableCell::TableInterface() const {
NOT_DESTROYED();
return ToInterface<LayoutNGTableInterface>(Parent()->Parent()->Parent());
}
LayoutNGTableCellInterface* LayoutNGTableCell::NextCellInterface() const {
NOT_DESTROYED();
return ToInterface<LayoutNGTableCellInterface>(LayoutObject::NextSibling());
}
LayoutNGTableCellInterface* LayoutNGTableCell::PreviousCellInterface() const {
NOT_DESTROYED();
return ToInterface<LayoutNGTableCellInterface>(
LayoutObject::PreviousSibling());
}
LayoutNGTableRowInterface* LayoutNGTableCell::RowInterface() const {
NOT_DESTROYED();
return ToInterface<LayoutNGTableRowInterface>(Parent());
}
LayoutNGTableSectionInterface* LayoutNGTableCell::SectionInterface() const {
NOT_DESTROYED();
return ToInterface<LayoutNGTableSectionInterface>(Parent()->Parent());
}
} // namespace blink