| 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 fd84b637ba340b1ca13fc583c046e9dc4c7bbbe6..fd846fb72d59ca5699445fc1b45a9b52221a8c80 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
|
| @@ -62,10 +62,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();
|
|
|
| @@ -83,10 +79,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 {
|
| @@ -101,200 +107,20 @@ 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));
|
| - }
|
| -}
|
| -
|
| -void NGInlineLayoutAlgorithm::SetEnd(unsigned index,
|
| - unsigned new_end_offset,
|
| - LayoutUnit inline_size_since_current_end) {
|
| - 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::AvailableWidth() const {
|
| + return current_opportunity_.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;
|
| - }
|
| -
|
| +bool NGInlineLayoutAlgorithm::CreateLine(
|
| + NGInlineItemResults* item_results,
|
| + RefPtr<NGInlineBreakToken> break_token) {
|
| 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(ContainerBfcOffset(), content_size_);
|
| PositionPendingFloats(origin_point.block_offset, &container_builder_,
|
| @@ -303,13 +129,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
|
| @@ -320,40 +140,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.
|
| @@ -402,7 +221,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();
|
| @@ -417,19 +237,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);
|
| @@ -444,7 +264,8 @@ bool NGInlineLayoutAlgorithm::PlaceItems(
|
| box = box_states_.OnCloseTag(item, &line_box, box, baseline_type_);
|
| 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
|
| @@ -462,15 +283,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()) {
|
| @@ -493,12 +314,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.
|
| @@ -517,17 +333,22 @@ 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);
|
| // For replaced elements, inline-block elements, and inline-table elements,
|
| // the height is the height of their margin box.
|
| // https://drafts.csswg.org/css2/visudet.html#line-height
|
| NGBoxFragment fragment(
|
| ConstraintSpace().WritingMode(),
|
| - ToNGPhysicalBoxFragment(LayoutItem(item)->PhysicalFragment().Get()));
|
| + ToNGPhysicalBoxFragment(
|
| + item_result->layout_result->PhysicalFragment().Get()));
|
| DCHECK(item.Style());
|
| NGBoxStrut margins = ComputeMargins(ConstraintSpace(), *item.Style(),
|
| ConstraintSpace().WritingMode(),
|
| @@ -568,13 +389,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);
|
| + 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.
|
| @@ -591,22 +413,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
|
|
|