Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(71)

Unified Diff: third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.cc

Issue 2803433002: [LayoutNG] Initial support for the 'vertical-align' property (Closed)
Patch Set: Move NGInlineBoxState to its own file Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.cc
diff --git a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.cc b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.cc
index d78d6b0761ca57ea16f2a0389a06c37edbcd456c..b8466de1d6513e7e0c37d33319b49c208d77b7a6 100644
--- a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.cc
+++ b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.cc
@@ -271,6 +271,11 @@ bool NGInlineLayoutAlgorithm::CreateLineUpToLastBreakOpportunity() {
// |last_break_opportunity|.
start_index_ = last_break_opportunity_index_;
start_offset_ = last_break_opportunity_offset_;
+ // If the offset is at the end of the item, move to the next item.
+ if (start_offset_ == items[start_index_].EndOffset() &&
+ start_index_ < items.size() - 1) {
+ start_index_++;
+ }
DCHECK_GE(end_position_, last_break_opportunity_position_);
end_position_ -= last_break_opportunity_position_;
last_break_opportunity_position_ = LayoutUnit();
@@ -304,8 +309,9 @@ void NGInlineLayoutAlgorithm::BidiReorder(
// runs instead of characters.
Vector<UBiDiLevel, 32> levels;
levels.ReserveInitialCapacity(line_item_chunks->size());
+ const Vector<NGLayoutInlineItem>& items = Node()->Items();
for (const auto& chunk : *line_item_chunks)
- levels.push_back(Node()->Items()[chunk.index].BidiLevel());
+ levels.push_back(items[chunk.index].BidiLevel());
Vector<int32_t, 32> indices_in_visual_order(line_item_chunks->size());
NGBidiParagraph::IndicesInVisualOrder(levels, &indices_in_visual_order);
@@ -318,6 +324,23 @@ void NGInlineLayoutAlgorithm::BidiReorder(
line_item_chunks_in_visual_order[visual_index] =
(*line_item_chunks)[logical_index];
}
+
+ // Keep Open before Close in the visual order.
+ HashMap<LayoutObject*, unsigned> first_index;
+ for (unsigned i = 0; i < line_item_chunks_in_visual_order.size(); i++) {
+ LineItemChunk& chunk = line_item_chunks_in_visual_order[i];
+ const NGLayoutInlineItem& item = items[chunk.index];
+ if (item.Type() != NGLayoutInlineItem::kOpenTag &&
+ item.Type() != NGLayoutInlineItem::kCloseTag) {
+ continue;
+ }
+ auto result = first_index.insert(item.GetLayoutObject(), i);
+ if (!result.is_new_entry && item.Type() == NGLayoutInlineItem::kOpenTag) {
+ std::swap(line_item_chunks_in_visual_order[i],
+ line_item_chunks_in_visual_order[result.stored_value->value]);
+ }
+ }
+
line_item_chunks->Swap(line_item_chunks_in_visual_order);
}
@@ -383,35 +406,29 @@ bool NGInlineLayoutAlgorithm::PlaceItems(
// Compute heights of all inline items by placing the dominant baseline at 0.
// The baseline is adjusted after the height of the line box is computed.
NGTextFragmentBuilder text_builder(Node());
+ NGInlineBoxState* box = OnBeginPlaceItems();
LayoutUnit inline_size;
for (const auto& line_item_chunk : line_item_chunks) {
const NGLayoutInlineItem& item = items[line_item_chunk.index];
- // Skip bidi controls.
- if (!item.GetLayoutObject())
- continue;
-
- LayoutUnit block_start;
+ LayoutUnit line_top;
if (item.Type() == NGLayoutInlineItem::kText) {
DCHECK(item.GetLayoutObject()->IsText());
- const ComputedStyle* style = item.Style();
- // The direction of a fragment is the CSS direction to resolve logical
- // properties, not the resolved bidi direction.
- text_builder.SetDirection(style->Direction())
- .SetInlineSize(line_item_chunk.inline_size);
-
- // |InlineTextBoxPainter| sets the baseline at |top +
- // ascent-of-primary-font|. Compute |top| to match.
- NGLineHeightMetrics metrics(*style, baseline_type_);
- block_start = -metrics.ascent;
- metrics.AddLeading(style->ComputedLineHeightAsFixed());
- text_builder.SetBlockSize(metrics.LineHeight());
- line_box.UniteMetrics(metrics);
-
+ if (box->text_metrics.IsEmpty())
+ box->ComputeTextMetrics(item, baseline_type_);
+ line_top = box->text_top;
+ text_builder.SetSize(
+ {line_item_chunk.inline_size, box->text_metrics.LineHeight()});
// Take all used fonts into account if 'line-height: normal'.
- if (style->LineHeight().IsNegative())
+ if (box->include_used_fonts)
AccumulateUsedFonts(item, line_item_chunk, &line_box);
+ } else if (item.Type() == NGLayoutInlineItem::kOpenTag) {
+ box = OnOpenTag(item, &line_box, &text_builder);
+ continue;
+ } else if (item.Type() == NGLayoutInlineItem::kCloseTag) {
+ box = OnCloseTag(item, &line_box, box);
+ continue;
} else if (item.Type() == NGLayoutInlineItem::kAtomicInline) {
- block_start = PlaceAtomicInline(item, &line_box, &text_builder);
+ line_top = PlaceAtomicInline(item, &line_box, &text_builder);
} else if (item.Type() == NGLayoutInlineItem::kOutOfFlowPositioned) {
// TODO(layout-dev): Report the correct static position for the out of
// flow descendant. We can't do this here yet as it doesn't know the
@@ -435,7 +452,7 @@ bool NGInlineLayoutAlgorithm::PlaceItems(
NGLogicalOffset logical_offset(
inline_size + current_opportunity_.InlineStartOffset() -
ConstraintSpace().BfcOffset().inline_offset,
- block_start);
+ line_top);
line_box.AddChild(std::move(text_fragment), logical_offset);
inline_size += line_item_chunk.inline_size;
}
@@ -444,6 +461,8 @@ bool NGInlineLayoutAlgorithm::PlaceItems(
return true; // The line was empty.
}
+ OnEndPlaceItems(&line_box);
+
// The baselines are always placed at pixel boundaries. Not doing so results
// in incorrect layout of text decorations, most notably underlines.
LayoutUnit baseline = content_size_ + line_box.Metrics().ascent;
@@ -480,6 +499,166 @@ bool NGInlineLayoutAlgorithm::PlaceItems(
return true;
}
+// Initialize the box state stack for a new line.
+// @return The initial box state for the line.
+NGInlineBoxState* NGInlineLayoutAlgorithm::OnBeginPlaceItems() {
+ if (box_states_.IsEmpty()) {
+ // For the first line, push a box state for the line itself.
+ box_states_.Resize(1);
+ NGInlineBoxState* box = &box_states_.back();
+ box->fragment_start = 0;
+ box->style = &LineStyle();
+ return box;
+ }
+
+ // For the following lines, clear states that are not shared across lines.
+ for (auto& box : box_states_) {
+ box.fragment_start = 0;
+ box.metrics = NGLineHeightMetrics();
+ DCHECK(box.pending_descendants.IsEmpty());
+ }
+ return &box_states_.back();
+}
+
+// Push a box state stack.
+NGInlineBoxState* NGInlineLayoutAlgorithm::OnOpenTag(
+ const NGLayoutInlineItem& item,
+ NGLineBoxFragmentBuilder* line_box,
+ NGTextFragmentBuilder* text_builder) {
+ box_states_.Resize(box_states_.size() + 1);
+ NGInlineBoxState* box = &box_states_.back();
+ box->fragment_start = line_box->Children().size();
+ box->style = item.Style();
+ text_builder->SetDirection(box->style->Direction());
+ return box;
+}
+
+// Pop a box state stack.
+NGInlineBoxState* NGInlineLayoutAlgorithm::OnCloseTag(
+ const NGLayoutInlineItem& item,
+ NGLineBoxFragmentBuilder* line_box,
+ NGInlineBoxState* box) {
+ EndBoxState(box, line_box);
+ // TODO(kojii): When the algorithm restarts from a break token, the stack may
+ // underflow. We need either synthesize a missing box state, or push all
+ // parents on initialize.
+ box_states_.pop_back();
+ return &box_states_.back();
+}
+
+// Compute all the pending positioning at the end of a line.
+void NGInlineLayoutAlgorithm::OnEndPlaceItems(
+ NGLineBoxFragmentBuilder* line_box) {
+ for (auto it = box_states_.rbegin(); it != box_states_.rend(); ++it) {
+ NGInlineBoxState* box = &(*it);
+ EndBoxState(box, line_box);
+ }
+ line_box->UniteMetrics(box_states_.front().metrics);
+}
+
+// End of a box state, either explicitly by close tag, or implicitly at the end
+// of a line.
+void NGInlineLayoutAlgorithm::EndBoxState(NGInlineBoxState* box,
+ NGLineBoxFragmentBuilder* line_box) {
+ ApplyBaselineShift(box, line_box);
+
+ // Unite the metrics to the parent box.
+ if (box != box_states_.begin()) {
+ box[-1].metrics.Unite(box->metrics);
+ }
+}
+
+// Compute vertical position for the 'vertical-align' property.
+// The timing to apply varies by values; some values apply at the layout of
+// the box was computed. Other values apply when the layout of the parent or
+// the line box was computed.
+// https://www.w3.org/TR/CSS22/visudet.html#propdef-vertical-align
+// https://www.w3.org/TR/css-inline-3/#propdef-vertical-align
+void NGInlineLayoutAlgorithm::ApplyBaselineShift(
+ NGInlineBoxState* box,
+ NGLineBoxFragmentBuilder* line_box) {
+ // Compute descendants that depend on the layout size of this box if any.
+ LayoutUnit baseline_shift;
+ if (!box->pending_descendants.IsEmpty()) {
+ for (const auto& child : box->pending_descendants) {
+ switch (child.vertical_align) {
+ case EVerticalAlign::kTextTop:
+ case EVerticalAlign::kTop:
+ baseline_shift = child.metrics.ascent - box->metrics.ascent;
+ break;
+ case EVerticalAlign::kTextBottom:
+ case EVerticalAlign::kBottom:
+ baseline_shift = box->metrics.descent - child.metrics.descent;
+ break;
+ default:
+ NOTREACHED();
+ continue;
+ }
+ line_box->MoveChildrenInBlockDirection(
+ baseline_shift, child.fragment_start, child.fragment_end);
+ }
+ box->pending_descendants.Clear();
+ }
+
+ const ComputedStyle& style = *box->style;
+ EVerticalAlign vertical_align = style.VerticalAlign();
+ if (vertical_align == EVerticalAlign::kBaseline)
+ return;
+
+ // 'vertical-align' aplies only to inline-level elements.
+ if (box == box_states_.begin())
+ return;
+
+ // Check if there are any fragments to move.
+ unsigned fragment_end = line_box->Children().size();
+ if (box->fragment_start == fragment_end)
+ return;
+
+ switch (vertical_align) {
+ case EVerticalAlign::kSub:
+ baseline_shift = style.ComputedFontSizeAsFixed() / 5 + 1;
+ break;
+ case EVerticalAlign::kSuper:
+ baseline_shift = -(style.ComputedFontSizeAsFixed() / 3 + 1);
+ break;
+ case EVerticalAlign::kLength: {
+ // 'Percentages: refer to the 'line-height' of the element itself'.
+ // https://www.w3.org/TR/CSS22/visudet.html#propdef-vertical-align
+ const Length& length = style.GetVerticalAlignLength();
+ LayoutUnit line_height = length.IsPercentOrCalc()
+ ? style.ComputedLineHeightAsFixed()
+ : box->text_metrics.LineHeight();
+ baseline_shift = -ValueForLength(length, line_height);
+ break;
+ }
+ case EVerticalAlign::kMiddle:
+ baseline_shift = (box->metrics.ascent - box->metrics.descent) / 2;
+ if (const SimpleFontData* font_data = style.GetFont().PrimaryFont()) {
+ baseline_shift -= LayoutUnit::FromFloatRound(
+ font_data->GetFontMetrics().XHeight() / 2);
+ }
+ break;
+ case EVerticalAlign::kBaselineMiddle:
+ baseline_shift = (box->metrics.ascent - box->metrics.descent) / 2;
+ break;
+ case EVerticalAlign::kTop:
+ case EVerticalAlign::kBottom:
+ // 'top' and 'bottom' require the layout size of the line box.
+ box_states_.front().pending_descendants.push_back(NGPendingPositions{
+ box->fragment_start, fragment_end, box->metrics, vertical_align});
+ return;
+ default:
+ // Other values require the layout size of the parent box.
+ SECURITY_CHECK(box != box_states_.begin());
+ box[-1].pending_descendants.push_back(NGPendingPositions{
+ box->fragment_start, fragment_end, box->metrics, vertical_align});
+ return;
+ }
+ box->metrics.Move(baseline_shift);
+ line_box->MoveChildrenInBlockDirection(baseline_shift, box->fragment_start,
+ fragment_end);
+}
+
void NGInlineLayoutAlgorithm::AccumulateUsedFonts(
const NGLayoutInlineItem& item,
const LineItemChunk& line_item_chunk,
@@ -508,21 +687,23 @@ LayoutUnit NGInlineLayoutAlgorithm::PlaceAtomicInline(
// TODO(kojii): Try to eliminate the wrapping text fragment and use the
// |fragment| directly. Currently |CopyFragmentDataToLayoutBlockFlow|
// requires a text fragment.
- text_builder->SetInlineSize(fragment.InlineSize()).SetBlockSize(block_size);
+ text_builder->SetSize({fragment.InlineSize(), block_size});
// TODO(kojii): Add baseline position to NGPhysicalFragment.
- LayoutBox* box = ToLayoutBox(item.GetLayoutObject());
+ LayoutBox* layout_box = ToLayoutBox(item.GetLayoutObject());
LineDirectionMode line_direction_mode =
IsHorizontalWritingMode() ? LineDirectionMode::kHorizontalLine
: LineDirectionMode::kVerticalLine;
- LayoutUnit baseline_offset(box->BaselinePosition(
+ LayoutUnit baseline_offset(layout_box->BaselinePosition(
baseline_type_, IsFirstLine(), line_direction_mode));
- line_box->UniteMetrics({baseline_offset, block_size - baseline_offset});
+
+ NGLineHeightMetrics metrics(baseline_offset, block_size - baseline_offset);
+ box_states_.back().metrics.Unite(metrics);
// TODO(kojii): Figure out what to do with OOF in NGLayoutResult.
// Floats are ok because atomic inlines are BFC?
- return -baseline_offset;
+ return -metrics.ascent;
}
void NGInlineLayoutAlgorithm::FindNextLayoutOpportunity() {

Powered by Google App Engine
This is Rietveld 408576698