OLD | NEW |
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_inline_items_builder.h" | 5 #include "core/layout/ng/inline/ng_inline_items_builder.h" |
6 | 6 |
7 #include "core/layout/LayoutObject.h" | 7 #include "core/layout/LayoutObject.h" |
8 #include "core/layout/ng/inline/ng_inline_node.h" | 8 #include "core/layout/ng/inline/ng_inline_node.h" |
9 #include "core/style/ComputedStyle.h" | 9 #include "core/style/ComputedStyle.h" |
10 | 10 |
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
106 static void AppendItem(Vector<NGInlineItem>* items, | 106 static void AppendItem(Vector<NGInlineItem>* items, |
107 NGInlineItem::NGInlineItemType type, | 107 NGInlineItem::NGInlineItemType type, |
108 unsigned start, | 108 unsigned start, |
109 unsigned end, | 109 unsigned end, |
110 const ComputedStyle* style = nullptr, | 110 const ComputedStyle* style = nullptr, |
111 LayoutObject* layout_object = nullptr) { | 111 LayoutObject* layout_object = nullptr) { |
112 DCHECK(items->IsEmpty() || items->back().EndOffset() == start); | 112 DCHECK(items->IsEmpty() || items->back().EndOffset() == start); |
113 items->push_back(NGInlineItem(type, start, end, style, layout_object)); | 113 items->push_back(NGInlineItem(type, start, end, style, layout_object)); |
114 } | 114 } |
115 | 115 |
116 static inline bool IsCollapsibleSpace(UChar c, bool preserve_newline) { | 116 static inline bool IsCollapsibleSpace(UChar c) { |
117 return c == kSpaceCharacter || c == kTabulationCharacter || | 117 return c == kSpaceCharacter || c == kTabulationCharacter || |
118 (!preserve_newline && c == kNewlineCharacter); | 118 c == kNewlineCharacter; |
| 119 } |
| 120 |
| 121 // Characters needing a separate control item than other text items. |
| 122 // It makes the line breaker easier to handle. |
| 123 static inline bool IsControlItemCharacter(UChar c) { |
| 124 return c == kTabulationCharacter || c == kNewlineCharacter; |
119 } | 125 } |
120 | 126 |
121 void NGInlineItemsBuilder::Append(const String& string, | 127 void NGInlineItemsBuilder::Append(const String& string, |
122 const ComputedStyle* style, | 128 const ComputedStyle* style, |
123 LayoutObject* layout_object) { | 129 LayoutObject* layout_object) { |
124 if (string.IsEmpty()) | 130 if (string.IsEmpty()) |
125 return; | 131 return; |
| 132 text_.ReserveCapacity(string.length()); |
126 | 133 |
127 EWhiteSpace whitespace = style->WhiteSpace(); | 134 EWhiteSpace whitespace = style->WhiteSpace(); |
128 bool preserve_newline = | 135 if (!ComputedStyle::CollapseWhiteSpace(whitespace)) |
129 ComputedStyle::PreserveNewline(whitespace) && !is_svgtext_; | 136 return AppendWithoutWhiteSpaceCollapsing(string, style, layout_object); |
130 bool collapse_whitespace = ComputedStyle::CollapseWhiteSpace(whitespace); | 137 if (ComputedStyle::PreserveNewline(whitespace) && !is_svgtext_) |
| 138 return AppendWithPreservingNewlines(string, style, layout_object); |
| 139 |
| 140 AppendWithWhiteSpaceCollapsing(string, 0, string.length(), style, |
| 141 layout_object); |
| 142 } |
| 143 |
| 144 void NGInlineItemsBuilder::AppendWithWhiteSpaceCollapsing( |
| 145 const String& string, |
| 146 unsigned start, |
| 147 unsigned end, |
| 148 const ComputedStyle* style, |
| 149 LayoutObject* layout_object) { |
131 unsigned start_offset = text_.length(); | 150 unsigned start_offset = text_.length(); |
132 | 151 for (unsigned i = start; i < end;) { |
133 if (!collapse_whitespace) { | 152 UChar c = string[i]; |
134 text_.Append(string); | 153 if (c == kNewlineCharacter) { |
135 last_collapsible_space_ = CollapsibleSpace::kNone; | 154 // LayoutBR does not set preserve_newline, but should be preserved. |
136 } else { | 155 if (!i && end == 1 && layout_object && layout_object->IsBR()) { |
137 text_.ReserveCapacity(string.length()); | 156 AppendForcedBreak(style, layout_object); |
138 for (unsigned i = 0; i < string.length();) { | 157 return; |
139 UChar c = string[i]; | |
140 if (c == kNewlineCharacter) { | |
141 if (preserve_newline) { | |
142 RemoveTrailingCollapsibleSpaceIfExists(&start_offset); | |
143 text_.Append(c); | |
144 // Remove collapsible spaces immediately following a newline. | |
145 last_collapsible_space_ = CollapsibleSpace::kSpace; | |
146 i++; | |
147 continue; | |
148 } | |
149 | |
150 if (last_collapsible_space_ == CollapsibleSpace::kNone) | |
151 text_.Append(kSpaceCharacter); | |
152 last_collapsible_space_ = CollapsibleSpace::kNewline; | |
153 i++; | |
154 continue; | |
155 } | 158 } |
156 | 159 |
157 if (c == kSpaceCharacter || c == kTabulationCharacter) { | 160 if (last_collapsible_space_ == CollapsibleSpace::kNone) |
158 if (last_collapsible_space_ == CollapsibleSpace::kNone) { | 161 text_.Append(kSpaceCharacter); |
159 text_.Append(kSpaceCharacter); | 162 last_collapsible_space_ = CollapsibleSpace::kNewline; |
160 last_collapsible_space_ = CollapsibleSpace::kSpace; | 163 i++; |
161 } | 164 continue; |
162 i++; | 165 } |
163 continue; | 166 |
| 167 if (c == kSpaceCharacter || c == kTabulationCharacter) { |
| 168 if (last_collapsible_space_ == CollapsibleSpace::kNone) { |
| 169 text_.Append(kSpaceCharacter); |
| 170 last_collapsible_space_ = CollapsibleSpace::kSpace; |
164 } | 171 } |
| 172 i++; |
| 173 continue; |
| 174 } |
165 | 175 |
166 if (last_collapsible_space_ == CollapsibleSpace::kNewline) { | 176 if (last_collapsible_space_ == CollapsibleSpace::kNewline) { |
167 RemoveTrailingCollapsibleNewlineIfNeeded(&start_offset, string, i, | 177 RemoveTrailingCollapsibleNewlineIfNeeded(&start_offset, string, i, style); |
168 style); | 178 } |
169 } | |
170 | 179 |
171 unsigned start_of_non_space = i; | 180 size_t end_of_non_space = string.Find(IsCollapsibleSpace, i + 1); |
172 for (i++; i < string.length(); i++) { | 181 if (end_of_non_space == kNotFound) |
173 if (IsCollapsibleSpace(string[i], false)) | 182 end_of_non_space = string.length(); |
174 break; | 183 text_.Append(string, i, end_of_non_space - i); |
175 } | 184 i = end_of_non_space; |
176 text_.Append(string, start_of_non_space, i - start_of_non_space); | 185 last_collapsible_space_ = CollapsibleSpace::kNone; |
177 last_collapsible_space_ = CollapsibleSpace::kNone; | |
178 } | |
179 } | 186 } |
180 | 187 |
181 if (text_.length() > start_offset) { | 188 if (text_.length() > start_offset) { |
182 AppendItem(items_, NGInlineItem::kText, start_offset, text_.length(), style, | 189 AppendItem(items_, NGInlineItem::kText, start_offset, text_.length(), style, |
183 layout_object); | 190 layout_object); |
184 } | 191 } |
185 } | 192 } |
186 | 193 |
| 194 // Even when without whitespace collapsing, control characters (newlines and |
| 195 // tabs) are in their own control items to make the line breaker easier. |
| 196 void NGInlineItemsBuilder::AppendWithoutWhiteSpaceCollapsing( |
| 197 const String& string, |
| 198 const ComputedStyle* style, |
| 199 LayoutObject* layout_object) { |
| 200 for (unsigned start = 0; start < string.length();) { |
| 201 UChar c = string[start]; |
| 202 if (IsControlItemCharacter(c)) { |
| 203 Append(NGInlineItem::kControl, c, style, layout_object); |
| 204 start++; |
| 205 continue; |
| 206 } |
| 207 |
| 208 size_t end = string.Find(IsControlItemCharacter, start + 1); |
| 209 if (end == kNotFound) |
| 210 end = string.length(); |
| 211 unsigned start_offset = text_.length(); |
| 212 text_.Append(string, start, end - start); |
| 213 AppendItem(items_, NGInlineItem::kText, start_offset, text_.length(), style, |
| 214 layout_object); |
| 215 start = end; |
| 216 } |
| 217 |
| 218 last_collapsible_space_ = CollapsibleSpace::kNone; |
| 219 } |
| 220 |
| 221 void NGInlineItemsBuilder::AppendWithPreservingNewlines( |
| 222 const String& string, |
| 223 const ComputedStyle* style, |
| 224 LayoutObject* layout_object) { |
| 225 for (unsigned start = 0; start < string.length();) { |
| 226 if (string[start] == kNewlineCharacter) { |
| 227 AppendForcedBreak(style, layout_object); |
| 228 start++; |
| 229 continue; |
| 230 } |
| 231 |
| 232 size_t end = string.find(kNewlineCharacter, start + 1); |
| 233 if (end == kNotFound) |
| 234 end = string.length(); |
| 235 AppendWithWhiteSpaceCollapsing(string, start, end, style, layout_object); |
| 236 start = end; |
| 237 } |
| 238 } |
| 239 |
| 240 void NGInlineItemsBuilder::AppendForcedBreak(const ComputedStyle* style, |
| 241 LayoutObject* layout_object) { |
| 242 // Remove collapsible spaces immediately before a preserved newline. |
| 243 unsigned start_offset = text_.length(); |
| 244 RemoveTrailingCollapsibleSpaceIfExists(&start_offset); |
| 245 |
| 246 Append(NGInlineItem::kControl, kNewlineCharacter, style, layout_object); |
| 247 |
| 248 // Remove collapsible spaces immediately after a preserved newline. |
| 249 last_collapsible_space_ = CollapsibleSpace::kSpace; |
| 250 } |
| 251 |
187 void NGInlineItemsBuilder::Append(NGInlineItem::NGInlineItemType type, | 252 void NGInlineItemsBuilder::Append(NGInlineItem::NGInlineItemType type, |
188 UChar character, | 253 UChar character, |
189 const ComputedStyle* style, | 254 const ComputedStyle* style, |
190 LayoutObject* layout_object) { | 255 LayoutObject* layout_object) { |
191 DCHECK_NE(character, kSpaceCharacter); | 256 DCHECK_NE(character, kSpaceCharacter); |
192 DCHECK_NE(character, kTabulationCharacter); | |
193 DCHECK_NE(character, kNewlineCharacter); | |
194 DCHECK_NE(character, kZeroWidthSpaceCharacter); | 257 DCHECK_NE(character, kZeroWidthSpaceCharacter); |
195 | 258 |
196 text_.Append(character); | 259 text_.Append(character); |
197 unsigned end_offset = text_.length(); | 260 unsigned end_offset = text_.length(); |
198 AppendItem(items_, type, end_offset - 1, end_offset, style, layout_object); | 261 AppendItem(items_, type, end_offset - 1, end_offset, style, layout_object); |
199 last_collapsible_space_ = CollapsibleSpace::kNone; | 262 last_collapsible_space_ = CollapsibleSpace::kNone; |
200 } | 263 } |
201 | 264 |
202 void NGInlineItemsBuilder::Append(NGInlineItem::NGInlineItemType type, | 265 void NGInlineItemsBuilder::Append(NGInlineItem::NGInlineItemType type, |
203 const ComputedStyle* style, | 266 const ComputedStyle* style, |
(...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
359 } | 422 } |
360 | 423 |
361 void NGInlineItemsBuilder::Exit(LayoutObject* node) { | 424 void NGInlineItemsBuilder::Exit(LayoutObject* node) { |
362 while (!exits_.IsEmpty() && exits_.back().node == node) { | 425 while (!exits_.IsEmpty() && exits_.back().node == node) { |
363 Append(NGInlineItem::kBidiControl, exits_.back().character); | 426 Append(NGInlineItem::kBidiControl, exits_.back().character); |
364 exits_.pop_back(); | 427 exits_.pop_back(); |
365 } | 428 } |
366 } | 429 } |
367 | 430 |
368 } // namespace blink | 431 } // namespace blink |
OLD | NEW |