Index: third_party/WebKit/Source/core/layout/ng/inline/ng_inline_box_state.cc |
diff --git a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_box_state.cc b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_box_state.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..a6cd1870212e1637e052d7798c4eb71de4f935f0 |
--- /dev/null |
+++ b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_box_state.cc |
@@ -0,0 +1,173 @@ |
+// Copyright 2017 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 "core/layout/ng/inline/ng_inline_box_state.h" |
+ |
+#include "core/layout/ng/inline/ng_inline_node.h" |
+#include "core/layout/ng/inline/ng_line_box_fragment_builder.h" |
+#include "core/layout/ng/inline/ng_text_fragment_builder.h" |
+#include "core/style/ComputedStyle.h" |
+ |
+namespace blink { |
+ |
+void NGInlineBoxState::ComputeTextMetrics(const NGLayoutInlineItem& item, |
+ FontBaseline baseline_type) { |
+ const ComputedStyle& style = *item.Style(); |
+ text_metrics = NGLineHeightMetrics(style, baseline_type); |
+ text_top = -text_metrics.ascent; |
+ text_metrics.AddLeading(style.ComputedLineHeightAsFixed()); |
+ metrics.Unite(text_metrics); |
+ |
+ include_used_fonts = style.LineHeight().IsNegative(); |
+} |
+ |
+NGInlineBoxState* NGInlineLayoutStateStack::OnBeginPlaceItems( |
+ const ComputedStyle* line_style) { |
+ if (stack_.IsEmpty()) { |
+ // For the first line, push a box state for the line itself. |
+ stack_.Resize(1); |
+ NGInlineBoxState* box = &stack_.back(); |
+ box->fragment_start = 0; |
+ box->style = line_style; |
+ return box; |
+ } |
+ |
+ // For the following lines, clear states that are not shared across lines. |
+ for (auto& box : stack_) { |
+ box.fragment_start = 0; |
+ box.metrics = NGLineHeightMetrics(); |
+ DCHECK(box.pending_descendants.IsEmpty()); |
+ } |
+ return &stack_.back(); |
+} |
+ |
+NGInlineBoxState* NGInlineLayoutStateStack::OnOpenTag( |
+ const NGLayoutInlineItem& item, |
+ NGLineBoxFragmentBuilder* line_box, |
+ NGTextFragmentBuilder* text_builder) { |
+ stack_.Resize(stack_.size() + 1); |
+ NGInlineBoxState* box = &stack_.back(); |
+ box->fragment_start = line_box->Children().size(); |
+ box->style = item.Style(); |
+ text_builder->SetDirection(box->style->Direction()); |
+ return box; |
+} |
+ |
+NGInlineBoxState* NGInlineLayoutStateStack::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. |
+ stack_.pop_back(); |
+ return &stack_.back(); |
+} |
+ |
+void NGInlineLayoutStateStack::OnEndPlaceItems( |
+ NGLineBoxFragmentBuilder* line_box) { |
+ for (auto it = stack_.rbegin(); it != stack_.rend(); ++it) { |
+ NGInlineBoxState* box = &(*it); |
+ EndBoxState(box, line_box); |
+ } |
+ line_box->UniteMetrics(stack_.front().metrics); |
+} |
+ |
+void NGInlineLayoutStateStack::EndBoxState(NGInlineBoxState* box, |
+ NGLineBoxFragmentBuilder* line_box) { |
+ ApplyBaselineShift(box, line_box); |
+ |
+ // Unite the metrics to the parent box. |
+ if (box != stack_.begin()) { |
+ box[-1].metrics.Unite(box->metrics); |
+ } |
+} |
+ |
+void NGInlineLayoutStateStack::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 == stack_.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. |
+ stack_.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 != stack_.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); |
+} |
+ |
+} // namespace blink |