Chromium Code Reviews| 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/ng_inline_node.h" | 5 #include "core/layout/ng/ng_inline_node.h" |
| 6 | 6 |
| 7 #include "core/layout/LayoutBlockFlow.h" | 7 #include "core/layout/LayoutBlockFlow.h" |
| 8 #include "core/layout/LayoutObject.h" | 8 #include "core/layout/LayoutObject.h" |
| 9 #include "core/layout/LayoutText.h" | 9 #include "core/layout/LayoutText.h" |
| 10 #include "core/layout/ng/ng_bidi_paragraph.h" | 10 #include "core/layout/ng/ng_bidi_paragraph.h" |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 23 #include "platform/fonts/shaping/CachingWordShaper.h" | 23 #include "platform/fonts/shaping/CachingWordShaper.h" |
| 24 #include "platform/fonts/shaping/HarfBuzzShaper.h" | 24 #include "platform/fonts/shaping/HarfBuzzShaper.h" |
| 25 #include "platform/fonts/shaping/ShapeResultBuffer.h" | 25 #include "platform/fonts/shaping/ShapeResultBuffer.h" |
| 26 #include "wtf/text/CharacterNames.h" | 26 #include "wtf/text/CharacterNames.h" |
| 27 | 27 |
| 28 namespace blink { | 28 namespace blink { |
| 29 | 29 |
| 30 NGInlineNode::NGInlineNode(LayoutObject* start_inline, LayoutBlockFlow* block) | 30 NGInlineNode::NGInlineNode(LayoutObject* start_inline, LayoutBlockFlow* block) |
| 31 : NGLayoutInputNode(NGLayoutInputNodeType::kLegacyInline), | 31 : NGLayoutInputNode(NGLayoutInputNodeType::kLegacyInline), |
| 32 start_inline_(start_inline), | 32 start_inline_(start_inline), |
| 33 block_(block) { | 33 block_(block), |
| 34 has_atomic_inlines_(false), | |
| 35 is_bidi_enabled_(false) { | |
| 34 DCHECK(start_inline); | 36 DCHECK(start_inline); |
| 35 DCHECK(block); | 37 DCHECK(block); |
| 36 } | 38 } |
| 37 | 39 |
| 38 NGInlineNode::NGInlineNode() | 40 NGInlineNode::NGInlineNode() |
| 39 : NGLayoutInputNode(NGLayoutInputNodeType::kLegacyInline), | 41 : NGLayoutInputNode(NGLayoutInputNodeType::kLegacyInline), |
| 40 start_inline_(nullptr), | 42 start_inline_(nullptr), |
| 41 block_(nullptr) {} | 43 block_(nullptr), |
| 44 has_atomic_inlines_(false), | |
| 45 is_bidi_enabled_(false) {} | |
| 42 | 46 |
| 43 NGInlineNode::~NGInlineNode() {} | 47 NGInlineNode::~NGInlineNode() {} |
| 44 | 48 |
| 45 NGLayoutInlineItemRange NGInlineNode::Items(unsigned start, unsigned end) { | 49 NGLayoutInlineItemRange NGInlineNode::Items(unsigned start, unsigned end) { |
| 46 return NGLayoutInlineItemRange(&items_, start, end); | 50 return NGLayoutInlineItemRange(&items_, start, end); |
| 47 } | 51 } |
| 48 | 52 |
| 49 void NGInlineNode::InvalidatePrepareLayout() { | 53 void NGInlineNode::InvalidatePrepareLayout() { |
| 50 text_content_ = String(); | 54 text_content_ = String(); |
| 51 items_.clear(); | 55 items_.clear(); |
| 56 has_atomic_inlines_ = false; | |
| 52 } | 57 } |
| 53 | 58 |
| 54 void NGInlineNode::PrepareLayout() { | 59 void NGInlineNode::PrepareLayout() { |
| 55 // Scan list of siblings collecting all in-flow non-atomic inlines. A single | 60 // Scan list of siblings collecting all in-flow non-atomic inlines. A single |
| 56 // NGInlineNode represent a collection of adjacent non-atomic inlines. | 61 // NGInlineNode represent a collection of adjacent non-atomic inlines. |
| 57 CollectInlines(start_inline_, block_); | 62 CollectInlines(start_inline_, block_); |
| 58 if (is_bidi_enabled_) | 63 if (is_bidi_enabled_) |
| 59 SegmentText(); | 64 SegmentText(); |
| 60 ShapeText(); | 65 ShapeText(); |
| 61 } | 66 } |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 100 // A block box found. End inline and transit to block layout. | 105 // A block box found. End inline and transit to block layout. |
| 101 return node; | 106 return node; |
| 102 | 107 |
| 103 } else { | 108 } else { |
| 104 builder->EnterInline(node); | 109 builder->EnterInline(node); |
| 105 | 110 |
| 106 // For atomic inlines add a unicode "object replacement character" to | 111 // For atomic inlines add a unicode "object replacement character" to |
| 107 // signal the presence of a non-text object to the unicode bidi algorithm. | 112 // signal the presence of a non-text object to the unicode bidi algorithm. |
| 108 if (node->isAtomicInlineLevel()) { | 113 if (node->isAtomicInlineLevel()) { |
| 109 builder->Append(objectReplacementCharacter, nullptr, node); | 114 builder->Append(objectReplacementCharacter, nullptr, node); |
| 115 has_atomic_inlines_ = true; | |
| 110 } | 116 } |
| 111 | 117 |
| 112 // Otherwise traverse to children if they exist. | 118 // Otherwise traverse to children if they exist. |
| 113 else if (LayoutObject* child = node->slowFirstChild()) { | 119 else if (LayoutObject* child = node->slowFirstChild()) { |
| 114 node = child; | 120 node = child; |
| 115 continue; | 121 continue; |
| 116 | 122 |
| 117 } else { | 123 } else { |
| 118 // An empty inline node. | 124 // An empty inline node. |
| 119 node->clearNeedsLayout(); | 125 node->clearNeedsLayout(); |
| (...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 212 return InlineSize(start_offset_, end_offset_); | 218 return InlineSize(start_offset_, end_offset_); |
| 213 } | 219 } |
| 214 | 220 |
| 215 LayoutUnit NGLayoutInlineItem::InlineSize(unsigned start, unsigned end) const { | 221 LayoutUnit NGLayoutInlineItem::InlineSize(unsigned start, unsigned end) const { |
| 216 DCHECK(start >= StartOffset() && start <= end && end <= EndOffset()); | 222 DCHECK(start >= StartOffset() && start <= end && end <= EndOffset()); |
| 217 | 223 |
| 218 if (start == end) | 224 if (start == end) |
| 219 return LayoutUnit(); | 225 return LayoutUnit(); |
| 220 | 226 |
| 221 if (!style_ || !shape_result_) { | 227 if (!style_ || !shape_result_) { |
| 222 // Bidi controls do not have widths. | 228 DCHECK(start == start_offset_ && end == end_offset_); |
| 223 // TODO(kojii): Atomic inline not supported yet. | 229 if (layout_result_) { |
| 230 DCHECK(layout_object_ && layout_object_->isAtomicInlineLevel() && | |
| 231 !layout_object_->isFloatingOrOutOfFlowPositioned()); | |
| 232 // TODO(kojii): Store parent's isHorizontalWritingMode in | |
| 233 // NGLayoutInlineItem. | |
| 234 if (!isHorizontalWritingMode( | |
| 235 layout_object_->parent()->styleRef().getWritingMode())) | |
| 236 return layout_result_->PhysicalFragment()->Height(); | |
| 237 return layout_result_->PhysicalFragment()->Width(); | |
| 238 } | |
| 239 DCHECK(!layout_object_ || | |
| 240 layout_object_->isFloatingOrOutOfFlowPositioned()); | |
| 241 // Bidi controls and out-of-flow objects do not have in-flow widths. | |
| 224 return LayoutUnit(); | 242 return LayoutUnit(); |
| 225 } | 243 } |
| 226 | 244 |
| 227 if (start == start_offset_ && end == end_offset_) | 245 if (start == start_offset_ && end == end_offset_) |
| 228 return LayoutUnit(shape_result_->width()); | 246 return LayoutUnit(shape_result_->width()); |
| 229 | 247 |
| 230 return LayoutUnit(ShapeResultBuffer::getCharacterRange( | 248 return LayoutUnit(ShapeResultBuffer::getCharacterRange( |
| 231 shape_result_, Direction(), shape_result_->width(), | 249 shape_result_, Direction(), shape_result_->width(), |
| 232 start - StartOffset(), end - StartOffset()) | 250 start - StartOffset(), end - StartOffset()) |
| 233 .width()); | 251 .width()); |
| 234 } | 252 } |
| 235 | 253 |
| 236 void NGLayoutInlineItem::GetFallbackFonts( | 254 void NGLayoutInlineItem::GetFallbackFonts( |
| 237 HashSet<const SimpleFontData*>* fallback_fonts, | 255 HashSet<const SimpleFontData*>* fallback_fonts, |
| 238 unsigned start, | 256 unsigned start, |
| 239 unsigned end) const { | 257 unsigned end) const { |
| 240 DCHECK(start >= StartOffset() && start <= end && end <= EndOffset()); | 258 DCHECK(start >= StartOffset() && start <= end && end <= EndOffset()); |
| 241 | 259 |
| 242 // TODO(kojii): Implement |start| and |end|. | 260 // TODO(kojii): Implement |start| and |end|. |
| 243 shape_result_->fallbackFonts(fallback_fonts); | 261 shape_result_->fallbackFonts(fallback_fonts); |
| 244 } | 262 } |
| 245 | 263 |
| 246 void NGInlineNode::ShapeText() { | 264 void NGInlineNode::ShapeText() { |
| 247 // TODO(eae): Add support for shaping latin-1 text? | 265 // TODO(eae): Add support for shaping latin-1 text? |
| 248 text_content_.ensure16Bit(); | 266 text_content_.ensure16Bit(); |
| 249 | 267 |
| 250 // Shape each item with the full context of the entire node. | 268 // Shape each item with the full context of the entire node. |
| 251 HarfBuzzShaper shaper(text_content_.characters16(), text_content_.length()); | 269 HarfBuzzShaper shaper(text_content_.characters16(), text_content_.length()); |
| 252 for (auto& item : items_) { | 270 for (auto& item : items_) { |
| 253 // Skip object replacement characters and bidi control characters. | 271 // Skip non-text items; e.g., bidi controls, atomic inlines, out-of-flow |
| 272 // objects. | |
| 254 if (!item.style_) | 273 if (!item.style_) |
| 255 continue; | 274 continue; |
| 256 | 275 |
| 257 item.shape_result_ = shaper.shape(&item.Style()->font(), item.Direction(), | 276 item.shape_result_ = shaper.shape(&item.Style()->font(), item.Direction(), |
| 258 item.StartOffset(), item.EndOffset()); | 277 item.StartOffset(), item.EndOffset()); |
| 259 } | 278 } |
| 260 } | 279 } |
| 261 | 280 |
| 262 RefPtr<NGLayoutResult> NGInlineNode::Layout(NGConstraintSpace*, NGBreakToken*) { | 281 RefPtr<NGLayoutResult> NGInlineNode::Layout(NGConstraintSpace*, NGBreakToken*) { |
| 263 ASSERT_NOT_REACHED(); | 282 ASSERT_NOT_REACHED(); |
| 264 return nullptr; | 283 return nullptr; |
| 265 } | 284 } |
| 266 | 285 |
| 286 void NGInlineNode::LayoutAtomicInlines( | |
| 287 const NGConstraintSpace& parent_constraint_space) { | |
| 288 NGConstraintSpaceBuilder constraint_space_builder(&parent_constraint_space); | |
| 289 constraint_space_builder.SetIsNewFormattingContext(true).SetIsShrinkToFit( | |
| 290 true); | |
| 291 for (auto& item : items_) { | |
| 292 if (item.style_) | |
| 293 continue; | |
| 294 LayoutObject* layout_object = item.layout_object_; | |
| 295 if (layout_object && layout_object->isAtomicInlineLevel() && | |
| 296 !layout_object->isFloatingOrOutOfFlowPositioned() && | |
| 297 (!item.layout_result_ || layout_object->needsLayout())) { | |
| 298 NGBlockNode* node = new NGBlockNode(layout_object); | |
| 299 const ComputedStyle& style = node->Style(); | |
| 300 RefPtr<NGConstraintSpace> constraint_space = | |
| 301 constraint_space_builder.SetTextDirection(style.direction()) | |
| 302 .ToConstraintSpace( | |
| 303 FromPlatformWritingMode(style.getWritingMode())); | |
| 304 item.layout_result_ = node->Layout(constraint_space.get()); | |
| 305 DCHECK(!layout_object->needsLayout()); | |
| 306 // TODO(kojii): Figure out what to do with OOF and unpositioned floats. | |
| 307 } | |
| 308 } | |
| 309 } | |
| 310 | |
| 267 void NGInlineNode::LayoutInline(NGConstraintSpace* constraint_space, | 311 void NGInlineNode::LayoutInline(NGConstraintSpace* constraint_space, |
| 268 NGLineBuilder* line_builder) { | 312 NGLineBuilder* line_builder) { |
| 269 if (!IsPrepareLayoutFinished()) | 313 if (!IsPrepareLayoutFinished()) |
| 270 PrepareLayout(); | 314 PrepareLayout(); |
| 271 | 315 |
| 272 if (text_content_.isEmpty()) | 316 if (text_content_.isEmpty()) |
| 273 return; | 317 return; |
| 274 | 318 |
| 319 if (has_atomic_inlines_) | |
| 320 LayoutAtomicInlines(*constraint_space); | |
|
ikilpatrick
2017/03/15 16:39:41
We need to decide if we are going to support fragm
kojii
2017/03/15 17:30:28
It'd be great if we can, though I acknowledge it's
mstensho (USE GERRIT)
2017/03/16 09:20:14
Lines are monolithic, which means that we shouldn'
| |
| 321 | |
| 275 NGTextLayoutAlgorithm(this, constraint_space).LayoutInline(line_builder); | 322 NGTextLayoutAlgorithm(this, constraint_space).LayoutInline(line_builder); |
| 276 } | 323 } |
| 277 | 324 |
| 278 MinMaxContentSize NGInlineNode::ComputeMinMaxContentSize() { | 325 MinMaxContentSize NGInlineNode::ComputeMinMaxContentSize() { |
| 279 // Compute the max of inline sizes of all line boxes with 0 available inline | 326 // Compute the max of inline sizes of all line boxes with 0 available inline |
| 280 // size. This gives the min-content, the width where lines wrap at every break | 327 // size. This gives the min-content, the width where lines wrap at every break |
| 281 // opportunity. | 328 // opportunity. |
| 282 NGWritingMode writing_mode = | 329 NGWritingMode writing_mode = |
| 283 FromPlatformWritingMode(BlockStyle()->getWritingMode()); | 330 FromPlatformWritingMode(BlockStyle()->getWritingMode()); |
| 284 RefPtr<NGConstraintSpace> constraint_space = | 331 RefPtr<NGConstraintSpace> constraint_space = |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 353 Vector<NGLayoutInlineItem>* items, | 400 Vector<NGLayoutInlineItem>* items, |
| 354 unsigned start_index, | 401 unsigned start_index, |
| 355 unsigned end_index) | 402 unsigned end_index) |
| 356 : start_item_(&(*items)[start_index]), | 403 : start_item_(&(*items)[start_index]), |
| 357 size_(end_index - start_index), | 404 size_(end_index - start_index), |
| 358 start_index_(start_index) { | 405 start_index_(start_index) { |
| 359 RELEASE_ASSERT(start_index <= end_index && end_index <= items->size()); | 406 RELEASE_ASSERT(start_index <= end_index && end_index <= items->size()); |
| 360 } | 407 } |
| 361 | 408 |
| 362 } // namespace blink | 409 } // namespace blink |
| OLD | NEW |