OLD | NEW |
(Empty) | |
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "core/layout/ng/inline/ng_inline_box_state.h" |
| 6 |
| 7 #include "core/layout/ng/inline/ng_inline_node.h" |
| 8 #include "core/layout/ng/inline/ng_line_box_fragment_builder.h" |
| 9 #include "core/layout/ng/inline/ng_text_fragment_builder.h" |
| 10 #include "core/style/ComputedStyle.h" |
| 11 |
| 12 namespace blink { |
| 13 |
| 14 void NGInlineBoxState::ComputeTextMetrics(const NGLayoutInlineItem& item, |
| 15 FontBaseline baseline_type) { |
| 16 const ComputedStyle& style = *item.Style(); |
| 17 text_metrics = NGLineHeightMetrics(style, baseline_type); |
| 18 text_top = -text_metrics.ascent; |
| 19 text_metrics.AddLeading(style.ComputedLineHeightAsFixed()); |
| 20 metrics.Unite(text_metrics); |
| 21 |
| 22 include_used_fonts = style.LineHeight().IsNegative(); |
| 23 } |
| 24 |
| 25 NGInlineBoxState* NGInlineLayoutStateStack::OnBeginPlaceItems( |
| 26 const ComputedStyle* line_style) { |
| 27 if (stack_.IsEmpty()) { |
| 28 // For the first line, push a box state for the line itself. |
| 29 stack_.Resize(1); |
| 30 NGInlineBoxState* box = &stack_.back(); |
| 31 box->fragment_start = 0; |
| 32 box->style = line_style; |
| 33 return box; |
| 34 } |
| 35 |
| 36 // For the following lines, clear states that are not shared across lines. |
| 37 for (auto& box : stack_) { |
| 38 box.fragment_start = 0; |
| 39 box.metrics = NGLineHeightMetrics(); |
| 40 DCHECK(box.pending_descendants.IsEmpty()); |
| 41 } |
| 42 return &stack_.back(); |
| 43 } |
| 44 |
| 45 NGInlineBoxState* NGInlineLayoutStateStack::OnOpenTag( |
| 46 const NGLayoutInlineItem& item, |
| 47 NGLineBoxFragmentBuilder* line_box, |
| 48 NGTextFragmentBuilder* text_builder) { |
| 49 stack_.Resize(stack_.size() + 1); |
| 50 NGInlineBoxState* box = &stack_.back(); |
| 51 box->fragment_start = line_box->Children().size(); |
| 52 box->style = item.Style(); |
| 53 text_builder->SetDirection(box->style->Direction()); |
| 54 return box; |
| 55 } |
| 56 |
| 57 NGInlineBoxState* NGInlineLayoutStateStack::OnCloseTag( |
| 58 const NGLayoutInlineItem& item, |
| 59 NGLineBoxFragmentBuilder* line_box, |
| 60 NGInlineBoxState* box) { |
| 61 EndBoxState(box, line_box); |
| 62 // TODO(kojii): When the algorithm restarts from a break token, the stack may |
| 63 // underflow. We need either synthesize a missing box state, or push all |
| 64 // parents on initialize. |
| 65 stack_.pop_back(); |
| 66 return &stack_.back(); |
| 67 } |
| 68 |
| 69 void NGInlineLayoutStateStack::OnEndPlaceItems( |
| 70 NGLineBoxFragmentBuilder* line_box) { |
| 71 for (auto it = stack_.rbegin(); it != stack_.rend(); ++it) { |
| 72 NGInlineBoxState* box = &(*it); |
| 73 EndBoxState(box, line_box); |
| 74 } |
| 75 line_box->UniteMetrics(stack_.front().metrics); |
| 76 } |
| 77 |
| 78 void NGInlineLayoutStateStack::EndBoxState(NGInlineBoxState* box, |
| 79 NGLineBoxFragmentBuilder* line_box) { |
| 80 ApplyBaselineShift(box, line_box); |
| 81 |
| 82 // Unite the metrics to the parent box. |
| 83 if (box != stack_.begin()) { |
| 84 box[-1].metrics.Unite(box->metrics); |
| 85 } |
| 86 } |
| 87 |
| 88 void NGInlineLayoutStateStack::ApplyBaselineShift( |
| 89 NGInlineBoxState* box, |
| 90 NGLineBoxFragmentBuilder* line_box) { |
| 91 // Compute descendants that depend on the layout size of this box if any. |
| 92 LayoutUnit baseline_shift; |
| 93 if (!box->pending_descendants.IsEmpty()) { |
| 94 for (const auto& child : box->pending_descendants) { |
| 95 switch (child.vertical_align) { |
| 96 case EVerticalAlign::kTextTop: |
| 97 case EVerticalAlign::kTop: |
| 98 baseline_shift = child.metrics.ascent - box->metrics.ascent; |
| 99 break; |
| 100 case EVerticalAlign::kTextBottom: |
| 101 case EVerticalAlign::kBottom: |
| 102 baseline_shift = box->metrics.descent - child.metrics.descent; |
| 103 break; |
| 104 default: |
| 105 NOTREACHED(); |
| 106 continue; |
| 107 } |
| 108 line_box->MoveChildrenInBlockDirection( |
| 109 baseline_shift, child.fragment_start, child.fragment_end); |
| 110 } |
| 111 box->pending_descendants.Clear(); |
| 112 } |
| 113 |
| 114 const ComputedStyle& style = *box->style; |
| 115 EVerticalAlign vertical_align = style.VerticalAlign(); |
| 116 if (vertical_align == EVerticalAlign::kBaseline) |
| 117 return; |
| 118 |
| 119 // 'vertical-align' aplies only to inline-level elements. |
| 120 if (box == stack_.begin()) |
| 121 return; |
| 122 |
| 123 // Check if there are any fragments to move. |
| 124 unsigned fragment_end = line_box->Children().size(); |
| 125 if (box->fragment_start == fragment_end) |
| 126 return; |
| 127 |
| 128 switch (vertical_align) { |
| 129 case EVerticalAlign::kSub: |
| 130 baseline_shift = style.ComputedFontSizeAsFixed() / 5 + 1; |
| 131 break; |
| 132 case EVerticalAlign::kSuper: |
| 133 baseline_shift = -(style.ComputedFontSizeAsFixed() / 3 + 1); |
| 134 break; |
| 135 case EVerticalAlign::kLength: { |
| 136 // 'Percentages: refer to the 'line-height' of the element itself'. |
| 137 // https://www.w3.org/TR/CSS22/visudet.html#propdef-vertical-align |
| 138 const Length& length = style.GetVerticalAlignLength(); |
| 139 LayoutUnit line_height = length.IsPercentOrCalc() |
| 140 ? style.ComputedLineHeightAsFixed() |
| 141 : box->text_metrics.LineHeight(); |
| 142 baseline_shift = -ValueForLength(length, line_height); |
| 143 break; |
| 144 } |
| 145 case EVerticalAlign::kMiddle: |
| 146 baseline_shift = (box->metrics.ascent - box->metrics.descent) / 2; |
| 147 if (const SimpleFontData* font_data = style.GetFont().PrimaryFont()) { |
| 148 baseline_shift -= LayoutUnit::FromFloatRound( |
| 149 font_data->GetFontMetrics().XHeight() / 2); |
| 150 } |
| 151 break; |
| 152 case EVerticalAlign::kBaselineMiddle: |
| 153 baseline_shift = (box->metrics.ascent - box->metrics.descent) / 2; |
| 154 break; |
| 155 case EVerticalAlign::kTop: |
| 156 case EVerticalAlign::kBottom: |
| 157 // 'top' and 'bottom' require the layout size of the line box. |
| 158 stack_.front().pending_descendants.push_back(NGPendingPositions{ |
| 159 box->fragment_start, fragment_end, box->metrics, vertical_align}); |
| 160 return; |
| 161 default: |
| 162 // Other values require the layout size of the parent box. |
| 163 SECURITY_CHECK(box != stack_.begin()); |
| 164 box[-1].pending_descendants.push_back(NGPendingPositions{ |
| 165 box->fragment_start, fragment_end, box->metrics, vertical_align}); |
| 166 return; |
| 167 } |
| 168 box->metrics.Move(baseline_shift); |
| 169 line_box->MoveChildrenInBlockDirection(baseline_shift, box->fragment_start, |
| 170 fragment_end); |
| 171 } |
| 172 |
| 173 } // namespace blink |
OLD | NEW |