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

Side by Side Diff: third_party/WebKit/Source/core/layout/ng/inline/ng_line_breaker.cc

Issue 2865903002: [LayoutNG] Inline margin/border/padding, inter-item breaking, and tests (Closed)
Patch Set: TestExpectations Created 3 years, 6 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 unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "core/layout/ng/inline/ng_line_breaker.h" 5 #include "core/layout/ng/inline/ng_line_breaker.h"
6 6
7 #include "core/layout/ng/inline/ng_inline_break_token.h" 7 #include "core/layout/ng/inline/ng_inline_break_token.h"
8 #include "core/layout/ng/inline/ng_inline_layout_algorithm.h" 8 #include "core/layout/ng/inline/ng_inline_layout_algorithm.h"
9 #include "core/layout/ng/inline/ng_inline_node.h" 9 #include "core/layout/ng/inline/ng_inline_node.h"
10 #include "core/layout/ng/inline/ng_text_fragment.h" 10 #include "core/layout/ng/inline/ng_text_fragment.h"
11 #include "core/layout/ng/ng_box_fragment.h" 11 #include "core/layout/ng/ng_box_fragment.h"
12 #include "core/layout/ng/ng_break_token.h" 12 #include "core/layout/ng/ng_break_token.h"
13 #include "core/layout/ng/ng_constraint_space.h" 13 #include "core/layout/ng/ng_constraint_space.h"
14 #include "core/layout/ng/ng_fragment_builder.h" 14 #include "core/layout/ng/ng_fragment_builder.h"
15 #include "core/layout/ng/ng_layout_opportunity_iterator.h" 15 #include "core/layout/ng/ng_layout_opportunity_iterator.h"
16 #include "core/layout/ng/ng_length_utils.h" 16 #include "core/layout/ng/ng_length_utils.h"
17 #include "core/style/ComputedStyle.h" 17 #include "core/style/ComputedStyle.h"
18 #include "platform/fonts/shaping/HarfBuzzShaper.h" 18 #include "platform/fonts/shaping/HarfBuzzShaper.h"
19 #include "platform/fonts/shaping/ShapingLineBreaker.h" 19 #include "platform/fonts/shaping/ShapingLineBreaker.h"
20 #include "platform/text/TextBreakIterator.h"
21 20
22 namespace blink { 21 namespace blink {
23 22
24 namespace { 23 namespace {
25 24
26 // Use a mock of ShapingLineBreaker for test/debug purposes. 25 // Use a mock of ShapingLineBreaker for test/debug purposes.
27 #define MOCK_SHAPE_LINE 26 #define MOCK_SHAPE_LINE
28 27
29 #if defined(MOCK_SHAPE_LINE) 28 #if defined(MOCK_SHAPE_LINE)
30 // The mock for ShapingLineBreaker::ShapeLine(). 29 // The mock for ShapingLineBreaker::ShapeLine().
31 // Given the design of ShapingLineBreaker, expected semantics are: 30 // See BreakText() for the expected semantics.
32 // - The returned offset is always > item.StartOffset().
33 // - offset < item.EndOffset():
34 // - width <= available_width: the break opportunity to fit is found.
35 // - width > available_width: the first break opportunity did not fit.
36 // - offset == item.EndOffset():
37 // - width <= available_width: the break opportunity at the end of the item
38 // fits.
39 // - width > available_width: the first break opportunity is at the end of
40 // the item and it does not fit.
41 // - offset > item.EndOffset():, the first break opportunity is beyond the
42 // end of item and thus cannot measure. In this case, inline_size shows the
43 // width until the end of the item. It may fit or may not.
44 std::pair<unsigned, LayoutUnit> ShapeLineMock( 31 std::pair<unsigned, LayoutUnit> ShapeLineMock(
45 const NGInlineItem& item, 32 const NGInlineItem& item,
46 unsigned offset, 33 unsigned offset,
47 LayoutUnit available_width, 34 LayoutUnit available_width,
48 const LazyLineBreakIterator& break_iterator) { 35 const LazyLineBreakIterator& break_iterator) {
49 bool has_break_opportunities = false; 36 bool has_break_opportunities = false;
50 LayoutUnit inline_size; 37 LayoutUnit inline_size;
51 while (true) { 38 while (true) {
52 unsigned next_break = break_iterator.NextBreakOpportunity(offset + 1); 39 unsigned next_break = break_iterator.NextBreakOpportunity(offset + 1);
53 LayoutUnit next_inline_size = 40 LayoutUnit next_inline_size =
54 inline_size + 41 inline_size +
55 item.InlineSize(offset, std::min(next_break, item.EndOffset())); 42 item.InlineSize(offset, std::min(next_break, item.EndOffset()));
56 if (next_inline_size > available_width) { 43 if (next_inline_size > available_width) {
57 if (!has_break_opportunities) 44 if (!has_break_opportunities)
58 return std::make_pair(next_break, next_inline_size); 45 return std::make_pair(next_break, next_inline_size);
59 return std::make_pair(offset, inline_size); 46 return std::make_pair(offset, inline_size);
60 } 47 }
61 if (next_break >= item.EndOffset()) 48 if (next_break >= item.EndOffset())
62 return std::make_pair(next_break, next_inline_size); 49 return std::make_pair(next_break, next_inline_size);
63 offset = next_break; 50 offset = next_break;
64 inline_size = next_inline_size; 51 inline_size = next_inline_size;
65 has_break_opportunities = true; 52 has_break_opportunities = true;
66 } 53 }
67 } 54 }
68 #endif 55 #endif
69 56
70 LineBreakType GetLineBreakType(const ComputedStyle& style) {
71 if (style.AutoWrap()) {
72 if (style.WordBreak() == EWordBreak::kBreakAll ||
73 style.WordBreak() == EWordBreak::kBreakWord)
74 return LineBreakType::kBreakAll;
75 if (style.WordBreak() == EWordBreak::kKeepAll)
76 return LineBreakType::kKeepAll;
77 }
78 return LineBreakType::kNormal;
79 }
80
81 } // namespace 57 } // namespace
82 58
83 NGLineBreaker::NGLineBreaker(NGInlineNode* node, 59 NGLineBreaker::NGLineBreaker(NGInlineNode* node,
84 const NGConstraintSpace* space, 60 const NGConstraintSpace* space,
85 NGInlineBreakToken* break_token) 61 NGInlineBreakToken* break_token)
86 : node_(node), constraint_space_(space), item_index_(0), offset_(0) { 62 : node_(node),
63 constraint_space_(space),
64 item_index_(0),
65 offset_(0),
66 break_iterator_(node->Text()) {
87 if (break_token) { 67 if (break_token) {
88 item_index_ = break_token->ItemIndex(); 68 item_index_ = break_token->ItemIndex();
89 offset_ = break_token->TextOffset(); 69 offset_ = break_token->TextOffset();
90 node->AssertOffset(item_index_, offset_); 70 node->AssertOffset(item_index_, offset_);
91 } 71 }
92 } 72 }
93 73
94 void NGLineBreaker::NextLine(NGInlineItemResults* item_results, 74 void NGLineBreaker::NextLine(NGInlineItemResults* item_results,
95 NGInlineLayoutAlgorithm* algorithm) { 75 NGInlineLayoutAlgorithm* algorithm) {
96 BreakLine(item_results, algorithm); 76 BreakLine(item_results, algorithm);
97 77
98 // TODO(kojii): When editing, or caret is enabled, trailing spaces at wrap 78 // TODO(kojii): When editing, or caret is enabled, trailing spaces at wrap
99 // point should not be removed. For other cases, we can a) remove, b) leave 79 // point should not be removed. For other cases, we can a) remove, b) leave
100 // characters without glyphs, or c) leave both characters and glyphs without 80 // characters without glyphs, or c) leave both characters and glyphs without
101 // measuring. Need to decide which one works the best. 81 // measuring. Need to decide which one works the best.
102 SkipCollapsibleWhitespaces(); 82 SkipCollapsibleWhitespaces();
103 } 83 }
104 84
105 void NGLineBreaker::BreakLine(NGInlineItemResults* item_results, 85 void NGLineBreaker::BreakLine(NGInlineItemResults* item_results,
106 NGInlineLayoutAlgorithm* algorithm) { 86 NGInlineLayoutAlgorithm* algorithm) {
107 DCHECK(item_results->IsEmpty()); 87 DCHECK(item_results->IsEmpty());
108 const Vector<NGInlineItem>& items = node_->Items(); 88 const Vector<NGInlineItem>& items = node_->Items();
109 const String& text = node_->Text();
110 const ComputedStyle& style = node_->Style(); 89 const ComputedStyle& style = node_->Style();
111 LazyLineBreakIterator break_iterator(text, style.LocaleForLineBreakIterator(), 90 UpdateBreakIterator(style);
112 GetLineBreakType(style));
113 #if !defined(MOCK_SHAPE_LINE) 91 #if !defined(MOCK_SHAPE_LINE)
92 // TODO(kojii): Instantiate in the constructor.
114 HarfBuzzShaper shaper(text.Characters16(), text.length()); 93 HarfBuzzShaper shaper(text.Characters16(), text.length());
115 #endif 94 #endif
116 LayoutUnit available_width = algorithm->AvailableWidth(); 95 available_width_ = algorithm->AvailableWidth();
117 LayoutUnit position; 96 position_ = LayoutUnit(0);
97 LineBreakState state = LineBreakState::kNotBreakable;
98
118 while (item_index_ < items.size()) { 99 while (item_index_ < items.size()) {
ikilpatrick 2017/05/30 16:05:26 If i get time this week i'll play with pulling thi
kojii 2017/05/30 17:56:18 Sounds good, will work on other files this week.
100 // CloseTag prohibits to break before.
119 const NGInlineItem& item = items[item_index_]; 101 const NGInlineItem& item = items[item_index_];
102 if (item.Type() == NGInlineItem::kCloseTag) {
103 item_results->push_back(
104 NGInlineItemResult(item_index_, offset_, item.EndOffset()));
105 HandleCloseTag(item, &item_results->back());
106 continue;
107 }
108
109 if (state == LineBreakState::kBreakAfterTrailings)
110 return;
111 if (state == LineBreakState::kIsBreakable && position_ > available_width_)
112 return HandleOverflow(item_results);
113
120 item_results->push_back( 114 item_results->push_back(
121 NGInlineItemResult(item_index_, offset_, item.EndOffset())); 115 NGInlineItemResult(item_index_, offset_, item.EndOffset()));
122 NGInlineItemResult* item_result = &item_results->back(); 116 NGInlineItemResult* item_result = &item_results->back();
117 if (item.Type() == NGInlineItem::kText) {
118 state = HandleText(item, item_result);
119 } else if (item.Type() == NGInlineItem::kAtomicInline) {
120 state = HandleAtomicInline(item, item_result);
121 } else if (item.Type() == NGInlineItem::kControl) {
122 state = HandleControlItem(item, item_result);
123 if (state == LineBreakState::kForcedBreak)
124 return;
125 } else if (item.Type() == NGInlineItem::kOpenTag) {
126 HandleOpenTag(item, item_result);
127 state = LineBreakState::kNotBreakable;
128 } else if (item.Type() == NGInlineItem::kFloating) {
129 HandleFloat(item, item_results, algorithm);
130 } else {
131 MoveToNextOf(item);
132 }
133 }
134 if (state == LineBreakState::kIsBreakable && position_ > available_width_)
135 return HandleOverflow(item_results);
136 }
123 137
124 // If the start offset is at the item boundary, try to add the entire item. 138 NGLineBreaker::LineBreakState NGLineBreaker::HandleText(
125 if (offset_ == item.StartOffset()) { 139 const NGInlineItem& item,
126 if (item.Type() == NGInlineItem::kText) { 140 NGInlineItemResult* item_result) {
127 item_result->inline_size = item.InlineSize(); 141 DCHECK_EQ(item.Type(), NGInlineItem::kText);
128 } else if (item.Type() == NGInlineItem::kAtomicInline) {
129 LayoutAtomicInline(item, item_result);
130 } else if (item.Type() == NGInlineItem::kControl) {
131 if (HandleControlItem(item, text, item_result, position)) {
132 MoveToNextOf(item);
133 break;
134 }
135 } else if (item.Type() == NGInlineItem::kFloating) {
136 algorithm->LayoutAndPositionFloat(position, item.GetLayoutObject());
137 // Floats may change the available width if they fit.
138 available_width = algorithm->AvailableWidth();
139 // Floats are already positioned in the container_builder.
140 item_results->pop_back();
141 MoveToNextOf(item);
142 continue;
143 } else {
144 MoveToNextOf(item);
145 continue;
146 }
147 LayoutUnit next_position = position + item_result->inline_size;
148 if (next_position <= available_width) {
149 MoveToNextOf(item);
150 position = next_position;
151 continue;
152 }
153 142
154 // The entire item does not fit. Handle non-text items as overflow, 143 // If the start offset is at the item boundary, try to add the entire item.
155 // since only text item is breakable. 144 if (offset_ == item.StartOffset()) {
156 if (item.Type() != NGInlineItem::kText) { 145 item_result->inline_size = item.InlineSize();
157 MoveToNextOf(item); 146 LayoutUnit next_position = position_ + item_result->inline_size;
158 return HandleOverflow(item_results, break_iterator); 147 if (!auto_wrap_ || next_position <= available_width_) {
159 } 148 position_ = next_position;
149 MoveToNextOf(item);
150 if (auto_wrap_ && break_iterator_.IsBreakable(item.EndOffset()))
151 return LineBreakState::kIsBreakable;
152 item_result->prohibit_break_after = true;
153 return LineBreakState::kNotBreakable;
160 } 154 }
155 }
161 156
162 // Either the start or the break is in the mid of a text item. 157 if (auto_wrap_) {
163 DCHECK_EQ(item.Type(), NGInlineItem::kText); 158 // Try to break inside of this text item.
164 DCHECK_LT(offset_, item.EndOffset()); 159 BreakText(item_result, item, available_width_ - position_);
165 break_iterator.SetLocale(item.Style()->LocaleForLineBreakIterator()); 160 position_ += item_result->inline_size;
166 break_iterator.SetBreakType(GetLineBreakType(*item.Style())); 161
162 bool is_overflow = position_ > available_width_;
163 item_result->no_break_opportunities_inside = is_overflow;
164 if (item_result->end_offset < item.EndOffset()) {
165 offset_ = item_result->end_offset;
166 return is_overflow ? LineBreakState::kIsBreakable
167 : LineBreakState::kBreakAfterTrailings;
168 }
169 MoveToNextOf(item);
170 return item_result->prohibit_break_after ? LineBreakState::kNotBreakable
171 : LineBreakState::kIsBreakable;
172 }
173
174 // Add the rest of the item if !auto_wrap.
175 // Because the start position may need to reshape, run ShapingLineBreaker
176 // with max available width.
177 DCHECK_NE(offset_, item.StartOffset());
178 BreakText(item_result, item, LayoutUnit::Max());
179 DCHECK_EQ(item_result->end_offset, item.EndOffset());
180 item_result->no_break_opportunities_inside = true;
181 item_result->prohibit_break_after = true;
182 position_ += item_result->inline_size;
183 MoveToNextOf(item);
184 return LineBreakState::kNotBreakable;
185 }
186
187 void NGLineBreaker::BreakText(NGInlineItemResult* item_result,
188 const NGInlineItem& item,
189 LayoutUnit available_width) {
190 DCHECK_EQ(item.Type(), NGInlineItem::kText);
191 item.AssertOffset(item_result->start_offset);
192
167 #if defined(MOCK_SHAPE_LINE) 193 #if defined(MOCK_SHAPE_LINE)
168 unsigned break_offset; 194 std::tie(item_result->end_offset, item_result->inline_size) = ShapeLineMock(
169 std::tie(break_offset, item_result->inline_size) = ShapeLineMock( 195 item, item_result->start_offset, available_width, break_iterator_);
170 item, offset_, available_width - position, break_iterator);
171 #else 196 #else
172 // TODO(kojii): We need to instantiate ShapingLineBreaker here because it 197 // TODO(kojii): We need to instantiate ShapingLineBreaker here because it
173 // has item-specific info as context. Should they be part of ShapeLine() to 198 // has item-specific info as context. Should they be part of ShapeLine() to
174 // instantiate once, or is this just fine since instatiation is not 199 // instantiate once, or is this just fine since instatiation is not
175 // expensive? 200 // expensive?
176 DCHECK_EQ(item.TextShapeResult()->StartIndexForResult(), 201 DCHECK_EQ(item.TextShapeResult()->StartIndexForResult(), item.StartOffset());
177 item.StartOffset()); 202 DCHECK_EQ(item.TextShapeResult()->EndIndexForResult(), item.EndOffset());
178 DCHECK_EQ(item.TextShapeResult()->EndIndexForResult(), item.EndOffset()); 203 ShapingLineBreaker breaker(&shaper, &item.Style()->GetFont(),
179 ShapingLineBreaker breaker(&shaper, &item.Style()->GetFont(), 204 item.TextShapeResult(), break_iterator_);
180 item.TextShapeResult(), &break_iterator); 205 item_result->shape_result = breaker.ShapeLine(
181 unsigned break_offset; 206 item_result->start_offset, available_width, &item_result->end_offset);
182 item_result->shape_result = 207 item_result->inline_size = item_result->shape_result->SnappedWidth();
183 breaker.ShapeLine(offset_, available_width - position, &break_offset);
184 item_result->inline_size = item_result->shape_result->SnappedWidth();
185 #endif 208 #endif
186 DCHECK_GT(break_offset, offset_); 209 DCHECK_GT(item_result->end_offset, item_result->start_offset);
187 position += item_result->inline_size; 210 // * If width <= available_width:
188 211 // * If offset < item.EndOffset(): the break opportunity to fit is found.
189 // If the break found within the item, break here. 212 // * If offset == item.EndOffset(): the break opportunity at the end fits.
190 if (break_offset < item.EndOffset()) { 213 // There may be room for more characters.
191 offset_ = item_result->end_offset = break_offset; 214 // * If offset > item.EndOffset(): the first break opportunity is beyond
192 if (position <= available_width) 215 // the end. There may be room for more characters.
193 break; 216 // * If width > available_width: The first break opporunity does not fit.
194 // The first break opportunity of the item does not fit. 217 // offset is the first break opportunity, either inside, at the end, or
195 } else { 218 // beyond the end.
196 // No break opporunity in the item, or the first break opportunity is at 219 if (item_result->end_offset <= item.EndOffset()) {
197 // the end of the item. If it fits, continue to the next item. 220 item_result->prohibit_break_after = false;
198 item_result->end_offset = item.EndOffset(); 221 } else {
199 MoveToNextOf(item); 222 item_result->prohibit_break_after = true;
200 if (position <= available_width) 223 item_result->end_offset = item.EndOffset();
201 continue;
202 }
203
204 // We need to look at next item if we're overflowing, and the break
205 // opportunity is beyond this item.
206 if (break_offset > item.EndOffset())
207 continue;
208 return HandleOverflow(item_results, break_iterator);
209 } 224 }
210 } 225 }
211 226
212 // Measure control items; new lines and tab, that are similar to text, affect 227 // Measure control items; new lines and tab, that are similar to text, affect
213 // layout, but do not need shaping/painting. 228 // layout, but do not need shaping/painting.
214 bool NGLineBreaker::HandleControlItem(const NGInlineItem& item, 229 NGLineBreaker::LineBreakState NGLineBreaker::HandleControlItem(
215 const String& text, 230 const NGInlineItem& item,
216 NGInlineItemResult* item_result, 231 NGInlineItemResult* item_result) {
217 LayoutUnit position) {
218 DCHECK_EQ(item.Length(), 1u); 232 DCHECK_EQ(item.Length(), 1u);
219 UChar character = text[item.StartOffset()]; 233 UChar character = node_->Text()[item.StartOffset()];
220 if (character == kNewlineCharacter) 234 if (character == kNewlineCharacter) {
221 return true; 235 MoveToNextOf(item);
222 236 return LineBreakState::kForcedBreak;
237 }
223 DCHECK_EQ(character, kTabulationCharacter); 238 DCHECK_EQ(character, kTabulationCharacter);
224 DCHECK(item.Style()); 239 DCHECK(item.Style());
225 const ComputedStyle& style = *item.Style(); 240 const ComputedStyle& style = *item.Style();
226 const Font& font = style.GetFont(); 241 const Font& font = style.GetFont();
227 item_result->inline_size = font.TabWidth(style.GetTabSize(), position); 242 item_result->inline_size = font.TabWidth(style.GetTabSize(), position_);
228 return false; 243 position_ += item_result->inline_size;
244 MoveToNextOf(item);
245 // TODO(kojii): Implement break around the tab character.
246 return LineBreakState::kIsBreakable;
229 } 247 }
230 248
231 void NGLineBreaker::LayoutAtomicInline(const NGInlineItem& item, 249 NGLineBreaker::LineBreakState NGLineBreaker::HandleAtomicInline(
232 NGInlineItemResult* item_result) { 250 const NGInlineItem& item,
251 NGInlineItemResult* item_result) {
233 DCHECK_EQ(item.Type(), NGInlineItem::kAtomicInline); 252 DCHECK_EQ(item.Type(), NGInlineItem::kAtomicInline);
234 NGBlockNode* node = new NGBlockNode(item.GetLayoutObject()); 253 NGBlockNode* node = new NGBlockNode(item.GetLayoutObject());
235 const ComputedStyle& style = node->Style(); 254 const ComputedStyle& style = node->Style();
236 NGConstraintSpaceBuilder constraint_space_builder(constraint_space_); 255 NGConstraintSpaceBuilder constraint_space_builder(constraint_space_);
237 RefPtr<NGConstraintSpace> constraint_space = 256 RefPtr<NGConstraintSpace> constraint_space =
238 constraint_space_builder.SetIsNewFormattingContext(true) 257 constraint_space_builder.SetIsNewFormattingContext(true)
239 .SetIsShrinkToFit(true) 258 .SetIsShrinkToFit(true)
240 .SetTextDirection(style.Direction()) 259 .SetTextDirection(style.Direction())
241 .ToConstraintSpace(FromPlatformWritingMode(style.GetWritingMode())); 260 .ToConstraintSpace(FromPlatformWritingMode(style.GetWritingMode()));
242 item_result->layout_result = node->Layout(constraint_space.Get()); 261 item_result->layout_result = node->Layout(constraint_space.Get());
243 262
244 item_result->inline_size = 263 item_result->inline_size =
245 NGBoxFragment(constraint_space_->WritingMode(), 264 NGBoxFragment(constraint_space_->WritingMode(),
246 ToNGPhysicalBoxFragment( 265 ToNGPhysicalBoxFragment(
247 item_result->layout_result->PhysicalFragment().Get())) 266 item_result->layout_result->PhysicalFragment().Get()))
248 .InlineSize(); 267 .InlineSize();
249 268
250 item_result->margins = 269 item_result->margins =
251 ComputeMargins(*constraint_space_, style, 270 ComputeMargins(*constraint_space_, style,
252 constraint_space_->WritingMode(), style.Direction()); 271 constraint_space_->WritingMode(), style.Direction());
253 item_result->inline_size += item_result->margins.InlineSum(); 272 item_result->inline_size += item_result->margins.InlineSum();
273
274 position_ += item_result->inline_size;
275 MoveToNextOf(item);
276 if (auto_wrap_)
277 return LineBreakState::kIsBreakable;
278 item_result->prohibit_break_after = true;
279 return LineBreakState::kNotBreakable;
280 }
281
282 void NGLineBreaker::HandleFloat(const NGInlineItem& item,
283 NGInlineItemResults* item_results,
284 NGInlineLayoutAlgorithm* algorithm) {
285 algorithm->LayoutAndPositionFloat(position_, item.GetLayoutObject());
286 // Floats may change the available width if they fit.
287 available_width_ = algorithm->AvailableWidth();
288 // Floats are already positioned in the container_builder.
289 item_results->pop_back();
290 MoveToNextOf(item);
291 }
292
293 void NGLineBreaker::HandleOpenTag(const NGInlineItem& item,
294 NGInlineItemResult* item_result) {
295 if (item.HasStartEdge()) {
296 DCHECK(item.Style());
297 // TODO(kojii): We compute 16 values and discard 12 out of that, and do it 3
298 // times per element. We may want to cache this. crrev.com/2865903002/#msg14
299 NGBoxStrut margins = ComputeMargins(*constraint_space_, *item.Style(),
300 constraint_space_->WritingMode(),
301 constraint_space_->Direction());
302 NGBoxStrut borders = ComputeBorders(*constraint_space_, *item.Style());
303 NGBoxStrut paddings = ComputePadding(*constraint_space_, *item.Style());
304 item_result->inline_size =
305 margins.inline_start + borders.inline_start + paddings.inline_start;
306 position_ += item_result->inline_size;
307 }
308 UpdateBreakIterator(*item.Style());
309 MoveToNextOf(item);
310 }
311
312 void NGLineBreaker::HandleCloseTag(const NGInlineItem& item,
313 NGInlineItemResult* item_result) {
314 if (item.HasEndEdge()) {
315 DCHECK(item.Style());
316 NGBoxStrut margins = ComputeMargins(*constraint_space_, *item.Style(),
317 constraint_space_->WritingMode(),
318 constraint_space_->Direction());
319 NGBoxStrut borders = ComputeBorders(*constraint_space_, *item.Style());
320 NGBoxStrut paddings = ComputePadding(*constraint_space_, *item.Style());
321 item_result->inline_size =
322 margins.inline_end + borders.inline_end + paddings.inline_end;
323 position_ += item_result->inline_size;
324 }
325 DCHECK(item.GetLayoutObject() && item.GetLayoutObject()->Parent());
326 UpdateBreakIterator(item.GetLayoutObject()->Parent()->StyleRef());
327 MoveToNextOf(item);
254 } 328 }
255 329
256 // Handles when the last item overflows. 330 // Handles when the last item overflows.
257 // At this point, item_results does not fit into the current line, and there 331 // At this point, item_results does not fit into the current line, and there
258 // are no break opportunities in item_results.back(). 332 // are no break opportunities in item_results.back().
259 void NGLineBreaker::HandleOverflow( 333 void NGLineBreaker::HandleOverflow(NGInlineItemResults* item_results) {
260 NGInlineItemResults* item_results, 334 const Vector<NGInlineItem>& items = node_->Items();
261 const LazyLineBreakIterator& break_iterator) { 335 LayoutUnit rewind_width = available_width_ - position_;
262 DCHECK_GT(offset_, 0u); 336 DCHECK_LT(rewind_width, 0);
263 337
264 // Find the last break opportunity. If none, let this line overflow. 338 // Search for a break opportunity that can fit.
265 unsigned line_start_offset = item_results->front().start_offset; 339 // Also keep track of the first break opportunity in case of overflow.
266 unsigned break_offset = 340 unsigned break_before = 0;
267 break_iterator.PreviousBreakOpportunity(offset_ - 1, line_start_offset); 341 unsigned break_before_if_before_allow = 0;
268 if (!break_offset || break_offset <= line_start_offset) { 342 LayoutUnit rewind_width_if_before_allow;
269 AppendCloseTags(item_results); 343 bool last_item_prohibits_break_before = true;
270 return; 344 for (unsigned i = item_results->size(); i;) {
345 NGInlineItemResult* item_result = &(*item_results)[--i];
346 const NGInlineItem& item = items[item_result->item_index];
347 rewind_width += item_result->inline_size;
348 if (item.Type() == NGInlineItem::kText ||
349 item.Type() == NGInlineItem::kAtomicInline) {
350 // Try to break inside of this item.
351 if (item.Type() == NGInlineItem::kText && rewind_width >= 0 &&
352 !item_result->no_break_opportunities_inside) {
353 // When the text fits but its right margin does not, the break point
354 // must not be at the end.
355 LayoutUnit item_available_width =
356 std::min(rewind_width, item_result->inline_size - 1);
357 BreakText(item_result, item, item_available_width);
358 if (item_result->inline_size <= item_available_width) {
359 DCHECK_LT(item_result->end_offset, item.EndOffset());
360 DCHECK(!item_result->prohibit_break_after);
361 return Rewind(item_results, i + 1);
362 }
363 if (!item_result->prohibit_break_after &&
364 !last_item_prohibits_break_before) {
365 break_before = i + 1;
366 }
367 }
368
369 // Try to break after this item.
370 if (break_before_if_before_allow && !item_result->prohibit_break_after) {
371 if (rewind_width_if_before_allow >= 0)
372 return Rewind(item_results, break_before_if_before_allow);
373 break_before = break_before_if_before_allow;
374 }
375
376 // Otherwise, before this item is a possible break point.
377 break_before_if_before_allow = i;
378 rewind_width_if_before_allow = rewind_width;
379 last_item_prohibits_break_before = false;
380 } else if (item.Type() == NGInlineItem::kCloseTag) {
381 last_item_prohibits_break_before = true;
382 } else {
383 if (i + 1 == break_before_if_before_allow) {
384 break_before_if_before_allow = i;
385 rewind_width_if_before_allow = rewind_width;
386 }
387 last_item_prohibits_break_before = false;
388 }
271 } 389 }
272 390
273 // Truncate the end of the line to the break opportunity. 391 // The rewind point did not found, let this line overflow.
274 const Vector<NGInlineItem>& items = node_->Items(); 392 // If there was a break opporunity, the overflow should stop there.
275 unsigned new_end = item_results->size(); 393 if (break_before)
276 while (true) { 394 Rewind(item_results, break_before);
277 NGInlineItemResult* item_result = &(*item_results)[--new_end]; 395 }
278 if (item_result->start_offset < break_offset) {
279 // The break is at the mid of the item. Adjust the end_offset to the new
280 // break offset.
281 const NGInlineItem& item = items[item_result->item_index];
282 item.AssertEndOffset(break_offset);
283 DCHECK_EQ(item.Type(), NGInlineItem::kText);
284 DCHECK_NE(item_result->end_offset, break_offset);
285 item_result->end_offset = break_offset;
286 item_result->inline_size =
287 item.InlineSize(item_result->start_offset, item_result->end_offset);
288 // TODO(kojii): May need to reshape. Add to ShapingLineBreaker?
289 new_end++;
290 break;
291 }
292 if (item_result->start_offset == break_offset) {
293 // The new break offset is at the item boundary. Remove items up to the
294 // new break offset.
295 // TODO(kojii): Remove open tags as well.
296 break;
297 }
298 }
299 DCHECK_GT(new_end, 0u);
300 396
397 void NGLineBreaker::Rewind(NGInlineItemResults* item_results,
398 unsigned new_end) {
301 // TODO(kojii): Should we keep results for the next line? We don't need to 399 // TODO(kojii): Should we keep results for the next line? We don't need to
302 // re-layout atomic inlines. 400 // re-layout atomic inlines.
303 // TODO(kojii): Removing processed floats is likely a problematic. Keep 401 // TODO(kojii): Removing processed floats is likely a problematic. Keep
304 // floats in this line, or keep it for the next line. 402 // floats in this line, or keep it for the next line.
305 item_results->Shrink(new_end); 403 item_results->Shrink(new_end);
306 404
307 // Update the current item index and offset to the new break point. 405 MoveToNextOf(item_results->back());
308 const NGInlineItemResult& last_item_result = item_results->back(); 406 }
309 offset_ = last_item_result.end_offset; 407
310 item_index_ = last_item_result.item_index; 408 void NGLineBreaker::UpdateBreakIterator(const ComputedStyle& style) {
311 if (items[item_index_].EndOffset() == offset_) 409 auto_wrap_ = style.AutoWrap();
312 item_index_++; 410
411 if (auto_wrap_) {
412 break_iterator_.SetLocale(style.LocaleForLineBreakIterator());
413
414 if (style.WordBreak() == EWordBreak::kBreakAll ||
415 style.WordBreak() == EWordBreak::kBreakWord) {
416 break_iterator_.SetBreakType(LineBreakType::kBreakAll);
417 } else if (style.WordBreak() == EWordBreak::kKeepAll) {
418 break_iterator_.SetBreakType(LineBreakType::kKeepAll);
419 } else {
420 break_iterator_.SetBreakType(LineBreakType::kNormal);
421 }
422
423 // TODO(kojii): Implement word-wrap/overflow-wrap property
424 }
313 } 425 }
314 426
315 void NGLineBreaker::MoveToNextOf(const NGInlineItem& item) { 427 void NGLineBreaker::MoveToNextOf(const NGInlineItem& item) {
316 DCHECK_EQ(&item, &node_->Items()[item_index_]); 428 DCHECK_EQ(&item, &node_->Items()[item_index_]);
317 offset_ = item.EndOffset(); 429 offset_ = item.EndOffset();
318 item_index_++; 430 item_index_++;
319 } 431 }
320 432
433 void NGLineBreaker::MoveToNextOf(const NGInlineItemResult& item_result) {
434 offset_ = item_result.end_offset;
435 item_index_ = item_result.item_index;
436 const NGInlineItem& item = node_->Items()[item_result.item_index];
437 if (offset_ == item.EndOffset())
438 item_index_++;
439 }
440
321 void NGLineBreaker::SkipCollapsibleWhitespaces() { 441 void NGLineBreaker::SkipCollapsibleWhitespaces() {
322 const Vector<NGInlineItem>& items = node_->Items(); 442 const Vector<NGInlineItem>& items = node_->Items();
323 if (item_index_ >= items.size()) 443 if (item_index_ >= items.size())
324 return; 444 return;
325 const NGInlineItem& item = items[item_index_]; 445 const NGInlineItem& item = items[item_index_];
326 if (item.Type() != NGInlineItem::kText || !item.Style()->CollapseWhiteSpace()) 446 if (item.Type() != NGInlineItem::kText || !item.Style()->CollapseWhiteSpace())
327 return; 447 return;
328 448
329 DCHECK_LT(offset_, item.EndOffset()); 449 DCHECK_LT(offset_, item.EndOffset());
330 if (node_->Text()[offset_] == kSpaceCharacter) { 450 if (node_->Text()[offset_] == kSpaceCharacter) {
331 // Skip one whitespace. Collapsible spaces are collapsed to single space in 451 // Skip one whitespace. Collapsible spaces are collapsed to single space in
332 // NGInlineItemBuilder, so this removes all collapsible spaces. 452 // NGInlineItemBuilder, so this removes all collapsible spaces.
333 offset_++; 453 offset_++;
334 if (offset_ == item.EndOffset()) 454 if (offset_ == item.EndOffset())
335 item_index_++; 455 item_index_++;
336 } 456 }
337 } 457 }
338 458
339 void NGLineBreaker::AppendCloseTags(NGInlineItemResults* item_results) {
340 const Vector<NGInlineItem>& items = node_->Items();
341 for (; item_index_ < items.size(); item_index_++) {
342 const NGInlineItem& item = items[item_index_];
343 if (item.Type() != NGInlineItem::kCloseTag)
344 break;
345 DCHECK_EQ(offset_, item.EndOffset());
346 item_results->push_back(NGInlineItemResult(item_index_, offset_, offset_));
347 }
348 }
349
350 RefPtr<NGInlineBreakToken> NGLineBreaker::CreateBreakToken() const { 459 RefPtr<NGInlineBreakToken> NGLineBreaker::CreateBreakToken() const {
351 const Vector<NGInlineItem>& items = node_->Items(); 460 const Vector<NGInlineItem>& items = node_->Items();
352 if (item_index_ >= items.size()) 461 if (item_index_ >= items.size())
353 return nullptr; 462 return nullptr;
354 return NGInlineBreakToken::Create(node_, item_index_, offset_); 463 return NGInlineBreakToken::Create(node_, item_index_, offset_);
355 } 464 }
356 465
357 } // namespace blink 466 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698