Chromium Code Reviews| Index: third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.cc |
| diff --git a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.cc b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.cc |
| index 2f108e1c039b1b000f73d21da54b39857489a291..2d754b310cb0e83d2caa98becfbb79d3ae4a2876 100644 |
| --- a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.cc |
| +++ b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.cc |
| @@ -61,10 +61,6 @@ NGInlineLayoutAlgorithm::NGInlineLayoutAlgorithm( |
| blink::IsHorizontalWritingMode(space->WritingMode())), |
| disallow_first_line_rules_(false), |
| space_builder_(space) |
| -#if DCHECK_IS_ON() |
| - , |
| - is_bidi_reordered_(false) |
| -#endif |
| { |
| container_builder_.MutableUnpositionedFloats() = space->UnpositionedFloats(); |
| @@ -82,10 +78,20 @@ NGInlineLayoutAlgorithm::NGInlineLayoutAlgorithm( |
| if (!is_horizontal_writing_mode_) |
| baseline_type_ = FontBaseline::kIdeographicBaseline; |
| - if (break_token) |
| - Initialize(break_token->ItemIndex(), break_token->TextOffset()); |
| - else |
| - Initialize(0, 0); |
| + |
| + if (break_token) { |
| + // If a break_token is given, we're re-starting layout for 2nd or later |
| + // lines, and that the first line we create should not use the first line |
| + // rules. |
| + DCHECK(!break_token->IsFinished()); |
| + DCHECK(break_token->TextOffset() || break_token->ItemIndex()); |
| + disallow_first_line_rules_ = true; |
| + } else { |
| + auto& engine = Node()->GetLayoutObject()->GetDocument().GetStyleEngine(); |
| + disallow_first_line_rules_ = !engine.UsesFirstLineRules(); |
| + } |
| + |
| + FindNextLayoutOpportunity(); |
| } |
| bool NGInlineLayoutAlgorithm::IsFirstLine() const { |
| @@ -100,200 +106,34 @@ const ComputedStyle& NGInlineLayoutAlgorithm::LineStyle() const { |
| return IsFirstLine() ? FirstLineStyle() : Style(); |
| } |
| -bool NGInlineLayoutAlgorithm::CanFitOnLine() const { |
| - LayoutUnit available_size = current_opportunity_.InlineSize(); |
| - if (available_size == NGSizeIndefinite) |
| - return true; |
| - return end_position_ <= available_size; |
| -} |
| - |
| -bool NGInlineLayoutAlgorithm::HasItems() const { |
| - return start_offset_ != end_offset_; |
| -} |
| - |
| -bool NGInlineLayoutAlgorithm::HasBreakOpportunity() const { |
| - return start_offset_ != last_break_opportunity_offset_; |
| -} |
| - |
| -bool NGInlineLayoutAlgorithm::HasItemsAfterLastBreakOpportunity() const { |
| - return last_break_opportunity_offset_ != end_offset_; |
| -} |
| - |
| -void NGInlineLayoutAlgorithm::Initialize(unsigned index, unsigned offset) { |
| - if (index || offset) |
| - Node()->AssertOffset(index, offset); |
| - |
| - start_index_ = last_index_ = last_break_opportunity_index_ = index; |
| - start_offset_ = end_offset_ = last_break_opportunity_offset_ = offset; |
| - end_position_ = last_break_opportunity_position_ = LayoutUnit(); |
| - |
| - auto& engine = Node()->GetLayoutObject()->GetDocument().GetStyleEngine(); |
| - disallow_first_line_rules_ = index || offset || !engine.UsesFirstLineRules(); |
| - |
| - FindNextLayoutOpportunity(); |
| -} |
| - |
| -void NGInlineLayoutAlgorithm::SetEnd(unsigned new_end_offset) { |
| - DCHECK_GT(new_end_offset, end_offset_); |
| - const Vector<NGInlineItem>& items = Node()->Items(); |
| - DCHECK_LE(new_end_offset, items.back().EndOffset()); |
| - |
| - // SetEnd() while |new_end_offset| is beyond the current last item. |
| - unsigned index = last_index_; |
| - const NGInlineItem* item = &items[index]; |
| - if (new_end_offset > item->EndOffset()) { |
| - if (end_offset_ < item->EndOffset()) { |
| - SetEnd(index, item->EndOffset(), |
| - InlineSize(*item, end_offset_, item->EndOffset())); |
| - } |
| - item = &items[++index]; |
| - |
| - while (new_end_offset > item->EndOffset()) { |
| - SetEnd(index, item->EndOffset(), InlineSize(*item)); |
| - item = &items[++index]; |
| - } |
| - } |
| - |
| - SetEnd(index, new_end_offset, InlineSize(*item, end_offset_, new_end_offset)); |
| - |
| - // Include closing elements. |
| - while (new_end_offset == item->EndOffset() && index < items.size() - 1) { |
| - item = &items[++index]; |
| - if (item->Type() != NGInlineItem::kCloseTag) |
| - break; |
| - SetEnd(index, new_end_offset, InlineSize(*item)); |
| - } |
| +LayoutUnit NGInlineLayoutAlgorithm::AvailableWidth() const { |
| + return current_opportunity_.InlineSize(); |
| } |
| -void NGInlineLayoutAlgorithm::SetEnd(unsigned index, |
| - unsigned new_end_offset, |
| - LayoutUnit inline_size_since_current_end) { |
| +bool NGInlineLayoutAlgorithm::CreateLine( |
| + NGInlineItemResults* item_results, |
| + RefPtr<NGInlineBreakToken> break_token) { |
| const Vector<NGInlineItem>& items = Node()->Items(); |
| - DCHECK_LE(new_end_offset, items.back().EndOffset()); |
| - |
| - // |new_end_offset| should be in the current item or next. |
| - // TODO(kojii): Reconsider this restriction if needed. |
| - DCHECK((index == last_index_ && new_end_offset > end_offset_) || |
| - (index == last_index_ + 1 && new_end_offset >= end_offset_ && |
| - end_offset_ == items[last_index_].EndOffset())); |
| - const NGInlineItem& item = items[index]; |
| - item.AssertEndOffset(new_end_offset); |
| - |
| - if (item.Type() == NGInlineItem::kFloating) { |
| - LayoutAndPositionFloat( |
| - LayoutUnit(end_position_) + inline_size_since_current_end, |
| - item.GetLayoutObject()); |
| - } |
| - |
| - last_index_ = index; |
| - end_offset_ = new_end_offset; |
| - end_position_ += inline_size_since_current_end; |
| -} |
| - |
| -void NGInlineLayoutAlgorithm::SetBreakOpportunity() { |
| - last_break_opportunity_index_ = last_index_; |
| - last_break_opportunity_offset_ = end_offset_; |
| - last_break_opportunity_position_ = end_position_; |
| -} |
| -void NGInlineLayoutAlgorithm::SetStartOfHangables(unsigned offset) { |
| - // TODO(kojii): Implement. |
| -} |
| - |
| -LayoutUnit NGInlineLayoutAlgorithm::InlineSize(const NGInlineItem& item) { |
| - if (item.Type() == NGInlineItem::kAtomicInline) |
| - return InlineSizeFromLayout(item); |
| - return item.InlineSize(); |
| -} |
| - |
| -LayoutUnit NGInlineLayoutAlgorithm::InlineSize(const NGInlineItem& item, |
| - unsigned start_offset, |
| - unsigned end_offset) { |
| - if (item.StartOffset() == start_offset && item.EndOffset() == end_offset) |
| - return InlineSize(item); |
| - return item.InlineSize(start_offset, end_offset); |
| -} |
| - |
| -LayoutUnit NGInlineLayoutAlgorithm::InlineSizeFromLayout( |
| - const NGInlineItem& item) { |
| - return NGBoxFragment(ConstraintSpace().WritingMode(), |
| - ToNGPhysicalBoxFragment( |
| - LayoutItem(item)->PhysicalFragment().Get())) |
| - .InlineSize(); |
| -} |
| - |
| -const NGLayoutResult* NGInlineLayoutAlgorithm::LayoutItem( |
| - const NGInlineItem& item) { |
| - // Returns the cached NGLayoutResult if available. |
| - const Vector<NGInlineItem>& items = Node()->Items(); |
| - if (layout_results_.IsEmpty()) |
| - layout_results_.resize(items.size()); |
| - unsigned index = std::distance(items.begin(), &item); |
| - RefPtr<NGLayoutResult>* layout_result = &layout_results_[index]; |
| - if (*layout_result) |
| - return layout_result->Get(); |
| - |
| - DCHECK_EQ(item.Type(), NGInlineItem::kAtomicInline); |
| - NGBlockNode* node = new NGBlockNode(item.GetLayoutObject()); |
| - // TODO(kojii): Keep node in NGInlineItem. |
| - const ComputedStyle& style = node->Style(); |
| - NGConstraintSpaceBuilder constraint_space_builder(&ConstraintSpace()); |
| - RefPtr<NGConstraintSpace> constraint_space = |
| - constraint_space_builder.SetIsNewFormattingContext(true) |
| - .SetIsShrinkToFit(true) |
| - .SetTextDirection(style.Direction()) |
| - .ToConstraintSpace(FromPlatformWritingMode(style.GetWritingMode())); |
| - *layout_result = node->Layout(constraint_space.Get()); |
| - return layout_result->Get(); |
| -} |
| - |
| -bool NGInlineLayoutAlgorithm::CreateLine() { |
| - if (HasItemsAfterLastBreakOpportunity()) |
| - SetBreakOpportunity(); |
| - return CreateLineUpToLastBreakOpportunity(); |
| -} |
| - |
| -bool NGInlineLayoutAlgorithm::CreateLineUpToLastBreakOpportunity() { |
| - const Vector<NGInlineItem>& items = Node()->Items(); |
| - |
| - // Create a list of LineItemChunk from |start| and |last_break_opportunity|. |
| - // TODO(kojii): Consider refactoring LineItemChunk once NGLineBuilder's public |
| - // API is more finalized. It does not fit well with the current API. |
| - Vector<LineItemChunk, 32> line_item_chunks; |
| - unsigned start_offset = start_offset_; |
| - for (unsigned i = start_index_; i <= last_break_opportunity_index_; i++) { |
| - const NGInlineItem& item = items[i]; |
| - unsigned end_offset = |
| - std::min(item.EndOffset(), last_break_opportunity_offset_); |
| - line_item_chunks.push_back( |
| - LineItemChunk{i, start_offset, end_offset, |
| - InlineSize(item, start_offset, end_offset)}); |
| - start_offset = end_offset; |
| + // TODO(crbug.com/716930): We may be an empty LayoutInline due to splitting. |
| + // Only resolve our BFC offset if we know that we are non-empty as we may |
| + // need to pass through our margin strut. |
| + if (!items.IsEmpty()) { |
| + NGLogicalOffset bfc_offset = ConstraintSpace().BfcOffset(); |
| + bfc_offset.block_offset += ConstraintSpace().MarginStrut().Sum(); |
| + MaybeUpdateFragmentBfcOffset(ConstraintSpace(), bfc_offset, |
| + &container_builder_); |
| + PositionPendingFloats(bfc_offset.block_offset, &container_builder_, |
| + MutableConstraintSpace()); |
| } |
| if (Node()->IsBidiEnabled()) |
| - BidiReorder(&line_item_chunks); |
| + BidiReorder(item_results); |
| - if (!PlaceItems(line_item_chunks)) |
| + if (!PlaceItems(item_results, break_token)) |
| return false; |
| // Prepare for the next line. |
| - // Move |start| to |last_break_opportunity|, keeping items after |
| - // |last_break_opportunity|. |
| - start_index_ = last_break_opportunity_index_; |
| - start_offset_ = last_break_opportunity_offset_; |
| - // If the offset is at the end of the item, move to the next item. |
| - if (start_offset_ == items[start_index_].EndOffset() && |
| - start_index_ < items.size() - 1) { |
| - start_index_++; |
| - } |
| - DCHECK_GE(end_position_, last_break_opportunity_position_); |
| - end_position_ -= last_break_opportunity_position_; |
| - last_break_opportunity_position_ = LayoutUnit(); |
| -#if DCHECK_IS_ON() |
| - is_bidi_reordered_ = false; |
| -#endif |
| - |
| NGLogicalOffset origin_point = |
| GetOriginPointForFloats(ConstraintSpace(), content_size_); |
| PositionPendingFloats(origin_point.block_offset, &container_builder_, |
| @@ -302,13 +142,7 @@ bool NGInlineLayoutAlgorithm::CreateLineUpToLastBreakOpportunity() { |
| return true; |
| } |
| -void NGInlineLayoutAlgorithm::BidiReorder( |
| - Vector<LineItemChunk, 32>* line_item_chunks) { |
| -#if DCHECK_IS_ON() |
| - DCHECK(!is_bidi_reordered_); |
| - is_bidi_reordered_ = true; |
| -#endif |
| - |
| +void NGInlineLayoutAlgorithm::BidiReorder(NGInlineItemResults* line_items) { |
| // TODO(kojii): UAX#9 L1 is not supported yet. Supporting L1 may change |
| // embedding levels of parts of runs, which requires to split items. |
| // http://unicode.org/reports/tr9/#L1 |
| @@ -319,40 +153,39 @@ void NGInlineLayoutAlgorithm::BidiReorder( |
| // handle the direction of each run, we use |ubidi_reorderVisual()| to reorder |
| // runs instead of characters. |
| Vector<UBiDiLevel, 32> levels; |
| - levels.ReserveInitialCapacity(line_item_chunks->size()); |
| + levels.ReserveInitialCapacity(line_items->size()); |
| const Vector<NGInlineItem>& items = Node()->Items(); |
| - for (const auto& chunk : *line_item_chunks) |
| - levels.push_back(items[chunk.index].BidiLevel()); |
| - Vector<int32_t, 32> indices_in_visual_order(line_item_chunks->size()); |
| + for (const auto& item_result : *line_items) |
| + levels.push_back(items[item_result.item_index].BidiLevel()); |
| + Vector<int32_t, 32> indices_in_visual_order(line_items->size()); |
| NGBidiParagraph::IndicesInVisualOrder(levels, &indices_in_visual_order); |
| - // Reorder |line_item_chunks| in visual order. |
| - Vector<LineItemChunk, 32> line_item_chunks_in_visual_order( |
| - line_item_chunks->size()); |
| + // Reorder to the visual order. |
| + NGInlineItemResults line_items_in_visual_order(line_items->size()); |
| for (unsigned visual_index = 0; visual_index < indices_in_visual_order.size(); |
| visual_index++) { |
| unsigned logical_index = indices_in_visual_order[visual_index]; |
| - line_item_chunks_in_visual_order[visual_index] = |
| - (*line_item_chunks)[logical_index]; |
| + line_items_in_visual_order[visual_index] = |
| + std::move((*line_items)[logical_index]); |
| } |
| // Keep Open before Close in the visual order. |
| HashMap<LayoutObject*, unsigned> first_index; |
| - for (unsigned i = 0; i < line_item_chunks_in_visual_order.size(); i++) { |
| - LineItemChunk& chunk = line_item_chunks_in_visual_order[i]; |
| - const NGInlineItem& item = items[chunk.index]; |
| + for (unsigned i = 0; i < line_items_in_visual_order.size(); i++) { |
| + NGInlineItemResult& item_result = line_items_in_visual_order[i]; |
| + const NGInlineItem& item = items[item_result.item_index]; |
| if (item.Type() != NGInlineItem::kOpenTag && |
| item.Type() != NGInlineItem::kCloseTag) { |
| continue; |
| } |
| auto result = first_index.insert(item.GetLayoutObject(), i); |
| if (!result.is_new_entry && item.Type() == NGInlineItem::kOpenTag) { |
| - std::swap(line_item_chunks_in_visual_order[i], |
| - line_item_chunks_in_visual_order[result.stored_value->value]); |
| + std::swap(line_items_in_visual_order[i], |
| + line_items_in_visual_order[result.stored_value->value]); |
| } |
| } |
| - line_item_chunks->swap(line_item_chunks_in_visual_order); |
| + line_items->swap(line_items_in_visual_order); |
| } |
| // TODO(glebl): Add the support of clearance for inline floats. |
| @@ -401,7 +234,8 @@ void NGInlineLayoutAlgorithm::LayoutAndPositionFloat( |
| } |
| bool NGInlineLayoutAlgorithm::PlaceItems( |
| - const Vector<LineItemChunk, 32>& line_item_chunks) { |
| + NGInlineItemResults* line_items, |
| + RefPtr<NGInlineBreakToken> break_token) { |
| const Vector<NGInlineItem>& items = Node()->Items(); |
| const ComputedStyle& line_style = LineStyle(); |
| @@ -416,19 +250,19 @@ bool NGInlineLayoutAlgorithm::PlaceItems( |
| NGInlineBoxState* box = |
| box_states_.OnBeginPlaceItems(&LineStyle(), baseline_type_); |
| LayoutUnit inline_size; |
| - for (const auto& line_item_chunk : line_item_chunks) { |
| - const NGInlineItem& item = items[line_item_chunk.index]; |
| + for (auto& item_result : *line_items) { |
| + const NGInlineItem& item = items[item_result.item_index]; |
| LayoutUnit line_top; |
| if (item.Type() == NGInlineItem::kText) { |
| DCHECK(item.GetLayoutObject()->IsText()); |
| DCHECK(!box->text_metrics.IsEmpty()); |
| line_top = box->text_top; |
| text_builder.SetSize( |
| - {line_item_chunk.inline_size, box->text_metrics.LineHeight()}); |
| + {item_result.inline_size, box->text_metrics.LineHeight()}); |
| // Take all used fonts into account if 'line-height: normal'. |
| if (box->include_used_fonts) { |
| - box->AccumulateUsedFonts(item, line_item_chunk.start_offset, |
| - line_item_chunk.end_offset, baseline_type_); |
| + box->AccumulateUsedFonts(item, item_result.start_offset, |
| + item_result.end_offset, baseline_type_); |
| } |
| } else if (item.Type() == NGInlineItem::kOpenTag) { |
| box = box_states_.OnOpenTag(item, &line_box, &text_builder); |
| @@ -443,7 +277,8 @@ bool NGInlineLayoutAlgorithm::PlaceItems( |
| box = box_states_.OnCloseTag(item, &line_box, box); |
| continue; |
| } else if (item.Type() == NGInlineItem::kAtomicInline) { |
| - line_top = PlaceAtomicInline(item, &line_box, box, &text_builder); |
| + line_top = |
| + PlaceAtomicInline(item, &item_result, &line_box, box, &text_builder); |
| } else if (item.Type() == NGInlineItem::kOutOfFlowPositioned) { |
| // TODO(layout-dev): Report the correct static position for the out of |
| // flow descendant. We can't do this here yet as it doesn't know the |
| @@ -461,15 +296,15 @@ bool NGInlineLayoutAlgorithm::PlaceItems( |
| } |
| RefPtr<NGPhysicalTextFragment> text_fragment = text_builder.ToTextFragment( |
| - line_item_chunk.index, line_item_chunk.start_offset, |
| - line_item_chunk.end_offset); |
| + item_result.item_index, item_result.start_offset, |
| + item_result.end_offset); |
| NGLogicalOffset logical_offset( |
| inline_size + current_opportunity_.InlineStartOffset() - |
| ConstraintSpace().BfcOffset().inline_offset, |
| line_top); |
| line_box.AddChild(std::move(text_fragment), logical_offset); |
| - inline_size += line_item_chunk.inline_size; |
| + inline_size += item_result.inline_size; |
| } |
| if (line_box.Children().IsEmpty()) { |
| @@ -492,12 +327,7 @@ bool NGInlineLayoutAlgorithm::PlaceItems( |
| return false; |
| } |
| - // If there are more content to consume, create an unfinished break token. |
| - if (last_break_opportunity_index_ != items.size() - 1 || |
| - last_break_opportunity_offset_ != Node()->Text().length()) { |
| - line_box.SetBreakToken(NGInlineBreakToken::Create( |
| - Node(), last_break_opportunity_index_, last_break_opportunity_offset_)); |
| - } |
| + line_box.SetBreakToken(std::move(break_token)); |
| // TODO(kojii): Implement flipped line (vertical-lr). In this case, line_top |
| // and block_start do not match. |
| @@ -516,14 +346,19 @@ bool NGInlineLayoutAlgorithm::PlaceItems( |
| return true; |
| } |
| +// TODO(kojii): Currently, this function does not change item_result, but |
| +// when NG paint is enabled, this will std::move() the LayoutResult. |
| LayoutUnit NGInlineLayoutAlgorithm::PlaceAtomicInline( |
| const NGInlineItem& item, |
| + NGInlineItemResult* item_result, |
| NGLineBoxFragmentBuilder* line_box, |
| NGInlineBoxState* state, |
| NGTextFragmentBuilder* text_builder) { |
| + DCHECK(item_result->layout_result); |
| NGBoxFragment fragment( |
| ConstraintSpace().WritingMode(), |
| - ToNGPhysicalBoxFragment(LayoutItem(item)->PhysicalFragment().Get())); |
| + ToNGPhysicalBoxFragment( |
| + item_result->layout_result->PhysicalFragment().Get())); |
| // TODO(kojii): Margin and border in block progression not implemented yet. |
| LayoutUnit block_size = fragment.BlockSize(); |
| @@ -561,13 +396,14 @@ void NGInlineLayoutAlgorithm::FindNextLayoutOpportunity() { |
| } |
| RefPtr<NGLayoutResult> NGInlineLayoutAlgorithm::Layout() { |
| - // TODO(koji): The relationship of NGInlineLayoutAlgorithm and NGLineBreaker |
| - // should be inverted. |
| - if (!Node()->Text().IsEmpty()) { |
| - // TODO(eae): Does this need the LayoutText::LocaleForLineBreakIterator |
| - // logic to switch the locale based on breaking mode? |
| - NGLineBreaker line_breaker(Node()->Style().Locale()); |
| - line_breaker.BreakLines(this, Node()->Text(), start_offset_); |
| + NGLineBreaker line_breaker(Node(), constraint_space_, BreakToken()); |
| + NGInlineItemResults item_results; |
| + while (true) { |
| + line_breaker.NextLine(&item_results, this); |
|
ikilpatrick
2017/05/11 15:55:55
So it'd be nice if we didn't have the shaper calli
|
| + if (item_results.IsEmpty()) |
| + break; |
| + CreateLine(&item_results, line_breaker.CreateBreakToken()); |
| + item_results.clear(); |
| } |
| // TODO(kojii): Check if the line box width should be content or available. |
| @@ -584,22 +420,4 @@ RefPtr<NGLayoutResult> NGInlineLayoutAlgorithm::Layout() { |
| return container_builder_.ToBoxFragment(); |
| } |
| -MinMaxContentSize NGInlineLayoutAlgorithm::ComputeMinMaxContentSizeByLayout() { |
| - DCHECK_EQ(ConstraintSpace().AvailableSize().inline_size, LayoutUnit()); |
| - DCHECK_EQ(ConstraintSpace().AvailableSize().block_size, NGSizeIndefinite); |
| - if (!Node()->Text().IsEmpty()) { |
| - NGLineBreaker line_breaker(Node()->Style().Locale()); |
| - line_breaker.BreakLines(this, Node()->Text(), start_offset_); |
| - } |
| - MinMaxContentSize sizes; |
| - sizes.min_content = MaxInlineSize(); |
| - |
| - // max-content is the width without any line wrapping. |
| - // TODO(kojii): Implement hard breaks (<br> etc.) to break. |
| - for (const auto& item : Node()->Items()) |
| - sizes.max_content += InlineSize(item); |
| - |
| - return sizes; |
| -} |
| - |
| } // namespace blink |